mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 19:03:42 +00:00
Compare commits
746 Commits
pr_packed_
...
pr_holey_e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f890bef20 | ||
|
|
cfefd1d95b | ||
|
|
d54b5f3ae1 | ||
|
|
5079074b1a | ||
|
|
99a222d63d | ||
|
|
3e9a66599a | ||
|
|
ce6a34597d | ||
|
|
7af4e3eefd | ||
|
|
130eac2f93 | ||
|
|
b8ce11dd9d | ||
|
|
b7c02e9bad | ||
|
|
544c26c0b8 | ||
|
|
c449c72498 | ||
|
|
1eae14a3be | ||
|
|
5abd21dfa5 | ||
|
|
f009ea6c3e | ||
|
|
8f3bdb6951 | ||
|
|
31d64b57d5 | ||
|
|
2be0721236 | ||
|
|
f191ba8ddd | ||
|
|
fb4a82f5cc | ||
|
|
c0fa86872b | ||
|
|
1ef81f4190 | ||
|
|
3962264c35 | ||
|
|
7c65d9e747 | ||
|
|
c4c51d7e78 | ||
|
|
440b55a44a | ||
|
|
02609f1872 | ||
|
|
483389d399 | ||
|
|
cc49bf07fe | ||
|
|
f4497c6158 | ||
|
|
fed0053481 | ||
|
|
9f74712ec6 | ||
|
|
3e2852cb1b | ||
|
|
ceaa7fb4e8 | ||
|
|
d85c0324b7 | ||
|
|
6938fce40c | ||
|
|
e958f4a3cd | ||
|
|
3f48576113 | ||
|
|
87ee9c84cb | ||
|
|
16394c3772 | ||
|
|
41ce86b577 | ||
|
|
51a9ada043 | ||
|
|
40fe59b6ef | ||
|
|
8b9972c8b6 | ||
|
|
cdb750c962 | ||
|
|
c49fb5ac5f | ||
|
|
a77d1cc6c1 | ||
|
|
ff9cae896c | ||
|
|
bf2395a62e | ||
|
|
d60e0211bc | ||
|
|
88da5e8cee | ||
|
|
d73f478bdc | ||
|
|
f90951cc61 | ||
|
|
af6be4f839 | ||
|
|
49e66e80f0 | ||
|
|
4f09675d8a | ||
|
|
76d07e8caa | ||
|
|
5ba279276e | ||
|
|
c8456eacd5 | ||
|
|
34bb37ddda | ||
|
|
08d74a1c27 | ||
|
|
8ea8755cc0 | ||
|
|
87dc1820c0 | ||
|
|
065c4b443b | ||
|
|
0a8f618e2b | ||
|
|
d472022a77 | ||
|
|
e2a294504e | ||
|
|
b527db9ddd | ||
|
|
c6352ce0ab | ||
|
|
53bb0b591a | ||
|
|
a0b3048f3f | ||
|
|
02e3487c9c | ||
|
|
e194c7cc87 | ||
|
|
161b321796 | ||
|
|
9b527a51b8 | ||
|
|
bb93b39b58 | ||
|
|
cd806f9dbe | ||
|
|
08d51e5c88 | ||
|
|
5b5cd7fa67 | ||
|
|
8e57a9f623 | ||
|
|
478773ffb1 | ||
|
|
f4ebabb9b3 | ||
|
|
611b8bbf67 | ||
|
|
7e2df41850 | ||
|
|
9c1e3bf8fb | ||
|
|
30d4f7791d | ||
|
|
6ab532fd0f | ||
|
|
08642ffe34 | ||
|
|
370ee61f6d | ||
|
|
36f8cefa85 | ||
|
|
fbdc9a4c19 | ||
|
|
6bdb069a66 | ||
|
|
7e6fa9e2d6 | ||
|
|
3ce38f2959 | ||
|
|
b6491e7de5 | ||
|
|
97a6f42b56 | ||
|
|
334848f3ae | ||
|
|
aba9361510 | ||
|
|
c22bfe6bc0 | ||
|
|
8e5ed5dbb7 | ||
|
|
7701b3c7e6 | ||
|
|
151b903172 | ||
|
|
56bb451c6d | ||
|
|
638a8bf84d | ||
|
|
ffb993d5bd | ||
|
|
d27c2159fa | ||
|
|
4fbf538b66 | ||
|
|
9d0c0b89f2 | ||
|
|
7a53db6874 | ||
|
|
dd7cecdbd4 | ||
|
|
807840dc5b | ||
|
|
f80a076588 | ||
|
|
db5973361f | ||
|
|
241edaf0c0 | ||
|
|
8752392838 | ||
|
|
a09da96c65 | ||
|
|
3c0446b082 | ||
|
|
c3f64fb127 | ||
|
|
e855019f84 | ||
|
|
1e602490e9 | ||
|
|
71c5a4f72c | ||
|
|
ade500b2cb | ||
|
|
c1e6cf812f | ||
|
|
091fb5057b | ||
|
|
d2fee7dbab | ||
|
|
8080610248 | ||
|
|
6c2f78a19f | ||
|
|
6f5e5811fc | ||
|
|
42a4adb4a5 | ||
|
|
59ceff4f1a | ||
|
|
b50ab7a5c9 | ||
|
|
433b725cbb | ||
|
|
82553384d1 | ||
|
|
8b82f5de38 | ||
|
|
98ec87d65e | ||
|
|
f56568d851 | ||
|
|
b5b7a127fd | ||
|
|
b61a614e8a | ||
|
|
0506d5b973 | ||
|
|
8518cf079f | ||
|
|
d7b1f0a99a | ||
|
|
eea4ce0e2c | ||
|
|
8c9a645bdf | ||
|
|
5dcfd8d7bb | ||
|
|
d966ee3fc3 | ||
|
|
dc100c5caa | ||
|
|
7f0e07492f | ||
|
|
9c2593444a | ||
|
|
525d64fe88 | ||
|
|
8bc8d40778 | ||
|
|
032da90ed1 | ||
|
|
5aaba213d4 | ||
|
|
af9219ada7 | ||
|
|
3d14381473 | ||
|
|
9f359e8d6d | ||
|
|
3f9c269013 | ||
|
|
11e4bd668c | ||
|
|
e7f73bfebe | ||
|
|
c06bb6cc03 | ||
|
|
4d9e5e8b6d | ||
|
|
1ef9a656d2 | ||
|
|
334f96c05a | ||
|
|
0cba752c8a | ||
|
|
4d075dc301 | ||
|
|
97d819a251 | ||
|
|
b961ee69aa | ||
|
|
42df731a2d | ||
|
|
520bbaf384 | ||
|
|
f58cd51fc4 | ||
|
|
897126a711 | ||
|
|
d4098e6ca0 | ||
|
|
918f972369 | ||
|
|
51166ab382 | ||
|
|
40a1ec21d7 | ||
|
|
29a2e25d1e | ||
|
|
a625fab098 | ||
|
|
052ceca3c1 | ||
|
|
10c9ebad93 | ||
|
|
26b86c8f4d | ||
|
|
2ed45eb848 | ||
|
|
73aeac81d1 | ||
|
|
5bcd9a329a | ||
|
|
4352fa2ef0 | ||
|
|
3617d2e077 | ||
|
|
f9c8775783 | ||
|
|
0f5732bc8c | ||
|
|
e0a4876981 | ||
|
|
ecdcffed4b | ||
|
|
58b1f28177 | ||
|
|
73112d64a3 | ||
|
|
b82d7e8ba1 | ||
|
|
ddd83f8d8a | ||
|
|
8e36fb0fec | ||
|
|
d573578b28 | ||
|
|
909f3b8b79 | ||
|
|
d15705e05b | ||
|
|
0b9ed84d32 | ||
|
|
fcba14707a | ||
|
|
482662d198 | ||
|
|
9ace1f97ac | ||
|
|
1d32607575 | ||
|
|
7c5d005510 | ||
|
|
2b699bca53 | ||
|
|
fb93295344 | ||
|
|
9ebfa7973a | ||
|
|
4f32624641 | ||
|
|
dfa482e292 | ||
|
|
82891e6850 | ||
|
|
850f327713 | ||
|
|
38ad336c69 | ||
|
|
a7711d452d | ||
|
|
e2e7790779 | ||
|
|
dfd2987118 | ||
|
|
e2d4791229 | ||
|
|
ccb40024c6 | ||
|
|
f8294ce06e | ||
|
|
b8302cdd97 | ||
|
|
dfab30734b | ||
|
|
569d02e212 | ||
|
|
e39d152b89 | ||
|
|
7e8a650729 | ||
|
|
0cb4cd159e | ||
|
|
7ecb35115b | ||
|
|
b421d0f8ee | ||
|
|
c452275e29 | ||
|
|
a18dcca744 | ||
|
|
49dfc3a0d4 | ||
|
|
16280d4e49 | ||
|
|
a7a8e364ea | ||
|
|
d94e535145 | ||
|
|
514a25c9a2 | ||
|
|
e449813c61 | ||
|
|
93fb219f10 | ||
|
|
1f8da3835f | ||
|
|
1af88a2d20 | ||
|
|
91e8e605d0 | ||
|
|
f0b5bf359e | ||
|
|
1f07fdd2dc | ||
|
|
ebeef1067f | ||
|
|
510ac84518 | ||
|
|
b7d8896d00 | ||
|
|
a5cc33c1d3 | ||
|
|
b211ada273 | ||
|
|
1a7bc6d878 | ||
|
|
e6f6c369ff | ||
|
|
485b414fce | ||
|
|
7695d51fc4 | ||
|
|
e2bed72b72 | ||
|
|
0861dabfa7 | ||
|
|
647c6687f1 | ||
|
|
af5fd3fea3 | ||
|
|
cab3342a2d | ||
|
|
8c3e62e6de | ||
|
|
95b1dda1db | ||
|
|
1f9cac1f5c | ||
|
|
67f9bc2f4b | ||
|
|
d6d28a9c79 | ||
|
|
6d59680217 | ||
|
|
2f402fcb82 | ||
|
|
793baf34ff | ||
|
|
6481482e0e | ||
|
|
2af9ddc286 | ||
|
|
70d057fcc6 | ||
|
|
8d0e853e0a | ||
|
|
d83ff81695 | ||
|
|
41c447b5f4 | ||
|
|
26ed469996 | ||
|
|
4aff12408c | ||
|
|
8ed0a63973 | ||
|
|
aeeccee50a | ||
|
|
e8bf6af0da | ||
|
|
78835562b1 | ||
|
|
3dda60a8ce | ||
|
|
dcc4e07e54 | ||
|
|
0df351bf50 | ||
|
|
d3b6dba616 | ||
|
|
e1be29942e | ||
|
|
cd220fe3e1 | ||
|
|
86d6f71f5a | ||
|
|
5b9ff963c5 | ||
|
|
65b26401bc | ||
|
|
2f127bf99f | ||
|
|
3c4246dd24 | ||
|
|
fc806710cb | ||
|
|
e2a306355c | ||
|
|
5c71fbab30 | ||
|
|
6bc52737b3 | ||
|
|
f29234b40f | ||
|
|
63c884038d | ||
|
|
986ca7dcd4 | ||
|
|
50ed43df42 | ||
|
|
ce4304ce97 | ||
|
|
f80ce139d5 | ||
|
|
d5c7abe3d2 | ||
|
|
8ce58fab26 | ||
|
|
70b3232d3a | ||
|
|
81d8c0fc17 | ||
|
|
91d1933ea2 | ||
|
|
b9c593404c | ||
|
|
556f217b4c | ||
|
|
d31cce557b | ||
|
|
be4d19e562 | ||
|
|
e0197a8380 | ||
|
|
d2d810585c | ||
|
|
80af252025 | ||
|
|
f485973459 | ||
|
|
f7a461a30c | ||
|
|
f796c01e3c | ||
|
|
9bb7e53e7f | ||
|
|
da9f7f671b | ||
|
|
69e0cdb6c0 | ||
|
|
b529f69518 | ||
|
|
aeb3fe9505 | ||
|
|
d408b94063 | ||
|
|
801733f286 | ||
|
|
6f4106bf5d | ||
|
|
c0861142f8 | ||
|
|
d0288d3b57 | ||
|
|
02fb0476ce | ||
|
|
2e9e7f13ee | ||
|
|
bbf6a62c90 | ||
|
|
6bbf9c3117 | ||
|
|
8f4bfda5f4 | ||
|
|
c3120b6121 | ||
|
|
2529f33760 | ||
|
|
464dc99376 | ||
|
|
3bee04d9f3 | ||
|
|
33dc2367e7 | ||
|
|
ddf5a9f6c5 | ||
|
|
05bba15623 | ||
|
|
5340005869 | ||
|
|
8881017c80 | ||
|
|
dcd0793f2b | ||
|
|
e479151473 | ||
|
|
e7f48cdd5c | ||
|
|
1a901bd94e | ||
|
|
4aaabbb5b8 | ||
|
|
6112c51e78 | ||
|
|
2df633180a | ||
|
|
555191a3f0 | ||
|
|
96043bdbb7 | ||
|
|
af3181e75b | ||
|
|
652edb229a | ||
|
|
08c2a1741d | ||
|
|
33e455c986 | ||
|
|
9fcc3b0599 | ||
|
|
a788bae318 | ||
|
|
3eddb64909 | ||
|
|
b7b1003e6a | ||
|
|
e28d2f42e9 | ||
|
|
a610f23060 | ||
|
|
d8391a5c49 | ||
|
|
a2031ec6cf | ||
|
|
712f5be7eb | ||
|
|
f053767132 | ||
|
|
05c74d6844 | ||
|
|
75b512bc6a | ||
|
|
e239968b80 | ||
|
|
cd9ce377f8 | ||
|
|
371f50f66d | ||
|
|
cc696f18c0 | ||
|
|
726195d784 | ||
|
|
21420d8b09 | ||
|
|
1863f6447f | ||
|
|
ccb72c784d | ||
|
|
511ab72342 | ||
|
|
45e21ce8f1 | ||
|
|
76c5f16ac5 | ||
|
|
9d61f2cdd1 | ||
|
|
1fddb61b3b | ||
|
|
3e47725c08 | ||
|
|
8091d76306 | ||
|
|
b3c1fbaf13 | ||
|
|
45b8434c7d | ||
|
|
67ad1ae159 | ||
|
|
cfd8f8b857 | ||
|
|
c71de10608 | ||
|
|
f4b9fcc8ac | ||
|
|
0bc3f5c74c | ||
|
|
f98964d99f | ||
|
|
f5d80ede80 | ||
|
|
5f056f87b2 | ||
|
|
46bb47a444 | ||
|
|
08b82c90f5 | ||
|
|
8b88b5fdd8 | ||
|
|
7b47987341 | ||
|
|
c8072b1eb2 | ||
|
|
0e3330ee96 | ||
|
|
d55cd40642 | ||
|
|
658c9da33e | ||
|
|
5e56f0a356 | ||
|
|
74df699ff1 | ||
|
|
1576563775 | ||
|
|
4091576ab7 | ||
|
|
d61897459d | ||
|
|
815bbf0e73 | ||
|
|
3fc87259bd | ||
|
|
15271dba2f | ||
|
|
2e9422df57 | ||
|
|
24aa92c14f | ||
|
|
031ad957ba | ||
|
|
506c8a5ce8 | ||
|
|
40fc2d0e76 | ||
|
|
efd603eb28 | ||
|
|
79ce3fe6b7 | ||
|
|
aa90d00caf | ||
|
|
2af602a5c8 | ||
|
|
d303c289fa | ||
|
|
9d08f6cee3 | ||
|
|
294b1566e7 | ||
|
|
820e2bee9c | ||
|
|
2864830941 | ||
|
|
dd3a4b2aba | ||
|
|
b534f34e95 | ||
|
|
3aaaed1acf | ||
|
|
40b37c96d2 | ||
|
|
baf3695c76 | ||
|
|
2c56872b5c | ||
|
|
ca5df9ab25 | ||
|
|
67442471ae | ||
|
|
e69eb99a15 | ||
|
|
041098e882 | ||
|
|
93c24fe1c5 | ||
|
|
ae9287c4f3 | ||
|
|
0a058a6b8f | ||
|
|
0806fb0b6f | ||
|
|
68b2e9eb6a | ||
|
|
fce86e5937 | ||
|
|
5fa96ef270 | ||
|
|
b8f6088ac0 | ||
|
|
52cf7dfde0 | ||
|
|
0347536ff2 | ||
|
|
d0b6b9346e | ||
|
|
8be82c36c9 | ||
|
|
4a056b1849 | ||
|
|
a3aea224c9 | ||
|
|
53460f312c | ||
|
|
922f7dfd71 | ||
|
|
3e8f44b232 | ||
|
|
8b39b2df7d | ||
|
|
6df050d6d2 | ||
|
|
34c87de984 | ||
|
|
d4b9c147ab | ||
|
|
07628b0dec | ||
|
|
80e6b35721 | ||
|
|
71515bf278 | ||
|
|
a0d78d259b | ||
|
|
1dbf614858 | ||
|
|
2f7586c066 | ||
|
|
720d0aee5c | ||
|
|
def1fea43a | ||
|
|
25c068c070 | ||
|
|
1bebc236bd | ||
|
|
449106a5a4 | ||
|
|
bb0006598d | ||
|
|
f5cb39289b | ||
|
|
af23bc2941 | ||
|
|
aaf6c408c6 | ||
|
|
706985997e | ||
|
|
9c85f4fd07 | ||
|
|
274762638f | ||
|
|
e9a4d096ab | ||
|
|
2f904535d0 | ||
|
|
96d6eee9bc | ||
|
|
67ea754b7f | ||
|
|
95a7695810 | ||
|
|
f73e03b132 | ||
|
|
d72b848d17 | ||
|
|
d6633ae1da | ||
|
|
4515b2dae2 | ||
|
|
30e552e3d3 | ||
|
|
c73eedfe6e | ||
|
|
911cef1621 | ||
|
|
ea9811a4d2 | ||
|
|
7f2e6a1359 | ||
|
|
a2ee709199 | ||
|
|
a65501325c | ||
|
|
cad8726907 | ||
|
|
aa605da92a | ||
|
|
09043f409f | ||
|
|
9e30b39412 | ||
|
|
4a63186cda | ||
|
|
782b75cc08 | ||
|
|
f420a5a273 | ||
|
|
7dfadb8b4e | ||
|
|
4eed341ba5 | ||
|
|
d98ef312f0 | ||
|
|
89978b48ba | ||
|
|
6d0983398b | ||
|
|
d6a71a1067 | ||
|
|
f7cb0322c2 | ||
|
|
78983f1876 | ||
|
|
0e1df88f7e | ||
|
|
a5f46a72ba | ||
|
|
4974baf7fa | ||
|
|
b82ff5a87b | ||
|
|
2a48182288 | ||
|
|
febc58e036 | ||
|
|
b0e6d28782 | ||
|
|
7974a2208c | ||
|
|
7cbe031909 | ||
|
|
4f5c0efaf2 | ||
|
|
821d0806fe | ||
|
|
2cdc0e913f | ||
|
|
dc3ffb6a71 | ||
|
|
56a3dd57fb | ||
|
|
7cccf36d7b | ||
|
|
d4027f25c4 | ||
|
|
75b9d66582 | ||
|
|
c21bf7f41b | ||
|
|
62a5bb4d0a | ||
|
|
fd379c2f94 | ||
|
|
1bd5a4a99e | ||
|
|
a27542195c | ||
|
|
69b2a6effc | ||
|
|
6d6489a9ab | ||
|
|
3b85c1a2e9 | ||
|
|
b9de2bb4f3 | ||
|
|
6f6e34ebb0 | ||
|
|
a55c15c651 | ||
|
|
7da2ffb751 | ||
|
|
5c843d3d60 | ||
|
|
a1777200c1 | ||
|
|
d51d88700b | ||
|
|
37dba853c9 | ||
|
|
755307be61 | ||
|
|
05a7a48a2b | ||
|
|
84f5060e94 | ||
|
|
ff005ad7dc | ||
|
|
6cc50ec316 | ||
|
|
58cf62451d | ||
|
|
00ac961ab1 | ||
|
|
0c3573e4a0 | ||
|
|
79b17b7c05 | ||
|
|
1660ddf98a | ||
|
|
c759d7abd1 | ||
|
|
04ccd2f4f0 | ||
|
|
680a13a142 | ||
|
|
21a161a535 | ||
|
|
1fbb67ffe9 | ||
|
|
b5f2eafed1 | ||
|
|
fe55dcb2be | ||
|
|
651fdbe586 | ||
|
|
d0dc4ac22f | ||
|
|
22d2cf2175 | ||
|
|
ecc6a1d92b | ||
|
|
6c35a36043 | ||
|
|
4ff35585f8 | ||
|
|
6d362e0ffe | ||
|
|
61e04ba0ed | ||
|
|
793cee4de1 | ||
|
|
9dda7ff7bc | ||
|
|
771369237c | ||
|
|
baec1955b5 | ||
|
|
21771765a2 | ||
|
|
3a55bae53f | ||
|
|
fcee829d85 | ||
|
|
8b895afcb5 | ||
|
|
09682ac7f1 | ||
|
|
a6595e5b49 | ||
|
|
9ff0333a4c | ||
|
|
24e5b21c90 | ||
|
|
f223f016f3 | ||
|
|
7de4ace949 | ||
|
|
6a5aa00701 | ||
|
|
79a65da22a | ||
|
|
29a7d60acb | ||
|
|
ca28c256f3 | ||
|
|
ebcfd96ae1 | ||
|
|
cd22560af5 | ||
|
|
7cd1777218 | ||
|
|
a93c5d79b9 | ||
|
|
c10f84b9d7 | ||
|
|
4a548deb08 | ||
|
|
d77ea07837 | ||
|
|
bf865fa75a | ||
|
|
d91297a330 | ||
|
|
9ff15da426 | ||
|
|
e265b3dfdd | ||
|
|
99f4cfa438 | ||
|
|
6908fb4011 | ||
|
|
080b0a03bd | ||
|
|
c314155fb3 | ||
|
|
f69809bb17 | ||
|
|
c948ab9b85 | ||
|
|
4bf323d6c4 | ||
|
|
538603e01d | ||
|
|
1ebdcb3bca | ||
|
|
d27061f6da | ||
|
|
71de7fca9e | ||
|
|
c8af0996fd | ||
|
|
5e55e16ad8 | ||
|
|
4789af71fe | ||
|
|
fc853cb726 | ||
|
|
11ead19bc1 | ||
|
|
aa0d8e9cfe | ||
|
|
b7b1313d21 | ||
|
|
d7e77b330f | ||
|
|
da7670c8e0 | ||
|
|
1244ffbf39 | ||
|
|
5e8cd318ef | ||
|
|
770f8d5513 | ||
|
|
d3af51e3ce | ||
|
|
ea7c2a4409 | ||
|
|
d53a9cf288 | ||
|
|
540b414c86 | ||
|
|
f09c549d42 | ||
|
|
7e88091de3 | ||
|
|
4c250d69a8 | ||
|
|
69ea1336fb | ||
|
|
09dcff71c8 | ||
|
|
0d53b6e027 | ||
|
|
4ef06a5cc5 | ||
|
|
446501b53b | ||
|
|
cb7bcae7f7 | ||
|
|
79f5a74408 | ||
|
|
04da0a6028 | ||
|
|
ac0179ced9 | ||
|
|
2311049b27 | ||
|
|
832ba815d1 | ||
|
|
12b90d7c07 | ||
|
|
e38cbd3c84 | ||
|
|
ab18962085 | ||
|
|
6320b0cd5b | ||
|
|
eed9cb0d3f | ||
|
|
1befb8d4a3 | ||
|
|
6336d2681b | ||
|
|
dda638c1ba | ||
|
|
43274bfb92 | ||
|
|
26107e931c | ||
|
|
34719cad9d | ||
|
|
8bd0422767 | ||
|
|
58813a3b2e | ||
|
|
a4dff1a03e | ||
|
|
2e4d344b43 | ||
|
|
f7c11a8978 | ||
|
|
a354b18fe1 | ||
|
|
0ffc0493a3 | ||
|
|
253fafb305 | ||
|
|
e96fad1eed | ||
|
|
995081b56a | ||
|
|
d43a5954c5 | ||
|
|
298ada3412 | ||
|
|
06b25bd2c4 | ||
|
|
2a046e6487 | ||
|
|
a33e2b76ae | ||
|
|
ddc47fecca | ||
|
|
c5b206d4ac | ||
|
|
630c304a2d | ||
|
|
0c890ff9a7 | ||
|
|
7a0069a134 | ||
|
|
20043ea09e | ||
|
|
b215ec3735 | ||
|
|
e0e698be9a | ||
|
|
fbf9e94145 | ||
|
|
b9b24e192a | ||
|
|
a64aa51fe9 | ||
|
|
f0e1eef65e | ||
|
|
4954469259 | ||
|
|
1d59e1cbb6 | ||
|
|
2a2474d395 | ||
|
|
d164f87fbc | ||
|
|
c0aa951ee0 | ||
|
|
6126a0bf46 | ||
|
|
d76ea4f323 | ||
|
|
c97a20ce49 | ||
|
|
12b9680291 | ||
|
|
cb156648d6 | ||
|
|
39629a1adc | ||
|
|
bd063113ec | ||
|
|
469a6044c0 | ||
|
|
c1f91c26a5 | ||
|
|
ccf90f5bcb | ||
|
|
02871c74de | ||
|
|
c770c0ad08 | ||
|
|
925dc5c131 | ||
|
|
0db742df7c | ||
|
|
759b8e46be | ||
|
|
03973dca30 | ||
|
|
881fbb8f81 | ||
|
|
c83a9c4c5c | ||
|
|
7b50d05d6b | ||
|
|
2d2a7f2347 | ||
|
|
1c287fb960 | ||
|
|
9ca646acd4 | ||
|
|
31ee75f10e | ||
|
|
2f5cfd6829 | ||
|
|
fd1e62a7e2 | ||
|
|
97f5474545 | ||
|
|
24f5dfbed2 | ||
|
|
3a103669d1 | ||
|
|
6aa54d533b | ||
|
|
f765898a75 | ||
|
|
ddb31ce968 | ||
|
|
494c24a7ce | ||
|
|
2b7c47b122 | ||
|
|
9de74b7097 | ||
|
|
ad5b5e3ec0 | ||
|
|
fe48de4406 | ||
|
|
cd946084ab | ||
|
|
648f82c2ed | ||
|
|
b7a275da1d | ||
|
|
284a80e96d | ||
|
|
c11b3f3fc7 | ||
|
|
a5186a9d8b | ||
|
|
f6aeca5765 | ||
|
|
58b36bd85e | ||
|
|
6d7ab08dee | ||
|
|
570deb10d3 | ||
|
|
22ba5abd63 | ||
|
|
173b8a8c58 | ||
|
|
9092244f87 | ||
|
|
e53a2ed19b | ||
|
|
5c5e7a9b6e | ||
|
|
96c165304d | ||
|
|
732f7752a9 | ||
|
|
14f86b3965 | ||
|
|
dc46350fa1 | ||
|
|
3f5016f60e | ||
|
|
4faa15f3ad | ||
|
|
1dcc364cd2 | ||
|
|
ce75042a9d | ||
|
|
841d30a213 | ||
|
|
05df263b84 | ||
|
|
051a536275 | ||
|
|
903b1b1016 | ||
|
|
fe3039410f | ||
|
|
96aba18963 | ||
|
|
43ee545789 | ||
|
|
a557e5a341 | ||
|
|
27abcdd57f | ||
|
|
d78ccbc27c | ||
|
|
288d5c4ac3 | ||
|
|
4202b606b1 | ||
|
|
c88894bf76 | ||
|
|
179897e55f | ||
|
|
9f74baa49d | ||
|
|
56ed4e0bb9 | ||
|
|
828cd58d8a | ||
|
|
8096fa45bd | ||
|
|
948fc29bb2 | ||
|
|
2c83f94544 | ||
|
|
832d896cda | ||
|
|
830153323a | ||
|
|
2bef08774f |
49
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
49
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -7,25 +7,26 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
- **Please provide a minimal code example that reproduces the Bug!** :bug:
|
||||
Reports with a reproducible example and descriptive detailed information will likely receive fixes faster.
|
||||
Please provide a minimal code example that reproduces the bug if possible.
|
||||
Reports with a reproducible example or detailed information will likely receive fixes faster.
|
||||
|
||||
- type: textarea
|
||||
id: nim-version
|
||||
attributes:
|
||||
label: Nim Version
|
||||
description: |
|
||||
Can be obtained from `nim -v` on the command line along with the OS/architecture.
|
||||
For development versions, including the commit hash may help.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
Use DETAILED DESCRIPTIVE information about the problem.
|
||||
Here, you go into more details about your Bug report. This section can be a few paragraphs long.
|
||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: nim-version
|
||||
attributes:
|
||||
label: Nim Version
|
||||
description: Copy and paste the output of `nim -v` on the command line. For development versions, make sure to include the commit hash.
|
||||
Describe the problem. Code example can be given here.
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -34,7 +35,7 @@ body:
|
||||
attributes:
|
||||
label: Current Output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
render: text
|
||||
|
||||
- type: textarea
|
||||
@@ -42,14 +43,14 @@ body:
|
||||
attributes:
|
||||
label: Expected Output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
render: text
|
||||
|
||||
- type: textarea
|
||||
id: possible-solution
|
||||
id: known-workarounds
|
||||
attributes:
|
||||
label: Possible Solution
|
||||
description: Propose a possible solution.
|
||||
label: Known Workarounds
|
||||
description: Provide any known workarounds.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
@@ -64,13 +65,9 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
- Thanks for your contributions!, your Bug report will receive feedback from the community soon...
|
||||
- Please check whether the problem still exists in the devel branch, see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
|
||||
- Please check whether the problem still exists in the devel branch, which can be installed via [choosenim](https://github.com/nim-lang/choosenim/), [nightlies](https://github.com/nim-lang/nightlies/), or by [creating a temporary build of the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler-building-an-instrumented-compiler).
|
||||
- Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
|
||||
- If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler)
|
||||
which should give more context on a compiler crash.
|
||||
- If it's a regression, you can help us by identifying which version introduced the bug,
|
||||
see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions),
|
||||
or at least try known past releases (e.g. `choosenim 2.0.0`). The Nim repo also supports online bisecting
|
||||
via making a comment, which contains a code block starting by `!nim c`, `!nim js` etc. , see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
|
||||
- If it's a pre-existing compiler bug, see [debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler) which should give more context on a compiler crash.
|
||||
- If it's a regression, you can help us by identifying which version introduced the bug by [bisecting](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions) or at least trying known past releases (e.g. `choosenim 2.0.0`).
|
||||
The Nim repo also supports bisecting in issue comments by adding `!nim c`, `!nim js` etc. before a code block, see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
|
||||
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
|
||||
|
||||
11
.github/workflows/bisects.yml
vendored
11
.github/workflows/bisects.yml
vendored
@@ -15,9 +15,16 @@ jobs:
|
||||
name: ${{ matrix.platform }}-bisects
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: jiro4989/setup-nim-action@v1
|
||||
- name: Install OpenSSL (Windows)
|
||||
if: |
|
||||
runner.os == 'Windows'
|
||||
run: choco install openssl.light --version=1.1.1.0 # OpenSSL 3.x removed SSL_library_init
|
||||
shell: 'powershell'
|
||||
|
||||
# v2 wont work here, because uses "hardcoded" nim versions, action "dynamically" finds version with bug.
|
||||
- uses: jiro4989/setup-nim-action@v1
|
||||
with:
|
||||
nim-version: 'devel'
|
||||
|
||||
|
||||
115
.github/workflows/ci_bench.yml
vendored
115
.github/workflows/ci_bench.yml
vendored
@@ -1,115 +0,0 @@
|
||||
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@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt-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@v4
|
||||
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
|
||||
6
.github/workflows/ci_docs.yml
vendored
6
.github/workflows/ci_docs.yml
vendored
@@ -41,11 +41,11 @@ jobs:
|
||||
target: [linux, windows, osx]
|
||||
include:
|
||||
- target: linux
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
os: windows-latest
|
||||
- target: osx
|
||||
os: macos-12
|
||||
os: macos-13
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
16
.github/workflows/ci_packages.yml
vendored
16
.github/workflows/ci_packages.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
- 'version-2-2'
|
||||
- 'version-2-0'
|
||||
- 'version-1-6'
|
||||
- 'version-1-2'
|
||||
@@ -17,9 +18,13 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-12]
|
||||
cpu: [amd64]
|
||||
os: [ubuntu-latest, macos-14]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
cpu: amd64
|
||||
- os: macos-14
|
||||
cpu: arm64
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
@@ -40,14 +45,15 @@ jobs:
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt-fast update -qq
|
||||
sudo apt-get update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
sudo apt-get install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
valgrind libc6-dbg libblas-dev liblapack-dev libpcre3 xorg-dev
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
# XXX can't find boehm and gtk on macos 13
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
|
||||
17
.github/workflows/ci_publish.yml
vendored
17
.github/workflows/ci_publish.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
os: [ubuntu-22.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -21,10 +21,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
- name: 'Install node.js'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version: ''
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
@@ -34,17 +34,6 @@ jobs:
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-20.04'
|
||||
vmImage: 'ubuntu-24.04'
|
||||
CPU: amd64
|
||||
# regularly breaks, refs bug #17325
|
||||
# Linux_i386:
|
||||
@@ -29,23 +29,23 @@ jobs:
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-12'
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-12'
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
vmImage: 'windows-2019'
|
||||
vmImage: 'windows-2025'
|
||||
CPU: amd64
|
||||
# see also: `NIM_TEST_PACKAGES`
|
||||
NIM_TESTAMENT_BATCH: "0_3"
|
||||
Windows_amd64_batch1_3:
|
||||
vmImage: 'windows-2019'
|
||||
vmImage: 'windows-2025'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "1_3"
|
||||
Windows_amd64_batch2_3:
|
||||
vmImage: 'windows-2019'
|
||||
vmImage: 'windows-2025'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "2_3"
|
||||
|
||||
@@ -80,10 +80,12 @@ jobs:
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo apt-fast update -qq
|
||||
echo_run sudo add-apt-repository universe
|
||||
echo_run sudo apt-get update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
echo_run sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
echo_run sudo apt-get install --no-install-recommends -yq \
|
||||
gcc-14 g++-14 libpcre3 liblapack-dev libpcre3 liblapack-dev libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
echo_run sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
displayName: 'Install dependencies (amd64 Linux)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
|
||||
|
||||
@@ -100,15 +102,16 @@ jobs:
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
|
||||
# echo_run sudo apt-fast update -qq
|
||||
echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
|
||||
# echo_run sudo apt-get update -qq
|
||||
echo_run sudo apt-get update -qq || echo "failed, see bug #17343"
|
||||
# `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
|
||||
# `could not load: libffi.so` during dynamic loading.
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
|
||||
echo_run sudo apt-get install --no-install-recommends --allow-downgrades -yq \
|
||||
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
|
||||
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
|
||||
|
||||
|
||||
cat << EOF > bin/gcc
|
||||
#!/bin/bash
|
||||
|
||||
@@ -130,6 +133,7 @@ jobs:
|
||||
- bash: brew install boehmgc make sfml
|
||||
displayName: 'Install dependencies (OSX)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
|
||||
# XXX can't find boehm on macos 13
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
|
||||
@@ -24,6 +24,6 @@ if not exist %nim_csources% (
|
||||
cd ..
|
||||
copy /y bin\nim.exe %nim_csources%
|
||||
)
|
||||
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
|
||||
162
changelog.md
162
changelog.md
@@ -1,115 +1,107 @@
|
||||
# v2.2.0 - yyyy-mm-dd
|
||||
# v2.x.x - yyyy-mm-dd
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to be in the same module where their type has been defined.
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
|
||||
```nim
|
||||
var a, b: int
|
||||
(a, b) = (1, 2, 3, 4)
|
||||
```
|
||||
will no longer compile.
|
||||
- `internalNew` is removed from system, use `new` instead.
|
||||
- `-d:nimPreviewHashRef` becomes the default. `hashes.hash` can now support `object` and `ref` (can be overloaded in user code).
|
||||
|
||||
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
|
||||
- `-d:nimPreviewFloatRoundtrip` becomes the default. `system.addFloat` and `system.$` now can produce string representations of
|
||||
floating point numbers that are minimal in size and possess round-trip and correct
|
||||
rounding guarantees (via the
|
||||
[Dragonbox](https://raw.githubusercontent.com/jk-jeon/dragonbox/master/other_files/Dragonbox.pdf) algorithm). Use `-d:nimLegacySprintf` to emulate old behaviors.
|
||||
|
||||
- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behavior.
|
||||
- The `default` parameter of `tables.getOrDefault` has been renamed to `def` to
|
||||
avoid conflicts with `system.default`, so named argument usage for this
|
||||
parameter like `getOrDefault(..., default = ...)` will have to be changed.
|
||||
|
||||
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
|
||||
|
||||
- Unknown warnings and hints now gives warnings `warnUnknownNotes` instead of
|
||||
errors.
|
||||
|
||||
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
|
||||
|
||||
- The bare `except:` now panics on `Defect`. Use `except Exception:` or `except Defect:` to catch `Defect`. `--legacy:noPanicOnExcept` is provided for a transition period.
|
||||
|
||||
- With `-d:nimPreviewCStringComparisons`, comparsions (`<`, `>`, `<=`, `>=`) between cstrings switch from reference semantics to value semantics like `==` and `!=`.
|
||||
|
||||
- `std/parsesql` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
|
||||
- With `-d:nimPreviewDuplicateModuleError`, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Use `import foo as foo1` (or other aliases) to avoid collisions.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- Changed `std/osfiles.copyFile` to allow to specify `bufferSize` instead of a hardcoded one.
|
||||
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
|
||||
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- 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.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
- `nimPreviewHashFarm` has been added to `lib/pure/hashes.nim` to default to a
|
||||
64-bit string `Hash` (based upon Google's Farm Hash) which is also faster than
|
||||
the present one. At present, this is incompatible with `--jsbigint=off` mode.
|
||||
- `setutils.symmetricDifference` along with its operator version
|
||||
`` setutils.`-+-` `` and in-place version `setutils.toggle` have been added
|
||||
to more efficiently calculate the symmetric difference of bitsets.
|
||||
- `strutils.multiReplace` overload for character set replacements in a single pass.
|
||||
Useful for string sanitation. Follows existing multiReplace semantics.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
- `std/files` adds:
|
||||
- Exports `CopyFlag` enum and `FilePermission` type for fine-grained control of file operations
|
||||
- New file operation procs with `Path` support:
|
||||
- `getFilePermissions`, `setFilePermissions` for managing permissions
|
||||
- `tryRemoveFile` for file deletion
|
||||
- `copyFile` with configurable buffer size and symlink handling
|
||||
- `copyFileWithPermissions` to preserve file attributes
|
||||
- `copyFileToDir` for copying files into directories
|
||||
|
||||
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
|
||||
- `std/dirs` adds:
|
||||
- New directory operation procs with `Path` support:
|
||||
- `copyDir` with special file handling options
|
||||
- `copyDirWithPermissions` to recursively preserve attributes
|
||||
|
||||
[//]: # "Removals:"
|
||||
- `system.setLenUninit` now supports refc, JS and VM backends.
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
|
||||
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
|
||||
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
|
||||
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
|
||||
|
||||
## Language changes
|
||||
|
||||
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
|
||||
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.htm#constructor-initializer
|
||||
- `member` can be used to attach a procedure to a C++ type.
|
||||
- C++ `constructor` now reuses `result` instead creating `this`.
|
||||
|
||||
- Tuple unpacking changes:
|
||||
- Tuple unpacking assignment now supports using underscores to discard values.
|
||||
```nim
|
||||
var a, c: int
|
||||
(a, _, c) = (1, 2, 3)
|
||||
```
|
||||
- Tuple unpacking variable declarations now support type annotations, but
|
||||
only for the entire tuple.
|
||||
```nim
|
||||
let (a, b): (int, int) = (1, 2)
|
||||
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
|
||||
```
|
||||
|
||||
- An experimental option `genericsOpenSym` has been added to allow captured
|
||||
symbols in generic routine bodies to be replaced by symbols injected locally
|
||||
by templates/macros at instantiation time. `bind` may be used to keep the
|
||||
captured symbols over the injected ones regardless of enabling the option.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`genericsOpenSym` needs to be enabled, and a warning is given in the case
|
||||
where an injected symbol would replace a captured symbol not bound by `bind`
|
||||
and the experimental switch isn't enabled.
|
||||
- An experimental option `--experimental:typeBoundOps` has been added that
|
||||
implements the RFC https://github.com/nim-lang/RFCs/issues/380.
|
||||
This makes the behavior of interfaces like `hash`, `$`, `==` etc. more
|
||||
reliable for nominal types across indirect/restricted imports.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped) =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
# objs.nim
|
||||
import std/hashes
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:genericsOpenSym`
|
||||
echo old[int]() # "captured"
|
||||
type
|
||||
Obj* = object
|
||||
x*, y*: int
|
||||
z*: string # to be ignored for equality
|
||||
|
||||
{.experimental: "genericsOpenSym".}
|
||||
proc `==`*(a, b: Obj): bool =
|
||||
a.x == b.x and a.y == b.y
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
proc hash*(a: Obj): Hash =
|
||||
$!(hash(a.x) &! hash(a.y))
|
||||
```
|
||||
|
||||
```nim
|
||||
# main.nim
|
||||
{.experimental: "typeBoundOps".}
|
||||
from objs import Obj # objs.hash, objs.`==` not imported
|
||||
import std/tables
|
||||
|
||||
var t: Table[Obj, int]
|
||||
t[Obj(x: 3, y: 4, z: "debug")] = 34
|
||||
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
|
||||
```
|
||||
|
||||
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#typeminusbound-overloads)
|
||||
for more information.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.
|
||||
|
||||
- Added `--stdinfile` flag to name of the file used when running program from stdin (defaults to `stdinfile.nim`)
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
- [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
|
||||
|
||||
- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
|
||||
now produces a `CountTable` with values correspoding to the number of occurrences
|
||||
now produces a `CountTable` with values corresponding to the number of occurrences
|
||||
of the key in the input. It used to produce a table with all values set to `1`.
|
||||
|
||||
Counting occurrences in a sequence used to be:
|
||||
@@ -339,7 +339,7 @@ for i in a..b:
|
||||
|
||||
- Fixed "ReraiseError when using try/except within finally block"
|
||||
([#5871](https://github.com/nim-lang/Nim/issues/5871))
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
- Fixed "Range type inference leads to counter-intuitive behaviour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "JSON % operator can fail in extern procs with dynamic types"
|
||||
([#6385](https://github.com/nim-lang/Nim/issues/6385))
|
||||
@@ -403,7 +403,7 @@ for i in a..b:
|
||||
([#6589](https://github.com/nim-lang/Nim/issues/6589))
|
||||
- Fixed "Generated c code calls function twice"
|
||||
([#6292](https://github.com/nim-lang/Nim/issues/6292))
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
- Fixed "Range type inference leads to counter-intuitive behaviour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "New backward indexing is too limited"
|
||||
([#6631](https://github.com/nim-lang/Nim/issues/6631))
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
- We removed `unicode.Rune16` without any deprecation period as the name
|
||||
was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
|
||||
and we didn't find any usages of it in the wild. If you still need it, add this
|
||||
and we didn't find any usage of it in the wild. If you still need it, add this
|
||||
piece of code to your project:
|
||||
```nim
|
||||
type
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
|
||||
* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
|
||||
* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
|
||||
* Fix how `relativePath` handle case sensitiviy
|
||||
* Fix how `relativePath` handles case sensitivity
|
||||
* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
|
||||
* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
|
||||
* VM: no special casing for big endian machines
|
||||
@@ -43,7 +43,7 @@
|
||||
* threadpool: fix link in docs (#12258)
|
||||
* Fix spellings (#12277)
|
||||
* fix #12278, don't expose internal PCRE documentation
|
||||
* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
|
||||
* Fixed "Documentation of quitprocs is wrong" [#12279](https://github.com/nim-lang/Nim/issues/12279)
|
||||
* Fix typo in docs
|
||||
* Fix reference to parseSpec proc in readme
|
||||
* [doc/tut1] removed discard discussion in comments
|
||||
|
||||
@@ -169,7 +169,7 @@ echo f
|
||||
- The Nim compiler now supports a new pragma called ``.localPassc`` to
|
||||
pass specific compiler options to the C(++) backend for the C(++) file
|
||||
that was produced from the current Nim module.
|
||||
- The compiler now inferes "sink parameters". To disable this for a specific routine,
|
||||
- The compiler now infers "sink parameters". To disable this for a specific routine,
|
||||
annotate it with `.nosinks`. To disable it for a section of code, use
|
||||
`{.push sinkInference: off.}`...`{.pop.}`.
|
||||
- The compiler now supports a new switch `--panics:on` that turns runtime
|
||||
@@ -261,7 +261,7 @@ echo f
|
||||
([#12812](https://github.com/nim-lang/Nim/issues/12812))
|
||||
- Fixed "Produce static/const initializations for variables when possible"
|
||||
([#12216](https://github.com/nim-lang/Nim/issues/12216))
|
||||
- Fixed "Assigning descriminator field leads to internal assert with --gc:destructors"
|
||||
- Fixed "Assigning discriminator field leads to internal assert with --gc:destructors"
|
||||
([#12821](https://github.com/nim-lang/Nim/issues/12821))
|
||||
- Fixed "nimsuggest `use` command does not return all instances of symbol"
|
||||
([#12832](https://github.com/nim-lang/Nim/issues/12832))
|
||||
|
||||
@@ -283,7 +283,7 @@ The definition of `"strictFuncs"` was changed.
|
||||
The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
|
||||
The new definition is: "A store to a ref/ptr deref is forbidden."
|
||||
|
||||
This new definition is much easier to understand, the price is some expressitivity. The following code used to be
|
||||
This new definition is much easier to understand, the price is some expressiveness. The following code used to be
|
||||
accepted:
|
||||
|
||||
```nim
|
||||
|
||||
@@ -1,12 +1,248 @@
|
||||
# v2.2.0 - 2023-mm-dd
|
||||
# v2.2.0 - 2024-10-02
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` is out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backward compatibility.
|
||||
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
|
||||
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to the same module where their type has been defined.
|
||||
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
|
||||
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
|
||||
```nim
|
||||
var a, b: int
|
||||
(a, b) = (1, 2, 3, 4)
|
||||
```
|
||||
will no longer compile.
|
||||
|
||||
- `internalNew` is removed from the `system` module, use `new` instead.
|
||||
|
||||
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
|
||||
|
||||
- The JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors.
|
||||
|
||||
- The JS backend now supports closure iterators.
|
||||
|
||||
- `owner` in `std/macros` is deprecated.
|
||||
|
||||
- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
|
||||
Previously; in templates they would error immediately at the template definition,
|
||||
and in generic procs a type symbol would arbitrarily be captured, losing the
|
||||
information of the other symbols. This means that generic code can now give
|
||||
errors for ambiguous type symbols, and macros operating on generic proc AST
|
||||
may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
|
||||
|
||||
- Partial generic instantiation of routines is no longer allowed. Previously
|
||||
it compiled in niche situations due to bugs in the compiler.
|
||||
|
||||
```nim
|
||||
proc foo[T, U](x: T, y: U) = echo (x, y)
|
||||
proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
|
||||
|
||||
proc bar[T]() =
|
||||
foo[float](1, "abc")
|
||||
|
||||
bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
|
||||
```
|
||||
|
||||
- `const` values now open a new scope for each constant, meaning symbols
|
||||
declared in them can no longer be used outside or in the value of
|
||||
other constants.
|
||||
|
||||
```nim
|
||||
const foo = (var a = 1; a)
|
||||
const bar = a # error
|
||||
let baz = a # error
|
||||
```
|
||||
|
||||
- The following POSIX wrappers have had their types changed from signed to
|
||||
unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
|
||||
- `Gid` (was `int32`, is now `uint32`)
|
||||
- `Uid` (was `int32`, is now `uint32`)
|
||||
- `Dev` (was `int32`, is now `uint32` on FreeBSD)
|
||||
- `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
|
||||
- `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
|
||||
(were `int32`, are now `uint32`)
|
||||
- `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
|
||||
|
||||
- The `Atomic[T]` type on C++ now uses C11 primitives by default instead of
|
||||
`std::atomic`. To use `std::atomic` instead, `-d:nimUseCppAtomics` can be defined.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
|
||||
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
|
||||
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- Added `newStringUninit` to the `system` module, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to the `system` module, which doesn't initialize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
|
||||
convert a value with a range type to its base type.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
|
||||
on Google's Farm Hash) which is also often faster than the present one. Define
|
||||
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
|
||||
produces the old values. This may impact your automated tests if they depend
|
||||
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
|
||||
is often an easy workaround.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
|
||||
|
||||
[//]: # "Removals:"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
|
||||
|
||||
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
|
||||
|
||||
- `member` can be used to attach a procedure to a C++ type.
|
||||
|
||||
- Inside a C++ constructor, `result` can be used to access the created object rather than `this`.
|
||||
|
||||
- Tuple unpacking changes:
|
||||
- Tuple unpacking assignment now supports using underscores to discard values.
|
||||
```nim
|
||||
var a, c: int
|
||||
(a, _, c) = (1, 2, 3)
|
||||
```
|
||||
- Tuple unpacking variable declarations now support type annotations, but
|
||||
only for the entire tuple.
|
||||
```nim
|
||||
let (a, b): (int, int) = (1, 2)
|
||||
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
|
||||
```
|
||||
|
||||
- The experimental option `--experimental:openSym` has been added to allow
|
||||
captured symbols in generic routine and template bodies respectively to be
|
||||
replaced by symbols injected locally by templates/macros at instantiation
|
||||
time. `bind` may be used to keep the captured symbols over the injected ones
|
||||
regardless of enabling the option, but other methods like renaming the
|
||||
captured symbols should be used instead so that the code is not affected by
|
||||
context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert barTempl() == "injected" # previously it would be "captured"
|
||||
|
||||
template bazTempl(): string =
|
||||
bind value
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert bazTempl() == "captured"
|
||||
```
|
||||
|
||||
This option also generates a new node kind `nnkOpenSym` which contains
|
||||
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
|
||||
modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
Another experimental switch `genericsOpenSym` exists that enables this behavior
|
||||
at instantiation time, meaning templates etc can enable it specifically when
|
||||
they are being called. However this does not generate `nnkOpenSym` nodes
|
||||
(unless the other switch is enabled) and so doesn't reflect the regular
|
||||
behavior of the switch.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
echo bar[int]() # "injected"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
var res: string
|
||||
foo(123):
|
||||
res = value
|
||||
res
|
||||
assert barTempl() == "injected"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# v1.xx.x - yyyy-mm-dd
|
||||
# v2.xx.x - yyyy-mm-dd
|
||||
|
||||
This is an example file.
|
||||
The changes should go to changelog.md!
|
||||
|
||||
175
compiler/ast.nim
175
compiler/ast.nim
@@ -41,7 +41,7 @@ type
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
TSymFlag* = enum # 52 flags!
|
||||
TSymFlag* = enum # 63 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
|
||||
@@ -93,7 +93,7 @@ type
|
||||
sfNamedParamCall, # symbol needs named parameter call syntax in target
|
||||
# language; for interfacing with Objective C
|
||||
sfDiscardable, # returned value may be discarded implicitly
|
||||
sfOverridden, # 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
|
||||
@@ -127,24 +127,24 @@ type
|
||||
sfMember # proc is a C++ member of a type
|
||||
sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl
|
||||
sfWasGenSym # symbol was 'gensym'ed
|
||||
sfForceLift # variable has to be lifted into closure environment
|
||||
|
||||
sfDirty # template is not hygienic (old styled template) module,
|
||||
# compiled from a dirty-buffer
|
||||
sfCustomPragma # symbol is custom pragma template
|
||||
sfBase, # a base method
|
||||
sfGoto # var is used for 'goto' code generation
|
||||
sfAnon, # symbol name that was generated by the compiler
|
||||
# the compiler will avoid printing such names
|
||||
# in user messages.
|
||||
sfAllUntyped # macro or template is immediately expanded in a generic context
|
||||
sfTemplateRedefinition # symbol is a redefinition of an earlier template
|
||||
|
||||
TSymFlags* = set[TSymFlag]
|
||||
|
||||
const
|
||||
sfNoInit* = sfMainModule # don't generate code to init the variable
|
||||
|
||||
sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
|
||||
# in a generic context
|
||||
|
||||
sfDirty* = sfPure
|
||||
# template is not hygienic (old styled template)
|
||||
# module, compiled from a dirty-buffer
|
||||
|
||||
sfAnon* = sfDiscardable
|
||||
# symbol name that was generated by the compiler
|
||||
# the compiler will avoid printing such names
|
||||
# in user messages.
|
||||
|
||||
sfNoForward* = sfRegister
|
||||
# forward declarations are not required (per module)
|
||||
sfReorder* = sfForward
|
||||
@@ -153,12 +153,8 @@ const
|
||||
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
|
||||
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
|
||||
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
|
||||
sfCustomPragma* = sfRegister # symbol is custom pragma template
|
||||
sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template
|
||||
sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition
|
||||
|
||||
const
|
||||
@@ -218,7 +214,8 @@ type
|
||||
tyUncheckedArray
|
||||
# An array with boundaries [0,+∞]
|
||||
|
||||
tyProxy # used as errornous type (for idetools)
|
||||
tyError # used as erroneous type (for idetools)
|
||||
# as an erroneous node should match everything
|
||||
|
||||
tyBuiltInTypeClass
|
||||
# Type such as the catch-all object, tuple, seq, etc
|
||||
@@ -280,13 +277,9 @@ static:
|
||||
const
|
||||
tyPureObject* = tyTuple
|
||||
GcTypeKinds* = {tyRef, tySequence, tyString}
|
||||
tyError* = tyProxy # as an errornous node should match everything
|
||||
tyUnknown* = tyFromExpr
|
||||
|
||||
tyUnknownTypes* = {tyError, tyFromExpr}
|
||||
|
||||
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
|
||||
tyUserTypeClass, tyUserTypeClassInst,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyConcept,
|
||||
tyAnd, tyOr, tyNot, tyAnything}
|
||||
|
||||
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
|
||||
@@ -329,7 +322,9 @@ type
|
||||
nfFirstWrite # this node is a first write
|
||||
nfHasComment # node has a comment
|
||||
nfSkipFieldChecking # node skips field visable checking
|
||||
nfOpenSym # node is a captured sym but can be overriden by local symbols
|
||||
nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot
|
||||
# because openSym experimental switch is disabled
|
||||
# gives warning instead
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47)
|
||||
@@ -450,6 +445,10 @@ const
|
||||
tfGcSafe* = tfThread
|
||||
tfObjHasKids* = tfEnumHasHoles
|
||||
tfReturnsNew* = tfInheritable
|
||||
tfNonConstExpr* = tfExplicitCallConv
|
||||
## tyFromExpr where the expression shouldn't be evaluated as a static value
|
||||
tfGenericHasDestructor* = tfExplicitCallConv
|
||||
## tyGenericBody where an instance has a generated destructor
|
||||
skError* = skUnknown
|
||||
|
||||
var
|
||||
@@ -494,13 +493,14 @@ type
|
||||
mAnd, mOr,
|
||||
mImplies, mIff, mExists, mForall, mOld,
|
||||
mEqStr, mLeStr, mLtStr,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
|
||||
mConStrStr, mSlice,
|
||||
mDotDot, # this one is only necessary to give nice compile time warnings
|
||||
mFields, mFieldPairs, mOmpParFor,
|
||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInSet, mRepr, mExit,
|
||||
mSetLengthStr, mSetLengthSeq,
|
||||
mSetLengthSeqUninit,
|
||||
mIsPartOf, mAstToStr, mParallel,
|
||||
mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
|
||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||
@@ -562,7 +562,7 @@ const
|
||||
mStrToStr, mEnumToStr,
|
||||
mAnd, mOr,
|
||||
mEqStr, mLeStr, mLtStr,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
|
||||
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInSet, mRepr, mOpenArrayToSeq}
|
||||
|
||||
@@ -594,7 +594,7 @@ type
|
||||
TNode*{.final, acyclic.} = object # on a 32bit machine, this takes 32 bytes
|
||||
when defined(useNodeIds):
|
||||
id*: int
|
||||
typ*: PType
|
||||
typField: PType
|
||||
info*: TLineInfo
|
||||
flags*: TNodeFlags
|
||||
case kind*: TNodeKind
|
||||
@@ -655,7 +655,7 @@ type
|
||||
storage*: TStorageLoc
|
||||
flags*: TLocFlags # location's flags
|
||||
lode*: PNode # Node where the location came from; can be faked
|
||||
r*: Rope # rope value of location (code generators)
|
||||
snippet*: Rope # C code snippet of location (code generators)
|
||||
|
||||
# ---------------- end of backend information ------------------------------
|
||||
|
||||
@@ -676,6 +676,9 @@ type
|
||||
TInstantiation* = object
|
||||
sym*: PSym
|
||||
concreteTypes*: seq[PType]
|
||||
genericParamsCount*: int # for terrible reasons `concreteTypes` contains all the types,
|
||||
# so we need to know how many generic params there were
|
||||
# this is not serialized for IC and that is fine.
|
||||
compilesId*: CompilesId
|
||||
|
||||
PInstantiation* = ref TInstantiation
|
||||
@@ -685,6 +688,7 @@ type
|
||||
symbols*: TStrTable
|
||||
parent*: PScope
|
||||
allowPrivateAccess*: seq[PSym] # # enable access to private fields
|
||||
optionStackLen*: int
|
||||
|
||||
PScope* = ref TScope
|
||||
|
||||
@@ -709,7 +713,7 @@ type
|
||||
when defined(nimsuggest):
|
||||
endInfo*: TLineInfo
|
||||
hasUserSpecifiedType*: bool # used for determining whether to display inlay type hints
|
||||
owner*: PSym
|
||||
ownerField: PSym
|
||||
flags*: TSymFlags
|
||||
ast*: PNode # syntax tree of proc, iterator, etc.:
|
||||
# the whole proc including header; this is used
|
||||
@@ -778,7 +782,7 @@ type
|
||||
# formal param list
|
||||
# for concepts, the concept body
|
||||
# else: unused
|
||||
owner*: PSym # the 'owner' of the type
|
||||
ownerField: PSym # the 'owner' of the type
|
||||
sym*: PSym # types have the sym associated with them
|
||||
# it is used for converting types to strings
|
||||
size*: BiggestInt # the size of the type in bytes
|
||||
@@ -796,6 +800,15 @@ type
|
||||
|
||||
TPairSeq* = seq[TPair]
|
||||
|
||||
TIdPair*[T] = object
|
||||
key*: ItemId
|
||||
val*: T
|
||||
|
||||
TIdPairSeq*[T] = seq[TIdPair[T]]
|
||||
TIdTable*[T] = object
|
||||
counter*: int
|
||||
data*: TIdPairSeq[T]
|
||||
|
||||
TNodePair* = object
|
||||
h*: Hash # because it is expensive to compute!
|
||||
key*: PNode
|
||||
@@ -806,6 +819,7 @@ type
|
||||
# nodes are compared by structure!
|
||||
counter*: int
|
||||
data*: TNodePairSeq
|
||||
ignoreTypes*: bool
|
||||
|
||||
TObjectSeq* = seq[RootRef]
|
||||
TObjectSet* = object
|
||||
@@ -817,6 +831,15 @@ type
|
||||
|
||||
template nodeId(n: PNode): int = cast[int](n)
|
||||
|
||||
template typ*(n: PNode): PType =
|
||||
n.typField
|
||||
|
||||
proc owner*(s: PSym|PType): PSym {.inline.} =
|
||||
result = s.ownerField
|
||||
|
||||
proc setOwner*(s: PSym|PType, owner: PSym) {.inline.} =
|
||||
s.ownerField = owner
|
||||
|
||||
type Gconfig = object
|
||||
# we put comments in a side channel to avoid increasing `sizeof(TNode)`, which
|
||||
# reduces memory usage given that `PNode` is the most allocated type by far.
|
||||
@@ -884,7 +907,7 @@ const
|
||||
nfFromTemplate, nfDefaultRefsParam,
|
||||
nfExecuteOnReload, nfLastRead,
|
||||
nfFirstWrite, nfSkipFieldChecking,
|
||||
nfOpenSym}
|
||||
nfDisabledOpenSym}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
@@ -898,7 +921,7 @@ const
|
||||
nfAllFieldsSet* = nfBase2
|
||||
|
||||
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
|
||||
nkClosedSymChoice}
|
||||
nkClosedSymChoice, nkOpenSym}
|
||||
|
||||
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
|
||||
nkLiterals* = {nkCharLit..nkTripleStrLit}
|
||||
@@ -925,15 +948,17 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
|
||||
case a.kind
|
||||
of nkSym: a.sym.name
|
||||
of nkIdent: a.ident
|
||||
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: a.sons[0].sym.name
|
||||
else: nil
|
||||
|
||||
const
|
||||
moduleShift = when defined(cpu32): 20 else: 24
|
||||
|
||||
template id*(a: PType | PSym): int =
|
||||
template toId*(a: ItemId): int =
|
||||
let x = a
|
||||
(x.itemId.module.int shl moduleShift) + x.itemId.item.int
|
||||
(x.module.int shl moduleShift) + x.item.int
|
||||
|
||||
template id*(a: PType | PSym): int = toId(a.itemId)
|
||||
|
||||
type
|
||||
IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
|
||||
@@ -1059,8 +1084,11 @@ proc getDeclPragma*(n: PNode): PNode =
|
||||
|
||||
proc extractPragma*(s: PSym): PNode =
|
||||
## gets the pragma node of routine/type/var/let/const symbol `s`
|
||||
if s.kind in routineKinds:
|
||||
result = s.ast[pragmasPos]
|
||||
if s.kind in routineKinds: # bug #24167
|
||||
if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
|
||||
result = s.ast[pragmasPos]
|
||||
else:
|
||||
result = nil
|
||||
elif s.kind in {skType, skVar, skLet, skConst}:
|
||||
if s.ast != nil and s.ast.len > 0:
|
||||
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
|
||||
@@ -1131,7 +1159,7 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
||||
## new node with line info, type, and no children
|
||||
result = newNode(kind)
|
||||
result.info = info
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
|
||||
proc newNode*(kind: TNodeKind, info: TLineInfo): PNode =
|
||||
## new node with line info, no type, and no children
|
||||
@@ -1196,7 +1224,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
|
||||
assert not name.isNil
|
||||
let id = nextSymId idgen
|
||||
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
|
||||
options: options, owner: owner, offset: defaultOffset,
|
||||
options: options, ownerField: owner, offset: defaultOffset,
|
||||
disamb: getOrDefault(idgen.disambTable, name).int32)
|
||||
idgen.disambTable.inc name
|
||||
when false:
|
||||
@@ -1257,6 +1285,11 @@ proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
|
||||
setLen(dest.data, src.data.len)
|
||||
for i in 0..high(src.data): dest.data[i] = src.data[i]
|
||||
|
||||
proc copyIdTable*[T](dest: var TIdTable[T], src: TIdTable[T]) =
|
||||
dest.counter = src.counter
|
||||
newSeq(dest.data, src.data.len)
|
||||
for i in 0..high(src.data): dest.data[i] = src.data[i]
|
||||
|
||||
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
|
||||
dest.counter = src.counter
|
||||
setLen(dest.data, src.data.len)
|
||||
@@ -1277,15 +1310,18 @@ proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode =
|
||||
proc newSymNode*(sym: PSym): PNode =
|
||||
result = newNode(nkSym)
|
||||
result.sym = sym
|
||||
result.typ = sym.typ
|
||||
result.typ() = sym.typ
|
||||
result.info = sym.info
|
||||
|
||||
proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||
result = newNode(nkSym)
|
||||
result.sym = sym
|
||||
result.typ = sym.typ
|
||||
result.typ() = sym.typ
|
||||
result.info = info
|
||||
|
||||
proc newOpenSym*(n: PNode): PNode {.inline.} =
|
||||
result = newTreeI(nkOpenSym, n.info, n)
|
||||
|
||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = intVal
|
||||
@@ -1360,7 +1396,7 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
|
||||
result = newNode(nkIntLit)
|
||||
else: raiseAssert $kind
|
||||
result.intVal = intVal
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
|
||||
proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
|
||||
# XXX: introduce range check
|
||||
@@ -1499,7 +1535,7 @@ iterator signature*(t: PType): PType =
|
||||
|
||||
proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType =
|
||||
let id = nextTypeId idgen
|
||||
result = PType(kind: kind, owner: owner, size: defaultSize,
|
||||
result = PType(kind: kind, ownerField: owner, size: defaultSize,
|
||||
align: defaultAlignment, itemId: id,
|
||||
uniqueId: id, sons: @[])
|
||||
if son != nil: result.sons.add son
|
||||
@@ -1510,13 +1546,14 @@ proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType
|
||||
|
||||
proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons
|
||||
proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son]
|
||||
proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len)
|
||||
|
||||
proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
if a.k == low(typeof(a.k)): a.k = b.k
|
||||
if a.storage == low(typeof(a.storage)): a.storage = b.storage
|
||||
a.flags.incl b.flags
|
||||
if a.lode == nil: a.lode = b.lode
|
||||
if a.r == "": a.r = b.r
|
||||
if a.snippet == "": a.snippet = b.snippet
|
||||
|
||||
proc newSons*(father: PNode, length: int) =
|
||||
setLen(father.sons, length)
|
||||
@@ -1553,7 +1590,7 @@ proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
|
||||
result.sym = t.sym # backend-info should not be copied
|
||||
|
||||
proc exactReplica*(t: PType): PType =
|
||||
result = PType(kind: t.kind, owner: t.owner, size: defaultSize,
|
||||
result = PType(kind: t.kind, ownerField: t.owner, size: defaultSize,
|
||||
align: defaultAlignment, itemId: t.itemId,
|
||||
uniqueId: t.uniqueId)
|
||||
assignType(result, t)
|
||||
@@ -1591,12 +1628,22 @@ proc initStrTable*(): TStrTable =
|
||||
result = TStrTable(counter: 0)
|
||||
newSeq(result.data, StartSize)
|
||||
|
||||
proc initIdTable*[T](): TIdTable[T] =
|
||||
result = TIdTable[T](counter: 0)
|
||||
newSeq(result.data, StartSize)
|
||||
|
||||
proc resetIdTable*[T](x: var TIdTable[T]) =
|
||||
x.counter = 0
|
||||
# clear and set to old initial size:
|
||||
setLen(x.data, 0)
|
||||
setLen(x.data, StartSize)
|
||||
|
||||
proc initObjectSet*(): TObjectSet =
|
||||
result = TObjectSet(counter: 0)
|
||||
newSeq(result.data, StartSize)
|
||||
|
||||
proc initNodeTable*(): TNodeTable =
|
||||
result = TNodeTable(counter: 0)
|
||||
proc initNodeTable*(ignoreTypes=false): TNodeTable =
|
||||
result = TNodeTable(counter: 0, ignoreTypes: ignoreTypes)
|
||||
newSeq(result.data, StartSize)
|
||||
|
||||
proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
|
||||
@@ -1631,7 +1678,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||
if mask != {} and propagateHasAsgn:
|
||||
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if o2.kind in {tyTuple, tyObject, tyArray,
|
||||
tySequence, tySet, tyDistinct}:
|
||||
tySequence, tyString, tySet, tyDistinct}:
|
||||
o2.flags.incl mask
|
||||
owner.flags.incl mask
|
||||
|
||||
@@ -1661,7 +1708,7 @@ proc copyNode*(src: PNode): PNode =
|
||||
return nil
|
||||
result = newNode(src.kind)
|
||||
result.info = src.info
|
||||
result.typ = src.typ
|
||||
result.typ() = src.typ
|
||||
result.flags = src.flags * PersistentNodeFlags
|
||||
result.comment = src.comment
|
||||
when defined(useNodeIds):
|
||||
@@ -1679,7 +1726,7 @@ proc copyNode*(src: PNode): PNode =
|
||||
|
||||
template transitionNodeKindCommon(k: TNodeKind) =
|
||||
let obj {.inject.} = n[]
|
||||
n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags)
|
||||
n[] = TNode(kind: k, typField: n.typ, info: obj.info, flags: obj.flags)
|
||||
# n.comment = obj.comment # shouldn't be needed, the address doesnt' change
|
||||
when defined(useNodeIds):
|
||||
n.id = obj.id
|
||||
@@ -1702,7 +1749,7 @@ proc transitionNoneToSym*(n: PNode) =
|
||||
template transitionSymKindCommon*(k: TSymKind) =
|
||||
let obj {.inject.} = s[]
|
||||
s[] = TSym(kind: k, itemId: obj.itemId, magic: obj.magic, typ: obj.typ, name: obj.name,
|
||||
info: obj.info, owner: obj.owner, flags: obj.flags, ast: obj.ast,
|
||||
info: obj.info, ownerField: obj.ownerField, flags: obj.flags, ast: obj.ast,
|
||||
options: obj.options, position: obj.position, offset: obj.offset,
|
||||
loc: obj.loc, annex: obj.annex, constraint: obj.constraint)
|
||||
when hasFFI:
|
||||
@@ -1730,7 +1777,7 @@ template copyNodeImpl(dst, src, processSonsStmt) =
|
||||
dst.info = src.info
|
||||
when defined(nimsuggest):
|
||||
result.endInfo = src.endInfo
|
||||
dst.typ = src.typ
|
||||
dst.typ() = src.typ
|
||||
dst.flags = src.flags * PersistentNodeFlags
|
||||
dst.comment = src.comment
|
||||
when defined(useNodeIds):
|
||||
@@ -2073,14 +2120,16 @@ proc canRaise*(fn: PNode): bool =
|
||||
result = false
|
||||
elif fn.kind == nkSym and fn.sym.magic == mEcho:
|
||||
result = true
|
||||
else:
|
||||
elif fn.typ != nil and fn.typ.kind == tyProc and fn.typ.n != nil:
|
||||
# TODO check for n having sons? or just return false for now if not
|
||||
if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym:
|
||||
if fn.typ.n[0].kind == nkSym:
|
||||
result = false
|
||||
else:
|
||||
result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
|
||||
result = ((fn.typ.n[0].len < effectListLen) or
|
||||
(fn.typ.n[0][exceptionEffects] != nil and
|
||||
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc toHumanStrImpl[T](kind: T, num: static int): string =
|
||||
result = $kind
|
||||
@@ -2117,14 +2166,8 @@ proc isTrue*(n: PNode): bool =
|
||||
n.kind == nkIntLit and n.intVal != 0
|
||||
|
||||
type
|
||||
TypeMapping* = Table[ItemId, PType]
|
||||
SymMapping* = Table[ItemId, PSym]
|
||||
TypeMapping* = TIdTable[PType]
|
||||
SymMapping* = TIdTable[PSym]
|
||||
|
||||
template idTableGet*(tab: typed; key: PSym | PType): untyped = tab.getOrDefault(key.itemId)
|
||||
template idTablePut*(tab: typed; key, val: PSym | PType) = tab[key.itemId] = val
|
||||
|
||||
template initSymMapping*(): Table[ItemId, PSym] = initTable[ItemId, PSym]()
|
||||
template initTypeMapping*(): Table[ItemId, PType] = initTable[ItemId, PType]()
|
||||
|
||||
template resetIdTable*(tab: Table[ItemId, PSym]) = tab.clear()
|
||||
template resetIdTable*(tab: Table[ItemId, PType]) = tab.clear()
|
||||
template initSymMapping*(): SymMapping = initIdTable[PSym]()
|
||||
template initTypeMapping*(): TypeMapping = initIdTable[PType]()
|
||||
|
||||
@@ -713,6 +713,70 @@ iterator items*(tab: TStrTable): PSym =
|
||||
yield s
|
||||
s = nextIter(it, tab)
|
||||
|
||||
proc isNil(x: ItemId): bool {.inline.} =
|
||||
x.module == 0 and x.item == 0
|
||||
|
||||
proc hasEmptySlot[T](data: TIdPairSeq[T]): bool =
|
||||
for h in 0..high(data):
|
||||
if isNil(data[h].key):
|
||||
return true
|
||||
result = false
|
||||
|
||||
proc idTableRawGet[T](t: TIdTable[T], key: int): int =
|
||||
var h: Hash
|
||||
h = key and high(t.data) # start with real hash value
|
||||
while not isNil(t.data[h].key):
|
||||
if toId(t.data[h].key) == key:
|
||||
return h
|
||||
h = nextTry(h, high(t.data))
|
||||
result = - 1
|
||||
|
||||
proc getOrDefault*[T](t: TIdTable[T], key: ItemId): T =
|
||||
var index = idTableRawGet(t, toId(key))
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = default(T)
|
||||
|
||||
template idTableGet*[T](t: TIdTable[T], key: PType | PSym): T =
|
||||
getOrDefault(t, key.itemId)
|
||||
|
||||
proc idTableRawInsert[T](data: var TIdPairSeq[T], key: ItemId, val: T) =
|
||||
var h: Hash
|
||||
let keyId = toId(key)
|
||||
h = keyId and high(data)
|
||||
while not isNil(data[h].key):
|
||||
assert(toId(data[h].key) != keyId)
|
||||
h = nextTry(h, high(data))
|
||||
assert(isNil(data[h].key))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc `[]=`*[T](t: var TIdTable[T], key: ItemId, val: T) =
|
||||
var
|
||||
index: int
|
||||
n: TIdPairSeq[T]
|
||||
index = idTableRawGet(t, toId(key))
|
||||
if index >= 0:
|
||||
assert(not isNil(t.data[index].key))
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(t.data.len, t.counter):
|
||||
newSeq(n, t.data.len * GrowthFactor)
|
||||
for i in 0..high(t.data):
|
||||
if not isNil(t.data[i].key):
|
||||
idTableRawInsert(n, t.data[i].key, t.data[i].val)
|
||||
assert(hasEmptySlot(n))
|
||||
swap(t.data, n)
|
||||
idTableRawInsert(t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
template idTablePut*[T](t: var TIdTable[T], key: PType | PSym, val: T) =
|
||||
t[key.itemId] = val
|
||||
|
||||
iterator idTablePairs*[T](t: TIdTable[T]): tuple[key: ItemId, val: T] =
|
||||
for i in 0..high(t.data):
|
||||
if not isNil(t.data[i].key):
|
||||
yield (t.data[i].key, t.data[i].val)
|
||||
|
||||
proc initIITable(x: var TIITable) =
|
||||
x.counter = 0
|
||||
newSeq(x.data, StartSize)
|
||||
|
||||
@@ -75,7 +75,7 @@ proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet;
|
||||
res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)])
|
||||
if card(n.loc.flags) > 0:
|
||||
res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
|
||||
res.addf("\n$1r: $2", [istr, n.loc.r])
|
||||
res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
|
||||
res.addf("\n$1lode: $2", [istr])
|
||||
res.treeToYamlAux(conf, n.loc.lode, marker, indent + 1, maxRecDepth - 1)
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import pragmas, options, ast, trees
|
||||
|
||||
proc pushBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
|
||||
optionsStack.add options
|
||||
|
||||
proc popBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
|
||||
options = optionsStack[^1]
|
||||
optionsStack.setLen(optionsStack.len-1)
|
||||
|
||||
proc processPushBackendOption*(optionsStack: var seq[TOptions], options: var TOptions,
|
||||
n: PNode, start: int) =
|
||||
pushBackendOption(optionsStack, options)
|
||||
for i in start..<n.len:
|
||||
let it = n[i]
|
||||
if it.kind in nkPragmaCallKinds and it.len == 2 and it[1].kind == nkIntLit:
|
||||
let sw = whichPragma(it[0])
|
||||
let opts = pragmaToOptions(sw)
|
||||
if opts != {}:
|
||||
if it[1].intVal != 0:
|
||||
options.incl opts
|
||||
else:
|
||||
options.excl opts
|
||||
|
||||
template processPopBackendOption*(optionsStack: var seq[TOptions], options: var TOptions) =
|
||||
popBackendOption(optionsStack, options)
|
||||
164
compiler/cbuilderbase.nim
Normal file
164
compiler/cbuilderbase.nim
Normal file
@@ -0,0 +1,164 @@
|
||||
import ropes, int128
|
||||
|
||||
type
|
||||
Snippet* = string
|
||||
Builder* = object
|
||||
buf*: string
|
||||
indents*: int
|
||||
|
||||
template newBuilder*(s: string): Builder =
|
||||
Builder(buf: s)
|
||||
|
||||
proc extract*(builder: Builder): Snippet =
|
||||
builder.buf
|
||||
|
||||
proc add*(builder: var Builder, s: string) =
|
||||
builder.buf.add(s)
|
||||
|
||||
proc add*(builder: var Builder, s: char) =
|
||||
builder.buf.add(s)
|
||||
|
||||
proc addNewline*(builder: var Builder) =
|
||||
builder.add('\n')
|
||||
for i in 0 ..< builder.indents:
|
||||
builder.add('\t')
|
||||
|
||||
proc addLineEnd*(builder: var Builder, s: string) =
|
||||
builder.add(s)
|
||||
builder.addNewline()
|
||||
|
||||
proc addLineEndIndent*(builder: var Builder, s: string) =
|
||||
inc builder.indents
|
||||
builder.add(s)
|
||||
builder.addNewline()
|
||||
|
||||
proc addDedent*(builder: var Builder, s: string) =
|
||||
if builder.buf.len > 0 and builder.buf[^1] == '\t':
|
||||
builder.buf.setLen(builder.buf.len - 1)
|
||||
builder.add(s)
|
||||
dec builder.indents
|
||||
|
||||
proc addLineEndDedent*(builder: var Builder, s: string) =
|
||||
builder.addDedent(s)
|
||||
builder.addNewline()
|
||||
|
||||
proc addLineComment*(builder: var Builder, comment: string) =
|
||||
# probably no-op on nifc
|
||||
builder.add("// ")
|
||||
builder.add(comment)
|
||||
builder.addNewline()
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: int) =
|
||||
builder.buf.addInt(val)
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: int64) =
|
||||
builder.buf.addInt(val)
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: uint64) =
|
||||
builder.buf.addInt(val)
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: Int128) =
|
||||
builder.buf.addInt128(val)
|
||||
|
||||
template cIntValue*(val: int): Snippet = $val
|
||||
template cIntValue*(val: int64): Snippet = $val
|
||||
template cIntValue*(val: uint64): Snippet = $val
|
||||
template cIntValue*(val: Int128): Snippet = $val
|
||||
|
||||
template cUintValue*(val: uint): Snippet = $val & "U"
|
||||
|
||||
import std/formatfloat
|
||||
|
||||
proc addFloatValue*(builder: var Builder, val: float) =
|
||||
builder.buf.addFloat(val)
|
||||
|
||||
template cFloatValue*(val: float): Snippet = $val
|
||||
|
||||
proc addInt64Literal*(result: var Builder; i: BiggestInt) =
|
||||
if i > low(int64):
|
||||
result.add "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result.add "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc addUint64Literal*(result: var Builder; i: uint64) =
|
||||
result.add rope($i & "ULL")
|
||||
|
||||
proc addIntLiteral*(result: var Builder; i: BiggestInt) =
|
||||
if i > low(int32) and i <= high(int32):
|
||||
result.addIntValue(i)
|
||||
elif i == low(int32):
|
||||
# Nim has the same bug for the same reasons :-)
|
||||
result.add "(-2147483647 -1)"
|
||||
elif i > low(int64):
|
||||
result.add "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result.add "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc addIntLiteral*(result: var Builder; i: Int128) =
|
||||
addIntLiteral(result, toInt64(i))
|
||||
|
||||
proc cInt64Literal*(i: BiggestInt): Snippet =
|
||||
if i > low(int64):
|
||||
result = "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result = "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc cUint64Literal*(i: uint64): Snippet =
|
||||
result = $i & "ULL"
|
||||
|
||||
proc cIntLiteral*(i: BiggestInt): Snippet =
|
||||
if i > low(int32) and i <= high(int32):
|
||||
result = rope(i)
|
||||
elif i == low(int32):
|
||||
# Nim has the same bug for the same reasons :-)
|
||||
result = "(-2147483647 -1)"
|
||||
elif i > low(int64):
|
||||
result = "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result = "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc cIntLiteral*(i: Int128): Snippet =
|
||||
result = cIntLiteral(toInt64(i))
|
||||
|
||||
const
|
||||
NimInt* = "NI"
|
||||
NimInt8* = "NI8"
|
||||
NimInt16* = "NI16"
|
||||
NimInt32* = "NI32"
|
||||
NimInt64* = "NI64"
|
||||
CInt* = "int"
|
||||
NimUint* = "NU"
|
||||
NimUint8* = "NU8"
|
||||
NimUint16* = "NU16"
|
||||
NimUint32* = "NU32"
|
||||
NimUint64* = "NU64"
|
||||
NimFloat* = "NF"
|
||||
NimFloat32* = "NF32"
|
||||
NimFloat64* = "NF64"
|
||||
NimFloat128* = "NF128" # not actually defined
|
||||
NimNan* = "NAN"
|
||||
NimInf* = "INF"
|
||||
NimBool* = "NIM_BOOL"
|
||||
NimTrue* = "NIM_TRUE"
|
||||
NimFalse* = "NIM_FALSE"
|
||||
NimChar* = "NIM_CHAR"
|
||||
CChar* = "char"
|
||||
NimCstring* = "NCSTRING"
|
||||
NimNil* = "NIM_NIL"
|
||||
CNil* = "NULL"
|
||||
NimStrlitFlag* = "NIM_STRLIT_FLAG"
|
||||
CVoid* = "void"
|
||||
CPointer* = "void*"
|
||||
CConstPointer* = "NIM_CONST void*"
|
||||
|
||||
proc cIntType*(bits: BiggestInt): Snippet =
|
||||
"NI" & $bits
|
||||
|
||||
proc cUintType*(bits: BiggestInt): Snippet =
|
||||
"NU" & $bits
|
||||
|
||||
type
|
||||
IfBuilderState* = enum
|
||||
WaitingIf, WaitingElseIf, InBlock
|
||||
IfBuilder* = object
|
||||
state*: IfBuilderState
|
||||
668
compiler/cbuilderdecls.nim
Normal file
668
compiler/cbuilderdecls.nim
Normal file
@@ -0,0 +1,668 @@
|
||||
type VarKind = enum
|
||||
Local
|
||||
Global
|
||||
Threadvar
|
||||
Const
|
||||
AlwaysConst ## const even on C++
|
||||
|
||||
proc addVarHeader(builder: var Builder, kind: VarKind) =
|
||||
## adds modifiers for given var kind:
|
||||
## Local has no modifier
|
||||
## Global has `static` modifier
|
||||
## Const has `static NIM_CONST` modifier
|
||||
## AlwaysConst has `static const` modifier (NIM_CONST is no-op on C++)
|
||||
## Threadvar is unimplemented
|
||||
case kind
|
||||
of Local: discard
|
||||
of Global:
|
||||
builder.add("static ")
|
||||
of Const:
|
||||
builder.add("static NIM_CONST ")
|
||||
of AlwaysConst:
|
||||
builder.add("static const ")
|
||||
of Threadvar:
|
||||
doAssert false, "unimplemented"
|
||||
|
||||
proc addVar(builder: var Builder, kind: VarKind = Local, name: string, typ: Snippet, initializer: Snippet = "") =
|
||||
## adds a variable declaration to the builder
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(typ)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
if initializer.len != 0:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addVarWithType(builder: var Builder, kind: VarKind = Local, name: string, body: typed) =
|
||||
## adds a variable declaration to the builder, with the `body` building the type
|
||||
builder.addVarHeader(kind)
|
||||
body
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string,
|
||||
typ: Snippet, initializerBody: typed) =
|
||||
## adds a variable declaration to the builder, with
|
||||
## `initializerBody` building the initializer. initializer must be provided
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(typ)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
initializerBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
|
||||
typeBody, initializerBody: typed) =
|
||||
## adds a variable declaration to the builder, with `typeBody` building the type, and
|
||||
## `initializerBody` building the initializer. initializer must be provided
|
||||
builder.addVarHeader(kind)
|
||||
typeBody
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
initializerBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
|
||||
## adds an array variable declaration to the builder
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(elementType)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addIntValue(len)
|
||||
builder.add("]")
|
||||
if initializer.len != 0:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
|
||||
## adds an array variable declaration to the builder with the initializer built according to `body`
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(elementType)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addIntValue(len)
|
||||
builder.add("] = ")
|
||||
body
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addTypedef(builder: var Builder, name: string, typeBody: typed) =
|
||||
## adds a typedef declaration to the builder with name `name` and type as
|
||||
## built in `typeBody`
|
||||
builder.add("typedef ")
|
||||
typeBody
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addProcTypedef(builder: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
|
||||
builder.add("typedef ")
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addArrayTypedef(builder: var Builder, name: string, len: BiggestInt, typeBody: typed) =
|
||||
## adds an array typedef declaration to the builder with name `name`,
|
||||
## length `len`, and element type as built in `typeBody`
|
||||
builder.add("typedef ")
|
||||
typeBody
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addIntValue(len)
|
||||
builder.addLineEnd("];")
|
||||
|
||||
type
|
||||
StructInitializerKind = enum
|
||||
siOrderedStruct ## struct constructor, but without named fields on C
|
||||
siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer
|
||||
siArray ## array constructor
|
||||
siWrapper ## wrapper for a single field, generates it verbatim
|
||||
|
||||
StructInitializer = object
|
||||
## context for building struct initializers, i.e. `{ field1, field2 }`
|
||||
kind: StructInitializerKind
|
||||
## if true, fields will not be named, instead values are placed in order
|
||||
needsComma: bool
|
||||
|
||||
proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer =
|
||||
## starts building a struct initializer, i.e. braced initializer list
|
||||
result = StructInitializer(kind: kind, needsComma: false)
|
||||
if kind != siWrapper:
|
||||
builder.add("{")
|
||||
|
||||
template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) =
|
||||
## adds a field to a struct initializer, with the value built in `valueBody`
|
||||
if constr.needsComma:
|
||||
assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields"
|
||||
builder.add(", ")
|
||||
else:
|
||||
constr.needsComma = true
|
||||
case constr.kind
|
||||
of siArray, siWrapper:
|
||||
# no name, can just add value
|
||||
valueBody
|
||||
of siOrderedStruct:
|
||||
# no name, can just add value on C
|
||||
assert name.len != 0, "name has to be given for struct initializer field"
|
||||
valueBody
|
||||
of siNamedStruct:
|
||||
assert name.len != 0, "name has to be given for struct initializer field"
|
||||
builder.add(".")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
valueBody
|
||||
|
||||
proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
|
||||
## finishes building a struct initializer
|
||||
if constr.kind != siWrapper:
|
||||
builder.add("}")
|
||||
|
||||
template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) =
|
||||
## builds a struct initializer, i.e. `{ field1, field2 }`
|
||||
## a `var StructInitializer` must be declared and passed as a parameter so
|
||||
## that it can be used with `addField`
|
||||
constr = builder.initStructInitializer(kind)
|
||||
body
|
||||
builder.finishStructInitializer(constr)
|
||||
|
||||
proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
|
||||
## adds a field inside a struct/union type
|
||||
obj.add('\t')
|
||||
obj.add(typ)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initializer: Snippet = "") =
|
||||
## adds an array field inside a struct/union type
|
||||
obj.add('\t')
|
||||
obj.add(elementType)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
obj.add("[")
|
||||
obj.addIntValue(len)
|
||||
obj.add("]")
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
|
||||
## adds an field inside a struct/union type, based on an `skField` symbol
|
||||
obj.add('\t')
|
||||
if field.alignment > 0:
|
||||
obj.add("NIM_ALIGN(")
|
||||
obj.addIntValue(field.alignment)
|
||||
obj.add(") ")
|
||||
obj.add(typ)
|
||||
if sfNoalias in field.flags:
|
||||
obj.add(" NIM_NOALIAS")
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if field.bitsize != 0:
|
||||
obj.add(":")
|
||||
obj.addIntValue(field.bitsize)
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addProcField(obj: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
|
||||
obj.add(CallingConvToStr[callConv])
|
||||
obj.add("_PTR(")
|
||||
obj.add(rettype)
|
||||
obj.add(", ")
|
||||
obj.add(name)
|
||||
obj.add(")")
|
||||
obj.add(params)
|
||||
obj.add(";\n")
|
||||
|
||||
type
|
||||
BaseClassKind = enum
|
||||
## denotes how and whether or not the base class/RTTI should be stored
|
||||
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
|
||||
StructBuilderInfo = object
|
||||
## context for building `struct` types
|
||||
baseKind: BaseClassKind
|
||||
named: bool
|
||||
preFieldsLen: int
|
||||
|
||||
proc structOrUnion(t: PType): Snippet =
|
||||
let t = t.skipTypes({tyAlias, tySink})
|
||||
if tfUnion in t.flags: "union"
|
||||
else: "struct"
|
||||
|
||||
proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
|
||||
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
|
||||
obj.add("struct")
|
||||
if result.named:
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if baseType.len != 0:
|
||||
if m.compileToCpp:
|
||||
result.baseKind = bcCppInherit
|
||||
else:
|
||||
result.baseKind = bcSupField
|
||||
if result.baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
result.preFieldsLen = obj.buf.len
|
||||
if result.baseKind == bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
|
||||
proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
|
||||
if info.baseKind == bcNone and info.preFieldsLen == obj.buf.len:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = CChar)
|
||||
if info.named:
|
||||
obj.add("};\n")
|
||||
else:
|
||||
obj.add("}")
|
||||
|
||||
template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
|
||||
## builds a struct type not based on a Nim type with fields according to `body`,
|
||||
## `name` can be empty to build as a type expression and not a statement
|
||||
let info = startSimpleStruct(obj, m, name, baseType)
|
||||
body
|
||||
finishSimpleStruct(obj, m, info)
|
||||
|
||||
proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
|
||||
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
|
||||
if tfPacked in t.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add(structOrUnion(t))
|
||||
obj.add(" __attribute__((__packed__))")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\n")
|
||||
obj.add(structOrUnion(t))
|
||||
else:
|
||||
obj.add(structOrUnion(t))
|
||||
if result.named:
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if t.kind == tyObject:
|
||||
if t.baseClass == nil:
|
||||
if lacksMTypeField(t):
|
||||
result.baseKind = bcNone
|
||||
elif optTinyRtti in m.config.globalOptions:
|
||||
result.baseKind = bcNoneTinyRtti
|
||||
else:
|
||||
result.baseKind = bcNoneRtti
|
||||
elif m.compileToCpp:
|
||||
result.baseKind = bcCppInherit
|
||||
else:
|
||||
result.baseKind = bcSupField
|
||||
elif baseType.len != 0:
|
||||
if m.compileToCpp:
|
||||
result.baseKind = bcCppInherit
|
||||
else:
|
||||
result.baseKind = bcSupField
|
||||
if result.baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
result.preFieldsLen = obj.buf.len
|
||||
case result.baseKind
|
||||
of bcNone:
|
||||
# rest of the options add a field or don't need it due to inheritance,
|
||||
# we need to add the dummy field for uncheckedarray ahead of time
|
||||
# so that it remains trailing
|
||||
if t.itemId notin m.g.graph.memberProcsPerType and
|
||||
t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
|
||||
t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
|
||||
# only consists of flexible array field, add *initial* dummy field
|
||||
obj.addField(name = "dummy", typ = CChar)
|
||||
of bcCppInherit: discard
|
||||
of bcNoneRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
|
||||
of bcNoneTinyRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
|
||||
of bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
|
||||
proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
|
||||
if info.baseKind == bcNone and info.preFieldsLen == obj.buf.len and
|
||||
t.itemId notin m.g.graph.memberProcsPerType:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = CChar)
|
||||
if info.named:
|
||||
obj.add("};\n")
|
||||
else:
|
||||
obj.add("}")
|
||||
if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
obj.add("#pragma pack(pop)\n")
|
||||
|
||||
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
|
||||
## builds a struct type directly based on `typ` with fields according to `body`,
|
||||
## `name` can be empty to build as a type expression and not a statement
|
||||
let info = startStruct(obj, m, typ, name, baseType)
|
||||
body
|
||||
finishStruct(obj, m, typ, info)
|
||||
|
||||
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
|
||||
## adds a field with a `struct { ... }` type, building the fields according to `body`
|
||||
obj.add('\t')
|
||||
if tfPacked in parentTyp.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add("struct __attribute__((__packed__)) {\n")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\nstruct {")
|
||||
else:
|
||||
obj.add("struct {\n")
|
||||
body
|
||||
obj.add("} ")
|
||||
obj.add(fieldName)
|
||||
obj.add(";\n")
|
||||
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
obj.add("#pragma pack(pop)\n")
|
||||
|
||||
template addAnonUnion(obj: var Builder; body: typed) =
|
||||
## adds an anonymous union i.e. `union { ... };` with fields according to `body`
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("};\n")
|
||||
|
||||
template addUnionType(obj: var Builder; body: typed) =
|
||||
## adds a union type i.e. `union { ... }` with fields according to `body`
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("}")
|
||||
|
||||
type DeclVisibility = enum
|
||||
None
|
||||
Extern
|
||||
ExternC
|
||||
ImportLib
|
||||
ExportLib
|
||||
ExportLibVar
|
||||
Private
|
||||
StaticProc
|
||||
|
||||
proc addVisibilityPrefix(builder: var Builder, visibility: DeclVisibility) =
|
||||
# internal proc
|
||||
case visibility
|
||||
of None: discard
|
||||
of Extern:
|
||||
builder.add("extern ")
|
||||
of ExternC:
|
||||
builder.add("NIM_EXTERNC ")
|
||||
of ImportLib:
|
||||
builder.add("N_LIB_IMPORT ")
|
||||
of ExportLib:
|
||||
builder.add("N_LIB_EXPORT ")
|
||||
of ExportLibVar:
|
||||
builder.add("N_LIB_EXPORT_VAR ")
|
||||
of Private:
|
||||
builder.add("N_LIB_PRIVATE ")
|
||||
of StaticProc:
|
||||
builder.add("static ")
|
||||
|
||||
template addDeclWithVisibility(builder: var Builder, visibility: DeclVisibility, declBody: typed) =
|
||||
## adds a declaration as in `declBody` with the given visibility
|
||||
builder.addVisibilityPrefix(visibility)
|
||||
declBody
|
||||
|
||||
type ProcParamBuilder = object
|
||||
needsComma: bool
|
||||
|
||||
proc initProcParamBuilder(builder: var Builder): ProcParamBuilder =
|
||||
result = ProcParamBuilder(needsComma: false)
|
||||
builder.add("(")
|
||||
|
||||
proc finishProcParamBuilder(builder: var Builder, params: ProcParamBuilder) =
|
||||
if params.needsComma:
|
||||
builder.add(")")
|
||||
else:
|
||||
builder.add("void)")
|
||||
|
||||
template cgDeclFrmt*(s: PSym): string =
|
||||
s.constraint.strVal
|
||||
|
||||
proc addParam(builder: var Builder, params: var ProcParamBuilder, name: string, typ: Snippet) =
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
builder.add(typ)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
|
||||
proc addParam(builder: var Builder, params: var ProcParamBuilder, param: PSym, typ: Snippet) =
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
var modifiedTyp = typ
|
||||
if sfNoalias in param.flags:
|
||||
modifiedTyp.add(" NIM_NOALIAS")
|
||||
if sfCodegenDecl notin param.flags:
|
||||
builder.add(modifiedTyp)
|
||||
builder.add(" ")
|
||||
builder.add(param.loc.snippet)
|
||||
else:
|
||||
builder.add runtimeFormat(param.cgDeclFrmt, [modifiedTyp, param.loc.snippet])
|
||||
|
||||
proc addUnnamedParam(builder: var Builder, params: var ProcParamBuilder, typ: Snippet) =
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
builder.add(typ)
|
||||
|
||||
proc addProcTypedParam(builder: var Builder, paramBuilder: var ProcParamBuilder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
|
||||
if paramBuilder.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
paramBuilder.needsComma = true
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
|
||||
proc addVarargsParam(builder: var Builder, params: var ProcParamBuilder) =
|
||||
# does not exist in NIFC, needs to be proc pragma
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
builder.add("...")
|
||||
|
||||
template addProcParams(builder: var Builder, params: out ProcParamBuilder, body: typed) =
|
||||
params = initProcParamBuilder(builder)
|
||||
body
|
||||
finishProcParamBuilder(builder, params)
|
||||
|
||||
type SimpleProcParam = tuple
|
||||
name, typ: string
|
||||
|
||||
proc cProcParams(params: varargs[SimpleProcParam]): Snippet =
|
||||
if params.len == 0: return "(void)"
|
||||
result = "("
|
||||
for i in 0 ..< params.len:
|
||||
if i != 0: result.add(", ")
|
||||
result.add(params[i].typ)
|
||||
if params[i].name.len != 0:
|
||||
result.add(" ")
|
||||
result.add(params[i].name)
|
||||
result.add(")")
|
||||
|
||||
template addProcHeaderWithParams(builder: var Builder, callConv: TCallingConvention,
|
||||
name: string, rettype: Snippet, paramBuilder: typed) =
|
||||
# on nifc should build something like (proc name params type pragmas
|
||||
# with no body given
|
||||
# or enforce this with secondary builder object
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
paramBuilder
|
||||
|
||||
proc addProcHeader(builder: var Builder, callConv: TCallingConvention,
|
||||
name: string, rettype, params: Snippet) =
|
||||
# on nifc should build something like (proc name params type pragmas
|
||||
# with no body given
|
||||
# or enforce this with secondary builder object
|
||||
addProcHeaderWithParams(builder, callConv, name, rettype):
|
||||
builder.add(params)
|
||||
|
||||
proc addProcHeader(builder: var Builder, name: string, rettype, params: Snippet, isConstructor = false) =
|
||||
# no callconv
|
||||
builder.add(rettype)
|
||||
builder.add(" ")
|
||||
if isConstructor:
|
||||
builder.add("__attribute__((constructor)) ")
|
||||
builder.add(name)
|
||||
builder.add(params)
|
||||
|
||||
proc addProcHeader(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet, addAttributes: bool) =
|
||||
# on nifc should build something like (proc name params type pragmas
|
||||
# with no body given
|
||||
# or enforce this with secondary builder object
|
||||
let noreturn = isNoReturn(m, prc)
|
||||
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(naked) ")
|
||||
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(noreturn) ")
|
||||
builder.add(CallingConvToStr[prc.typ.callConv])
|
||||
builder.add("(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
if addAttributes:
|
||||
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((naked))")
|
||||
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((noreturn))")
|
||||
|
||||
proc finishProcHeaderAsProto(builder: var Builder) =
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template finishProcHeaderWithBody(builder: var Builder, body: typed) =
|
||||
builder.addLineEndIndent(" {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
builder.addNewline
|
||||
|
||||
proc addProcVar(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet,
|
||||
isStatic = false, ignoreAttributes = false) =
|
||||
# on nifc, builds full variable
|
||||
if isStatic:
|
||||
builder.add("static ")
|
||||
let noreturn = isNoReturn(m, prc)
|
||||
if not ignoreAttributes:
|
||||
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(naked) ")
|
||||
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(noreturn) ")
|
||||
builder.add(CallingConvToStr[prc.typ.callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
if not ignoreAttributes:
|
||||
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((naked))")
|
||||
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((noreturn))")
|
||||
# ensure we are just adding a variable:
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addProcVar(builder: var Builder, callConv: TCallingConvention,
|
||||
name: string, params, rettype: Snippet,
|
||||
isStatic = false, isVolatile = false) =
|
||||
# on nifc, builds full variable
|
||||
if isStatic:
|
||||
builder.add("static ")
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
if isVolatile:
|
||||
builder.add("volatile ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
# ensure we are just adding a variable:
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addProcVar(builder: var Builder,
|
||||
name: string, params, rettype: Snippet,
|
||||
isStatic = false, isVolatile = false) =
|
||||
# no callconv
|
||||
if isStatic:
|
||||
builder.add("static ")
|
||||
builder.add(rettype)
|
||||
builder.add(" (*")
|
||||
if isVolatile:
|
||||
builder.add("volatile ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
# ensure we are just adding a variable:
|
||||
builder.addLineEnd(";")
|
||||
|
||||
type VarInitializerKind = enum
|
||||
Assignment, CppConstructor
|
||||
|
||||
proc addVar(builder: var Builder, m: BModule, s: PSym, name: string, typ: Snippet, kind = Local, visibility: DeclVisibility = None, initializer: Snippet = "", initializerKind: VarInitializerKind = Assignment) =
|
||||
if sfCodegenDecl in s.flags:
|
||||
builder.add(runtimeFormat(s.cgDeclFrmt, [typ, name]))
|
||||
if initializer.len != 0:
|
||||
if initializerKind == Assignment:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
return
|
||||
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
|
||||
builder.add("NIM_ALIGN(" & $s.alignment & ") ")
|
||||
builder.addVisibilityPrefix(visibility)
|
||||
if kind == Threadvar:
|
||||
if optThreads in m.config.globalOptions:
|
||||
let sym = s.typ.sym
|
||||
if sym != nil and sfCppNonPod in sym.flags:
|
||||
builder.add("NIM_THREAD_LOCAL ")
|
||||
else: builder.add("NIM_THREADVAR ")
|
||||
else:
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(typ)
|
||||
if sfRegister in s.flags: builder.add(" register")
|
||||
if sfVolatile in s.flags: builder.add(" volatile")
|
||||
if sfNoalias in s.flags: builder.add(" NIM_NOALIAS")
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
if initializer.len != 0:
|
||||
if initializerKind == Assignment:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addInclude(builder: var Builder, value: Snippet) =
|
||||
builder.addLineEnd("#include " & value)
|
||||
259
compiler/cbuilderexprs.nim
Normal file
259
compiler/cbuilderexprs.nim
Normal file
@@ -0,0 +1,259 @@
|
||||
proc constType(t: Snippet): Snippet =
|
||||
# needs manipulation of `t` in nifc
|
||||
"NIM_CONST " & t
|
||||
|
||||
proc constPtrType(t: Snippet): Snippet =
|
||||
t & "* NIM_CONST"
|
||||
|
||||
proc ptrConstType(t: Snippet): Snippet =
|
||||
"NIM_CONST " & t & "*"
|
||||
|
||||
proc ptrType(t: Snippet): Snippet =
|
||||
t & "*"
|
||||
|
||||
proc cppRefType(t: Snippet): Snippet =
|
||||
t & "&"
|
||||
|
||||
const
|
||||
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
|
||||
"N_STDCALL", "N_CDECL", "N_SAFECALL",
|
||||
"N_SYSCALL", # this is probably not correct for all platforms,
|
||||
# but one can #define it to what one wants
|
||||
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
|
||||
"N_NOCONV" #ccMember is N_NOCONV
|
||||
]
|
||||
|
||||
proc procPtrTypeUnnamed(rettype, params: Snippet): Snippet =
|
||||
rettype & "(*)" & params
|
||||
|
||||
proc procPtrTypeUnnamedNimCall(rettype, params: Snippet): Snippet =
|
||||
rettype & "(N_RAW_NIMCALL*)" & params
|
||||
|
||||
proc procPtrTypeUnnamed(callConv: TCallingConvention, rettype, params: Snippet): Snippet =
|
||||
CallingConvToStr[callConv] & "_PTR(" & rettype & ", )" & params
|
||||
|
||||
type CppCaptureKind = enum None, ByReference, ByCopy
|
||||
|
||||
template addCppLambda(builder: var Builder, captures: CppCaptureKind, params: Snippet, body: typed) =
|
||||
builder.add("[")
|
||||
case captures
|
||||
of None: discard
|
||||
of ByReference: builder.add("&")
|
||||
of ByCopy: builder.add("=")
|
||||
builder.add("] ")
|
||||
builder.add(params)
|
||||
builder.addLineEndIndent(" {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
proc cCast(typ, value: Snippet): Snippet =
|
||||
"((" & typ & ") " & value & ")"
|
||||
|
||||
proc wrapPar(value: Snippet): Snippet =
|
||||
# used for expression group, no-op on sexp
|
||||
"(" & value & ")"
|
||||
|
||||
proc removeSinglePar(value: Snippet): Snippet =
|
||||
# removes a single paren layer expected to exist, to silence Wparentheses-equality
|
||||
assert value[0] == '(' and value[^1] == ')'
|
||||
value[1..^2]
|
||||
|
||||
template addCast(builder: var Builder, typ: Snippet, valueBody: typed) =
|
||||
## adds a cast to `typ` with value built by `valueBody`
|
||||
builder.add "(("
|
||||
builder.add typ
|
||||
builder.add ") "
|
||||
valueBody
|
||||
builder.add ")"
|
||||
|
||||
proc cAddr(value: Snippet): Snippet =
|
||||
"(&" & value & ")"
|
||||
|
||||
proc cLabelAddr(value: TLabel): Snippet =
|
||||
"&&" & value
|
||||
|
||||
proc cDeref(value: Snippet): Snippet =
|
||||
"(*" & value & ")"
|
||||
|
||||
proc subscript(a, b: Snippet): Snippet =
|
||||
a & "[" & b & "]"
|
||||
|
||||
proc dotField(a, b: Snippet): Snippet =
|
||||
a & "." & b
|
||||
|
||||
proc derefField(a, b: Snippet): Snippet =
|
||||
a & "->" & b
|
||||
|
||||
type CallBuilder = object
|
||||
needsComma: bool
|
||||
|
||||
proc initCallBuilder(builder: var Builder, callee: Snippet): CallBuilder =
|
||||
result = CallBuilder(needsComma: false)
|
||||
builder.add(callee)
|
||||
builder.add("(")
|
||||
|
||||
const cArgumentSeparator = ", "
|
||||
|
||||
proc addArgumentSeparator(builder: var Builder) =
|
||||
# no-op on NIFC
|
||||
# used by "single argument" builders
|
||||
builder.add(cArgumentSeparator)
|
||||
|
||||
template addArgument(builder: var Builder, call: var CallBuilder, valueBody: typed) =
|
||||
if call.needsComma:
|
||||
builder.addArgumentSeparator()
|
||||
else:
|
||||
call.needsComma = true
|
||||
valueBody
|
||||
|
||||
proc finishCallBuilder(builder: var Builder, call: CallBuilder) =
|
||||
builder.add(")")
|
||||
|
||||
template addCall(builder: var Builder, call: out CallBuilder, callee: Snippet, body: typed) =
|
||||
call = initCallBuilder(builder, callee)
|
||||
body
|
||||
finishCallBuilder(builder, call)
|
||||
|
||||
proc addCall(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
|
||||
builder.add(callee)
|
||||
builder.add("(")
|
||||
if args.len != 0:
|
||||
builder.add(args[0])
|
||||
for i in 1 ..< args.len:
|
||||
builder.add(", ")
|
||||
builder.add(args[i])
|
||||
builder.add(")")
|
||||
|
||||
proc cCall(callee: Snippet, args: varargs[Snippet]): Snippet =
|
||||
result = callee
|
||||
result.add("(")
|
||||
if args.len != 0:
|
||||
result.add(args[0])
|
||||
for i in 1 ..< args.len:
|
||||
result.add(", ")
|
||||
result.add(args[i])
|
||||
result.add(")")
|
||||
|
||||
proc addSizeof(builder: var Builder, val: Snippet) =
|
||||
builder.add("sizeof(")
|
||||
builder.add(val)
|
||||
builder.add(")")
|
||||
|
||||
proc addAlignof(builder: var Builder, val: Snippet) =
|
||||
builder.add("NIM_ALIGNOF(")
|
||||
builder.add(val)
|
||||
builder.add(")")
|
||||
|
||||
proc addOffsetof(builder: var Builder, val, member: Snippet) =
|
||||
builder.add("offsetof(")
|
||||
builder.add(val)
|
||||
builder.add(", ")
|
||||
builder.add(member)
|
||||
builder.add(")")
|
||||
|
||||
template cSizeof(val: Snippet): Snippet =
|
||||
"sizeof(" & val & ")"
|
||||
|
||||
template cAlignof(val: Snippet): Snippet =
|
||||
"NIM_ALIGNOF(" & val & ")"
|
||||
|
||||
template cOffsetof(val, member: Snippet): Snippet =
|
||||
"offsetof(" & val & ", " & member & ")"
|
||||
|
||||
type TypedBinaryOp = enum
|
||||
Add, Sub, Mul, Div, Mod
|
||||
Shr, Shl, BitAnd, BitOr, BitXor
|
||||
|
||||
const typedBinaryOperators: array[TypedBinaryOp, string] = [
|
||||
Add: "+",
|
||||
Sub: "-",
|
||||
Mul: "*",
|
||||
Div: "/",
|
||||
Mod: "%",
|
||||
Shr: ">>",
|
||||
Shl: "<<",
|
||||
BitAnd: "&",
|
||||
BitOr: "|",
|
||||
BitXor: "^"
|
||||
]
|
||||
|
||||
type TypedUnaryOp = enum
|
||||
Neg, BitNot
|
||||
|
||||
const typedUnaryOperators: array[TypedUnaryOp, string] = [
|
||||
Neg: "-",
|
||||
BitNot: "~",
|
||||
]
|
||||
|
||||
type UntypedBinaryOp = enum
|
||||
LessEqual, LessThan, GreaterEqual, GreaterThan, Equal, NotEqual
|
||||
And, Or
|
||||
|
||||
const untypedBinaryOperators: array[UntypedBinaryOp, string] = [
|
||||
LessEqual: "<=",
|
||||
LessThan: "<",
|
||||
GreaterEqual: ">=",
|
||||
GreaterThan: ">",
|
||||
Equal: "==",
|
||||
NotEqual: "!=",
|
||||
And: "&&",
|
||||
Or: "||"
|
||||
]
|
||||
|
||||
type UntypedUnaryOp = enum
|
||||
Not
|
||||
|
||||
const untypedUnaryOperators: array[UntypedUnaryOp, string] = [
|
||||
Not: "!"
|
||||
]
|
||||
|
||||
proc addOp(builder: var Builder, binOp: TypedBinaryOp, t: Snippet, a, b: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(typedBinaryOperators[binOp])
|
||||
builder.add(' ')
|
||||
builder.add(b)
|
||||
builder.add(')')
|
||||
|
||||
proc addOp(builder: var Builder, unOp: TypedUnaryOp, t: Snippet, a: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(typedUnaryOperators[unOp])
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add("))")
|
||||
|
||||
proc addOp(builder: var Builder, binOp: UntypedBinaryOp, a, b: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(untypedBinaryOperators[binOp])
|
||||
builder.add(' ')
|
||||
builder.add(b)
|
||||
builder.add(')')
|
||||
|
||||
proc addOp(builder: var Builder, unOp: UntypedUnaryOp, a: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(untypedUnaryOperators[unOp])
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add("))")
|
||||
|
||||
template cOp(binOp: TypedBinaryOp, t: Snippet, a, b: Snippet): Snippet =
|
||||
'(' & a & ' ' & typedBinaryOperators[binOp] & ' ' & b & ')'
|
||||
|
||||
template cOp(binOp: TypedUnaryOp, t: Snippet, a: Snippet): Snippet =
|
||||
'(' & typedUnaryOperators[binOp] & '(' & a & "))"
|
||||
|
||||
template cOp(binOp: UntypedBinaryOp, a, b: Snippet): Snippet =
|
||||
'(' & a & ' ' & untypedBinaryOperators[binOp] & ' ' & b & ')'
|
||||
|
||||
template cOp(binOp: UntypedUnaryOp, a: Snippet): Snippet =
|
||||
'(' & untypedUnaryOperators[binOp] & '(' & a & "))"
|
||||
|
||||
template cIfExpr(cond, a, b: Snippet): Snippet =
|
||||
# XXX used for `min` and `max`, maybe add nifc primitives for these
|
||||
"(" & cond & " ? " & a & " : " & b & ")"
|
||||
|
||||
template cUnlikely(val: Snippet): Snippet =
|
||||
"NIM_UNLIKELY(" & val & ")"
|
||||
329
compiler/cbuilderstmts.nim
Normal file
329
compiler/cbuilderstmts.nim
Normal file
@@ -0,0 +1,329 @@
|
||||
template addAssignmentWithValue(builder: var Builder, lhs: Snippet, valueBody: typed) =
|
||||
builder.add(lhs)
|
||||
builder.add(" = ")
|
||||
valueBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addFieldAssignmentWithValue(builder: var Builder, lhs: Snippet, name: string, valueBody: typed) =
|
||||
builder.add(lhs)
|
||||
builder.add("." & name & " = ")
|
||||
valueBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addAssignment(builder: var Builder, lhs, rhs: Snippet) =
|
||||
builder.addAssignmentWithValue(lhs):
|
||||
builder.add(rhs)
|
||||
|
||||
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
|
||||
builder.addFieldAssignmentWithValue(lhs, name):
|
||||
builder.add(rhs)
|
||||
|
||||
template addMutualFieldAssignment(builder: var Builder, lhs, rhs: Snippet, name: string) =
|
||||
builder.addFieldAssignmentWithValue(lhs, name):
|
||||
builder.add(rhs)
|
||||
builder.add("." & name)
|
||||
|
||||
template addAssignment(builder: var Builder, lhs: Snippet, rhs: int | int64 | uint64 | Int128) =
|
||||
builder.addAssignmentWithValue(lhs):
|
||||
builder.addIntValue(rhs)
|
||||
|
||||
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: int | int64 | uint64 | Int128) =
|
||||
builder.addFieldAssignmentWithValue(lhs, name):
|
||||
builder.addIntValue(rhs)
|
||||
|
||||
template addDerefFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
|
||||
builder.add(lhs)
|
||||
builder.add("->" & name & " = ")
|
||||
builder.add(rhs)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addSubscriptAssignment(builder: var Builder, lhs: Snippet, index: Snippet, rhs: Snippet) =
|
||||
builder.add(lhs)
|
||||
builder.add("[" & index & "] = ")
|
||||
builder.add(rhs)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addStmt(builder: var Builder, stmtBody: typed) =
|
||||
## makes an expression built by `stmtBody` into a statement
|
||||
stmtBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addCallStmt(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
|
||||
builder.addStmt():
|
||||
builder.addCall(callee, args)
|
||||
|
||||
# XXX blocks need indent tracker in `Builder` object
|
||||
|
||||
template addSingleIfStmt(builder: var Builder, cond: Snippet, body: typed) =
|
||||
builder.add("if (")
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
template addSingleIfStmtWithCond(builder: var Builder, condBody: typed, body: typed) =
|
||||
builder.add("if (")
|
||||
condBody
|
||||
builder.addLineEndIndent(") {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
proc initIfStmt(builder: var Builder): IfBuilder =
|
||||
IfBuilder(state: WaitingIf)
|
||||
|
||||
proc finishIfStmt(builder: var Builder, stmt: IfBuilder) =
|
||||
assert stmt.state != InBlock
|
||||
builder.addNewline()
|
||||
|
||||
template addIfStmt(builder: var Builder, stmt: out IfBuilder, body: typed) =
|
||||
stmt = initIfStmt(builder)
|
||||
body
|
||||
finishIfStmt(builder, stmt)
|
||||
|
||||
proc initElifBranch(builder: var Builder, stmt: var IfBuilder, cond: Snippet) =
|
||||
case stmt.state
|
||||
of WaitingIf:
|
||||
builder.add("if (")
|
||||
of WaitingElseIf:
|
||||
builder.add(" else if (")
|
||||
else: assert false, $stmt.state
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
stmt.state = InBlock
|
||||
|
||||
proc initElseBranch(builder: var Builder, stmt: var IfBuilder) =
|
||||
assert stmt.state == WaitingElseIf, $stmt.state
|
||||
builder.addLineEndIndent(" else {")
|
||||
stmt.state = InBlock
|
||||
|
||||
proc finishBranch(builder: var Builder, stmt: var IfBuilder) =
|
||||
builder.addDedent("}")
|
||||
stmt.state = WaitingElseIf
|
||||
|
||||
template addElifBranch(builder: var Builder, stmt: var IfBuilder, cond: Snippet, body: typed) =
|
||||
initElifBranch(builder, stmt, cond)
|
||||
body
|
||||
finishBranch(builder, stmt)
|
||||
|
||||
template addElseBranch(builder: var Builder, stmt: var IfBuilder, body: typed) =
|
||||
initElseBranch(builder, stmt)
|
||||
body
|
||||
finishBranch(builder, stmt)
|
||||
|
||||
proc initForRange(builder: var Builder, i, start, bound: Snippet, inclusive: bool = false) =
|
||||
builder.add("for (")
|
||||
builder.add(i)
|
||||
builder.add(" = ")
|
||||
builder.add(start)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
if inclusive:
|
||||
builder.add(" <= ")
|
||||
else:
|
||||
builder.add(" < ")
|
||||
builder.add(bound)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
builder.addLineEndIndent("++) {")
|
||||
|
||||
proc initForStep(builder: var Builder, i, start, bound, step: Snippet, inclusive: bool = false) =
|
||||
builder.add("for (")
|
||||
builder.add(i)
|
||||
builder.add(" = ")
|
||||
builder.add(start)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
if inclusive:
|
||||
builder.add(" <= ")
|
||||
else:
|
||||
builder.add(" < ")
|
||||
builder.add(bound)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
builder.add(" += ")
|
||||
builder.add(step)
|
||||
builder.addLineEndIndent(") {")
|
||||
|
||||
proc finishFor(builder: var Builder) {.inline.} =
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
template addForRangeExclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
|
||||
initForRange(builder, i, start, bound, false)
|
||||
body
|
||||
finishFor(builder)
|
||||
|
||||
template addForRangeInclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
|
||||
initForRange(builder, i, start, bound, true)
|
||||
body
|
||||
finishFor(builder)
|
||||
|
||||
template addSwitchStmt(builder: var Builder, val: Snippet, body: typed) =
|
||||
builder.add("switch (")
|
||||
builder.add(val)
|
||||
builder.addLineEnd(") {") # no indent
|
||||
body
|
||||
builder.addLineEnd("}")
|
||||
|
||||
template addSingleSwitchCase(builder: var Builder, val: Snippet, body: typed) =
|
||||
builder.add("case ")
|
||||
builder.add(val)
|
||||
builder.addLineEndIndent(":")
|
||||
body
|
||||
builder.addLineEndDedent("")
|
||||
|
||||
type
|
||||
SwitchCaseState = enum
|
||||
None, Of, Else, Finished
|
||||
SwitchCaseBuilder = object
|
||||
state: SwitchCaseState
|
||||
|
||||
proc addCase(builder: var Builder, info: var SwitchCaseBuilder, val: Snippet) =
|
||||
if info.state != Of:
|
||||
assert info.state == None
|
||||
info.state = Of
|
||||
builder.add("case ")
|
||||
builder.add(val)
|
||||
builder.addLineEndIndent(":")
|
||||
|
||||
proc addCaseRange(builder: var Builder, info: var SwitchCaseBuilder, first, last: Snippet) =
|
||||
if info.state != Of:
|
||||
assert info.state == None
|
||||
info.state = Of
|
||||
builder.add("case ")
|
||||
builder.add(first)
|
||||
builder.add(" ... ")
|
||||
builder.add(last)
|
||||
builder.addLineEndIndent(":")
|
||||
|
||||
proc addCaseElse(builder: var Builder, info: var SwitchCaseBuilder) =
|
||||
assert info.state == None
|
||||
info.state = Else
|
||||
builder.addLineEndIndent("default:")
|
||||
|
||||
template addSwitchCase(builder: var Builder, info: out SwitchCaseBuilder, caseBody, body: typed) =
|
||||
info = SwitchCaseBuilder(state: None)
|
||||
caseBody
|
||||
info.state = Finished
|
||||
body
|
||||
builder.addLineEndDedent("")
|
||||
|
||||
template addSwitchElse(builder: var Builder, body: typed) =
|
||||
builder.addLineEndIndent("default:")
|
||||
body
|
||||
builder.addLineEndDedent("")
|
||||
|
||||
proc addBreak(builder: var Builder) =
|
||||
builder.addLineEnd("break;")
|
||||
|
||||
type ScopeBuilder = object
|
||||
inside: bool
|
||||
|
||||
proc initScope(builder: var Builder): ScopeBuilder =
|
||||
builder.addLineEndIndent("{")
|
||||
result = ScopeBuilder(inside: true)
|
||||
|
||||
proc finishScope(builder: var Builder, scope: var ScopeBuilder) =
|
||||
assert scope.inside, "scope not inited"
|
||||
builder.addLineEndDedent("}")
|
||||
scope.inside = false
|
||||
|
||||
template addScope(builder: var Builder, body: typed) =
|
||||
builder.addLineEndIndent("{")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
type WhileBuilder = object
|
||||
inside: bool
|
||||
|
||||
proc initWhileStmt(builder: var Builder, cond: Snippet): WhileBuilder =
|
||||
builder.add("while (")
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
result = WhileBuilder(inside: true)
|
||||
|
||||
proc finishWhileStmt(builder: var Builder, stmt: var WhileBuilder) =
|
||||
assert stmt.inside, "while stmt not inited"
|
||||
builder.addLineEndDedent("}")
|
||||
stmt.inside = false
|
||||
|
||||
template addWhileStmt(builder: var Builder, cond: Snippet, body: typed) =
|
||||
builder.add("while (")
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
proc addLabel(builder: var Builder, name: TLabel) =
|
||||
builder.add(name)
|
||||
builder.addLineEnd(": ;")
|
||||
|
||||
proc addReturn(builder: var Builder) =
|
||||
builder.addLineEnd("return;")
|
||||
|
||||
proc addReturn(builder: var Builder, value: Snippet) =
|
||||
builder.add("return ")
|
||||
builder.add(value)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addGoto(builder: var Builder, label: TLabel) =
|
||||
builder.add("goto ")
|
||||
builder.add(label)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addComputedGoto(builder: var Builder, value: Snippet) =
|
||||
builder.add("goto *")
|
||||
builder.add(value)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addIncr(builder: var Builder, val: Snippet) =
|
||||
builder.add(val)
|
||||
builder.addLineEnd("++;")
|
||||
|
||||
proc addDecr(builder: var Builder, val: Snippet) =
|
||||
builder.add(val)
|
||||
builder.addLineEnd("--;")
|
||||
|
||||
proc addInPlaceOp(builder: var Builder, binOp: TypedBinaryOp, t: Snippet, a, b: Snippet) =
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(typedBinaryOperators[binOp])
|
||||
builder.add("= ")
|
||||
builder.add(b)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addInPlaceOp(builder: var Builder, binOp: UntypedBinaryOp, a, b: Snippet) =
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(untypedBinaryOperators[binOp])
|
||||
builder.add("= ")
|
||||
builder.add(b)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc cInPlaceOp(binOp: TypedBinaryOp, t: Snippet, a, b: Snippet): Snippet =
|
||||
result = ""
|
||||
result.add(a)
|
||||
result.add(' ')
|
||||
result.add(typedBinaryOperators[binOp])
|
||||
result.add("= ")
|
||||
result.add(b)
|
||||
result.add(";\n")
|
||||
|
||||
proc cInPlaceOp(binOp: UntypedBinaryOp, a, b: Snippet): Snippet =
|
||||
result = ""
|
||||
result.add(a)
|
||||
result.add(' ')
|
||||
result.add(untypedBinaryOperators[binOp])
|
||||
result.add("= ")
|
||||
result.add(b)
|
||||
result.add(";\n")
|
||||
|
||||
template addCPragma(builder: var Builder, val: Snippet) =
|
||||
builder.addNewline()
|
||||
builder.add("#pragma ")
|
||||
builder.add(val)
|
||||
builder.addNewline()
|
||||
|
||||
proc addDiscard(builder: var Builder, val: Snippet) =
|
||||
builder.add("(void)")
|
||||
builder.add(val)
|
||||
builder.addLineEnd(";")
|
||||
@@ -84,20 +84,21 @@ proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
|
||||
let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor)
|
||||
var op = initLocExpr(p, newSymNode(dtor))
|
||||
var callee = rdLoc(op)
|
||||
let destroy = if dtor.typ.firstParamType.kind == tyVar:
|
||||
callee & "(&" & rdLoc(tmp) & ")"
|
||||
let destroyArg =
|
||||
if dtor.typ.firstParamType.kind == tyVar:
|
||||
cAddr(rdLoc(tmp))
|
||||
else:
|
||||
callee & "(" & rdLoc(tmp) & ")"
|
||||
rdLoc(tmp)
|
||||
let destroy = cCall(callee, destroyArg)
|
||||
raiseExitCleanup(p, destroy)
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
callee, params: Rope) =
|
||||
result: var Builder, call: var CallBuilder) =
|
||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||
genLineDir(p, ri)
|
||||
var pl = callee & "(" & params
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if typ.returnType != nil:
|
||||
@@ -106,7 +107,6 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
# perhaps generate no temp if the call doesn't have side effects
|
||||
flags.incl needTempForOpenArray
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if params.len != 0: pl.add(", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
|
||||
# Great, we can use 'd':
|
||||
@@ -114,34 +114,40 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
discard "resetLoc(p, d)"
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add(");\n")
|
||||
line(p, cpsStmts, pl)
|
||||
let rad = addrLoc(p.config, d)
|
||||
result.addArgument(call):
|
||||
result.add(rad)
|
||||
result.finishCallBuilder(call)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(result))
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add(");\n")
|
||||
line(p, cpsStmts, pl)
|
||||
let ratmp = addrLoc(p.config, tmp)
|
||||
result.addArgument(call):
|
||||
result.add(ratmp)
|
||||
result.finishCallBuilder(call)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(result))
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
pl.add(")")
|
||||
result.finishCallBuilder(call)
|
||||
if p.module.compileToCpp:
|
||||
if lfSingleUse in d.flags:
|
||||
# do not generate spurious temporaries for C++! For C we're better off
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
d.r = pl
|
||||
d.snippet = extract(result)
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone and p.splitDecls == 0:
|
||||
d = getTempCpp(p, typ.returnType, pl)
|
||||
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
|
||||
d = getTempCpp(p, typ.returnType, extract(result))
|
||||
else:
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
var list = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
list.snippet = extract(result)
|
||||
genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
elif isHarmlessStore(p, canRaise, d):
|
||||
@@ -151,26 +157,27 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, flags) # no need for deep copying
|
||||
list.snippet = extract(result)
|
||||
genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying
|
||||
if canRaise:
|
||||
if not (useTemp and cleanupTemp(p, typ.returnType, d)):
|
||||
raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
var list = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, tmp, list, flags) # no need for deep copying
|
||||
list.snippet = extract(result)
|
||||
genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying
|
||||
if canRaise:
|
||||
if not cleanupTemp(p, typ.returnType, tmp):
|
||||
raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
else:
|
||||
pl.add(");\n")
|
||||
line(p, cpsStmts, pl)
|
||||
finishCallBuilder(result, call)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(result))
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
|
||||
|
||||
proc reifiedOpenArray(n: PNode): bool {.inline.} =
|
||||
var x = n
|
||||
@@ -191,57 +198,57 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||
var a = initLocExpr(p, q[1])
|
||||
var b = initLocExpr(p, q[2])
|
||||
var c = initLocExpr(p, q[3])
|
||||
# but first produce the required index checks:
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, a, b, c)
|
||||
if prepareForMutation:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
# 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
|
||||
# in the `genDeref`. We need to skip these ptrs here
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
|
||||
# but first produce the required index checks:
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, a, b, c, ty)
|
||||
if prepareForMutation:
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
let dest = getTypeDesc(p.module, destType)
|
||||
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
||||
let ra = rdLoc(a)
|
||||
let rb = rdLoc(b)
|
||||
let rc = rdLoc(c)
|
||||
let lengthExpr = cOp(Add, NimInt, cOp(Sub, NimInt, rc, rb), cIntValue(1))
|
||||
case ty.kind
|
||||
of tyArray:
|
||||
let first = toInt64(firstOrd(p.config, ty))
|
||||
if first == 0:
|
||||
result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, rb)), lengthExpr)
|
||||
else:
|
||||
var lit = newRopeAppender()
|
||||
intLiteral(first, lit)
|
||||
result = ("($4*)($1)+(($2)-($3))" %
|
||||
[rdLoc(a), rdLoc(b), lit, dest],
|
||||
lengthExpr)
|
||||
let lit = cIntLiteral(first)
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, cOp(Sub, NimInt, rb, lit))), lengthExpr)
|
||||
of tyOpenArray, tyVarargs:
|
||||
if reifiedOpenArray(q[1]):
|
||||
result = ("($3*)($1.Field0)+($2)" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
else:
|
||||
result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
let data = if reifiedOpenArray(q[1]): dotField(ra, "Field0") else: ra
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, data, rb)), lengthExpr)
|
||||
of tyUncheckedArray, tyCstring:
|
||||
result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, rb)), lengthExpr)
|
||||
of tyString, tySequence:
|
||||
let atyp = skipTypes(a.t, abstractInst)
|
||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
|
||||
optSeqDestructors in p.config.globalOptions:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
var val: Snippet
|
||||
if atyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||
result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
|
||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
|
||||
lengthExpr)
|
||||
val = cDeref(ra)
|
||||
else:
|
||||
result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
|
||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
|
||||
lengthExpr)
|
||||
val = ra
|
||||
result = (
|
||||
cIfExpr(dataFieldAccessor(p, val),
|
||||
cCast(ptrType(dest), cOp(Add, NimInt, dataField(p, val), rb)),
|
||||
NimNil),
|
||||
lengthExpr)
|
||||
else:
|
||||
result = ("", "")
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
|
||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
|
||||
var q = skipConv(n)
|
||||
var skipped = false
|
||||
while q.kind == nkStmtListExpr and q.len > 0:
|
||||
@@ -256,42 +263,66 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||
genStmts(p, q[i])
|
||||
q = q.lastSon
|
||||
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType)
|
||||
result.add x & ", " & y
|
||||
result.add(x)
|
||||
result.addArgumentSeparator()
|
||||
result.add(y)
|
||||
else:
|
||||
var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
|
||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
let ra = rdLoc(a)
|
||||
if reifiedOpenArray(n):
|
||||
if a.t.kind in {tyVar, tyLent}:
|
||||
result.add "$1->Field0, $1->Field1" % [rdLoc(a)]
|
||||
result.add(derefField(ra, "Field0"))
|
||||
result.addArgumentSeparator()
|
||||
result.add(derefField(ra, "Field1"))
|
||||
else:
|
||||
result.add "$1.Field0, $1.Field1" % [rdLoc(a)]
|
||||
result.add(dotField(ra, "Field0"))
|
||||
result.addArgumentSeparator()
|
||||
result.add(dotField(ra, "Field1"))
|
||||
else:
|
||||
result.add "$1, $1Len_0" % [rdLoc(a)]
|
||||
result.add(ra)
|
||||
result.addArgumentSeparator()
|
||||
result.add(ra & "Len_0")
|
||||
of tyString, tySequence:
|
||||
let ntyp = skipTypes(n.typ, abstractInst)
|
||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
|
||||
optSeqDestructors in p.config.globalOptions:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||
var t = TLoc(r: "(*$1)" % [a.rdLoc])
|
||||
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
||||
let ra = a.rdLoc
|
||||
var t = TLoc(snippet: cDeref(ra))
|
||||
let lt = lenExpr(p, t)
|
||||
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
|
||||
result.addArgumentSeparator()
|
||||
result.add(lt)
|
||||
else:
|
||||
result.add "($4) ? ($1$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
|
||||
let ra = a.rdLoc
|
||||
let la = lenExpr(p, a)
|
||||
result.add(cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
|
||||
result.addArgumentSeparator()
|
||||
result.add(la)
|
||||
of tyArray:
|
||||
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
|
||||
let ra = rdLoc(a)
|
||||
result.add(ra)
|
||||
result.addArgumentSeparator()
|
||||
result.addIntValue(lengthOrd(p.config, a.t))
|
||||
of tyPtr, tyRef:
|
||||
case elementType(a.t).kind
|
||||
of tyString, tySequence:
|
||||
var t = TLoc(r: "(*$1)" % [a.rdLoc])
|
||||
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
||||
let ra = a.rdLoc
|
||||
var t = TLoc(snippet: cDeref(ra))
|
||||
let lt = lenExpr(p, t)
|
||||
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
|
||||
result.addArgumentSeparator()
|
||||
result.add(lt)
|
||||
of tyArray:
|
||||
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, elementType(a.t)))]
|
||||
let ra = rdLoc(a)
|
||||
result.add(ra)
|
||||
result.addArgumentSeparator()
|
||||
result.addIntValue(lengthOrd(p.config, elementType(a.t)))
|
||||
else:
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
@@ -307,15 +338,16 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
|
||||
else:
|
||||
result = a
|
||||
|
||||
proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||
proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||
result = getTemp(p, a.lode.typ, needsInit=false)
|
||||
genAssignment(p, result, a, {})
|
||||
|
||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
|
||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
|
||||
var a = initLocExpr(p, n[0])
|
||||
appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
|
||||
let ra = withTmpIfNeeded(p, a, needsTmp).rdLoc
|
||||
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
|
||||
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
genArgStringToCString(p, n, result, needsTmp)
|
||||
@@ -326,32 +358,41 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
(optByRef notin param.options or not p.module.compileToCpp):
|
||||
a = initLocExpr(p, n)
|
||||
if n.kind in {nkCharLit..nkNilLit}:
|
||||
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
|
||||
addAddrLoc(p.config, expressionsNeedsTmp(p, a), result)
|
||||
else:
|
||||
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
|
||||
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
|
||||
n.kind == nkHiddenAddr:
|
||||
a = initLocExprSingleUse(p, n[0])
|
||||
# bug #23748: we need to introduce a temporary here. The expression type
|
||||
# will be a reference in C++ and we cannot create a temporary reference
|
||||
# variable. Thus, we create a temporary pointer variable instead.
|
||||
let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
|
||||
if needsIndirect:
|
||||
n.typ() = n.typ.exactReplica
|
||||
n.typ.flags.incl tfVarIsPtr
|
||||
a = initLocExprSingleUse(p, n)
|
||||
a = withTmpIfNeeded(p, a, needsTmp)
|
||||
if needsIndirect: a.flags.incl lfIndirect
|
||||
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
|
||||
# means '*T'. See posix.nim for lots of examples that do that in the wild.
|
||||
let callee = call[0]
|
||||
if callee.kind == nkSym and
|
||||
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
|
||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
|
||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and
|
||||
needsIndirect:
|
||||
addAddrLoc(p.config, a, result)
|
||||
else:
|
||||
addRdLoc(a, result)
|
||||
else:
|
||||
a = initLocExprSingleUse(p, n)
|
||||
if param.typ.kind in abstractPtrs:
|
||||
if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
|
||||
let typ = skipTypes(param.typ, abstractPtrs)
|
||||
if typ.sym != nil and sfImportc in typ.sym.flags:
|
||||
a.r = "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
|
||||
if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
|
||||
a.snippet = cCast(getTypeDesc(p.module, param.typ), rdCharLoc(a))
|
||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
||||
#assert result != nil
|
||||
|
||||
proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
|
||||
proc genArgNoParam(p: BProc, n: PNode; result: var Builder; needsTmp = false) =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
genArgStringToCString(p, n, result, needsTmp)
|
||||
@@ -377,35 +418,6 @@ proc skipTrivialIndirections(n: PNode): PNode =
|
||||
result = result[1]
|
||||
else: break
|
||||
|
||||
proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
|
||||
case n.kind:
|
||||
of nkLiterals, nkIdent, nkFormalParams: discard
|
||||
of nkSym:
|
||||
if mutate: result.add n
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
getPotentialWrites(n[0], true, result)
|
||||
getPotentialWrites(n[1], mutate, result)
|
||||
of nkAddr, nkHiddenAddr:
|
||||
getPotentialWrites(n[0], true, result)
|
||||
of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr:
|
||||
getPotentialWrites(n[0], mutate, result)
|
||||
of nkCallKinds:
|
||||
case n.getMagic:
|
||||
of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mAddr, mNew, mNewFinalize, mWasMoved, mDestroy:
|
||||
getPotentialWrites(n[1], true, result)
|
||||
for i in 2..<n.len:
|
||||
getPotentialWrites(n[i], mutate, result)
|
||||
of mSwap:
|
||||
for i in 1..<n.len:
|
||||
getPotentialWrites(n[i], true, result)
|
||||
else:
|
||||
for i in 1..<n.len:
|
||||
getPotentialWrites(n[i], mutate, result)
|
||||
else:
|
||||
for s in n:
|
||||
getPotentialWrites(s, mutate, result)
|
||||
|
||||
proc getPotentialReads(n: PNode; result: var seq[PNode]) =
|
||||
case n.kind:
|
||||
of nkLiterals, nkIdent, nkFormalParams: discard
|
||||
@@ -414,7 +426,7 @@ proc getPotentialReads(n: PNode; result: var seq[PNode]) =
|
||||
for s in n:
|
||||
getPotentialReads(s, result)
|
||||
|
||||
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
||||
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Builder, argBuilder: var CallBuilder) =
|
||||
# We must generate temporaries in cases like #14396
|
||||
# to keep the strict Left-To-Right evaluation
|
||||
var needTmp = newSeq[bool](ri.len - 1)
|
||||
@@ -430,25 +442,28 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
||||
if not needTmp[i - 1]:
|
||||
needTmp[i - 1] = potentialAlias(n, potentialWrites)
|
||||
getPotentialWrites(ri[i], false, potentialWrites)
|
||||
if ri[i].kind in {nkHiddenAddr, nkAddr}:
|
||||
# Optimization: don't use a temp, if we would only take the address anyway
|
||||
needTmp[i - 1] = false
|
||||
when false:
|
||||
# this optimization is wrong, see bug #23748
|
||||
if ri[i].kind in {nkHiddenAddr, nkAddr}:
|
||||
# Optimization: don't use a temp, if we would only take the address anyway
|
||||
needTmp[i - 1] = false
|
||||
|
||||
var oldLen = result.len
|
||||
for i in 1..<ri.len:
|
||||
if i < typ.n.len:
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
let paramType = typ.n[i]
|
||||
if not paramType.typ.isCompileTimeOnly:
|
||||
if oldLen != result.len:
|
||||
result.add(", ")
|
||||
oldLen = result.len
|
||||
genArg(p, ri[i], paramType.sym, ri, result, needTmp[i-1])
|
||||
var arg = newBuilder("")
|
||||
genArg(p, ri[i], paramType.sym, ri, arg, needTmp[i-1])
|
||||
if arg.buf.len != 0:
|
||||
result.addArgument(argBuilder):
|
||||
result.add(extract(arg))
|
||||
else:
|
||||
if oldLen != result.len:
|
||||
result.add(", ")
|
||||
oldLen = result.len
|
||||
genArgNoParam(p, ri[i], result, needTmp[i-1])
|
||||
var arg = newBuilder("")
|
||||
genArgNoParam(p, ri[i], arg, needTmp[i-1])
|
||||
if arg.buf.len != 0:
|
||||
result.addArgument(argBuilder):
|
||||
result.add(extract(arg))
|
||||
|
||||
proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
|
||||
if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
|
||||
@@ -462,21 +477,39 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var typ = skipTypes(ri[0].typ, abstractInstOwned)
|
||||
assert(typ.kind == tyProc)
|
||||
|
||||
var params = newRopeAppender()
|
||||
genParams(p, ri, typ, params)
|
||||
|
||||
var callee = rdLoc(op)
|
||||
if p.hcrOn and ri[0].kind == nkSym:
|
||||
callee.addActualSuffixForHCR(p.module.module, ri[0].sym)
|
||||
fixupCall(p, le, ri, d, callee, params)
|
||||
|
||||
var res = newBuilder("")
|
||||
var call = initCallBuilder(res, callee)
|
||||
genParams(p, ri, typ, res, call)
|
||||
fixupCall(p, le, ri, d, res, call)
|
||||
|
||||
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
|
||||
proc addComma(r: Rope): Rope =
|
||||
if r.len == 0: r else: r & ", "
|
||||
template callProc(rp, params, pTyp: Snippet): Snippet =
|
||||
let e = dotField(rp, "ClE_0")
|
||||
let p = dotField(rp, "ClP_0")
|
||||
let eCall =
|
||||
# note `params` here is actually multiple params
|
||||
if params.len == 0:
|
||||
cCall(p, e)
|
||||
else:
|
||||
cCall(p, params, e)
|
||||
cIfExpr(e,
|
||||
eCall,
|
||||
cCall(cCast(pTyp, p), params))
|
||||
|
||||
const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
|
||||
const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
|
||||
template callIter(rp, params: Snippet): Snippet =
|
||||
# we know the env exists
|
||||
let e = dotField(rp, "ClE_0")
|
||||
let p = dotField(rp, "ClP_0")
|
||||
# note `params` here is actually multiple params
|
||||
if params.len == 0:
|
||||
cCall(p, e)
|
||||
else:
|
||||
cCall(p, params, e)
|
||||
|
||||
var op = initLocExpr(p, ri[0])
|
||||
|
||||
@@ -484,20 +517,23 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var typ = skipTypes(ri[0].typ, abstractInstOwned)
|
||||
assert(typ.kind == tyProc)
|
||||
|
||||
var pl = newRopeAppender()
|
||||
genParams(p, ri, typ, pl)
|
||||
var params = newBuilder("")
|
||||
var argBuilder = default(CallBuilder) # not initCallBuilder, we just want the params
|
||||
genParams(p, ri, typ, params, argBuilder)
|
||||
|
||||
template genCallPattern {.dirty.} =
|
||||
if tfIterator in typ.flags:
|
||||
lineF(p, cpsStmts, PatIter & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
|
||||
else:
|
||||
lineF(p, cpsStmts, PatProc & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
|
||||
let rp = rdLoc(op)
|
||||
let pars = extract(params)
|
||||
p.s(cpsStmts).addStmt():
|
||||
if tfIterator in typ.flags:
|
||||
p.s(cpsStmts).add(callIter(rp, pars))
|
||||
else:
|
||||
p.s(cpsStmts).add(callProc(rp, pars, rawProc))
|
||||
|
||||
let rawProc = getClosureType(p.module, typ, clHalf)
|
||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||
if typ.returnType != nil:
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if ri.len > 1: pl.add(", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
|
||||
# Great, we can use 'd':
|
||||
@@ -506,12 +542,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
discard "resetLoc(p, d)"
|
||||
pl.add(addrLoc(p.config, d))
|
||||
params.addArgument(argBuilder):
|
||||
params.add(addrLoc(p.config, d))
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
params.addArgument(argBuilder):
|
||||
params.add(addrLoc(p.config, tmp))
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
@@ -519,20 +557,24 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
|
||||
let rp = rdLoc(op)
|
||||
let pars = extract(params)
|
||||
if tfIterator in typ.flags:
|
||||
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
list.snippet = callIter(rp, pars)
|
||||
else:
|
||||
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
list.snippet = callProc(rp, pars, rawProc)
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
|
||||
let rp = rdLoc(op)
|
||||
let pars = extract(params)
|
||||
if tfIterator in typ.flags:
|
||||
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
list.snippet = callIter(rp, pars)
|
||||
else:
|
||||
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
list.snippet = callProc(rp, pars, rawProc)
|
||||
genAssignment(p, tmp, list, {})
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
@@ -540,8 +582,8 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
|
||||
argsCounter: var int) =
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder;
|
||||
argBuilder: var CallBuilder) =
|
||||
if i < typ.n.len:
|
||||
# 'var T' is 'T&' in C++. This means we ignore the request of
|
||||
# any nkHiddenAddr when it's a 'var T'.
|
||||
@@ -550,20 +592,17 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
|
||||
if paramType.typ.isCompileTimeOnly:
|
||||
discard
|
||||
elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i][0], result)
|
||||
inc argsCounter
|
||||
result.addArgument(argBuilder):
|
||||
genArgNoParam(p, ri[i][0], result)
|
||||
else:
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
|
||||
inc argsCounter
|
||||
result.addArgument(argBuilder):
|
||||
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
|
||||
else:
|
||||
if tfVarargs notin typ.flags:
|
||||
localError(p.config, ri.info, "wrong argument count")
|
||||
else:
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i], result)
|
||||
inc argsCounter
|
||||
result.addArgument(argBuilder):
|
||||
genArgNoParam(p, ri[i], result)
|
||||
|
||||
discard """
|
||||
Dot call syntax in C++
|
||||
@@ -620,7 +659,7 @@ proc skipAddrDeref(node: PNode): PNode =
|
||||
else:
|
||||
result = node
|
||||
|
||||
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
|
||||
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
|
||||
# for better or worse c2nim translates the 'this' argument to a 'var T'.
|
||||
# However manual wrappers may also use 'ptr T'. In any case we support both
|
||||
# for convenience.
|
||||
@@ -655,15 +694,15 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
|
||||
genArgNoParam(p, ri, result) #, typ.n[i].sym)
|
||||
result.add(".")
|
||||
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) =
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Builder) =
|
||||
var i = 0
|
||||
var j = 1
|
||||
while i < pat.len:
|
||||
case pat[i]
|
||||
of '@':
|
||||
var argsCounter = 0
|
||||
var callBuilder = default(CallBuilder) # not init call builder
|
||||
for k in j..<ri.len:
|
||||
genOtherArg(p, ri, k, typ, result, argsCounter)
|
||||
genOtherArg(p, ri, k, typ, result, callBuilder)
|
||||
inc i
|
||||
of '#':
|
||||
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
|
||||
@@ -673,11 +712,11 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro
|
||||
if pat[i+1] == '+': genArgNoParam(p, ri[0], result)
|
||||
result.add("(")
|
||||
if 1 < ri.len:
|
||||
var argsCounterB = 0
|
||||
genOtherArg(p, ri, 1, typ, result, argsCounterB)
|
||||
var callBuilder: CallBuilder = default(CallBuilder)
|
||||
genOtherArg(p, ri, 1, typ, result, callBuilder)
|
||||
for k in j+1..<ri.len:
|
||||
var argsCounterB = 0
|
||||
genOtherArg(p, ri, k, typ, result, argsCounterB)
|
||||
var callBuilder: CallBuilder = default(CallBuilder)
|
||||
genOtherArg(p, ri, k, typ, result, callBuilder)
|
||||
result.add(")")
|
||||
else:
|
||||
localError(p.config, ri.info, "call expression expected for C++ pattern")
|
||||
@@ -691,15 +730,15 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro
|
||||
genArgNoParam(p, arg, result)
|
||||
#result.add debugTree(arg, 0, 10)
|
||||
else:
|
||||
var argsCounter = 0
|
||||
genOtherArg(p, ri, j, typ, result, argsCounter)
|
||||
var callBuilder = default(CallBuilder) # not init call builder
|
||||
genOtherArg(p, ri, j, typ, result, callBuilder)
|
||||
inc j
|
||||
inc i
|
||||
of '\'':
|
||||
var idx, stars: int = 0
|
||||
if scanCppGenericSlot(pat, i, idx, stars):
|
||||
var t = resolveStarsInCppType(typ, idx, stars)
|
||||
if t == nil: result.add("void")
|
||||
if t == nil: result.add(CVoid)
|
||||
else: result.add(getTypeDesc(p.module, t))
|
||||
else:
|
||||
let start = i
|
||||
@@ -715,10 +754,10 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
# don't call '$' here for efficiency:
|
||||
let pat = $ri[0].sym.loc.r
|
||||
let pat = $ri[0].sym.loc.snippet
|
||||
internalAssert p.config, pat.len > 0
|
||||
if pat.contains({'#', '(', '@', '\''}):
|
||||
var pl = newRopeAppender()
|
||||
var pl = newBuilder("")
|
||||
genPatternCall(p, ri, pat, typ, pl)
|
||||
# simpler version of 'fixupCall' that works with the pl+params combination:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
@@ -728,43 +767,43 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
d.r = pl
|
||||
d.snippet = extract(pl)
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
list.snippet = extract(pl)
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add(";\n")
|
||||
line(p, cpsStmts, pl)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
else:
|
||||
var pl = newRopeAppender()
|
||||
var argsCounter = 0
|
||||
var pl = newBuilder("")
|
||||
if 1 < ri.len:
|
||||
genThisArg(p, ri, 1, typ, pl)
|
||||
pl.add(op.r)
|
||||
var params = newRopeAppender()
|
||||
pl.add(op.snippet)
|
||||
var res = newBuilder("")
|
||||
var call = initCallBuilder(res, extract(pl))
|
||||
for i in 2..<ri.len:
|
||||
genOtherArg(p, ri, i, typ, params, argsCounter)
|
||||
fixupCall(p, le, ri, d, pl, params)
|
||||
genOtherArg(p, ri, i, typ, res, call)
|
||||
fixupCall(p, le, ri, d, res, call)
|
||||
|
||||
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
# generates a crappy ObjC call
|
||||
var op = initLocExpr(p, ri[0])
|
||||
var pl = "["
|
||||
var pl = newBuilder("[")
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
|
||||
# don't call '$' here for efficiency:
|
||||
let pat = $ri[0].sym.loc.r
|
||||
let pat = $ri[0].sym.loc.snippet
|
||||
internalAssert p.config, pat.len > 0
|
||||
var start = 3
|
||||
if ' ' in pat:
|
||||
start = 1
|
||||
pl.add(op.r)
|
||||
pl.add(op.snippet)
|
||||
if ri.len > 1:
|
||||
pl.add(": ")
|
||||
genArg(p, ri[1], typ.n[1].sym, ri, pl)
|
||||
@@ -773,7 +812,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
if ri.len > 1:
|
||||
genArg(p, ri[1], typ.n[1].sym, ri, pl)
|
||||
pl.add(" ")
|
||||
pl.add(op.r)
|
||||
pl.add(op.snippet)
|
||||
if ri.len > 2:
|
||||
pl.add(": ")
|
||||
genArg(p, ri[2], typ.n[2].sym, ri, pl)
|
||||
@@ -795,24 +834,27 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add("Result: ")
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add("];\n")
|
||||
line(p, cpsStmts, pl)
|
||||
pl.add("]")
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add("];\n")
|
||||
line(p, cpsStmts, pl)
|
||||
pl.add("]")
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add("]")
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, ri, OnUnknown)
|
||||
list.r = pl
|
||||
list.snippet = extract(pl)
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add("];\n")
|
||||
line(p, cpsStmts, pl)
|
||||
pl.add("]")
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
|
||||
proc notYetAlive(n: PNode): bool {.inline.} =
|
||||
let r = getRoot(n)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,52 +36,84 @@ proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
|
||||
cgsym(m, "TGenericSeq")
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n",
|
||||
[tmp, makeCString(s), rope(s.len)])
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(AlwaysConst, name = tmp):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "Sup", typ = "TGenericSeq")
|
||||
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
|
||||
do:
|
||||
var strInit: StructInitializer
|
||||
res.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
res.addField(strInit, name = "Sup"):
|
||||
var seqInit: StructInitializer
|
||||
res.addStructInitializer(seqInit, kind = siOrderedStruct):
|
||||
res.addField(seqInit, name = "len"):
|
||||
res.addIntValue(s.len)
|
||||
res.addField(seqInit, name = "reserved"):
|
||||
res.add(cCast(NimInt, cOp(BitOr, NimUint, cCast(NimUint, cIntValue(s.len)), NimStrlitFlag)))
|
||||
res.addField(strInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
|
||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
|
||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Builder) =
|
||||
if s.isNil:
|
||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
|
||||
else:
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var name: string = ""
|
||||
if id == m.labels:
|
||||
# string literal not found in the cache:
|
||||
appcg(m, result, "((#NimStringDesc*) &", [])
|
||||
genStringLiteralDataOnlyV1(m, n.strVal, result)
|
||||
result.add ")"
|
||||
genStringLiteralDataOnlyV1(m, n.strVal, name)
|
||||
else:
|
||||
appcg(m, result, "((#NimStringDesc*) &$1$2)",
|
||||
[m.tmpBase, id])
|
||||
name = m.tmpBase & $id
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), cAddr(name)))
|
||||
|
||||
# ------ Version 2: destructor based strings and seqs -----------------------
|
||||
|
||||
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
|
||||
m.s[cfsStrData].addf("static $4 struct {$n" &
|
||||
" NI cap; NIM_CHAR data[$2+1];$n" &
|
||||
"} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
|
||||
[result, rope(s.len), makeCString(s),
|
||||
rope(if isConst: "const" else: "")])
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = result):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "cap", typ = NimInt)
|
||||
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
|
||||
do:
|
||||
var structInit: StructInitializer
|
||||
res.addStructInitializer(structInit, kind = siOrderedStruct):
|
||||
res.addField(structInit, name = "cap"):
|
||||
res.add(cOp(BitOr, NimInt, cIntValue(s.len), NimStrlitFlag))
|
||||
res.addField(structInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
|
||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Builder) =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var litName: string
|
||||
if id == m.labels:
|
||||
let pureLit = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
cgsym(m, "NimStrPayload")
|
||||
cgsym(m, "NimStringV2")
|
||||
# string literal not found in the cache:
|
||||
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
|
||||
litName = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, litName, isConst)
|
||||
else:
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[tmp, rope(n.strVal.len), m.tmpBase & rope(id),
|
||||
rope(if isConst: "const" else: "")])
|
||||
litName = m.tmpBase & $id
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
var res = newBuilder("")
|
||||
res.addVarWithInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = tmp,
|
||||
typ = "NimStringV2"):
|
||||
var strInit: StructInitializer
|
||||
res.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
res.addField(strInit, name = "len"):
|
||||
res.addIntValue(n.strVal.len)
|
||||
res.addField(strInit, name = "p"):
|
||||
res.add(cCast(ptrType("NimStrPayload"), cAddr(litName)))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var pureLit: Rope
|
||||
if id == m.labels:
|
||||
@@ -92,7 +124,12 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Ro
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
||||
else:
|
||||
pureLit = m.tmpBase & rope(id)
|
||||
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
|
||||
var strInit: StructInitializer
|
||||
result.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
result.addField(strInit, name = "len"):
|
||||
result.addIntValue(n.strVal.len)
|
||||
result.addField(strInit, name = "p"):
|
||||
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
|
||||
|
||||
# ------ Version selector ---------------------------------------------------
|
||||
|
||||
@@ -107,10 +144,10 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
else:
|
||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
||||
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
|
||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Builder) =
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
|
||||
|
||||
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
|
||||
proc genStringLiteral(m: BModule; n: PNode; result: var Builder) =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: genStringLiteralV1(m, n, result)
|
||||
of 2: genStringLiteralV2(m, n, isConst = true, result)
|
||||
|
||||
@@ -24,28 +24,31 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
||||
of nkRecCase:
|
||||
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
|
||||
let disc = n[0].sym
|
||||
if disc.loc.r == "": fillObjectFields(p.module, typ)
|
||||
if disc.loc.snippet == "": fillObjectFields(p.module, typ)
|
||||
if disc.loc.t == nil:
|
||||
internalError(p.config, n.info, "specializeResetN()")
|
||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(p, branch)
|
||||
else:
|
||||
lineF(p, cpsStmts, "default:$n", [])
|
||||
specializeResetN(p, accessor, lastSon(branch), typ)
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
lineF(p, cpsStmts, "} $n", [])
|
||||
specializeResetT(p, "$1.$2" % [accessor, disc.loc.r], disc.loc.t)
|
||||
let discField = dotField(accessor, disc.loc.snippet)
|
||||
p.s(cpsStmts).addSwitchStmt(discField):
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
var caseBuilder: SwitchCaseBuilder
|
||||
p.s(cpsStmts).addSwitchCase(caseBuilder):
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(p, branch, caseBuilder)
|
||||
else:
|
||||
p.s(cpsStmts).addCaseElse(caseBuilder)
|
||||
do:
|
||||
specializeResetN(p, accessor, lastSon(branch), typ)
|
||||
p.s(cpsStmts).addBreak()
|
||||
specializeResetT(p, discField, disc.loc.t)
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.r == "": fillObjectFields(p.module, typ)
|
||||
if field.loc.snippet == "": fillObjectFields(p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(p.config, n.info, "specializeResetN()")
|
||||
specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
||||
specializeResetT(p, dotField(accessor, field.loc.snippet), field.loc.t)
|
||||
else: internalError(p.config, n.info, "specializeResetN()")
|
||||
|
||||
proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
@@ -58,45 +61,56 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
of tyArray:
|
||||
let arraySize = lengthOrd(p.config, typ.indexType)
|
||||
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.r, arraySize])
|
||||
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ.elementType)
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
|
||||
specializeResetT(p, subscript(accessor, i.snippet), typ.elementType)
|
||||
of tyObject:
|
||||
var x = typ.baseClass
|
||||
if x != nil: x = x.skipTypes(skipPtrs)
|
||||
specializeResetT(p, accessor.parentObj(p.module), x)
|
||||
if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
|
||||
if typ.n != nil:
|
||||
if typ.sym != nil and sfImportc in typ.sym.flags:
|
||||
# imported C struct, nimZeroMem
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
|
||||
cCast(ptrType(CPointer), cAddr(accessor)),
|
||||
cSizeof(getTypeDesc(p.module, typ)))
|
||||
else:
|
||||
specializeResetN(p, accessor, typ.n, typ)
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i, a in typ.ikids:
|
||||
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
|
||||
specializeResetT(p, dotField(accessor, "Field" & $i), a)
|
||||
|
||||
of tyString, tyRef, tySequence:
|
||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
|
||||
cCast(ptrType(CPointer), cAddr(accessor)),
|
||||
NimNil)
|
||||
|
||||
of tyProc:
|
||||
if typ.callConv == ccClosure:
|
||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1.ClE_0, NIM_NIL);$n", [accessor])
|
||||
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
|
||||
cCast(ptrType(CPointer), cAddr(dotField(accessor, "ClE_0"))),
|
||||
NimNil)
|
||||
p.s(cpsStmts).addFieldAssignment(accessor, "ClP_0", NimNil)
|
||||
else:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
p.s(cpsStmts).addAssignment(accessor, NimNil)
|
||||
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
|
||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
|
||||
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
p.s(cpsStmts).addAssignment(accessor, NimNil)
|
||||
of tySet:
|
||||
case mapSetType(p.config, typ)
|
||||
of ctArray:
|
||||
lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
|
||||
[accessor, getTypeDesc(p.module, typ)])
|
||||
let t = getTypeDesc(p.module, typ)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
|
||||
accessor,
|
||||
cSizeof(t))
|
||||
of ctInt8, ctInt16, ctInt32, ctInt64:
|
||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
|
||||
else:
|
||||
raiseAssert "unexpected set type kind"
|
||||
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
||||
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
|
||||
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
|
||||
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
|
||||
discard
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,12 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
|
||||
if emulatedThreadVars(p.config) and threadVarAccessed notin p.flags:
|
||||
p.flags.incl threadVarAccessed
|
||||
incl p.module.flags, usesThreadVars
|
||||
p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", [])
|
||||
p.procSec(cpsInit).add(
|
||||
ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n", []))
|
||||
p.procSec(cpsLocals).addVar(kind = Local,
|
||||
name = "NimTV_",
|
||||
typ = ptrType("NimThreadVars"))
|
||||
p.procSec(cpsInit).addAssignment("NimTV_",
|
||||
cCast(ptrType("NimThreadVars"),
|
||||
cCall(cgsymValue(p.module, "GetThreadLocalVars"))))
|
||||
|
||||
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||
if emulatedThreadVars(m.config):
|
||||
@@ -30,30 +33,32 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||
# allocator for it :-(
|
||||
if not containsOrIncl(m.g.nimtvDeclared, s.id):
|
||||
m.g.nimtvDeps.add(s.loc.t)
|
||||
m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
|
||||
m.g.nimtv.addField(name = s.loc.snippet, typ = getTypeDesc(m, s.loc.t))
|
||||
else:
|
||||
if isExtern: m.s[cfsVars].add("extern ")
|
||||
elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
|
||||
else: m.s[cfsVars].add("N_LIB_PRIVATE ")
|
||||
if optThreads in m.config.globalOptions:
|
||||
let sym = s.typ.sym
|
||||
if sym != nil and sfCppNonPod in sym.flags:
|
||||
m.s[cfsVars].add("NIM_THREAD_LOCAL ")
|
||||
else: m.s[cfsVars].add("NIM_THREADVAR ")
|
||||
m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
|
||||
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
|
||||
let vis =
|
||||
if isExtern: Extern
|
||||
elif lfExportLib in s.loc.flags: ExportLibVar
|
||||
else: Private
|
||||
m.s[cfsVars].addVar(m, s,
|
||||
name = s.loc.snippet,
|
||||
typ = getTypeDesc(m, s.loc.t),
|
||||
kind = Threadvar,
|
||||
visibility = vis)
|
||||
|
||||
proc generateThreadLocalStorage(m: BModule) =
|
||||
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
if m.g.nimtv.buf.len != 0 and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
|
||||
finishTypeDescriptions(m)
|
||||
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
||||
m.s[cfsSeqTypes].addTypedef(name = "NimThreadVars"):
|
||||
m.s[cfsSeqTypes].addSimpleStruct(m, name = "", baseType = ""):
|
||||
m.s[cfsSeqTypes].add(extract(m.g.nimtv))
|
||||
|
||||
proc generateThreadVarsSize(m: BModule) =
|
||||
if m.g.nimtv != "":
|
||||
if m.g.nimtv.buf.len != 0:
|
||||
let externc = if m.config.backend == backendCpp or
|
||||
sfCompileToCpp in m.module.flags: "extern \"C\" "
|
||||
else: ""
|
||||
m.s[cfsProcs].addf(
|
||||
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
|
||||
[externc.rope])
|
||||
sfCompileToCpp in m.module.flags: ExternC
|
||||
else: None
|
||||
m.s[cfsProcs].addDeclWithVisibility(externc):
|
||||
m.s[cfsProcs].addProcHeader("NimThreadVarsSize", NimInt, cProcParams())
|
||||
m.s[cfsProcs].finishProcHeaderWithBody():
|
||||
m.s[cfsProcs].addReturn(cCast(NimInt, cSizeof("NimThreadVars")))
|
||||
|
||||
@@ -16,13 +16,16 @@ type
|
||||
p: BProc
|
||||
visitorFrmt: string
|
||||
|
||||
const
|
||||
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
|
||||
|
||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
|
||||
proc genCaseRange(p: BProc, branch: PNode)
|
||||
proc genCaseRange(p: BProc, branch: PNode, info: var SwitchCaseBuilder)
|
||||
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
|
||||
|
||||
proc visit(p: BProc, data, visitor: Snippet) =
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisit"),
|
||||
cCast(CPointer, data),
|
||||
visitor)
|
||||
|
||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
typ: PType) =
|
||||
if n == nil: return
|
||||
@@ -34,32 +37,35 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
|
||||
var p = c.p
|
||||
let disc = n[0].sym
|
||||
if disc.loc.r == "": fillObjectFields(c.p.module, typ)
|
||||
if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
||||
if disc.loc.t == nil:
|
||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch)
|
||||
else:
|
||||
lineF(p, cpsStmts, "default:$n", [])
|
||||
genTraverseProc(c, accessor, lastSon(branch), typ)
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
lineF(p, cpsStmts, "} $n", [])
|
||||
let discField = dotField(accessor, disc.loc.snippet)
|
||||
p.s(cpsStmts).addSwitchStmt(discField):
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
var caseBuilder: SwitchCaseBuilder
|
||||
p.s(cpsStmts).addSwitchCase(caseBuilder):
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch, caseBuilder)
|
||||
else:
|
||||
p.s(cpsStmts).addCaseElse(caseBuilder)
|
||||
do:
|
||||
genTraverseProc(c, accessor, lastSon(branch), typ)
|
||||
p.s(cpsStmts).addBreak()
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.r == "": fillObjectFields(c.p.module, typ)
|
||||
if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
||||
genTraverseProc(c, dotField(accessor, field.loc.snippet), field.loc.t)
|
||||
else: internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
|
||||
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
|
||||
if not m.compileToCpp:
|
||||
result = "$1.Sup" % [accessor]
|
||||
result = dotField(accessor, "Sup")
|
||||
else:
|
||||
result = accessor
|
||||
|
||||
@@ -76,16 +82,14 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
let arraySize = lengthOrd(c.p.config, typ.indexType)
|
||||
var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
|
||||
var oldCode = p.s(cpsStmts)
|
||||
freeze oldCode
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.r, arraySize])
|
||||
let oldLen = p.s(cpsStmts).len
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ.elementType)
|
||||
if p.s(cpsStmts).len == oldLen:
|
||||
var oldLen, newLen: int
|
||||
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
|
||||
oldLen = p.s(cpsStmts).buf.len
|
||||
genTraverseProc(c, subscript(accessor, i.snippet), typ.elementType)
|
||||
newLen = p.s(cpsStmts).buf.len
|
||||
if oldLen == newLen:
|
||||
# do not emit dummy long loops for faster debug builds:
|
||||
p.s(cpsStmts) = oldCode
|
||||
else:
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
of tyObject:
|
||||
var x = typ.baseClass
|
||||
if x != nil: x = x.skipTypes(skipPtrs)
|
||||
@@ -94,23 +98,25 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i, a in typ.ikids:
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a)
|
||||
genTraverseProc(c, dotField(accessor, "Field" & $i), a)
|
||||
of tyRef:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
visit(p, accessor, c.visitorFrmt)
|
||||
of tySequence:
|
||||
if optSeqDestructors notin c.p.module.config.globalOptions:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
visit(p, accessor, c.visitorFrmt)
|
||||
elif containsGarbageCollectedRef(typ.elementType):
|
||||
# destructor based seqs are themselves not traced but their data is, if
|
||||
# they contain a GC'ed type:
|
||||
lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisitSeq"),
|
||||
cCast(CPointer, accessor),
|
||||
c.visitorFrmt)
|
||||
#genTraverseProcSeq(c, accessor, typ)
|
||||
of tyString:
|
||||
if tfHasAsgn notin typ.flags:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
visit(p, accessor, c.visitorFrmt)
|
||||
of tyProc:
|
||||
if typ.callConv == ccClosure:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [ropecg(c.p.module, "$1.ClE_0", [accessor]), c.visitorFrmt])
|
||||
visit(p, dotField(accessor, "ClE_0"), c.visitorFrmt)
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -119,18 +125,17 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
assert typ.kind == tySequence
|
||||
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
|
||||
var oldCode = p.s(cpsStmts)
|
||||
freeze oldCode
|
||||
var a = TLoc(r: accessor)
|
||||
var oldLen, newLen: int
|
||||
var a = TLoc(snippet: accessor)
|
||||
let le = lenExpr(c.p, a)
|
||||
|
||||
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.r, lenExpr(c.p, a)])
|
||||
let oldLen = p.s(cpsStmts).len
|
||||
genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ.elementType)
|
||||
if p.s(cpsStmts).len == oldLen:
|
||||
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), le):
|
||||
oldLen = p.s(cpsStmts).buf.len
|
||||
genTraverseProc(c, subscript(dataField(c.p, accessor), i.snippet), typ.elementType)
|
||||
newLen = p.s(cpsStmts).buf.len
|
||||
if newLen == oldLen:
|
||||
# do not emit dummy long loops for faster debug builds:
|
||||
p.s(cpsStmts) = oldCode
|
||||
else:
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
|
||||
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
var p = newProc(nil, m)
|
||||
@@ -139,11 +144,10 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
hcrOn = m.hcrOn
|
||||
typ = origTyp.skipTypes(abstractInstOwned)
|
||||
markerName = if hcrOn: result & "_actual" else: result
|
||||
header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [markerName]
|
||||
t = getTypeDesc(m, typ)
|
||||
|
||||
lineF(p, cpsLocals, "$1 a;$n", [t])
|
||||
lineF(p, cpsInit, "a = ($1)p;$n", [t])
|
||||
p.s(cpsLocals).addVar(kind = Local, name = "a", typ = t)
|
||||
p.s(cpsInit).addAssignment("a", cCast(t, "p"))
|
||||
|
||||
var c = TTraversalClosure(p: p,
|
||||
visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
|
||||
@@ -157,18 +161,40 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
# C's arrays are broken beyond repair:
|
||||
genTraverseProc(c, "a".rope, typ.elementType)
|
||||
else:
|
||||
genTraverseProc(c, "(*a)".rope, typ.elementType)
|
||||
genTraverseProc(c, cDeref("a"), typ.elementType)
|
||||
|
||||
let generatedProc = "$1 {$n$2$3$4}\n" %
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||
var headerBuilder = newBuilder("")
|
||||
headerBuilder.addProcHeaderWithParams(ccNimCall, markerName, CVoid):
|
||||
var paramBuilder: ProcParamBuilder
|
||||
headerBuilder.addProcParams(paramBuilder):
|
||||
headerBuilder.addParam(paramBuilder, name = "p", typ = CPointer)
|
||||
headerBuilder.addParam(paramBuilder, name = "op", typ = NimInt)
|
||||
let header = extract(headerBuilder)
|
||||
|
||||
m.s[cfsProcHeaders].addf("$1;\n", [header])
|
||||
m.s[cfsProcs].add(generatedProc)
|
||||
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcHeaders].add(header)
|
||||
m.s[cfsProcHeaders].finishProcHeaderAsProto()
|
||||
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcs].add(header)
|
||||
m.s[cfsProcs].finishProcHeaderWithBody():
|
||||
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsInit)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
|
||||
|
||||
if hcrOn:
|
||||
m.s[cfsProcHeaders].addf("N_NIMCALL_PTR(void, $1)(void*, NI);\n", [result])
|
||||
m.s[cfsDynLibInit].addf("\t$1 = (N_NIMCALL_PTR(void, )(void*, NI)) hcrRegisterProc($3, \"$1\", (void*)$2);\n",
|
||||
[result, markerName, getModuleDllPath(m)])
|
||||
var desc = newBuilder("")
|
||||
var unnamedParamBuilder: ProcParamBuilder
|
||||
desc.addProcParams(unnamedParamBuilder):
|
||||
desc.addUnnamedParam(unnamedParamBuilder, CPointer)
|
||||
desc.addUnnamedParam(unnamedParamBuilder, NimInt)
|
||||
let unnamedParams = extract(desc)
|
||||
m.s[cfsProcHeaders].addProcVar(ccNimCall, result, unnamedParams, CVoid)
|
||||
m.s[cfsDynLibInit].addAssignmentWithValue(result):
|
||||
m.s[cfsDynLibInit].addCast(procPtrTypeUnnamed(ccNimCall, CVoid, unnamedParams)):
|
||||
m.s[cfsDynLibInit].addCall("hcrRegisterProc",
|
||||
getModuleDllPath(m),
|
||||
'"' & result & '"',
|
||||
cCast(CPointer, markerName))
|
||||
|
||||
proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
|
||||
discard genTypeInfoV1(m, s.loc.t, info)
|
||||
@@ -179,17 +205,28 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
|
||||
|
||||
if sfThread in s.flags and emulatedThreadVars(m.config):
|
||||
accessThreadLocalVar(p, s)
|
||||
sLoc = "NimTV_->" & sLoc
|
||||
sLoc = derefField("NimTV_", sLoc)
|
||||
|
||||
var c = TTraversalClosure(p: p,
|
||||
visitorFrmt: "0" # "#nimGCvisit((void*)$1, 0);$n"
|
||||
visitorFrmt: cIntValue(0) # "#nimGCvisit((void*)$1, 0);$n"
|
||||
)
|
||||
|
||||
let header = "static N_NIMCALL(void, $1)(void)" % [result]
|
||||
genTraverseProc(c, sLoc, s.loc.t)
|
||||
|
||||
let generatedProc = "$1 {$n$2$3$4}$n" %
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||
var headerBuilder = newBuilder("")
|
||||
headerBuilder.addProcHeaderWithParams(ccNimCall, result, CVoid):
|
||||
var paramBuilder: ProcParamBuilder
|
||||
headerBuilder.addProcParams(paramBuilder):
|
||||
# (void)
|
||||
discard
|
||||
let header = extract(headerBuilder)
|
||||
|
||||
m.s[cfsProcHeaders].addf("$1;$n", [header])
|
||||
m.s[cfsProcs].add(generatedProc)
|
||||
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcHeaders].add(header)
|
||||
m.s[cfsProcHeaders].finishProcHeaderAsProto()
|
||||
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcs].add(header)
|
||||
m.s[cfsProcs].finishProcHeaderWithBody():
|
||||
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsInit)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, types, msgs, wordrecg,
|
||||
platform, trees, options, cgendata, mangleutils
|
||||
platform, trees, options, cgendata, mangleutils, renderer
|
||||
|
||||
import std/[hashes, strutils, formatfloat]
|
||||
|
||||
@@ -90,9 +90,6 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
||||
if s.typ.sym != nil and sfForward in s.typ.sym.flags:
|
||||
# forwarded objects are *always* passed by pointers for consistency!
|
||||
result = true
|
||||
elif s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
|
||||
# bug #23354:
|
||||
result = false
|
||||
elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
|
||||
result = true # requested anyway
|
||||
elif (tfFinal in pt.flags) and (pt[0] == nil):
|
||||
@@ -120,14 +117,14 @@ proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
|
||||
result.add "_u"
|
||||
result.add $s.itemId.item
|
||||
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string =
|
||||
#Module::Type
|
||||
var name = s.name.s
|
||||
var name = s.name.s & extra
|
||||
if makeUnique:
|
||||
name = makeUnique(m, s, name)
|
||||
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
|
||||
|
||||
proc encodeType*(m: BModule; t: PType): string =
|
||||
proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
|
||||
result = ""
|
||||
var kindName = ($t.kind)[2..^1]
|
||||
kindName[0] = toLower($kindName[0])[0]
|
||||
@@ -138,10 +135,10 @@ proc encodeType*(m: BModule; t: PType): string =
|
||||
result = encodeName(t[0].sym.name.s)
|
||||
result.add "I"
|
||||
for i in 1..<t.len - 1:
|
||||
result.add encodeType(m, t[i])
|
||||
result.add encodeType(m, t[i], staticLists)
|
||||
result.add "E"
|
||||
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
result =
|
||||
case t.kind:
|
||||
of tySequence: encodeName("seq")
|
||||
@@ -150,8 +147,13 @@ proc encodeType*(m: BModule; t: PType): string =
|
||||
for i in 0..<t.len:
|
||||
let s = t[i]
|
||||
if s.isNil: continue
|
||||
result.add encodeType(m, s)
|
||||
result.add encodeType(m, s, staticLists)
|
||||
result.add "E"
|
||||
of tyStatic:
|
||||
if t.n != nil:
|
||||
staticLists.add "_s" & renderTree(t.n)
|
||||
else:
|
||||
raiseAssert "unreachable"
|
||||
of tyRange:
|
||||
var val = "range_"
|
||||
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
|
||||
@@ -164,7 +166,7 @@ proc encodeType*(m: BModule; t: PType): string =
|
||||
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
|
||||
result = encodeName(kindName)
|
||||
of tyAlias, tyInferred, tyOwned:
|
||||
result = encodeType(m, t.elementType)
|
||||
result = encodeType(m, t.elementType, staticLists)
|
||||
else:
|
||||
assert false, "encodeType " & $t.kind
|
||||
|
||||
|
||||
1530
compiler/cgen.nim
1530
compiler/cgen.nim
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, ropes, options,
|
||||
ndi, lineinfos, pathutils, modulegraphs
|
||||
lineinfos, pathutils, modulegraphs, cbuilderbase
|
||||
|
||||
import std/[intsets, tables, sets]
|
||||
|
||||
@@ -43,12 +43,12 @@ type
|
||||
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
|
||||
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
|
||||
ctCString
|
||||
TCFileSections* = array[TCFileSection, Rope] # represents a generated C file
|
||||
TCFileSections* = array[TCFileSection, Builder] # represents a generated C file
|
||||
TCProcSection* = enum # the sections a generated C proc consists of
|
||||
cpsLocals, # section of local variables for C proc
|
||||
cpsInit, # section for init of variables for C proc
|
||||
cpsStmts # section of local statements for C proc
|
||||
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
|
||||
TCProcSections* = array[TCProcSection, Builder] # represents a generated C proc
|
||||
BModule* = ref TCGen
|
||||
BProc* = ref TCProc
|
||||
TBlock* = object
|
||||
@@ -88,7 +88,7 @@ type
|
||||
options*: TOptions # options that should be used for code
|
||||
# generation; this is the same as prc.options
|
||||
# unless prc == nil
|
||||
optionsStack*: seq[TOptions]
|
||||
optionsStack*: seq[(TOptions, TNoteKinds)]
|
||||
module*: BModule # used to prevent excessive parameter passing
|
||||
withinLoop*: int # > 0 if we are within a loop
|
||||
splitDecls*: int # > 0 if we are in some context for C++ that
|
||||
@@ -115,7 +115,7 @@ type
|
||||
# computing alive data on our own.
|
||||
|
||||
BModuleList* = ref object of RootObj
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Builder
|
||||
mapping*: Rope # the generated mapping file (if requested)
|
||||
modules*: seq[BModule] # list of all compiled modules
|
||||
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
||||
@@ -127,7 +127,7 @@ type
|
||||
graph*: ModuleGraph
|
||||
strVersion*, seqVersion*: int # version of the string/seq implementation to use
|
||||
|
||||
nimtv*: Rope # Nim thread vars; the struct body
|
||||
nimtv*: Builder # Nim thread vars; the struct body
|
||||
nimtvDeps*: seq[PType] # type deps: every module needs whole struct
|
||||
nimtvDeclared*: IntSet # so that every var/field exists only once
|
||||
# in the struct
|
||||
@@ -161,18 +161,18 @@ type
|
||||
typeInfoMarkerV2*: TypeCache
|
||||
initProc*: BProc # code for init procedure
|
||||
preInitProc*: BProc # code executed before the init proc
|
||||
hcrCreateTypeInfosProc*: Rope # type info globals are in here when HCR=on
|
||||
hcrCreateTypeInfosProc*: Builder # type info globals are in here when HCR=on
|
||||
inHcrInitGuard*: bool # We are currently within a HCR reloading guard.
|
||||
hcrInitGuard*: IfBuilder
|
||||
typeStack*: TTypeSeq # used for type generation
|
||||
dataCache*: TNodeTable
|
||||
typeNodes*, nimTypes*: int # used for type info generation
|
||||
typeNodesName*, nimTypesName*: Rope # used for type info generation
|
||||
labels*: Natural # for generating unique module-scope names
|
||||
extensionLoaders*: array['0'..'9', Rope] # special procs for the
|
||||
extensionLoaders*: array['0'..'9', Builder] # special procs for the
|
||||
# OpenGL wrapper
|
||||
sigConflicts*: CountTable[SigHash]
|
||||
g*: BModuleList
|
||||
ndi*: NdiFile
|
||||
|
||||
template config*(m: BModule): ConfigRef = m.g.config
|
||||
template config*(p: BProc): ConfigRef = p.module.g.config
|
||||
@@ -182,18 +182,18 @@ proc includeHeader*(this: BModule; header: string) =
|
||||
if not this.headerFiles.contains header:
|
||||
this.headerFiles.add header
|
||||
|
||||
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
proc s*(p: BProc, s: TCProcSection): var Builder {.inline.} =
|
||||
# section in the current block
|
||||
result = p.blocks[^1].sections[s]
|
||||
|
||||
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
proc procSec*(p: BProc, s: TCProcSection): var Builder {.inline.} =
|
||||
# top level proc sections
|
||||
result = p.blocks[0].sections[s]
|
||||
|
||||
proc initBlock*(): TBlock =
|
||||
result = TBlock()
|
||||
for i in low(result.sections)..high(result.sections):
|
||||
result.sections[i] = newRopeAppender()
|
||||
result.sections[i] = newBuilder("")
|
||||
|
||||
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
result = BProc(
|
||||
|
||||
@@ -55,7 +55,7 @@ proc methodCall*(n: PNode; conf: ConfigRef): PNode =
|
||||
# replace ordinary method by dispatcher method:
|
||||
let disp = getDispatcher(result[0].sym)
|
||||
if disp != nil:
|
||||
result[0].typ = disp.typ
|
||||
result[0].typ() = disp.typ
|
||||
result[0].sym = disp
|
||||
# change the arguments to up/downcasts to fit the dispatcher's parameters:
|
||||
for i in 1..<result.len:
|
||||
@@ -133,7 +133,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
|
||||
disp.ast = copyTree(s.ast)
|
||||
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
|
||||
disp.loc.r = ""
|
||||
disp.loc.snippet = ""
|
||||
if s.typ.returnType != nil:
|
||||
if disp.ast.len > resultPos:
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
|
||||
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
|
||||
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
|
||||
|
||||
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
|
||||
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils]
|
||||
import
|
||||
msgs, options, nversion, condsyms, extccomp, platform,
|
||||
wordrecg, nimblecmd, lineinfos, pathutils
|
||||
@@ -203,13 +203,14 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
else: invalidCmdLineOption(conf, pass, orig, info)
|
||||
|
||||
let isSomeHint = state in {wHint, wHintAsError}
|
||||
let isSomeWarning = state in {wWarning, wWarningAsError}
|
||||
template findNote(noteMin, noteMax, name) =
|
||||
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
|
||||
let x = findStr(noteMin, noteMax, id, errUnknown)
|
||||
if x != errUnknown: notes = {TNoteKind(x)}
|
||||
else:
|
||||
if isSomeHint:
|
||||
message(conf, info, hintUnknownHint, id)
|
||||
if isSomeHint or isSomeWarning:
|
||||
message(conf, info, warnUnknownNotes, "unknown $#: $#" % [name, id])
|
||||
else:
|
||||
localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
case id.normalize
|
||||
@@ -248,6 +249,7 @@ const
|
||||
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
|
||||
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
|
||||
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
|
||||
|
||||
template warningOptionNoop(switch: string) =
|
||||
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
|
||||
@@ -303,6 +305,12 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||
else:
|
||||
result = false
|
||||
localError(conf, info, errInvalidExceptionSystem % arg)
|
||||
of "experimental":
|
||||
try:
|
||||
result = conf.features.contains parseEnum[Feature](arg)
|
||||
except ValueError:
|
||||
result = false
|
||||
localError(conf, info, errInvalidFeatureButXFound % arg)
|
||||
else:
|
||||
result = false
|
||||
invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
@@ -451,7 +459,7 @@ template handleStdinOrCmdInput =
|
||||
conf.outDir = getNimcacheDir(conf)
|
||||
|
||||
proc handleStdinInput*(conf: ConfigRef) =
|
||||
conf.projectName = "stdinfile"
|
||||
conf.projectName = conf.stdinFile.string
|
||||
conf.projectIsStdin = true
|
||||
handleStdinOrCmdInput()
|
||||
|
||||
@@ -462,10 +470,10 @@ proc handleCmdInput*(conf: ConfigRef) =
|
||||
proc parseCommand*(command: string): Command =
|
||||
case command.normalize
|
||||
of "c", "cc", "compile", "compiletoc": cmdCompileToC
|
||||
of "nir": cmdCompileToNir
|
||||
of "cpp", "compiletocpp": cmdCompileToCpp
|
||||
of "objc", "compiletooc": cmdCompileToOC
|
||||
of "js", "compiletojs": cmdCompileToJS
|
||||
of "nif": cmdCompileToNif
|
||||
of "r": cmdCrun
|
||||
of "m": cmdM
|
||||
of "run": cmdTcc
|
||||
@@ -500,7 +508,7 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
|
||||
of cmdCompileToCpp: conf.backend = backendCpp
|
||||
of cmdCompileToOC: conf.backend = backendObjc
|
||||
of cmdCompileToJS: conf.backend = backendJs
|
||||
of cmdCompileToNir: conf.backend = backendNir
|
||||
of cmdCompileToNif: conf.backend = backendNif
|
||||
else: discard
|
||||
|
||||
proc setCommandEarly*(conf: ConfigRef, command: string) =
|
||||
@@ -876,11 +884,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "import":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||
let m = findModule(conf, arg, toFullPath(conf, info)).string
|
||||
if m.len == 0:
|
||||
localError(conf, info, "Cannot resolve filename: " & arg)
|
||||
else:
|
||||
conf.implicitImports.add m
|
||||
of "include":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||
let m = findModule(conf, arg, toFullPath(conf, info)).string
|
||||
if m.len == 0:
|
||||
localError(conf, info, "Cannot resolve filename: " & arg)
|
||||
else:
|
||||
conf.implicitIncludes.add m
|
||||
of "listcmd":
|
||||
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
|
||||
of "asm":
|
||||
@@ -914,6 +930,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
|
||||
conf.maxLoopIterationsVM = value
|
||||
of "maxcalldepthvm":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
var value: int = 2_000
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
if value <= 0: localError(conf, info, "maxCallDepthVM must be a positive integer greater than zero")
|
||||
conf.maxCallDepthVM = value
|
||||
of "errormax":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
# Note: `nim check` (etc) can overwrite this.
|
||||
@@ -923,6 +945,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
var value: int = 0
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
conf.errorMax = if value == 0: high(int) else: value
|
||||
of "stdinfile":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.stdinFile = if os.isAbsolute(arg): AbsoluteFile(arg)
|
||||
else: AbsoluteFile(getCurrentDir() / arg)
|
||||
of "verbosity":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
let verbosity = parseInt(arg)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## for details. Note this is a first implementation and only the "Concept matching"
|
||||
## section has been implemented.
|
||||
|
||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types
|
||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
|
||||
|
||||
import std/intsets
|
||||
|
||||
@@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
const
|
||||
logBindings = false
|
||||
logBindings = when defined(debugConcepts): true else: false
|
||||
|
||||
## Code dealing with Concept declarations
|
||||
## --------------------------------------
|
||||
@@ -70,88 +70,247 @@ proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
|
||||
## ----------------
|
||||
|
||||
type
|
||||
MatchFlags* = enum
|
||||
mfDontBind # Do not bind generic parameters
|
||||
mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand
|
||||
|
||||
MatchCon = object ## Context we pass around during concept matching.
|
||||
inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
|
||||
## that turned out to be wrong.
|
||||
bindings: LayeredIdTable
|
||||
marker: IntSet ## Some protection against wild runaway recursions.
|
||||
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
|
||||
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
|
||||
## cannot be fixed that easily.
|
||||
## Thus we special case it here.
|
||||
concpt: PType ## current concept being evaluated
|
||||
depthCount = 0
|
||||
flags: set[MatchFlags]
|
||||
|
||||
MatchKind = enum
|
||||
mkNoMatch, mkSubset, mkSame
|
||||
|
||||
const
|
||||
asymmetricConceptParamMods = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred} # param modifiers that to not have to match implementation -> concept
|
||||
bindableTypes = {tyGenericParam, tyOr, tyTypeDesc}
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||
|
||||
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool
|
||||
|
||||
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool
|
||||
|
||||
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool
|
||||
|
||||
proc existingBinding(m: MatchCon; key: PType): PType =
|
||||
## checks if we bound the type variable 'key' already to some
|
||||
## concrete type.
|
||||
for i in 0..<m.inferred.len:
|
||||
if m.inferred[i][0] == key: return m.inferred[i][1]
|
||||
return nil
|
||||
result = m.bindings.lookup(key)
|
||||
if result == nil:
|
||||
result = key
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||
const
|
||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred}
|
||||
|
||||
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
proc unrollGenericParam(param: PType): PType =
|
||||
result = param.skipTypes(ignorableForArgType)
|
||||
while result.kind in {tyGenericParam, tyTypeDesc} and result.hasElementType and result.elementType.kind != tyNone:
|
||||
result = result.elementType
|
||||
|
||||
proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable .} =
|
||||
if v.kind == tyTypeDesc:
|
||||
return false
|
||||
var value = unrollGenericParam(v)
|
||||
if value.kind == tyGenericParam:
|
||||
value = existingBinding(m, value)
|
||||
if value.kind == tyGenericParam:
|
||||
if value.hasElementType:
|
||||
value = value.elementType
|
||||
else:
|
||||
return true
|
||||
if value.kind == tyStatic:
|
||||
return false
|
||||
|
||||
if m.magic in {mArrPut, mArrGet} and value.kind in arrPutGetMagicApplies:
|
||||
value = value.last
|
||||
|
||||
let old = existingBinding(m, key)
|
||||
if old != key:
|
||||
# check previously bound value
|
||||
if not matchType(c, old, value, m):
|
||||
return false
|
||||
elif key.hasElementType and key.elementType.kind != tyNone:
|
||||
# check constaint
|
||||
if matchType(c, unrollGenericParam(key), value, m) == false:
|
||||
return false
|
||||
|
||||
when logBindings: echo "bind table adding '", key, "', ", value
|
||||
assert value != nil
|
||||
assert value.kind != tyVoid
|
||||
m.bindings.put(key, value)
|
||||
return true
|
||||
|
||||
proc defSignatureType(n: PNode): PType = n[0].sym.typ
|
||||
|
||||
proc conceptBody*(n: PType): PNode = n.n.lastSon
|
||||
|
||||
proc acceptsAllTypes(t: PType): bool=
|
||||
result = false
|
||||
if t.kind == tyAnything:
|
||||
result = true
|
||||
elif t.kind == tyGenericParam:
|
||||
if tfImplicitTypeParam in t.flags:
|
||||
result = true
|
||||
if not t.hasElementType or t.elementType.kind == tyNone:
|
||||
result = true
|
||||
|
||||
proc procDefSignature(s: PSym): PNode {. deprecated .} =
|
||||
var nc = s.ast.copyNode()
|
||||
for i in 0 .. 5:
|
||||
nc.add s.ast[i]
|
||||
nc
|
||||
|
||||
proc matchKids(c: PContext; f, a: PType; m: var MatchCon, start=0): bool=
|
||||
result = true
|
||||
for i in start ..< f.kidsLen - ord(f.kind in {tyGenericInst, tyGenericInvocation}):
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
|
||||
iterator traverseTyOr(t: PType): PType {. closure .}=
|
||||
for i in t.kids:
|
||||
case i.kind:
|
||||
of tyGenericParam:
|
||||
if i.hasElementType:
|
||||
for s in traverseTyOr(i.elementType):
|
||||
yield s
|
||||
else:
|
||||
yield i
|
||||
else:
|
||||
yield i
|
||||
|
||||
proc matchConceptToImpl(c: PContext, f, potentialImpl: PType; m: var MatchCon): bool =
|
||||
assert not(potentialImpl.reduceToBase.kind == tyConcept)
|
||||
let concpt = f.reduceToBase
|
||||
if m.depthCount > 0:
|
||||
# concepts that are more then 2 levels deep are treated like
|
||||
# tyAnything to stop dependencies from getting out of control
|
||||
return true
|
||||
var efPot = potentialImpl
|
||||
if potentialImpl.isSelf:
|
||||
if m.concpt.n == concpt.n:
|
||||
return true
|
||||
efPot = m.potentialImplementation
|
||||
|
||||
var oldBindings = m.bindings
|
||||
m.bindings = newTypeMapLayer(m.bindings)
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = efPot
|
||||
let oldConcept = m.concpt
|
||||
m.concpt = concpt
|
||||
|
||||
var invocation: PType = nil
|
||||
if f.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
invocation = f
|
||||
inc m.depthCount
|
||||
result = processConcept(c, concpt, invocation, oldBindings, m)
|
||||
dec m.depthCount
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
m.concpt = oldConcept
|
||||
m.bindings = oldBindings
|
||||
|
||||
proc cmpConceptDefs(c: PContext, fn, an: PNode, m: var MatchCon): bool=
|
||||
if fn.kind != an.kind:
|
||||
return false
|
||||
if fn[namePos].sym.name != an[namePos].sym.name:
|
||||
return false
|
||||
let
|
||||
ft = fn.defSignatureType
|
||||
at = an.defSignatureType
|
||||
if ft.len != at.len:
|
||||
return false
|
||||
|
||||
for i in 1 ..< ft.n.len:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
|
||||
let aType = at.n[i].typ
|
||||
let fType = ft.n[i].typ
|
||||
|
||||
if aType.isSelf and fType.isSelf:
|
||||
continue
|
||||
|
||||
if not matchType(c, fType, aType, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
return false
|
||||
result = true
|
||||
if not matchReturnType(c, ft.returnType, at.returnType, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
result = false
|
||||
|
||||
proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind =
|
||||
# XXX: In the future this may need extra parameters to carry info for container types
|
||||
if fc.n == ac.n:
|
||||
# This will have to take generic parameters into account at some point
|
||||
return mkSame
|
||||
let
|
||||
fn = fc.conceptBody
|
||||
an = ac.conceptBody
|
||||
sameLen = fc.len == ac.len
|
||||
var match = false
|
||||
for fdef in fn:
|
||||
var cmpResult = false
|
||||
for ia, ndef in an:
|
||||
match = cmpConceptDefs(c, fdef, ndef, m)
|
||||
if match:
|
||||
break
|
||||
if not match:
|
||||
return mkNoMatch
|
||||
return mkSubset
|
||||
|
||||
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
|
||||
## The heart of the concept matching process. 'f' is the formal parameter of some
|
||||
## routine inside the concept that we're looking for. 'a' is the formal parameter
|
||||
## of a routine that might match.
|
||||
const
|
||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
|
||||
|
||||
var
|
||||
a = ao
|
||||
f = fo
|
||||
if a.isSelf:
|
||||
if m.magic in {mArrPut, mArrGet}:
|
||||
return false
|
||||
a = m.potentialImplementation
|
||||
if a.kind in bindableTypes:
|
||||
a = existingBinding(m, ao)
|
||||
if a == ao and a.kind == tyGenericParam and a.hasElementType and a.elementType.kind != tyNone:
|
||||
a = a.elementType
|
||||
|
||||
if f.isConcept:
|
||||
if a.acceptsAllTypes:
|
||||
return false
|
||||
if a.skipTypes(ignorableForArgType).isConcept:
|
||||
# if f is a subset of a then any match to a will also match f. Not the other way around
|
||||
return conceptsMatch(c, a.reduceToBase, f.reduceToBase, m) >= mkSubset
|
||||
else:
|
||||
return matchConceptToImpl(c, f, a, m)
|
||||
|
||||
result = false
|
||||
|
||||
case f.kind
|
||||
of tyAlias:
|
||||
result = matchType(c, f.skipModifier, a, m)
|
||||
of tyTypeDesc:
|
||||
if isSelf(f):
|
||||
#let oldLen = m.inferred.len
|
||||
result = matchType(c, a, m.potentialImplementation, m)
|
||||
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
|
||||
#m.inferred.setLen oldLen
|
||||
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
let ua = a.skipTypes(asymmetricConceptParamMods)
|
||||
if m.magic in {mArrPut, mArrGet}:
|
||||
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
|
||||
bindParam(c, m, a, last m.potentialImplementation)
|
||||
result = true
|
||||
#elif ua.isConcept:
|
||||
# result = matchType(c, m.concpt, ua, m)
|
||||
else:
|
||||
result = matchType(c, a.skipTypes(ignorableForArgType), m.potentialImplementation, m)
|
||||
else:
|
||||
if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
|
||||
if f.hasElementType:
|
||||
if a.kind == tyTypeDesc:
|
||||
if not(a.hasElementType) or a.elementType.kind == tyNone:
|
||||
result = true
|
||||
elif f.hasElementType:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
result = true # both lack it
|
||||
else:
|
||||
result = false
|
||||
|
||||
of tyGenericInvocation:
|
||||
result = false
|
||||
if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
|
||||
if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
|
||||
for i in FirstGenericParamAt ..< f.kidsLen:
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
return true
|
||||
of tyGenericParam:
|
||||
let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
|
||||
if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
|
||||
result = false
|
||||
else:
|
||||
let old = existingBinding(m, f)
|
||||
if old == nil:
|
||||
if f.hasElementType and f.elementType.kind != tyNone:
|
||||
# also check the generic's constraints:
|
||||
let oldLen = m.inferred.len
|
||||
result = matchType(c, f.elementType, a, m)
|
||||
m.inferred.setLen oldLen
|
||||
if result:
|
||||
when logBindings: echo "A adding ", f, " ", ak
|
||||
m.inferred.add((f, ak))
|
||||
elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}:
|
||||
when logBindings: echo "B adding ", f, " ", lastSon ak
|
||||
m.inferred.add((f, last ak))
|
||||
result = true
|
||||
else:
|
||||
when logBindings: echo "C adding ", f, " ", ak
|
||||
m.inferred.add((f, ak))
|
||||
#echo "binding ", typeToString(ak), " to ", typeToString(f)
|
||||
result = true
|
||||
elif not m.marker.containsOrIncl(old.id):
|
||||
result = matchType(c, old, ak, m)
|
||||
if m.magic == mArrPut and ak.kind == tyGenericParam:
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
|
||||
of tyVar, tySink, tyLent, tyOwned:
|
||||
# modifiers in the concept must be there in the actual implementation
|
||||
# too but not vice versa.
|
||||
@@ -159,70 +318,126 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
elif m.magic == mArrPut:
|
||||
result = matchType(c, f.elementType, a, m)
|
||||
else:
|
||||
result = false
|
||||
of tyEnum, tyObject, tyDistinct:
|
||||
result = sameType(f, a)
|
||||
if a.kind in ignorableForArgType:
|
||||
result = matchType(c, f, a.skipTypes(ignorableForArgType), m)
|
||||
else:
|
||||
if a.kind == tyGenericInst:
|
||||
# tyOr does this to generic typeclasses
|
||||
result = a.base.sym == f.sym
|
||||
else:
|
||||
result = sameType(f, a)
|
||||
of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
|
||||
result = a.skipTypes(ignorableForArgType).kind == f.kind
|
||||
of tyBool, tyChar, tyInt..tyUInt64:
|
||||
let ak = a.skipTypes(ignorableForArgType)
|
||||
result = ak.kind == f.kind or ak.kind == tyOrdinal or
|
||||
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
|
||||
of tyConcept:
|
||||
let oldLen = m.inferred.len
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = a
|
||||
result = conceptMatchNode(c, f.n.lastSon, m)
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
|
||||
tyGenericInst:
|
||||
# ^ XXX Rewrite this logic, it's more complex than it needs to be.
|
||||
result = false
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
|
||||
for i in 0..<ak.kidsLen:
|
||||
if not matchType(c, f[i], ak[i], m): return false
|
||||
return true
|
||||
of tyOr:
|
||||
let oldLen = m.inferred.len
|
||||
if a.kind == tyOr:
|
||||
# say the concept requires 'int|float|string' if the potentialImplementation
|
||||
# says 'int|string' that is good enough.
|
||||
var covered = 0
|
||||
for ff in f.kids:
|
||||
for aa in a.kids:
|
||||
let oldLenB = m.inferred.len
|
||||
let r = matchType(c, ff, aa, m)
|
||||
if r:
|
||||
inc covered
|
||||
break
|
||||
m.inferred.setLen oldLenB
|
||||
|
||||
result = covered >= a.kidsLen
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
|
||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr:
|
||||
if f.kind == tyArray and f.kidsLen == 3 and a.kind == tyArray:
|
||||
# XXX: this is a work-around!
|
||||
# system.nim creates these for the magic array typeclass
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
for ff in f.kids:
|
||||
result = matchType(c, ff, a, m)
|
||||
if result: break # and remember the binding!
|
||||
m.inferred.setLen oldLen
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind:
|
||||
if f.base.kind == tyNone:
|
||||
result = true
|
||||
elif f.kidsLen == ak.kidsLen:
|
||||
result = matchKids(c, f, ak, m)
|
||||
of tyGenericInvocation, tyGenericInst:
|
||||
result = false
|
||||
let ea = a.skipTypes(ignorableForArgType)
|
||||
if ea.kind in {tyGenericInst, tyGenericInvocation}:
|
||||
var
|
||||
k1 = f.kidsLen - ord(f.kind == tyGenericInst)
|
||||
k2 = ea.kidsLen - ord(ea.kind == tyGenericInst)
|
||||
if sameType(f.genericHead, ea.genericHead) and k1 == k2:
|
||||
result = true
|
||||
for i in 1 ..< k2:
|
||||
if not matchType(c, f[i], ea[i], m):
|
||||
result = false
|
||||
break
|
||||
of tyOrdinal:
|
||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
||||
of tyStatic:
|
||||
var scomp = f.base
|
||||
if scomp.kind == tyGenericParam:
|
||||
if f.base.kidsLen > 0:
|
||||
scomp = scomp.base
|
||||
if a.kind == tyStatic:
|
||||
result = matchType(c, scomp, a.base, m)
|
||||
else:
|
||||
result = matchType(c, scomp, a, m)
|
||||
of tyGenericParam:
|
||||
if a.acceptsAllTypes:
|
||||
discard bindParam(c, m, f, a)
|
||||
result = f.acceptsAllTypes
|
||||
else:
|
||||
result = bindParam(c, m, f, a)
|
||||
of tyAnything:
|
||||
result = true
|
||||
of tyNot:
|
||||
if a.kind == tyNot:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
let oldLen = m.inferred.len
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
result = not matchType(c, f.elementType, a, m)
|
||||
m.inferred.setLen oldLen
|
||||
of tyAnything:
|
||||
m.bindings.setToPreviousLayer()
|
||||
of tyAnd:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
result = true
|
||||
of tyOrdinal:
|
||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
||||
for ff in traverseTyOr(f):
|
||||
let r = matchType(c, ff, a, m)
|
||||
if not r:
|
||||
m.bindings.setToPreviousLayer()
|
||||
result = false
|
||||
break
|
||||
of tyGenericBody:
|
||||
var ak = a
|
||||
if a.kind == tyGenericBody:
|
||||
ak = last(a)
|
||||
result = matchType(c, last(f), ak, m)
|
||||
of tyCompositeTypeClass:
|
||||
if a.kind == tyCompositeTypeClass:
|
||||
result = matchKids(c, f, a, m)
|
||||
else:
|
||||
result = matchType(c, last(f), a, m)
|
||||
of tyBuiltInTypeClass:
|
||||
let target = f.genericHead.kind
|
||||
result = a.skipTypes(ignorableForArgType).reduceToBase.kind == target
|
||||
of tyOr:
|
||||
if a.kind == tyOr:
|
||||
var covered = 0
|
||||
for ff in traverseTyOr(f):
|
||||
for aa in traverseTyOr(a):
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
let r = matchType(c, ff, aa, m)
|
||||
if r:
|
||||
inc covered
|
||||
break
|
||||
m.bindings.setToPreviousLayer()
|
||||
|
||||
result = covered >= a.kidsLen
|
||||
else:
|
||||
for ff in f.kids:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
result = matchType(c, ff, a, m)
|
||||
if result: break # and remember the binding!
|
||||
m.bindings.setToPreviousLayer()
|
||||
of tySet:
|
||||
result = false
|
||||
if a.kind == tySet:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
result = false
|
||||
if result and ao.kind == tyGenericParam:
|
||||
let bf = if f.isSelf: m.potentialImplementation else: f
|
||||
if bindParam(c, m, ao, bf):
|
||||
when logBindings: echo " ^ reverse binding"
|
||||
|
||||
proc checkConstraint(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
result = matchType(c, f, a, m) or matchType(c, a, f, m)
|
||||
|
||||
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
## Like 'matchType' but with extra logic dealing with proc return types
|
||||
@@ -232,30 +447,38 @@ proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
elif a == nil:
|
||||
result = false
|
||||
else:
|
||||
result = matchType(c, f, a, m)
|
||||
result = checkConstraint(c, f, a, m)
|
||||
|
||||
proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
|
||||
## or similar.
|
||||
|
||||
# watch out: only add bindings after a completely successful match.
|
||||
let oldLen = m.inferred.len
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
|
||||
let can = candidate.typ.n
|
||||
let con = n[0].sym.typ.n
|
||||
|
||||
let con = defSignatureType(n).n
|
||||
if can.len < con.len:
|
||||
# too few arguments, cannot be a match:
|
||||
return false
|
||||
|
||||
|
||||
if can.len > con.len:
|
||||
# too many arguments (not optional)
|
||||
for i in con.len ..< can.len:
|
||||
if can[i].sym.ast == nil:
|
||||
return false
|
||||
|
||||
when defined(debugConcepts):
|
||||
echo "considering: ", renderTree(candidate.procDefSignature), " ", candidate.magic
|
||||
|
||||
let common = min(can.len, con.len)
|
||||
for i in 1 ..< common:
|
||||
if not matchType(c, con[i].typ, can[i].typ, m):
|
||||
m.inferred.setLen oldLen
|
||||
if not checkConstraint(c, con[i].typ, can[i].typ, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
return false
|
||||
|
||||
if not matchReturnType(c, n[0].sym.typ.returnType, candidate.typ.returnType, m):
|
||||
m.inferred.setLen oldLen
|
||||
|
||||
if not matchReturnType(c, n.defSignatureType.returnType, candidate.typ.returnType, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
return false
|
||||
|
||||
# all other parameters have to be optional parameters:
|
||||
@@ -263,7 +486,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
assert can[i].kind == nkSym
|
||||
if can[i].sym.ast == nil:
|
||||
# has too many arguments one of which is not optional:
|
||||
m.inferred.setLen oldLen
|
||||
m.bindings.setToPreviousLayer()
|
||||
return false
|
||||
|
||||
return true
|
||||
@@ -271,12 +494,14 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
|
||||
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
|
||||
## 'n' is the nkProcDef or similar from the concept that we try to match.
|
||||
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
|
||||
for candidate in candidates:
|
||||
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
||||
m.magic = candidate.magic
|
||||
if matchSym(c, candidate, n, m): return true
|
||||
result = false
|
||||
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
|
||||
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
|
||||
for candidate in candidates:
|
||||
m.magic = candidate.magic
|
||||
if matchSym(c, candidate, n, m):
|
||||
result = true
|
||||
break
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
## Traverse the concept's AST ('n') and see if every declaration inside 'n'
|
||||
@@ -309,7 +534,48 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
# error was reported earlier.
|
||||
result = false
|
||||
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool =
|
||||
proc fixBindings(bindings: var LayeredIdTable; concpt: PType; invocation: PType; m: var MatchCon) =
|
||||
# invocation != nil means we have a non-atomic concept:
|
||||
if invocation != nil and invocation.kind == tyGenericInvocation:
|
||||
assert concpt.sym.typ.kind == tyGenericBody
|
||||
|
||||
for i in 0 .. concpt.sym.typ.len - 1:
|
||||
let thisSym = concpt.sym.typ[i]
|
||||
if lookup(bindings, thisSym) != nil:
|
||||
# dont trust the bindings over existing ones
|
||||
continue
|
||||
let found = m.bindings.lookup(thisSym)
|
||||
if found != nil:
|
||||
when logBindings: echo "Invocation bind: ", thisSym, " ", found
|
||||
bindings.put(thisSym, found)
|
||||
|
||||
# bind even more generic parameters
|
||||
let genBody = invocation.base
|
||||
assert genBody.kind == tyGenericBody
|
||||
for i in FirstGenericParamAt ..< invocation.kidsLen:
|
||||
let bpram = genBody[i - 1]
|
||||
if lookup(bindings, invocation[i]) != nil:
|
||||
# dont trust the bindings over existing ones
|
||||
continue
|
||||
let boundV = lookup(bindings, bpram)
|
||||
when logBindings: echo "generic body bind: '", invocation[i], "' '", boundV, "'"
|
||||
if boundV != nil:
|
||||
bindings.put(invocation[i], boundV)
|
||||
bindings.put(concpt, m.potentialImplementation)
|
||||
|
||||
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool =
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
if invocation != nil and invocation.kind == tyGenericInst:
|
||||
let genericBody = invocation.base
|
||||
for i in 1..<invocation.kidsLen-1:
|
||||
# instGenericContainer can bind `tyVoid`
|
||||
if invocation[i].kind != tyVoid:
|
||||
bindParam(c, m, genericBody[i-1], invocation[i])
|
||||
result = conceptMatchNode(c, concpt.conceptBody, m)
|
||||
if result and mfDontBind notin m.flags:
|
||||
fixBindings(bindings, concpt, invocation, m)
|
||||
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType, flags: set[MatchFlags] = {}): bool =
|
||||
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
|
||||
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
|
||||
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
|
||||
@@ -318,26 +584,16 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; i
|
||||
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
|
||||
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
|
||||
## we do not add any bindings at all on an unsuccessful match!
|
||||
var m = MatchCon(inferred: @[], potentialImplementation: arg)
|
||||
result = conceptMatchNode(c, concpt.n.lastSon, m)
|
||||
if result:
|
||||
for (a, b) in m.inferred:
|
||||
if b.kind == tyGenericParam:
|
||||
var dest = b
|
||||
while true:
|
||||
dest = existingBinding(m, dest)
|
||||
if dest == nil or dest.kind != tyGenericParam: break
|
||||
if dest != nil:
|
||||
bindings.idTablePut(a, dest)
|
||||
when logBindings: echo "A bind ", a, " ", dest
|
||||
else:
|
||||
bindings.idTablePut(a, b)
|
||||
when logBindings: echo "B bind ", a, " ", b
|
||||
# we have a match, so bind 'arg' itself to 'concpt':
|
||||
bindings.idTablePut(concpt, arg)
|
||||
# invocation != nil means we have a non-atomic concept:
|
||||
if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1:
|
||||
# bind even more generic parameters
|
||||
assert invocation.kind == tyGenericInvocation
|
||||
for i in FirstGenericParamAt ..< invocation.kidsLen:
|
||||
bindings.idTablePut(invocation[i], arg[i])
|
||||
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags)
|
||||
if arg.isConcept:
|
||||
result = conceptsMatch(c, concpt.reduceToBase, arg.reduceToBase, m) >= mkSubset
|
||||
elif arg.acceptsAllTypes:
|
||||
# XXX: I think this is wrong, or at least partially wrong. Can still test ambiguous types
|
||||
result = false
|
||||
elif mfCheckGeneric in m.flags:
|
||||
# prioritize concepts the least. Specifically if the arg is not a catch all as per above
|
||||
result = true
|
||||
else:
|
||||
result = processConcept(c, concpt, invocation, bindings, m)
|
||||
|
||||
|
||||
|
||||
@@ -159,11 +159,19 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasEnsureMove")
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimUseStrictDefs")
|
||||
defineSymbol("nimUseStrictDefs") # deadcode
|
||||
defineSymbol("nimHasNolineTooLong")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
defineSymbol("nimHasWarnStdPrefix")
|
||||
|
||||
defineSymbol("nimHasVtables")
|
||||
defineSymbol("nimHasGenericsOpenSym2")
|
||||
defineSymbol("nimHasGenericsOpenSym3")
|
||||
defineSymbol("nimHasJsNoLambdaLifting")
|
||||
defineSymbol("nimHasDefaultFloatRoundtrip")
|
||||
defineSymbol("nimHasXorSet")
|
||||
|
||||
defineSymbol("nimHasSetLengthSeqUninitMagic")
|
||||
defineSymbol("nimHasPreviewDuplicateModuleError")
|
||||
|
||||
|
||||
@@ -439,8 +439,8 @@ proc gen(c: var Con; n: PNode) =
|
||||
genUse(c, n)
|
||||
of nkIfStmt, nkIfExpr: genIf(c, n)
|
||||
of nkWhenStmt:
|
||||
# This is "when nimvm" node. Chose the first branch.
|
||||
gen(c, n[0][1])
|
||||
# This is "when nimvm" node. Chose the second branch.
|
||||
gen(c, n[1][0])
|
||||
of nkCaseStmt: genCase(c, n)
|
||||
of nkWhileStmt: genWhile(c, n)
|
||||
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
|
||||
|
||||
@@ -115,7 +115,7 @@ proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, s
|
||||
|
||||
proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex =
|
||||
let invalid = rstast.FileIndex(-1)
|
||||
result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid)
|
||||
result = d.nimToRstFid.getOrDefault(fileIndex, invalid)
|
||||
if result == invalid:
|
||||
let fname = toFullPath(d.conf, fileIndex)
|
||||
result = addFilename(d.sharedState, fname)
|
||||
@@ -830,8 +830,8 @@ proc getName(n: PNode): string =
|
||||
of nkAccQuoted:
|
||||
result = "`"
|
||||
for i in 0..<n.len: result.add(getName(n[i]))
|
||||
result = "`"
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
result.add('`')
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getName(n[0])
|
||||
else:
|
||||
result = ""
|
||||
@@ -849,7 +849,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
|
||||
var r = ""
|
||||
for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
|
||||
result = getIdent(cache, r)
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getNameIdent(cache, n[0])
|
||||
else:
|
||||
result = nil
|
||||
@@ -863,7 +863,7 @@ proc getRstName(n: PNode): PRstNode =
|
||||
of nkAccQuoted:
|
||||
result = getRstName(n[0])
|
||||
for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getRstName(n[0])
|
||||
else:
|
||||
result = nil
|
||||
@@ -1206,7 +1206,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
|
||||
var param = %{"name": %($genericParam)}
|
||||
if genericParam.sym.typ.len > 0:
|
||||
param["types"] = newJArray()
|
||||
param["types"].add %($genericParam.sym.typ.elementType)
|
||||
param["types"] = %($genericParam.sym.typ.elementType)
|
||||
result.json["signature"]["genericParams"].add param
|
||||
if optGenIndex in d.conf.globalOptions:
|
||||
genItem(d, n, nameNode, k, kForceExport)
|
||||
@@ -1320,7 +1320,7 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id
|
||||
if t.startsWith("ref "): t = substr(t, 4)
|
||||
effects[i] = newIdentNode(getIdent(cache, t), n.info)
|
||||
# set the type so that the following analysis doesn't screw up:
|
||||
effects[i].typ = real[i].typ
|
||||
effects[i].typ() = real[i].typ
|
||||
|
||||
result = newTreeI(nkExprColonExpr, n.info,
|
||||
newIdentNode(getIdent(cache, $effectType), n.info), effects)
|
||||
@@ -1406,11 +1406,14 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
|
||||
for it in n: traceDeps(d, it)
|
||||
of nkExportStmt:
|
||||
for it in n:
|
||||
# bug #23051; don't generate documentation for exported symbols again
|
||||
if it.kind == nkSym and sfExported notin it.sym.flags:
|
||||
if d.module != nil and d.module == it.sym.owner:
|
||||
generateDoc(d, it.sym.ast, orig, config, kForceExport)
|
||||
if it.kind == nkSym:
|
||||
if d.module != nil and d.module == it.sym.owner: # in current module
|
||||
# bug #23051; don't generate documentation for exported symbols again
|
||||
if sfExported notin it.sym.flags:
|
||||
generateDoc(d, it.sym.ast, orig, config, kForceExport)
|
||||
# else it's to be handled in `of XxxSection` branch
|
||||
elif it.sym.ast != nil:
|
||||
# only export symbols in imported modules, not in current module
|
||||
exportSym(d, it.sym)
|
||||
of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
|
||||
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
|
||||
@@ -1889,6 +1892,9 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
|
||||
else:
|
||||
#echo getOutFile(gProjectFull, JsonExt)
|
||||
let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
|
||||
conf.outFile = filename.relativeTo(conf.outDir)
|
||||
let dir = filename.splitFile.dir
|
||||
createDir(dir)
|
||||
try:
|
||||
writeFile(filename, content)
|
||||
except IOError:
|
||||
@@ -1909,8 +1915,10 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
|
||||
if optStdout in d.conf.globalOptions:
|
||||
write(stdout, content)
|
||||
else:
|
||||
#echo getOutFile(gProjectFull, TagsExt)
|
||||
let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
|
||||
conf.outFile = filename.relativeTo(conf.outDir)
|
||||
let dir = filename.splitFile.dir
|
||||
createDir(dir)
|
||||
try:
|
||||
writeFile(filename, content)
|
||||
except IOError:
|
||||
|
||||
@@ -33,6 +33,10 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
|
||||
caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
|
||||
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
|
||||
#newIntTypeNode(nkIntLit, field.position, t)
|
||||
# safety branch for invalid data:
|
||||
caseStmt.add newTree(nkElse,
|
||||
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res),
|
||||
newStrNode("", info))))
|
||||
|
||||
body.add(caseStmt)
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
# the nkPar node:
|
||||
if n.isNil:
|
||||
result = newNode(nkTupleConstr)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
if typ.n.isNil:
|
||||
internalError(conf, "cannot unpack unnamed tuple")
|
||||
unpackObjectAdd(conf, x, typ.n, result)
|
||||
@@ -298,7 +298,7 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
if n.isNil:
|
||||
result = newNode(nkBracket)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
newSeq(result.sons, lengthOrd(conf, typ).toInt)
|
||||
else:
|
||||
result = n
|
||||
@@ -319,7 +319,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
template aw(k, v, field: untyped): untyped =
|
||||
if n.isNil:
|
||||
result = newNode(k)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
else:
|
||||
# check we have the right field:
|
||||
result = n
|
||||
@@ -333,12 +333,12 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
template setNil() =
|
||||
if n.isNil:
|
||||
result = newNode(nkNilLit)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
else:
|
||||
reset n[]
|
||||
result = n
|
||||
result[] = TNode(kind: nkNilLit)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
|
||||
template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
|
||||
template awf(kind, v: untyped): untyped = aw(kind, v, floatVal)
|
||||
@@ -427,7 +427,7 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
# cast through a pointer needs a new inner object:
|
||||
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
|
||||
else: x.copyTree
|
||||
y.typ = x.typ
|
||||
y.typ() = x.typ
|
||||
result = unpack(conf, a, destTyp, y)
|
||||
dealloc a
|
||||
|
||||
@@ -481,7 +481,7 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||
if aTyp.isNil:
|
||||
internalAssert conf, i+1 < fntyp.len
|
||||
aTyp = fntyp[i+1]
|
||||
args[i+start].typ = aTyp
|
||||
args[i+start].typ() = aTyp
|
||||
sig[i] = mapType(conf, aTyp)
|
||||
if sig[i].isNil: globalError(conf, info, "cannot map FFI type")
|
||||
|
||||
|
||||
@@ -33,6 +33,19 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
let x = param
|
||||
if x.kind == nkArgList:
|
||||
for y in items(x): result.add(y)
|
||||
elif nfDefaultRefsParam in x.flags:
|
||||
# value of default param needs to be evaluated like template body
|
||||
# if it contains other template params
|
||||
var res: PNode
|
||||
if isAtom(x):
|
||||
res = newNodeI(nkPar, x.info)
|
||||
evalTemplateAux(x, actual, c, res)
|
||||
if res.len == 1: res = res[0]
|
||||
else:
|
||||
res = copyNode(x)
|
||||
for i in 0..<x.safeLen:
|
||||
evalTemplateAux(x[i], actual, c, res)
|
||||
result.add res
|
||||
else:
|
||||
result.add copyTree(x)
|
||||
|
||||
@@ -51,7 +64,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
if x == nil:
|
||||
x = copySym(s, c.idgen)
|
||||
# sem'check needs to set the owner properly later, see bug #9476
|
||||
x.owner = nil # c.genSymOwner
|
||||
setOwner(x, nil) # c.genSymOwner
|
||||
#if x.kind == skParam and x.owner.kind == skModule:
|
||||
# internalAssert c.config, false
|
||||
idTablePut(c.mapping, s, x)
|
||||
@@ -169,7 +182,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
|
||||
d.add newSymNode(sym, info)
|
||||
result.add d
|
||||
result.add res
|
||||
result.typ = res.typ
|
||||
result.typ() = res.typ
|
||||
|
||||
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
conf: ConfigRef;
|
||||
|
||||
@@ -125,7 +125,7 @@ proc expandDefault(t: PType; info: TLineInfo): PNode =
|
||||
of tyString:
|
||||
result = newZero(t, info, nkStrLit)
|
||||
of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
|
||||
tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
|
||||
tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
|
||||
tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
|
||||
result = newZero(t, info, nkEmpty) # bug indicator
|
||||
|
||||
@@ -63,7 +63,7 @@ type
|
||||
|
||||
# Configuration settings for various compilers.
|
||||
# When adding new compilers, the cmake sources could be a good reference:
|
||||
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
||||
# https://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
||||
|
||||
template compiler(name, settings: untyped): untyped =
|
||||
proc name: TInfoCC {.compileTime.} = settings
|
||||
@@ -162,7 +162,11 @@ compiler vcc:
|
||||
linkerExe: "cl",
|
||||
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
|
||||
includeCmd: " /I",
|
||||
linkDirCmd: " /LIBPATH:",
|
||||
# HACK: we call `cl` so we have to pass `/link` for linker options,
|
||||
# but users may still want to pass arguments to `cl` (see #14221)
|
||||
# to deal with this, we add `/link` before each `/LIBPATH`,
|
||||
# the linker ignores extra `/link`s since it's an unrecognized argument
|
||||
linkDirCmd: " /link /LIBPATH:",
|
||||
linkLibCmd: " $1.lib",
|
||||
debug: " /RTC1 /Z7 ",
|
||||
pic: "",
|
||||
@@ -172,6 +176,22 @@ compiler vcc:
|
||||
cppXsupport: "",
|
||||
props: {hasCpp, hasAssume, hasDeclspec})
|
||||
|
||||
# Nvidia CUDA NVCC Compiler
|
||||
compiler nvcc:
|
||||
result = gcc()
|
||||
result.name = "nvcc"
|
||||
result.compilerExe = "nvcc"
|
||||
result.cppCompiler = "nvcc"
|
||||
result.compileTmpl = "-c -x cu -Xcompiler=\"$options\" $include -o $objfile $file"
|
||||
result.linkTmpl = "$buildgui $builddll -o $exefile $objfiles -Xcompiler=\"$options\""
|
||||
|
||||
# AMD HIPCC Compiler (rocm/cuda)
|
||||
compiler hipcc:
|
||||
result = clang()
|
||||
result.name = "hipcc"
|
||||
result.compilerExe = "hipcc"
|
||||
result.cppCompiler = "hipcc"
|
||||
|
||||
compiler clangcl:
|
||||
result = vcc()
|
||||
result.name = "clang_cl"
|
||||
@@ -237,8 +257,8 @@ compiler tcc:
|
||||
linkerExe: "tcc",
|
||||
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
linkDirCmd: " -L",
|
||||
linkLibCmd: " -l$1",
|
||||
debug: " -g ",
|
||||
pic: "",
|
||||
asmStmtFrmt: "asm($1);$n",
|
||||
@@ -285,7 +305,9 @@ const
|
||||
envcc(),
|
||||
icl(),
|
||||
icc(),
|
||||
clangcl()]
|
||||
clangcl(),
|
||||
hipcc(),
|
||||
nvcc()]
|
||||
|
||||
hExt* = ".h"
|
||||
|
||||
@@ -319,7 +341,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
|
||||
var fullSuffix = suffix
|
||||
case conf.backend
|
||||
of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
|
||||
of backendC, backendNir: discard
|
||||
of backendC, backendNif: discard
|
||||
of backendInvalid:
|
||||
# during parsing of cfg files; we don't know the backend yet, no point in
|
||||
# guessing wrong thing
|
||||
@@ -452,6 +474,11 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
|
||||
proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
|
||||
result = conf.compileOptions
|
||||
|
||||
if (conf.cCompiler == ccGcc or conf.cCompiler == ccCLang) and
|
||||
conf.selectedGC == gcRefc:
|
||||
# bug #10625
|
||||
addOpt(result, "-fno-omit-frame-pointer")
|
||||
|
||||
for option in conf.compileOptionsCmd:
|
||||
if strutils.find(result, option, 0) < 0:
|
||||
addOpt(result, option)
|
||||
@@ -759,7 +786,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||
# https://blog.molecular-matters.com/2017/05/09/deleting-pdb-files-locked-by-visual-studio/
|
||||
# and a bit about the .pdb format in case that is ever needed:
|
||||
# https://github.com/crosire/blink
|
||||
# http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
|
||||
# https://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
|
||||
if conf.hcrOn and isVSCompatible(conf):
|
||||
let t = now()
|
||||
let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
|
||||
@@ -783,6 +810,59 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body
|
||||
(ose.msg & " " & $ose.errorCode))
|
||||
raise
|
||||
|
||||
proc createMacAppBundle(conf: ConfigRef; exefile: AbsoluteFile) =
|
||||
let (dir, name, _) = splitFile(exefile.string)
|
||||
let appBundleName = name & ".app"
|
||||
let appBundlePath = dir / appBundleName
|
||||
let contentsPath = appBundlePath / "Contents"
|
||||
let macosPath = contentsPath / "MacOS"
|
||||
|
||||
createDir(macosPath)
|
||||
|
||||
let bundleExePath = macosPath / name
|
||||
copyFileWithPermissions(exefile.string, bundleExePath)
|
||||
|
||||
let infoPlistPath = contentsPath / "Info.plist"
|
||||
|
||||
proc xmlEscape(s: string): string =
|
||||
result = newStringOfCap(s.len)
|
||||
for c in items(s):
|
||||
case c:
|
||||
of '<': result.add("<")
|
||||
of '>': result.add(">")
|
||||
of '&': result.add("&")
|
||||
of '"': result.add(""")
|
||||
of '\'': result.add("'")
|
||||
else:
|
||||
if ord(c) < 32:
|
||||
result.add("&#" & $ord(c) & ';')
|
||||
else:
|
||||
result.add(c)
|
||||
|
||||
let escapedName = xmlEscape(name)
|
||||
let infoPlistContent = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$1</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.nim.$1</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$1</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>""" % [escapedName]
|
||||
|
||||
writeFile(infoPlistPath, infoPlistContent)
|
||||
|
||||
removeFile(exefile.string)
|
||||
|
||||
rawMessage(conf, hintUserRaw, "Created Mac app bundle: " & appBundlePath)
|
||||
|
||||
proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] =
|
||||
result = @[]
|
||||
when defined(macosx):
|
||||
@@ -967,6 +1047,10 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||
preventLinkCmdMaxCmdLen(conf, linkCmd)
|
||||
for cmd in extraCmds:
|
||||
execExternalProgram(conf, cmd, hintExecuting)
|
||||
# create Mac app bundle for GUI apps on macOS
|
||||
when defined(macosx):
|
||||
if conf.globalOptions * {optGenGuiApp, optGenDynLib, optGenStaticLib} == {optGenGuiApp}:
|
||||
createMacAppBundle(conf, mainOutput)
|
||||
else:
|
||||
linkCmd = ""
|
||||
if optGenScript in conf.globalOptions:
|
||||
@@ -982,10 +1066,11 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
|
||||
# works out of the box with `hashMainCompilationParams`.
|
||||
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
|
||||
|
||||
const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
|
||||
const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
|
||||
type BuildCache = object
|
||||
cacheVersion: string
|
||||
outputFile: string
|
||||
outputLastModificationTime: string
|
||||
compile: seq[(string, string)]
|
||||
link: seq[string]
|
||||
linkcmd: string
|
||||
@@ -1029,6 +1114,8 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
|
||||
bcache.depfiles.add (path, $secureHashFile(path))
|
||||
|
||||
bcache.nimexe = hashNimExe()
|
||||
if fileExists(bcache.outputFile):
|
||||
bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
|
||||
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
|
||||
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
|
||||
|
||||
@@ -1049,6 +1136,8 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
|
||||
# xxx optimize by returning false if stdin input was the same
|
||||
for (file, hash) in bcache.depfiles:
|
||||
if $secureHashFile(file) != hash: return true
|
||||
if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
|
||||
return true
|
||||
|
||||
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
var bcache: BuildCache = default(BuildCache)
|
||||
@@ -1065,7 +1154,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
"jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
|
||||
[outputCurrent, output, jsonFile.string])
|
||||
var cmds: TStringSeq = default(TStringSeq)
|
||||
var prettyCmds: TStringSeq= default(TStringSeq)
|
||||
var prettyCmds: TStringSeq = default(TStringSeq)
|
||||
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
|
||||
for (name, cmd) in bcache.compile:
|
||||
cmds.add cmd
|
||||
|
||||
@@ -1104,7 +1104,7 @@ proc settype(n: PNode): PType =
|
||||
|
||||
proc buildOf(it, loc: PNode; o: Operators): PNode =
|
||||
var s = newNodeI(nkCurly, it.info, it.len-1)
|
||||
s.typ = settype(loc)
|
||||
s.typ() = settype(loc)
|
||||
for i in 0..<it.len-1: s[i] = it[i]
|
||||
result = newNodeI(nkCall, it.info, 3)
|
||||
result[0] = newSymNode(o.opContains)
|
||||
@@ -1165,8 +1165,12 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||
if check[1].kind == nkCurly:
|
||||
result = copyTree(check)
|
||||
if access.kind == nkDotExpr:
|
||||
# change the access to the discriminator field access
|
||||
var a = copyTree(access)
|
||||
# set field name to discriminator field name
|
||||
a[1] = check[2]
|
||||
# set discriminator field type: important for `neg`
|
||||
a.typ() = check[2].typ
|
||||
result[2] = a
|
||||
# 'access.kind != nkDotExpr' can happen for object constructors
|
||||
# which we don't check yet
|
||||
|
||||
@@ -16,7 +16,7 @@ from std/os import removeFile, isAbsolute
|
||||
|
||||
import ../../dist/checksums/src/checksums/sha1
|
||||
|
||||
import ".." / nir / nirlineinfos
|
||||
import iclineinfos
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions, formatfloat]
|
||||
@@ -33,42 +33,7 @@ type
|
||||
HasDatInitProc
|
||||
HasModuleInitProc
|
||||
|
||||
PackedModuleReader* = object ## the parts of a PackedEncoder that are part of the .rod file
|
||||
definedSymbols: string
|
||||
moduleFlags: TSymFlags
|
||||
includes*: seq[(LitId, string)] # first entry is the module filename itself
|
||||
imports: seq[LitId] # the modules this module depends on
|
||||
toReplay*: PackedTree # pragmas and VM specific state to replay.
|
||||
topLevel*: PackedTree # top level statements
|
||||
bodies*: PackedTree # other trees. Referenced from typ.n and sym.ast by their position.
|
||||
#producedGenerics*: Table[GenericKey, SymId]
|
||||
exports*: seq[(LitId, int32)]
|
||||
hidden: seq[(LitId, int32)]
|
||||
reexports: seq[(LitId, PackedItemId)]
|
||||
compilerProcs*: seq[(LitId, int32)]
|
||||
converters*, methods*, trmacros*, pureEnums*: seq[int32]
|
||||
|
||||
typeInstCache*: seq[(PackedItemId, PackedItemId)]
|
||||
procInstCache*: seq[PackedInstantiation]
|
||||
attachedOps*: seq[(PackedItemId, TTypeAttachedOp, PackedItemId)]
|
||||
methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
|
||||
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
|
||||
methodsPerType*: seq[(PackedItemId, PackedItemId)]
|
||||
dispatchers*: seq[PackedItemId]
|
||||
|
||||
emittedTypeInfo*: seq[string]
|
||||
backendFlags*: set[ModuleBackendFlag]
|
||||
|
||||
syms*: OrderedTable[int32, PackedSym]
|
||||
types*: OrderedTable[int32, PackedType]
|
||||
strings*: BiTable[string] # we could share these between modules.
|
||||
numbers*: BiTable[BiggestInt] # we also store floats in here so
|
||||
# that we can assure that every bit is kept
|
||||
man*: LineInfoManager
|
||||
|
||||
cfg: PackedConfig
|
||||
|
||||
PackedModuleWriter* = object ## the parts of a PackedEncoder that are part of the .rod file
|
||||
PackedModule* = object ## the parts of a PackedEncoder that are part of the .rod file
|
||||
definedSymbols: string
|
||||
moduleFlags: TSymFlags
|
||||
includes*: seq[(LitId, string)] # first entry is the module filename itself
|
||||
@@ -104,7 +69,7 @@ type
|
||||
cfg: PackedConfig
|
||||
|
||||
PackedEncoder* = object
|
||||
#m*: PackedModuleWriter
|
||||
#m*: PackedModule
|
||||
thisModule*: int32
|
||||
lastFile*: FileIndex # remember the last lookup entry.
|
||||
lastLit*: LitId
|
||||
@@ -115,7 +80,7 @@ type
|
||||
symMarker*: IntSet #Table[ItemId, SymId] # ItemId.item -> SymId
|
||||
config*: ConfigRef
|
||||
|
||||
proc toString*(tree: PackedTree; pos: NodePos; m: PackedModuleWriter|PackedModuleReader; nesting: int;
|
||||
proc toString*(tree: PackedTree; pos: NodePos; m: PackedModule; nesting: int;
|
||||
result: var string) =
|
||||
if result.len > 0 and result[^1] notin {' ', '\n'}:
|
||||
result.add ' '
|
||||
@@ -151,11 +116,11 @@ proc toString*(tree: PackedTree; pos: NodePos; m: PackedModuleWriter|PackedModul
|
||||
result.add ")"
|
||||
#for i in 1..nesting*2: result.add ' '
|
||||
|
||||
proc toString*(tree: PackedTree; n: NodePos; m: PackedModuleWriter|PackedModuleReader): string =
|
||||
proc toString*(tree: PackedTree; n: NodePos; m: PackedModule): string =
|
||||
result = ""
|
||||
toString(tree, n, m, 0, result)
|
||||
|
||||
proc debug*(tree: PackedTree; m: PackedModuleWriter|PackedModuleReader) =
|
||||
proc debug*(tree: PackedTree; m: PackedModule) =
|
||||
stdout.write toString(tree, NodePos 0, m)
|
||||
|
||||
proc isActive*(e: PackedEncoder): bool = e.config != nil
|
||||
@@ -175,7 +140,7 @@ proc definedSymbolsAsString(config: ConfigRef): string =
|
||||
result.add ' '
|
||||
result.add d
|
||||
|
||||
proc rememberConfig(c: var PackedEncoder; m: var PackedModuleWriter; config: ConfigRef; pc: PackedConfig) =
|
||||
proc rememberConfig(c: var PackedEncoder; m: var PackedModule; config: ConfigRef; pc: PackedConfig) =
|
||||
m.definedSymbols = definedSymbolsAsString(config)
|
||||
#template rem(x) =
|
||||
# c.m.cfg.x = config.x
|
||||
@@ -188,7 +153,7 @@ const
|
||||
when debugConfigDiff:
|
||||
import hashes, tables, intsets, sha1, strutils, sets
|
||||
|
||||
proc configIdentical(m: PackedModuleReader; config: ConfigRef): bool =
|
||||
proc configIdentical(m: PackedModule; config: ConfigRef): bool =
|
||||
result = m.definedSymbols == definedSymbolsAsString(config)
|
||||
when debugConfigDiff:
|
||||
if not result:
|
||||
@@ -218,7 +183,7 @@ proc hashFileCached(conf: ConfigRef; fileIdx: FileIndex): string =
|
||||
result = $secureHashFile(fullpath)
|
||||
msgs.setHash(conf, fileIdx, result)
|
||||
|
||||
proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModuleWriter): LitId =
|
||||
proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModule): LitId =
|
||||
## store a file index as a literal
|
||||
if x == c.lastFile:
|
||||
result = c.lastLit
|
||||
@@ -232,16 +197,16 @@ proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModuleWriter): Lit
|
||||
c.lastLit = result
|
||||
assert result != LitId(0)
|
||||
|
||||
proc toFileIndex*(x: LitId; m: PackedModuleReader; config: ConfigRef): FileIndex =
|
||||
proc toFileIndex*(x: LitId; m: PackedModule; config: ConfigRef): FileIndex =
|
||||
result = msgs.fileInfoIdx(config, AbsoluteFile m.strings[x])
|
||||
|
||||
proc includesIdentical(m: var PackedModuleReader; config: ConfigRef): bool =
|
||||
proc includesIdentical(m: var PackedModule; config: ConfigRef): bool =
|
||||
for it in mitems(m.includes):
|
||||
if hashFileCached(config, toFileIndex(it[0], m, config)) != it[1]:
|
||||
return false
|
||||
result = true
|
||||
|
||||
proc initEncoder*(c: var PackedEncoder; m: var PackedModuleWriter; moduleSym: PSym; config: ConfigRef; pc: PackedConfig) =
|
||||
proc initEncoder*(c: var PackedEncoder; m: var PackedModule; moduleSym: PSym; config: ConfigRef; pc: PackedConfig) =
|
||||
## setup a context for serializing to packed ast
|
||||
c.thisModule = moduleSym.itemId.module
|
||||
c.config = config
|
||||
@@ -263,54 +228,54 @@ proc initEncoder*(c: var PackedEncoder; m: var PackedModuleWriter; moduleSym: PS
|
||||
|
||||
rememberConfig(c, m, config, pc)
|
||||
|
||||
proc addIncludeFileDep*(c: var PackedEncoder; m: var PackedModuleWriter; f: FileIndex) =
|
||||
proc addIncludeFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex) =
|
||||
m.includes.add((toLitId(f, c, m), hashFileCached(c.config, f)))
|
||||
|
||||
proc addImportFileDep*(c: var PackedEncoder; m: var PackedModuleWriter; f: FileIndex) =
|
||||
proc addImportFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex) =
|
||||
m.imports.add toLitId(f, c, m)
|
||||
|
||||
proc addHidden*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym) =
|
||||
proc addHidden*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
|
||||
assert s.kind != skUnknown
|
||||
let nameId = getOrIncl(m.strings, s.name.s)
|
||||
m.hidden.add((nameId, s.itemId.item))
|
||||
assert s.itemId.module == c.thisModule
|
||||
|
||||
proc addExported*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym) =
|
||||
proc addExported*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
|
||||
assert s.kind != skUnknown
|
||||
assert s.itemId.module == c.thisModule
|
||||
let nameId = getOrIncl(m.strings, s.name.s)
|
||||
m.exports.add((nameId, s.itemId.item))
|
||||
|
||||
proc addConverter*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym) =
|
||||
proc addConverter*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
|
||||
assert c.thisModule == s.itemId.module
|
||||
m.converters.add(s.itemId.item)
|
||||
|
||||
proc addTrmacro*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym) =
|
||||
proc addTrmacro*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
|
||||
m.trmacros.add(s.itemId.item)
|
||||
|
||||
proc addPureEnum*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym) =
|
||||
proc addPureEnum*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
|
||||
assert s.kind == skType
|
||||
m.pureEnums.add(s.itemId.item)
|
||||
|
||||
proc addMethod*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym) =
|
||||
proc addMethod*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
|
||||
m.methods.add s.itemId.item
|
||||
|
||||
proc addReexport*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym) =
|
||||
proc addReexport*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
|
||||
assert s.kind != skUnknown
|
||||
if s.kind == skModule: return
|
||||
let nameId = getOrIncl(m.strings, s.name.s)
|
||||
m.reexports.add((nameId, PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m),
|
||||
item: s.itemId.item)))
|
||||
|
||||
proc addCompilerProc*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym) =
|
||||
proc addCompilerProc*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
|
||||
let nameId = getOrIncl(m.strings, s.name.s)
|
||||
m.compilerProcs.add((nameId, s.itemId.item))
|
||||
|
||||
proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModuleWriter)
|
||||
proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModuleWriter): PackedItemId
|
||||
proc storeType(t: PType; c: var PackedEncoder; m: var PackedModuleWriter): PackedItemId
|
||||
proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule)
|
||||
proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
|
||||
proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId
|
||||
|
||||
proc flush(c: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
proc flush(c: var PackedEncoder; m: var PackedModule) =
|
||||
## serialize any pending types or symbols from the context
|
||||
while true:
|
||||
if c.pendingTypes.len > 0:
|
||||
@@ -320,19 +285,19 @@ proc flush(c: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
else:
|
||||
break
|
||||
|
||||
proc toLitId(x: string; m: var PackedModuleWriter): LitId =
|
||||
proc toLitId(x: string; m: var PackedModule): LitId =
|
||||
## store a string as a literal
|
||||
result = getOrIncl(m.strings, x)
|
||||
|
||||
proc toLitId(x: BiggestInt; m: var PackedModuleWriter): LitId =
|
||||
proc toLitId(x: BiggestInt; m: var PackedModule): LitId =
|
||||
## store an integer as a literal
|
||||
result = getOrIncl(m.numbers, x)
|
||||
|
||||
proc toPackedInfo(x: TLineInfo; c: var PackedEncoder; m: var PackedModuleWriter): PackedLineInfo =
|
||||
proc toPackedInfo(x: TLineInfo; c: var PackedEncoder; m: var PackedModule): PackedLineInfo =
|
||||
pack(m.man, toLitId(x.fileIndex, c, m), x.line.int32, x.col.int32)
|
||||
#PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m))
|
||||
|
||||
proc safeItemId(s: PSym; c: var PackedEncoder; m: var PackedModuleWriter): PackedItemId {.inline.} =
|
||||
proc safeItemId(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId {.inline.} =
|
||||
## given a symbol, produce an ItemId with the correct properties
|
||||
## for local or remote symbols, packing the symbol as necessary
|
||||
if s == nil or s.kind == skPackage:
|
||||
@@ -366,7 +331,7 @@ template storeNode(dest, src, field) =
|
||||
nodeId = emptyNodeId
|
||||
dest.field = nodeId
|
||||
|
||||
proc storeTypeLater(t: PType; c: var PackedEncoder; m: var PackedModuleWriter): PackedItemId =
|
||||
proc storeTypeLater(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId =
|
||||
# We store multiple different trees in m.bodies. For this to work out, we
|
||||
# cannot immediately store types/syms. We enqueue them instead to ensure
|
||||
# we only write one tree into m.bodies after the other.
|
||||
@@ -379,7 +344,7 @@ proc storeTypeLater(t: PType; c: var PackedEncoder; m: var PackedModuleWriter):
|
||||
# the type belongs to this module, so serialize it here, eventually.
|
||||
addMissing(c, t)
|
||||
|
||||
proc storeSymLater(s: PSym; c: var PackedEncoder; m: var PackedModuleWriter): PackedItemId =
|
||||
proc storeSymLater(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId =
|
||||
if s.isNil: return nilItemId
|
||||
assert s.itemId.module >= 0
|
||||
assert s.itemId.item >= 0
|
||||
@@ -388,7 +353,7 @@ proc storeSymLater(s: PSym; c: var PackedEncoder; m: var PackedModuleWriter): Pa
|
||||
# the sym belongs to this module, so serialize it here, eventually.
|
||||
addMissing(c, s)
|
||||
|
||||
proc storeType(t: PType; c: var PackedEncoder; m: var PackedModuleWriter): PackedItemId =
|
||||
proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId =
|
||||
## serialize a ptype
|
||||
if t.isNil: return nilItemId
|
||||
|
||||
@@ -415,7 +380,7 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModuleWriter): Packe
|
||||
# fill the reserved slot, nothing else:
|
||||
m.types[t.uniqueId.item] = p
|
||||
|
||||
proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModuleWriter): PackedLib =
|
||||
proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib =
|
||||
## the plib hangs off the psym via the .annex field
|
||||
if l.isNil: return
|
||||
result = PackedLib(kind: l.kind, generated: l.generated,
|
||||
@@ -423,7 +388,7 @@ proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModuleWriter): Pack
|
||||
)
|
||||
storeNode(result, l, path)
|
||||
|
||||
proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModuleWriter): PackedItemId =
|
||||
proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId =
|
||||
## serialize a psym
|
||||
if s.isNil: return nilItemId
|
||||
|
||||
@@ -449,7 +414,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModuleWriter): Packed
|
||||
p.bitsize = s.bitsize
|
||||
p.alignment = s.alignment
|
||||
|
||||
p.externalName = toLitId(s.loc.r, m)
|
||||
p.externalName = toLitId(s.loc.snippet, m)
|
||||
p.locFlags = s.loc.flags
|
||||
c.addMissing s.typ
|
||||
p.typ = s.typ.storeType(c, m)
|
||||
@@ -463,7 +428,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModuleWriter): Packed
|
||||
# fill the reserved slot, nothing else:
|
||||
m.syms[s.itemId.item] = p
|
||||
|
||||
proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
|
||||
## add a remote symbol reference to the tree
|
||||
let info = n.info.toPackedInfo(c, m)
|
||||
if n.typ != n.sym.typ:
|
||||
@@ -478,7 +443,7 @@ proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pac
|
||||
ir.addNode(kind = nkNone, info = info,
|
||||
operand = n.sym.itemId.item)
|
||||
|
||||
proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
|
||||
## serialize a node into the tree
|
||||
if n == nil:
|
||||
ir.addNode(kind = nkNilRodNode, operand = 1, info = NoLineInfo)
|
||||
@@ -526,13 +491,13 @@ proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pa
|
||||
toPackedNode(n[i], ir, c, m)
|
||||
ir.patch patchPos
|
||||
|
||||
proc storeTypeInst*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym; inst: PType) =
|
||||
proc storeTypeInst*(c: var PackedEncoder; m: var PackedModule; s: PSym; inst: PType) =
|
||||
m.typeInstCache.add (storeSymLater(s, c, m), storeTypeLater(inst, c, m))
|
||||
|
||||
proc addPragmaComputation*(c: var PackedEncoder; m: var PackedModuleWriter; n: PNode) =
|
||||
proc addPragmaComputation*(c: var PackedEncoder; m: var PackedModule; n: PNode) =
|
||||
toPackedNode(n, m.toReplay, c, m)
|
||||
|
||||
proc toPackedProcDef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
proc toPackedProcDef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
|
||||
let info = toPackedInfo(n.info, c, m)
|
||||
let patchPos = ir.prepare(n.kind, n.flags,
|
||||
storeTypeLater(n.typ, c, m), info)
|
||||
@@ -547,7 +512,7 @@ proc toPackedProcDef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var
|
||||
typeId = nilItemId, info = info)
|
||||
ir.patch patchPos
|
||||
|
||||
proc toPackedNodeIgnoreProcDefs(n: PNode, encoder: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
proc toPackedNodeIgnoreProcDefs(n: PNode, encoder: var PackedEncoder; m: var PackedModule) =
|
||||
case n.kind
|
||||
of routineDefs:
|
||||
toPackedProcDef(n, m.topLevel, encoder, m)
|
||||
@@ -569,11 +534,11 @@ proc toPackedNodeIgnoreProcDefs(n: PNode, encoder: var PackedEncoder; m: var Pac
|
||||
else:
|
||||
toPackedNode(n, m.topLevel, encoder, m)
|
||||
|
||||
proc toPackedNodeTopLevel*(n: PNode, encoder: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
proc toPackedNodeTopLevel*(n: PNode, encoder: var PackedEncoder; m: var PackedModule) =
|
||||
toPackedNodeIgnoreProcDefs(n, encoder, m)
|
||||
flush encoder, m
|
||||
|
||||
proc toPackedGeneratedProcDef*(s: PSym, encoder: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
proc toPackedGeneratedProcDef*(s: PSym, encoder: var PackedEncoder; m: var PackedModule) =
|
||||
## Generic procs and generated `=hook`'s need explicit top-level entries so
|
||||
## that the code generator can work without having to special case these. These
|
||||
## entries will also be useful for other tools and are the cleanest design
|
||||
@@ -583,7 +548,7 @@ proc toPackedGeneratedProcDef*(s: PSym, encoder: var PackedEncoder; m: var Packe
|
||||
#flush encoder, m
|
||||
|
||||
proc storeAttachedProcDef*(t: PType; op: TTypeAttachedOp; s: PSym,
|
||||
encoder: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
encoder: var PackedEncoder; m: var PackedModule) =
|
||||
assert s.kind in routineKinds
|
||||
assert isActive(encoder)
|
||||
let tid = storeTypeLater(t, encoder, m)
|
||||
@@ -591,7 +556,7 @@ proc storeAttachedProcDef*(t: PType; op: TTypeAttachedOp; s: PSym,
|
||||
m.attachedOps.add (tid, op, sid)
|
||||
toPackedGeneratedProcDef(s, encoder, m)
|
||||
|
||||
proc storeInstantiation*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSym; i: PInstantiation) =
|
||||
proc storeInstantiation*(c: var PackedEncoder; m: var PackedModule; s: PSym; i: PInstantiation) =
|
||||
var t = newSeq[PackedItemId](i.concreteTypes.len)
|
||||
for j in 0..high(i.concreteTypes):
|
||||
t[j] = storeTypeLater(i.concreteTypes[j], c, m)
|
||||
@@ -600,7 +565,7 @@ proc storeInstantiation*(c: var PackedEncoder; m: var PackedModuleWriter; s: PSy
|
||||
concreteTypes: t)
|
||||
toPackedGeneratedProcDef(i.sym, c, m)
|
||||
|
||||
proc storeExpansion*(c: var PackedEncoder; m: var PackedModuleWriter; info: TLineInfo; s: PSym) =
|
||||
proc storeExpansion*(c: var PackedEncoder; m: var PackedModule; info: TLineInfo; s: PSym) =
|
||||
toPackedNode(newSymNode(s, info), m.bodies, c, m)
|
||||
|
||||
proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) =
|
||||
@@ -631,7 +596,7 @@ when BenchIC:
|
||||
else:
|
||||
template bench(x, body) = body
|
||||
|
||||
proc loadRodFile*(filename: AbsoluteFile; m: var PackedModuleReader; config: ConfigRef;
|
||||
proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef;
|
||||
ignoreConfig = false): RodFileError =
|
||||
var f = rodfiles.open(filename.string)
|
||||
f.loadHeader()
|
||||
@@ -711,7 +676,7 @@ proc storeError(err: RodFileError; filename: AbsoluteFile) =
|
||||
echo "Error: ", $err, "; couldn't write to ", filename.string
|
||||
removeFile(filename.string)
|
||||
|
||||
proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var PackedModuleWriter) =
|
||||
proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var PackedModule) =
|
||||
flush encoder, m
|
||||
#rememberConfig(encoder, encoder.config)
|
||||
|
||||
@@ -783,7 +748,7 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
|
||||
|
||||
when false:
|
||||
# basic loader testing:
|
||||
var m2: PackedModuleReader
|
||||
var m2: PackedModule
|
||||
discard loadRodFile(filename, m2, encoder.config)
|
||||
echo "loaded ", filename.string
|
||||
|
||||
@@ -809,8 +774,7 @@ type
|
||||
LoadedModule* = object
|
||||
status*: ModuleStatus
|
||||
symsInit, typesInit, loadedButAliveSetChanged*: bool
|
||||
fromDisk*: PackedModuleReader
|
||||
toDisk*: PackedModuleWriter
|
||||
fromDisk*: PackedModule
|
||||
syms: OrderedTable[int32, PSym] # indexed by itemId
|
||||
types: OrderedTable[int32, PType]
|
||||
module*: PSym # the one true module symbol.
|
||||
@@ -873,8 +837,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
|
||||
of nkSym:
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].soperand))
|
||||
if result.typ == nil and nfOpenSym notin result.flags:
|
||||
result.typ = result.sym.typ
|
||||
if result.typ == nil:
|
||||
result.typ() = result.sym.typ
|
||||
of externIntLit:
|
||||
result.intVal = g[thisModule].fromDisk.numbers[n.litId]
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
@@ -887,8 +851,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
assert n2.kind == nkNone
|
||||
transitionNoneToSym(result)
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand))
|
||||
if result.typ == nil and nfOpenSym notin result.flags:
|
||||
result.typ = result.sym.typ
|
||||
if result.typ == nil:
|
||||
result.typ() = result.sym.typ
|
||||
else:
|
||||
for n0 in sonsReadonly(tree, n):
|
||||
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
|
||||
@@ -978,10 +942,10 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
result.guard = loadSym(c, g, si, s.guard)
|
||||
result.bitsize = s.bitsize
|
||||
result.alignment = s.alignment
|
||||
result.owner = loadSym(c, g, si, s.owner)
|
||||
setOwner(result, loadSym(c, g, si, s.owner))
|
||||
let externalName = g[si].fromDisk.strings[s.externalName]
|
||||
if externalName != "":
|
||||
result.loc.r = rope externalName
|
||||
result.loc.snippet = externalName
|
||||
result.loc.flags = s.locFlags
|
||||
result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom)
|
||||
|
||||
@@ -1034,7 +998,7 @@ proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
t: PackedType; si, item: int32; result: PType) =
|
||||
result.sym = loadSym(c, g, si, t.sym)
|
||||
result.owner = loadSym(c, g, si, t.owner)
|
||||
setOwner(result, loadSym(c, g, si, t.owner))
|
||||
when false:
|
||||
for op, item in pairs t.attachedOps:
|
||||
result.attachedOps[op] = loadSym(c, g, si, item)
|
||||
@@ -1098,7 +1062,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
|
||||
name: getIdent(cache, splitFile(filename).name),
|
||||
info: newLineInfo(fileIdx, 1, 1),
|
||||
position: int(fileIdx))
|
||||
m.module.owner = getPackage(conf, cache, fileIdx)
|
||||
setOwner(m.module, getPackage(conf, cache, fileIdx))
|
||||
m.module.flags = m.fromDisk.moduleFlags
|
||||
|
||||
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||
@@ -1229,7 +1193,7 @@ proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; confi
|
||||
ItemId(module: toFileIndex(id.module, g[thisModule].fromDisk, config).int32, item: id.item)
|
||||
|
||||
proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||
moduleSym: PSym; m: PackedModuleWriter) =
|
||||
moduleSym: PSym; m: PackedModule) =
|
||||
# For now only used for heavy debugging. In the future we could use this to reduce the
|
||||
# compiler's memory consumption.
|
||||
let idx = moduleSym.position
|
||||
@@ -1327,7 +1291,7 @@ proc searchForCompilerproc*(m: LoadedModule; name: string): int32 =
|
||||
# ------------------------- .rod file viewer ---------------------------------
|
||||
|
||||
proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =
|
||||
var m: PackedModuleReader = PackedModuleReader()
|
||||
var m: PackedModule = PackedModule()
|
||||
let err = loadRodFile(rodfile, m, config, ignoreConfig=true)
|
||||
if err != ok:
|
||||
config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
# (c) Copyright 2024 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -100,11 +100,11 @@ proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
|
||||
proc checkTree(c: var CheckedContext; t: PackedTree) =
|
||||
for p in allNodes(t): checkNode(c, t, p)
|
||||
|
||||
proc checkLocalSymIds(c: var CheckedContext; m: PackedModuleReader; symIds: seq[int32]) =
|
||||
proc checkLocalSymIds(c: var CheckedContext; m: PackedModule; symIds: seq[int32]) =
|
||||
for symId in symIds:
|
||||
assert symId >= 0 and symId < m.syms.len, $symId & " " & $m.syms.len
|
||||
|
||||
proc checkModule(c: var CheckedContext; m: PackedModuleReader) =
|
||||
proc checkModule(c: var CheckedContext; m: PackedModule) =
|
||||
# We check that:
|
||||
# - Every symbol references existing types and symbols.
|
||||
# - Every tree node references existing types and symbols.
|
||||
|
||||
@@ -20,7 +20,7 @@ when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import ".." / [ast, modulegraphs, msgs, options]
|
||||
import ".." / nir / nirlineinfos
|
||||
import iclineinfos
|
||||
import packed_ast, bitabs, ic
|
||||
|
||||
type
|
||||
|
||||
@@ -16,7 +16,7 @@ import std/[hashes, tables, strtabs]
|
||||
import bitabs, rodfiles
|
||||
import ".." / [ast, options]
|
||||
|
||||
import ".." / nir / nirlineinfos
|
||||
import iclineinfos
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
@@ -231,7 +231,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
|
||||
for i in 0..n.safeLen-1:
|
||||
importForwarded(c, n[i], exceptSet, fromMod, importSet)
|
||||
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, trackUnusedImport: bool): PSym =
|
||||
result = realModule
|
||||
template createModuleAliasImpl(ident): untyped =
|
||||
createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
|
||||
@@ -246,7 +246,10 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
|
||||
result = createModuleAliasImpl(realModule.name)
|
||||
if importHidden:
|
||||
result.options.incl optImportHidden
|
||||
c.unusedImports.add((result, n.info))
|
||||
let moduleIdent = if n.kind in {nkInfix, nkImportAs}: n[^1] else: n
|
||||
result.info = moduleIdent.info
|
||||
if trackUnusedImport:
|
||||
c.unusedImports.add((result, result.info))
|
||||
c.importModuleMap[result.id] = realModule.id
|
||||
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
|
||||
|
||||
@@ -287,10 +290,11 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
toFullPath(c.config, c.graph.importStack[i+1])
|
||||
c.recursiveDep = err
|
||||
|
||||
let trackUnusedImport = warnUnusedImportX in c.config.notes
|
||||
var realModule: PSym
|
||||
discard pushOptionEntry(c)
|
||||
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
|
||||
result = importModuleAs(c, n, realModule, transf.importHidden)
|
||||
result = importModuleAs(c, n, realModule, transf.importHidden, trackUnusedImport)
|
||||
popOptionEntry(c)
|
||||
|
||||
#echo "set back to ", L
|
||||
@@ -334,7 +338,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
|
||||
let m = myImportModule(c, it, importStmtResult)
|
||||
if m != nil:
|
||||
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
||||
addDecl(c, m, it.info) # add symbol to symbol table of module
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
importAllSymbols(c, m)
|
||||
#importForwarded(c, m.ast, emptySet, m)
|
||||
afterImport(c, m)
|
||||
@@ -371,7 +375,7 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
|
||||
var m = myImportModule(c, n[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
|
||||
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
|
||||
for i in 1..<n.len:
|
||||
@@ -386,7 +390,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
||||
var m = myImportModule(c, n[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
importAllSymbolsExcept(c, m, readExceptSet(c, n))
|
||||
#importForwarded(c, m.ast, exceptSet, m)
|
||||
afterImport(c, m)
|
||||
|
||||
@@ -24,7 +24,7 @@ import std/[strtabs, tables, strutils, intsets]
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
from trees import exprStructuralEquivalent, getRoot, whichPragma
|
||||
from trees import exprStructuralEquivalent, getRoot, whichPragma, getPotentialWrites
|
||||
|
||||
type
|
||||
Con = object
|
||||
@@ -100,6 +100,7 @@ when false:
|
||||
proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
||||
let root = parampatterns.exprRoot(n, allowCalls=false)
|
||||
if root == nil: return false
|
||||
elif sfSingleUsedTemp in root.flags: return true
|
||||
|
||||
var s = addr(scope)
|
||||
while s != nil:
|
||||
@@ -162,13 +163,16 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
||||
else:
|
||||
result = false
|
||||
|
||||
template hasDestructorOrAsgn(c: var Con, typ: PType): bool =
|
||||
# bug #23354; an object type could have a non-trivial assignements when it is passed to a sink parameter
|
||||
hasDestructor(c, typ) or (c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
typ.kind == tyObject and not isTrivial(getAttachedOp(c.graph, typ, attachedAsgn)))
|
||||
|
||||
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
|
||||
# bug #23354; an object type could have a non-trival assignements when it is passed to a sink parameter
|
||||
if not hasDestructor(c, n.typ) and (n.typ.kind != tyObject or isTrival(getAttachedOp(c.graph, n.typ, attachedAsgn))): return true
|
||||
if not hasDestructorOrAsgn(c, n.typ): return true
|
||||
|
||||
let m = skipConvDfa(n)
|
||||
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
|
||||
isLastReadImpl(n, c, s)
|
||||
result = isLastReadImpl(n, c, s)
|
||||
|
||||
proc isFirstWrite(n: PNode; c: var Con): bool =
|
||||
let m = skipConvDfa(n)
|
||||
@@ -185,10 +189,11 @@ proc isCursor(n: PNode): bool =
|
||||
else:
|
||||
false
|
||||
|
||||
template isUnpackedTuple(n: PNode): bool =
|
||||
template isFullyUnpackedTuple(n: PNode): bool =
|
||||
## we move out all elements of unpacked tuples,
|
||||
## hence unpacked tuples themselves don't need to be destroyed
|
||||
## except it's already a cursor
|
||||
## restricted to `skTemp`, tuple temps where not every field is unpacked should not use `skTemp`
|
||||
(n.kind == nkSym and n.sym.kind == skTemp and
|
||||
n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
|
||||
|
||||
@@ -197,7 +202,8 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFr
|
||||
if inferredFromCopy:
|
||||
m.add ", which is inferred from unavailable '=copy'"
|
||||
|
||||
if (opname == "=" or opname == "=copy" or opname == "=dup") and ri != nil:
|
||||
if (opname == "=" or opname == "=copy" or opname == "=dup") and
|
||||
ri != nil:
|
||||
m.add "; requires a copy because it's not the last read of '"
|
||||
m.add renderTree(ri)
|
||||
m.add '\''
|
||||
@@ -247,7 +253,12 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode
|
||||
dbg:
|
||||
if kind == attachedDestructor:
|
||||
echo "destructor is ", op.id, " ", op.ast
|
||||
if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
|
||||
if sfError in op.flags:
|
||||
if ri != nil:
|
||||
checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
|
||||
else:
|
||||
# uses the lineinfos of `dest` is `ri` is not available
|
||||
checkForErrorPragma(c, t, dest, AttachedOpToStr[kind])
|
||||
c.genOp(op, dest)
|
||||
|
||||
proc genDestroy(c: var Con; dest: PNode): PNode =
|
||||
@@ -275,7 +286,7 @@ proc deepAliases(dest, ri: PNode): bool =
|
||||
return aliases(dest, ri) != no
|
||||
|
||||
proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
|
||||
if (c.inLoopCond == 0 and (isFullyUnpackedTuple(dest) or IsDecl in flags or
|
||||
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
|
||||
isNoInit(dest) or IsReturn in flags:
|
||||
# optimize sink call into a bitwise memcopy
|
||||
@@ -317,8 +328,9 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
|
||||
]#
|
||||
result = dest.kind != nkSym
|
||||
|
||||
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc:
|
||||
proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags:
|
||||
# add cyclic flag, but not to sink calls, which IsExplicitSink generates
|
||||
let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if cyclicType(c.graph, t):
|
||||
result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
|
||||
@@ -331,7 +343,7 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) =
|
||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
|
||||
else:
|
||||
let xenv = genBuiltin(c.graph, c.idgen, mAccessEnv, "accessEnv", dest)
|
||||
xenv.typ = getSysType(c.graph, dest.info, tyPointer)
|
||||
xenv.typ() = getSysType(c.graph, dest.info, tyPointer)
|
||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
|
||||
|
||||
proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
|
||||
@@ -399,7 +411,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode =
|
||||
result = genOp(c, op, n)
|
||||
else:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "`=wasMoved`", mWasMoved)))
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
|
||||
result.add copyTree(n) #mWasMoved does not take the address
|
||||
#if n.kind != nkSym:
|
||||
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
|
||||
@@ -407,7 +419,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode =
|
||||
proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
||||
result = newNodeI(nkCall, info)
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault)))
|
||||
result.typ = t
|
||||
result.typ() = t
|
||||
|
||||
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
# generate: (let tmp = v; reset(v); tmp)
|
||||
@@ -448,7 +460,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
|
||||
let tmp = c.getTemp(s, nTyp, n.info)
|
||||
if hasDestructor(c, nTyp):
|
||||
if hasDestructorOrAsgn(c, nTyp):
|
||||
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let op = getAttachedOp(c.graph, typ, attachedDup)
|
||||
if op != nil and tfHasOwned notin typ.flags:
|
||||
@@ -464,7 +476,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
var newCall = newTreeIT(nkCall, src.info, src.typ,
|
||||
newSymNode(op),
|
||||
src)
|
||||
c.finishCopy(newCall, n, isFromSink = true)
|
||||
c.finishCopy(newCall, n, {}, isFromSink = true)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
newCall
|
||||
@@ -473,7 +485,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
c.finishCopy(m, n, {}, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
message(c.graph.config, n.info, hintPerformance,
|
||||
@@ -501,7 +513,7 @@ proc containsConstSeq(n: PNode): bool =
|
||||
return true
|
||||
result = false
|
||||
case n.kind
|
||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
|
||||
result = containsConstSeq(n[1])
|
||||
of nkObjConstr, nkClosure:
|
||||
for i in 1..<n.len:
|
||||
@@ -558,7 +570,7 @@ proc cycleCheck(n: PNode; c: var Con) =
|
||||
proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
|
||||
# move the variable declaration to the top of the frame:
|
||||
s.vars.add v.sym
|
||||
if isUnpackedTuple(v):
|
||||
if isFullyUnpackedTuple(v):
|
||||
if c.inLoop > 0:
|
||||
# unpacked tuple needs reset at every loop iteration
|
||||
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
|
||||
@@ -653,7 +665,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
|
||||
for j in 0 ..< it.len-1:
|
||||
branch[j] = copyTree(it[j])
|
||||
var ofScope = nestedScope(s, it.lastSon)
|
||||
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
|
||||
branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
|
||||
processScope(c, ofScope, maybeVoid(it[^1], ofScope))
|
||||
else:
|
||||
processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags)
|
||||
@@ -701,7 +713,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
|
||||
#Condition needs to be destroyed outside of the condition/branch scope
|
||||
branch[0] = p(it[0], c, s, normal)
|
||||
|
||||
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
|
||||
branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
|
||||
processScope(c, branchScope, maybeVoid(it[^1], branchScope))
|
||||
else:
|
||||
processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
|
||||
@@ -761,7 +773,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||
let tmp = c.getTemp(s, n[0].typ, n.info)
|
||||
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
|
||||
m.add p(n[0], c, s, normal)
|
||||
c.finishCopy(m, n[0], isFromSink = false)
|
||||
c.finishCopy(m, n[0], {}, isFromSink = false)
|
||||
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
|
||||
var toDisarm = n[0]
|
||||
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
|
||||
@@ -801,15 +813,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result = passCopyToSink(n, c, s)
|
||||
elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
|
||||
nkCallKinds + nkLiterals:
|
||||
if n.kind in nkCallKinds and n[0].kind == nkSym:
|
||||
if n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
result = p(n, c, s, consumed)
|
||||
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
||||
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
@@ -821,14 +825,17 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
|
||||
# allow conversions from owned to unowned via this little hack:
|
||||
let nTyp = n[1].typ
|
||||
n[1].typ = n.typ
|
||||
n[1].typ() = n.typ
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
result[1].typ = nTyp
|
||||
result[1].typ() = nTyp
|
||||
else:
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
elif n.kind in {nkObjDownConv, nkObjUpConv}:
|
||||
result = copyTree(n)
|
||||
result[0] = p(n[0], c, s, sinkArg)
|
||||
elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}:
|
||||
result = copyTree(n)
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
elif n.typ == nil:
|
||||
# 'raise X' can be part of a 'case' expression. Deal with it here:
|
||||
result = p(n, c, s, normal)
|
||||
@@ -882,19 +889,14 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
return
|
||||
|
||||
let inSpawn = c.inSpawn
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
|
||||
c.inSpawn.inc
|
||||
elif c.inSpawn > 0:
|
||||
c.inSpawn.dec
|
||||
|
||||
let parameters = n[0].typ
|
||||
# bug #23907; skips tyGenericInst for generic callbacks
|
||||
let parameters = if n[0].typ != nil: n[0].typ.skipTypes(abstractInst) else: n[0].typ
|
||||
let L = if parameters != nil: parameters.signatureLen else: 0
|
||||
|
||||
when false:
|
||||
@@ -904,13 +906,19 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
isDangerous = true
|
||||
|
||||
result = shallowCopy(n)
|
||||
for i in 1..<n.len:
|
||||
if i < L and isCompileTimeOnly(parameters[i]):
|
||||
result[i] = n[i]
|
||||
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
|
||||
result[i] = p(n[i], c, s, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
for i in 1..<n.len:
|
||||
if i < L and isCompileTimeOnly(parameters[i]):
|
||||
result[i] = n[i]
|
||||
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
|
||||
result[i] = p(n[i], c, s, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
|
||||
when false:
|
||||
if isDangerous:
|
||||
@@ -938,6 +946,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
of nkVarSection, nkLetSection:
|
||||
# transform; var x = y to var x; x op y where op is a move or copy
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
let isInProc = c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}
|
||||
|
||||
for it in n:
|
||||
var ri = it[^1]
|
||||
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
|
||||
@@ -953,7 +964,15 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
s.locals.add v.sym
|
||||
pVarTopLevel(v, c, s, result)
|
||||
if ri.kind != nkEmpty:
|
||||
result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
||||
let isGlobalPragma = v.kind == nkSym and
|
||||
{sfPure, sfGlobal} <= v.sym.flags and
|
||||
isInProc
|
||||
|
||||
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
||||
if isGlobalPragma:
|
||||
c.graph.procGlobals.add value
|
||||
else:
|
||||
result.add value
|
||||
elif ri.kind == nkEmpty and c.inLoop > 0:
|
||||
let skipInit = v.kind == nkDotExpr and # Closure var
|
||||
sfNoInit in v[1].sym.flags
|
||||
@@ -1017,9 +1036,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
|
||||
# allow conversions from owned to unowned via this little hack:
|
||||
let nTyp = n[1].typ
|
||||
n[1].typ = n.typ
|
||||
n[1].typ() = n.typ
|
||||
result[1] = p(n[1], c, s, mode)
|
||||
result[1].typ = nTyp
|
||||
result[1].typ() = nTyp
|
||||
else:
|
||||
result[1] = p(n[1], c, s, mode)
|
||||
|
||||
@@ -1125,6 +1144,25 @@ proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags:
|
||||
var snk = c.genSink(s, dest, newAccess, flags)
|
||||
result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
|
||||
|
||||
proc ownsData(c: var Con; s: var Scope; orig: PNode; flags: set[MoveOrCopyFlag]): PNode =
|
||||
var n = orig
|
||||
while true:
|
||||
case n.kind
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
|
||||
n = n[0]
|
||||
else:
|
||||
break
|
||||
if n.kind in nkCallKinds and n.typ != nil and hasDestructor(c, n.typ):
|
||||
result = newNodeIT(nkStmtListExpr, orig.info, orig.typ)
|
||||
let tmp = c.getTemp(s, n.typ, n.info)
|
||||
tmp.sym.flags.incl sfSingleUsedTemp
|
||||
result.add newTree(nkFastAsgn, tmp, copyTree(n))
|
||||
s.final.add c.genDestroy(tmp)
|
||||
n[] = tmp[]
|
||||
result.add copyTree(orig)
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
var ri = ri
|
||||
var isEnsureMove = 0
|
||||
@@ -1151,7 +1189,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
of nkCallKinds:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkBracketExpr:
|
||||
if isUnpackedTuple(ri[0]):
|
||||
if isFullyUnpackedTuple(ri[0]):
|
||||
# unpacking of tuple: take over the elements
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
|
||||
@@ -1169,7 +1207,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
@@ -1177,7 +1215,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
else:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
@@ -1198,17 +1236,24 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
if IsExplicitSink in flags:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
else:
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock:
|
||||
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
|
||||
# We know the result will be a stmt so we use that fact to optimize
|
||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||
of nkRaiseStmt:
|
||||
result = pRaiseStmt(ri, c, s)
|
||||
else:
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
|
||||
let isOwnsData = ownsData(c, s, ri2, flags)
|
||||
|
||||
if isOwnsData != nil:
|
||||
result = moveOrCopy(dest, isOwnsData, c, s, flags)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
|
||||
canBeMoved(c, dest.typ):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(s, dest, ri, flags)
|
||||
@@ -1218,7 +1263,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
|
||||
when false:
|
||||
proc computeUninit(c: var Con) =
|
||||
@@ -1247,6 +1292,55 @@ when false:
|
||||
for i in 0..<n.safeLen:
|
||||
injectDefaultCalls(n[i], c)
|
||||
|
||||
proc replaceSinkParam(n: PNode, mapping: Table[int, PSym]): PNode =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym.id in mapping:
|
||||
result = newSymNode(mapping[n.sym.id])
|
||||
else:
|
||||
result = n
|
||||
of nkVarSection, nkLetSection:
|
||||
result = copyNode(n)
|
||||
newSons(result, n.len)
|
||||
for i in 0..<n.len:
|
||||
result[i] = copyNode(n[i])
|
||||
for j in 0..<n[i].len-1:
|
||||
result[i].add n[i][j]
|
||||
result[i].add replaceSinkParam(n[i][^1], mapping)
|
||||
of {nkNone..nkNilLit}-{nkSym}, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
|
||||
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
|
||||
result = n
|
||||
else:
|
||||
result = copyNode(n)
|
||||
for i in 0..<n.len:
|
||||
result.add replaceSinkParam(n[i], mapping)
|
||||
|
||||
proc addSinkCopy(c: var Con; s: var Scope; sinkParams: seq[PSym]; n: PNode): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
var mapping = initTable[int, PSym]()
|
||||
|
||||
var mutated = newSeq[PNode]()
|
||||
getPotentialWrites(n, false, mutated)
|
||||
var mutatedSet = initIntSet()
|
||||
for m in mutated:
|
||||
mutatedSet.incl m.sym.id
|
||||
for param in sinkParams:
|
||||
if param.id in mutatedSet:
|
||||
let newSym = newSym(skTemp, getIdent(c.graph.cache, "sinkCopy"), c.idgen, param.owner, n.info)
|
||||
newSym.flags.incl sfFromGeneric
|
||||
newSym.typ = param.typ.elementType
|
||||
mapping[param.id] = newSym
|
||||
let v = newNodeI(nkVarSection, n.info)
|
||||
v.addVar(newSymNode(newSym), newSymNode(param))
|
||||
result.add v
|
||||
if mapping.len > 0:
|
||||
result.add replaceSinkParam(n, mapping)
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
|
||||
when toDebug.len > 0:
|
||||
shouldDebug = toDebug == owner.name.s or toDebug == "always"
|
||||
@@ -1260,15 +1354,24 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n:
|
||||
var scope = Scope(body: n)
|
||||
let body = p(n, c, scope, normal)
|
||||
|
||||
var sinkParams = newSeq[PSym]()
|
||||
|
||||
if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
|
||||
let params = owner.typ.n
|
||||
for i in 1..<params.len:
|
||||
let t = params[i].sym.typ
|
||||
if isSinkTypeForParam(t) and hasDestructor(c, t.skipTypes({tySink})):
|
||||
scope.final.add c.genDestroy(params[i])
|
||||
if isSinkTypeForParam(t):
|
||||
let baseType = t.skipTypes({tySink})
|
||||
if baseType.kind in {tyString, tySequence, tyArray, tyTuple, tyObject}:
|
||||
sinkParams.add params[i].sym
|
||||
if hasDestructor(c, baseType):
|
||||
scope.final.add c.genDestroy(params[i])
|
||||
#if optNimV2 in c.graph.config.globalOptions:
|
||||
# injectDefaultCalls(n, c)
|
||||
result = optimize processScope(c, scope, body)
|
||||
if sinkParams.len > 0:
|
||||
result = addSinkCopy(c, scope, sinkParams, result)
|
||||
|
||||
dbg:
|
||||
echo ">---------transformed-to--------->"
|
||||
echo renderTree(result, {renderIds})
|
||||
|
||||
@@ -6,7 +6,7 @@ Name: "Nim"
|
||||
Version: "$version"
|
||||
Platforms: """
|
||||
windows: i386;amd64
|
||||
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
|
||||
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;s390x;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
|
||||
macosx: i386;amd64;powerpc64;arm64
|
||||
solaris: i386;amd64;sparc;sparc64
|
||||
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
|
||||
@@ -80,6 +80,7 @@ Files: "lib"
|
||||
Files: "examples"
|
||||
Files: "dist/nimble"
|
||||
Files: "dist/checksums"
|
||||
Files: "dist/nimony"
|
||||
|
||||
Files: "tests"
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ proc containsVariable(n: PNode): bool =
|
||||
proc checkIsolate*(n: PNode): bool =
|
||||
if types.containsTyRef(n.typ):
|
||||
# XXX Maybe require that 'n.typ' is acyclic. This is not much
|
||||
# worse than the already exisiting inheritance and closure restrictions.
|
||||
# worse than the already existing inheritance and closure restrictions.
|
||||
case n.kind
|
||||
of nkCharLit..nkNilLit:
|
||||
result = true
|
||||
|
||||
@@ -33,7 +33,7 @@ import
|
||||
nversion, msgs, idents, types,
|
||||
ropes, wordrecg, renderer,
|
||||
cgmeth, lowerings, sighashes, modulegraphs, lineinfos,
|
||||
transf, injectdestructors, sourcemap, astmsgs, backendpragmas,
|
||||
transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas,
|
||||
mangleutils
|
||||
|
||||
import pipelineutils
|
||||
@@ -103,7 +103,7 @@ type
|
||||
prc: PSym
|
||||
globals, locals, body: Rope
|
||||
options: TOptions
|
||||
optionsStack: seq[TOptions]
|
||||
optionsStack: seq[(TOptions, TNoteKinds)]
|
||||
module: BModule
|
||||
g: PGlobals
|
||||
beforeRetNeeded: bool
|
||||
@@ -195,7 +195,7 @@ proc mapType(typ: PType): TJSTypeKind =
|
||||
of tyPointer:
|
||||
# treat a tyPointer like a typed pointer to an array of bytes
|
||||
result = etyBaseIndex
|
||||
of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent:
|
||||
of tyRange, tyDistinct, tyOrdinal, tyError, tyLent:
|
||||
# tyLent is no-op as JS has pass-by-reference semantics
|
||||
result = mapType(skipModifier t)
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
|
||||
@@ -246,7 +246,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
|
||||
for chr in name:
|
||||
if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
|
||||
return false
|
||||
result = s.loc.r
|
||||
result = s.loc.snippet
|
||||
if result == "":
|
||||
if s.kind == skField and s.name.s.validJsName:
|
||||
result = rope(s.name.s)
|
||||
@@ -277,7 +277,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
|
||||
else:
|
||||
result.add("_")
|
||||
result.add(rope(s.id))
|
||||
s.loc.r = result
|
||||
s.loc.snippet = result
|
||||
|
||||
proc escapeJSString(s: string): string =
|
||||
result = newStringOfCap(s.len + s.len shr 2)
|
||||
@@ -611,6 +611,17 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||
r.res = frmt % [a, tmp]
|
||||
r.kind = resExpr
|
||||
|
||||
proc genBreakState(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var a: TCompRes = default(TCompRes)
|
||||
# mangle `:state` properly somehow
|
||||
if n.kind == nkClosure:
|
||||
gen(p, n[1], a)
|
||||
r.res = "(($1).HEX3Astate < 0)" % [rdLoc(a)]
|
||||
else:
|
||||
gen(p, n, a)
|
||||
r.res = "((($1.ClE_0).HEX3Astate) < 0)" % [rdLoc(a)]
|
||||
r.kind = resExpr
|
||||
|
||||
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
var
|
||||
x, y: TCompRes = default(TCompRes)
|
||||
@@ -981,7 +992,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
|
||||
# if isJsObject(throwObj.typ):
|
||||
if isImportedException(throwObj.typ, p.config):
|
||||
orExpr.addf("lastJSError instanceof $1",
|
||||
[throwObj.typ.sym.loc.r])
|
||||
[throwObj.typ.sym.loc.snippet])
|
||||
else:
|
||||
orExpr.addf("isObj(lastJSError.m_type, $1)",
|
||||
[genTypeInfo(p, throwObj.typ)])
|
||||
@@ -991,8 +1002,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
|
||||
# If some branch requires a local alias introduce it here. This is needed
|
||||
# since JS cannot do ``catch x as y``.
|
||||
if excAlias != nil:
|
||||
excAlias.sym.loc.r = mangleName(p.module, excAlias.sym)
|
||||
lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r)
|
||||
excAlias.sym.loc.snippet = mangleName(p.module, excAlias.sym)
|
||||
lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.snippet)
|
||||
gen(p, n[i][^1], a)
|
||||
moveInto(p, a, r)
|
||||
lineF(p, "}$n", [])
|
||||
@@ -1098,14 +1109,19 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
|
||||
lineF(p, "break;$n", [])
|
||||
of nkElse:
|
||||
if transferRange:
|
||||
lineF(p, "else{$n", [])
|
||||
if n.len == 2: # a dangling else for a case statement
|
||||
transferRange = false
|
||||
lineF(p, "switch ($1) {$n", [cond.rdLoc])
|
||||
lineF(p, "default: $n", [])
|
||||
else:
|
||||
lineF(p, "else{$n", [])
|
||||
else:
|
||||
lineF(p, "default: $n", [])
|
||||
p.nested:
|
||||
gen(p, it[0], stmt)
|
||||
moveInto(p, stmt, r)
|
||||
if transferRange:
|
||||
lineF(p, "}$n", [])
|
||||
lineF(p, "}$n", [])
|
||||
else:
|
||||
lineF(p, "break;$n", [])
|
||||
else: internalError(p.config, it.info, "jsgen.genCaseStmt")
|
||||
@@ -1218,7 +1234,7 @@ proc generateHeader(p: PProc, prc: PSym): Rope =
|
||||
# to keep it simple
|
||||
let env = prc.ast[paramsPos].lastSon
|
||||
assert env.kind == nkSym, "env is missing"
|
||||
env.sym.loc.r = "this"
|
||||
env.sym.loc.snippet = "this"
|
||||
|
||||
for i in 1..<typ.n.len:
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
@@ -1360,8 +1376,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else:
|
||||
if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
|
||||
var f = b[1].sym
|
||||
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
|
||||
r.res = makeJSString($f.loc.r)
|
||||
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
|
||||
r.res = makeJSString($f.loc.snippet)
|
||||
internalAssert p.config, a.typ != etyBaseIndex
|
||||
r.address = a.res
|
||||
r.kind = resExpr
|
||||
@@ -1388,8 +1404,8 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else:
|
||||
if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess")
|
||||
var f = n[1].sym
|
||||
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
|
||||
r.res = "$1.$2" % [r.res, f.loc.r]
|
||||
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
|
||||
r.res = "$1.$2" % [r.res, f.loc.snippet]
|
||||
mkTemp(1)
|
||||
r.kind = resExpr
|
||||
|
||||
@@ -1409,11 +1425,11 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
|
||||
# Field symbol
|
||||
var field = accessExpr[1].sym
|
||||
internalAssert p.config, field.kind == skField
|
||||
if field.loc.r == "": field.loc.r = mangleName(p.module, field)
|
||||
if field.loc.snippet == "": field.loc.snippet = mangleName(p.module, field)
|
||||
# Discriminant symbol
|
||||
let disc = checkExpr[2].sym
|
||||
internalAssert p.config, disc.kind == skField
|
||||
if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc)
|
||||
if disc.loc.snippet == "": disc.loc.snippet = mangleName(p.module, disc)
|
||||
|
||||
var setx: TCompRes = default(TCompRes)
|
||||
gen(p, checkExpr[1], setx)
|
||||
@@ -1430,16 +1446,16 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
|
||||
useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen
|
||||
let msg = genFieldDefect(p.config, field.name.s, disc)
|
||||
lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n",
|
||||
setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===",
|
||||
setx.res, tmp, disc.loc.snippet, if negCheck: "!==" else: "===",
|
||||
makeJSString(msg), genTypeInfo(p, disc.typ))
|
||||
|
||||
if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex:
|
||||
r.typ = etyBaseIndex
|
||||
r.res = makeJSString($field.loc.r)
|
||||
r.res = makeJSString($field.loc.snippet)
|
||||
r.address = tmp
|
||||
else:
|
||||
r.typ = etyNone
|
||||
r.res = "$1.$2" % [tmp, field.loc.r]
|
||||
r.res = "$1.$2" % [tmp, field.loc.snippet]
|
||||
r.kind = resExpr
|
||||
|
||||
proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
@@ -1506,10 +1522,10 @@ template isIndirect(x: PSym): bool =
|
||||
|
||||
proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
|
||||
let s = n.sym
|
||||
if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3")
|
||||
if s.loc.snippet == "": internalError(p.config, n.info, "genAddr: 3")
|
||||
case s.kind
|
||||
of skParam:
|
||||
r.res = s.loc.r
|
||||
r.res = s.loc.snippet
|
||||
r.address = ""
|
||||
r.typ = etyNone
|
||||
of skVar, skLet, skResult:
|
||||
@@ -1523,15 +1539,15 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
|
||||
# make addr() a no-op:
|
||||
r.typ = etyNone
|
||||
if isIndirect(s):
|
||||
r.res = s.loc.r & "[0]"
|
||||
r.res = s.loc.snippet & "[0]"
|
||||
else:
|
||||
r.res = s.loc.r
|
||||
r.res = s.loc.snippet
|
||||
r.address = ""
|
||||
elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
|
||||
# for ease of code generation, we do not distinguish between
|
||||
# sfAddrTaken and sfGlobal.
|
||||
r.typ = etyBaseIndex
|
||||
r.address = s.loc.r
|
||||
r.address = s.loc.snippet
|
||||
r.res = rope("0")
|
||||
else:
|
||||
# 'var openArray' for instance produces an 'addr' but this is harmless:
|
||||
@@ -1569,15 +1585,12 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
|
||||
of nkObjDownConv:
|
||||
gen(p, n[0], r)
|
||||
of nkHiddenDeref:
|
||||
gen(p, n[0], r)
|
||||
of nkDerefExpr:
|
||||
var x = n[0]
|
||||
if n.kind == nkHiddenAddr:
|
||||
x = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
x.typ = n.typ
|
||||
gen(p, x, r)
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
if n.kind in {nkAddr, nkHiddenAddr}:
|
||||
# addr ( deref ( x )) --> x
|
||||
gen(p, n[0][0], r)
|
||||
else:
|
||||
gen(p, n[0], r)
|
||||
of nkHiddenAddr:
|
||||
gen(p, n[0], r)
|
||||
of nkConv:
|
||||
@@ -1624,7 +1637,7 @@ proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
|
||||
internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s)
|
||||
if owner.prc == s.owner:
|
||||
if not owner.generatedParamCopies.containsOrIncl(s.id):
|
||||
let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)]
|
||||
let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.snippet, genTypeInfo(p, s.typ)]
|
||||
owner.locals.add(owner.indentLine(copy))
|
||||
return
|
||||
owner = owner.up
|
||||
@@ -1635,7 +1648,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var s = n.sym
|
||||
case s.kind
|
||||
of skVar, skLet, skParam, skTemp, skResult, skForVar:
|
||||
if s.loc.r == "":
|
||||
if s.loc.snippet == "":
|
||||
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
|
||||
if sfCompileTime in s.flags:
|
||||
genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info))
|
||||
@@ -1646,29 +1659,29 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.typ = etyBaseIndex
|
||||
if {sfAddrTaken, sfGlobal} * s.flags != {}:
|
||||
if isIndirect(s):
|
||||
r.address = "$1[0][0]" % [s.loc.r]
|
||||
r.res = "$1[0][1]" % [s.loc.r]
|
||||
r.address = "$1[0][0]" % [s.loc.snippet]
|
||||
r.res = "$1[0][1]" % [s.loc.snippet]
|
||||
else:
|
||||
r.address = "$1[0]" % [s.loc.r]
|
||||
r.res = "$1[1]" % [s.loc.r]
|
||||
r.address = "$1[0]" % [s.loc.snippet]
|
||||
r.res = "$1[1]" % [s.loc.snippet]
|
||||
else:
|
||||
r.address = s.loc.r
|
||||
r.res = s.loc.r & "_Idx"
|
||||
r.address = s.loc.snippet
|
||||
r.res = s.loc.snippet & "_Idx"
|
||||
elif isIndirect(s):
|
||||
r.res = "$1[0]" % [s.loc.r]
|
||||
r.res = "$1[0]" % [s.loc.snippet]
|
||||
else:
|
||||
r.res = s.loc.r
|
||||
r.res = s.loc.snippet
|
||||
of skConst:
|
||||
genConstant(p, s)
|
||||
if s.loc.r == "":
|
||||
if s.loc.snippet == "":
|
||||
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
|
||||
r.res = s.loc.r
|
||||
r.res = s.loc.snippet
|
||||
of skProc, skFunc, skConverter, skMethod, skIterator:
|
||||
if sfCompileTime in s.flags:
|
||||
localError(p.config, n.info, "request to generate code for .compileTime proc: " &
|
||||
s.name.s)
|
||||
discard mangleName(p.module, s)
|
||||
r.res = s.loc.r
|
||||
r.res = s.loc.snippet
|
||||
if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or
|
||||
{sfImportc, sfInfixCall} * s.flags != {}:
|
||||
discard
|
||||
@@ -1680,13 +1693,13 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else:
|
||||
genProcForSymIfNeeded(p, s)
|
||||
else:
|
||||
if s.loc.r == "":
|
||||
if s.loc.snippet == "":
|
||||
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
|
||||
if mapType(p, s.typ) == etyBaseIndex:
|
||||
r.address = s.loc.r
|
||||
r.res = s.loc.r & "_Idx"
|
||||
r.address = s.loc.snippet
|
||||
r.res = s.loc.snippet & "_Idx"
|
||||
else:
|
||||
r.res = s.loc.r
|
||||
r.res = s.loc.snippet
|
||||
r.kind = resVal
|
||||
|
||||
proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
|
||||
@@ -1828,9 +1841,9 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
|
||||
proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
|
||||
# don't call '$' here for efficiency:
|
||||
let f = n[0].sym
|
||||
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
|
||||
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
|
||||
if sfInfixCall in f.flags:
|
||||
let pat = $n[0].sym.loc.r
|
||||
let pat = $n[0].sym.loc.snippet
|
||||
internalAssert p.config, pat.len > 0
|
||||
if pat.contains({'#', '(', '@'}):
|
||||
var typ = skipTypes(n[0].typ, abstractInst)
|
||||
@@ -1945,7 +1958,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar:
|
||||
result = putToSeq("0", indirect)
|
||||
of tyInt, tyUInt:
|
||||
if $t.sym.loc.r == "bigint":
|
||||
if $t.sym.loc.snippet == "bigint":
|
||||
result = putToSeq("0n", indirect)
|
||||
else:
|
||||
result = putToSeq("0", indirect)
|
||||
@@ -2067,28 +2080,28 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||
if a.typ == etyBaseIndex:
|
||||
if targetBaseIndex:
|
||||
line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
|
||||
[returnType, v.loc.r, a.address, a.res]))
|
||||
[returnType, v.loc.snippet, a.address, a.res]))
|
||||
else:
|
||||
if isIndirect(v):
|
||||
line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
|
||||
[returnType, v.loc.r, a.address, a.res]))
|
||||
[returnType, v.loc.snippet, a.address, a.res]))
|
||||
else:
|
||||
line(p, runtimeFormat(varCode & " = [$3, $4];$n",
|
||||
[returnType, v.loc.r, a.address, a.res]))
|
||||
[returnType, v.loc.snippet, a.address, a.res]))
|
||||
else:
|
||||
if targetBaseIndex:
|
||||
let tmp = p.getTemp
|
||||
lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
|
||||
[tmp, a.res, v.loc.r])
|
||||
[tmp, a.res, v.loc.snippet])
|
||||
else:
|
||||
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res]))
|
||||
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, a.res]))
|
||||
return
|
||||
else:
|
||||
s = a.res
|
||||
if isIndirect(v):
|
||||
line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s]))
|
||||
line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.snippet, s]))
|
||||
else:
|
||||
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s]))
|
||||
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, s]))
|
||||
|
||||
if useReloadingGuard or useGlobalPragmas:
|
||||
dec p.extraIndent
|
||||
@@ -2425,7 +2438,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
binaryExpr(p, n, r, "mnewString",
|
||||
"""if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); }
|
||||
else {$3.length = $4; }""")
|
||||
of mSetLengthSeq:
|
||||
of mSetLengthSeq, mSetLengthSeqUninit:
|
||||
var x, y: TCompRes = default(TCompRes)
|
||||
gen(p, n[1], x)
|
||||
gen(p, n[2], y)
|
||||
@@ -2442,6 +2455,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)")
|
||||
of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
|
||||
of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
|
||||
of mXorSet: binaryExpr(p, n, r, "SetXor", "SetXor($1, $2)")
|
||||
of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
|
||||
of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
|
||||
of mInSet:
|
||||
@@ -2563,17 +2577,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
let val = it[1]
|
||||
gen(p, val, a)
|
||||
var f = it[0].sym
|
||||
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
|
||||
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
|
||||
fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id)
|
||||
|
||||
let typ = val.typ.skipTypes(abstractInst)
|
||||
if a.typ == etyBaseIndex:
|
||||
initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res])
|
||||
initList.addf("$#: [$#, $#]", [f.loc.snippet, a.address, a.res])
|
||||
else:
|
||||
if not needsNoCopy(p, val):
|
||||
useMagic(p, "nimCopy")
|
||||
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
||||
initList.addf("$#: $#", [f.loc.r, a.res])
|
||||
initList.addf("$#: $#", [f.loc.snippet, a.res])
|
||||
let t = skipTypes(n.typ, abstractInst + skipPtrs)
|
||||
createObjInitList(p, t, fieldIDs, initList)
|
||||
r.res = ("{$1}") % [initList]
|
||||
@@ -2632,7 +2646,13 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
|
||||
let src = skipTypes(n[0].typ, abstractVarRange)
|
||||
let dest = skipTypes(n.typ, abstractVarRange)
|
||||
if optRangeCheck notin p.options:
|
||||
return
|
||||
if optJsBigInt64 in p.config.globalOptions and
|
||||
dest.kind in {tyUInt..tyUInt32, tyInt..tyInt32} and
|
||||
src.kind in {tyInt64, tyUInt64}:
|
||||
# conversions to Number are kept
|
||||
r.res = "Number($1)" % [r.res]
|
||||
else:
|
||||
discard
|
||||
elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures:
|
||||
if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res]
|
||||
@@ -2813,9 +2833,9 @@ proc genPragma(p: PProc, n: PNode) =
|
||||
case whichPragma(it)
|
||||
of wEmit: genAsmOrEmitStmt(p, it[1])
|
||||
of wPush:
|
||||
processPushBackendOption(p.optionsStack, p.options, n, i+1)
|
||||
processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
|
||||
of wPop:
|
||||
processPopBackendOption(p.optionsStack, p.options)
|
||||
processPopBackendOption(p.config, p.optionsStack, p.options)
|
||||
else: discard
|
||||
|
||||
proc genCast(p: PProc, n: PNode, r: var TCompRes) =
|
||||
@@ -2859,6 +2879,8 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
|
||||
elif dest.kind in tyFloat..tyFloat64:
|
||||
if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "Number($1)" % [r.res]
|
||||
elif dest.kind == tyChar and (fromInt or fromUint):
|
||||
r.res = "($1 & 255)" % [r.res]
|
||||
elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
|
||||
r.address = r.res
|
||||
r.res = "null"
|
||||
@@ -2992,7 +3014,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of nkLambdaKinds:
|
||||
let s = n[namePos].sym
|
||||
discard mangleName(p.module, s)
|
||||
r.res = s.loc.r
|
||||
r.res = s.loc.snippet
|
||||
if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard
|
||||
elif not p.g.generatedSyms.containsOrIncl(s.id):
|
||||
p.locals.add(genProc(p, s))
|
||||
@@ -3043,16 +3065,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of nkGotoState, nkState:
|
||||
globalError(p.config, n.info, "not implemented")
|
||||
of nkBreakState:
|
||||
var a: TCompRes = default(TCompRes)
|
||||
if n[0].kind == nkClosure:
|
||||
gen(p, n[0][1], a)
|
||||
let sym = n[0][1].typ[0].n[0].sym
|
||||
r.res = "(($1).$2 < 0)" % [rdLoc(a), mangleName(p.module, sym)]
|
||||
else:
|
||||
gen(p, n[0], a)
|
||||
let sym = n[0].typ[0].n[0].sym
|
||||
r.res = "((($1.ClE_0).$2) < 0)" % [rdLoc(a), mangleName(p.module, sym)]
|
||||
r.kind = resExpr
|
||||
genBreakState(p, n[0], r)
|
||||
of nkPragmaBlock: gen(p, n.lastSon, r)
|
||||
of nkComesFrom:
|
||||
discard "XXX to implement for better stack traces"
|
||||
|
||||
@@ -122,7 +122,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
|
||||
[name, genTypeInfo(p, typ.baseClass)])
|
||||
|
||||
proc genTypeInfo(p: PProc, typ: PType): Rope =
|
||||
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
|
||||
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned} + tyUserTypeClasses)
|
||||
result = "NTI$1" % [rope(t.id)]
|
||||
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
|
||||
case t.kind
|
||||
|
||||
@@ -145,11 +145,14 @@ proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
|
||||
result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info)
|
||||
result.typ = createClosureIterStateType(g, iter, idgen)
|
||||
|
||||
template isIterator*(owner: PSym): bool =
|
||||
owner.kind == skIterator and owner.typ.callConv == ccClosure
|
||||
|
||||
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
|
||||
# YYY meh, just add the state field for every closure for now, it's too
|
||||
# hard to figure out if it comes from a closure iterator:
|
||||
result = createObj(g, idgen, owner, info, final=false)
|
||||
rawAddField(result, createStateField(g, owner, idgen))
|
||||
result.flags.incl tfFinal
|
||||
if owner.isIterator:
|
||||
rawAddField(result, createStateField(g, owner, idgen))
|
||||
|
||||
proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
|
||||
if resultPos < iter.ast.len:
|
||||
@@ -172,26 +175,23 @@ proc addHiddenParam(routine: PSym, param: PSym) =
|
||||
assert sfFromGeneric in param.flags
|
||||
#echo "produced environment: ", param.id, " for ", routine.id
|
||||
|
||||
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
|
||||
proc getEnvParam*(routine: PSym): PSym =
|
||||
if routine.ast.isNil: return nil
|
||||
let params = routine.ast[paramsPos]
|
||||
let hidden = lastSon(params)
|
||||
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
|
||||
result = hidden.sym
|
||||
assert sfFromGeneric in result.flags
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
|
||||
result = getEnvParam(routine)
|
||||
if result.isNil:
|
||||
# writeStackTrace()
|
||||
localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
|
||||
result = routine
|
||||
|
||||
proc getEnvParam*(routine: PSym): PSym =
|
||||
let params = routine.ast[paramsPos]
|
||||
let hidden = lastSon(params)
|
||||
if hidden.kind == nkSym and hidden.sym.name.s == paramName:
|
||||
result = hidden.sym
|
||||
assert sfFromGeneric in result.flags
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc interestingVar(s: PSym): bool {.inline.} =
|
||||
result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
|
||||
sfGlobal notin s.flags and
|
||||
@@ -200,7 +200,7 @@ proc interestingVar(s: PSym): bool {.inline.} =
|
||||
proc illegalCapture(s: PSym): bool {.inline.} =
|
||||
result = classifyViewType(s.typ) != noView or s.kind == skResult
|
||||
|
||||
proc isInnerProc(s: PSym): bool =
|
||||
proc isInnerProc*(s: PSym): bool =
|
||||
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
|
||||
result = s.skipGenericOwner.kind in routineKinds
|
||||
else:
|
||||
@@ -230,15 +230,6 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf
|
||||
if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
|
||||
prc.flags.incl sfInjectDestructors
|
||||
|
||||
proc interestingIterVar(s: PSym): bool {.inline.} =
|
||||
# XXX optimization: Only lift the variable if it lives across
|
||||
# yield/return boundaries! This can potentially speed up
|
||||
# closure iterators quite a bit.
|
||||
result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
|
||||
|
||||
template isIterator*(owner: PSym): bool =
|
||||
owner.kind == skIterator and owner.typ.callConv == ccClosure
|
||||
|
||||
template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
|
||||
## lambda lifting can be harmful for JS-like code generators.
|
||||
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
|
||||
@@ -285,15 +276,6 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
|
||||
result.add makeClosure(g, idgen, iter, env, n.info)
|
||||
|
||||
proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
|
||||
let envParam = getHiddenParam(g, owner)
|
||||
let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
let field = addField(obj, s, g.cache, idgen)
|
||||
|
||||
var access = newSymNode(envParam)
|
||||
assert obj.kind == tyObject
|
||||
result = rawIndirectAccess(access, field, s.info)
|
||||
|
||||
# ------------------ new stuff -------------------------------------------
|
||||
|
||||
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
@@ -303,8 +285,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
localError(g.config, n.info,
|
||||
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
|
||||
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
|
||||
" Consider using a <ref $2> which can be captured.") %
|
||||
[s.name.s, typeToString(s.typ), g.config$s.info])
|
||||
" Consider using a <ref T> which can be captured.") %
|
||||
[s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
|
||||
elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
|
||||
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
|
||||
[s.name.s, owner.name.s, $owner.typ.callConv])
|
||||
@@ -341,9 +323,13 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
|
||||
info: TLineInfo): PType =
|
||||
result = c.ownerToType.getOrDefault(owner.id)
|
||||
if result.isNil:
|
||||
result = newType(tyRef, c.idgen, owner)
|
||||
let obj = createEnvObj(c.graph, c.idgen, owner, info)
|
||||
rawAddSon(result, obj)
|
||||
let env = getEnvParam(owner)
|
||||
if env.isNil or not owner.isIterator:
|
||||
result = newType(tyRef, c.idgen, owner)
|
||||
let obj = createEnvObj(c.graph, c.idgen, owner, info)
|
||||
rawAddSon(result, obj)
|
||||
else:
|
||||
result = env.typ
|
||||
c.ownerToType[owner.id] = result
|
||||
|
||||
proc asOwnedRef(c: var DetectionPass; t: PType): PType =
|
||||
@@ -435,6 +421,12 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
|
||||
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
|
||||
#echo "adding closure to ", fn.name.s
|
||||
|
||||
proc iterEnvHasUpField(g: ModuleGraph, iter: PSym): bool =
|
||||
let cp = getEnvParam(iter)
|
||||
doAssert(cp != nil, "Env param not present in iter")
|
||||
let upField = lookupInRecord(cp.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
|
||||
upField != nil
|
||||
|
||||
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
@@ -453,23 +445,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
let body = transformBody(c.graph, c.idgen, s, {useCache})
|
||||
detectCapturedVars(body, s, c)
|
||||
let ow = s.skipGenericOwner
|
||||
let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
|
||||
let innerClosure = innerProc and s.typ.callConv == ccClosure and (not s.isIterator or iterEnvHasUpField(c.graph, s))
|
||||
let interested = interestingVar(s)
|
||||
if ow == owner:
|
||||
if owner.isIterator:
|
||||
c.somethingToDo = true
|
||||
addClosureParam(c, owner, n.info)
|
||||
if interestingIterVar(s):
|
||||
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})
|
||||
|
||||
if s.name.id == getIdent(c.graph.cache, ":state").id:
|
||||
obj.n[0].sym.flags.incl sfNoInit
|
||||
obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
else:
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
# direct or indirect dependency:
|
||||
elif innerClosure or interested:
|
||||
discard """
|
||||
@@ -629,7 +610,7 @@ proc rawClosureCreation(owner: PSym;
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
let env2 = copyTree(env)
|
||||
env2.typ = unowned.typ
|
||||
env2.typ() = unowned.typ
|
||||
result.add newAsgnStmt(unowned, env2, env.info)
|
||||
createTypeBoundOpsLL(d.graph, unowned.typ, env.info, d.idgen, owner)
|
||||
|
||||
@@ -669,16 +650,27 @@ proc finishClosureCreation(owner: PSym; d: var DetectionPass; c: LiftingPass;
|
||||
res.add newAsgnStmt(unowned, nilLit, info)
|
||||
createTypeBoundOpsLL(d.graph, unowned.typ, info, d.idgen, owner)
|
||||
|
||||
proc closureCreationForIter(iter: PNode;
|
||||
proc getUpForIter(g: ModuleGraph; owner, iterOwner: PSym, expectedUpTyp: PType): PNode =
|
||||
var p = getHiddenParam(g, owner)
|
||||
var res = p.newSymNode
|
||||
while res.typ.skipTypes({tyOwned, tyRef, tyPtr}) != expectedUpTyp:
|
||||
let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
|
||||
if upField == nil:
|
||||
return nil
|
||||
p = upField
|
||||
res = rawIndirectAccess(res, upField, p.info)
|
||||
res
|
||||
|
||||
proc closureCreationForIter(owner: PSym, iter: PNode;
|
||||
d: var DetectionPass; c: var LiftingPass): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
|
||||
let owner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
|
||||
let iterOwner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, iterOwner, iter.info)
|
||||
incl(v.flags, sfShadowed)
|
||||
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
|
||||
var vnode: PNode
|
||||
if owner.isIterator:
|
||||
let it = getHiddenParam(d.graph, owner)
|
||||
if iterOwner.isIterator:
|
||||
let it = getHiddenParam(d.graph, iterOwner)
|
||||
addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
|
||||
vnode = indirectAccess(newSymNode(it), v, v.info)
|
||||
else:
|
||||
@@ -687,12 +679,14 @@ proc closureCreationForIter(iter: PNode;
|
||||
addVar(vs, vnode)
|
||||
result.add(vs)
|
||||
result.add genCreateEnv(vnode)
|
||||
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
|
||||
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, iterOwner)
|
||||
|
||||
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
|
||||
if upField != nil:
|
||||
let u = setupEnvVar(owner, d, c, iter.info)
|
||||
if u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == upField.typ.skipTypes({tyOwned, tyRef, tyPtr}):
|
||||
let expectedUpTyp = upField.typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
let u = if iterOwner == owner: setupEnvVar(iterOwner, d, c, iter.info)
|
||||
else: getUpForIter(d.graph, owner, iterOwner, expectedUpTyp)
|
||||
if u != nil and u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == expectedUpTyp:
|
||||
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
|
||||
u, iter.info))
|
||||
else:
|
||||
@@ -726,7 +720,7 @@ proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
let available = getHiddenParam(d.graph, owner)
|
||||
result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
|
||||
elif s.isIterator:
|
||||
result = closureCreationForIter(n, d, c)
|
||||
result = closureCreationForIter(owner, n, d, c)
|
||||
elif s.skipGenericOwner == owner:
|
||||
# direct dependency, so use the outer's env variable:
|
||||
result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
|
||||
@@ -773,8 +767,6 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
elif s.id in d.capturedVars:
|
||||
if s.owner != owner:
|
||||
result = accessViaEnvParam(d.graph, n, owner)
|
||||
elif owner.isIterator and interestingIterVar(s):
|
||||
result = accessViaEnvParam(d.graph, n, owner)
|
||||
else:
|
||||
result = accessViaEnvVar(n, owner, d, c)
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
|
||||
@@ -795,7 +787,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
let oldInContainer = c.inContainer
|
||||
c.inContainer = 0
|
||||
let m = newSymNode(n[namePos].sym)
|
||||
m.typ = n.typ
|
||||
m.typ() = n.typ
|
||||
result = liftCapturedVars(m, owner, d, c)
|
||||
c.inContainer = oldInContainer
|
||||
of nkHiddenStdConv:
|
||||
@@ -892,6 +884,9 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
|
||||
# ignore forward declaration:
|
||||
result = body
|
||||
tooEarly = true
|
||||
if fn.isIterator:
|
||||
var d = initDetectionPass(g, fn, idgen)
|
||||
addClosureParam(d, fn, body.info)
|
||||
else:
|
||||
var d = initDetectionPass(g, fn, idgen)
|
||||
detectCapturedVars(body, fn, d)
|
||||
@@ -980,6 +975,20 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
for i in 0..<op.len-1:
|
||||
result.add op[i]
|
||||
|
||||
elif op.kind != nkSym: # might have side effects
|
||||
# bug #25046
|
||||
# create a temp for the closure
|
||||
# var :closureTemp
|
||||
# :closureTemp = ...
|
||||
let tempSym = newSym(skLet, getIdent(g.cache, ":closureTemp"), idgen, owner, body.info)
|
||||
tempSym.typ = call[0].typ
|
||||
let temp = newSymNode(tempSym)
|
||||
var v = newNodeI(nkVarSection, body.info)
|
||||
addVar(v, temp)
|
||||
result.add(v)
|
||||
result.add newAsgnStmt(temp, call[0], body.info)
|
||||
call[0] = temp
|
||||
|
||||
var loopBody = newNodeI(nkStmtList, body.info, 3)
|
||||
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
|
||||
whileLoop[0] = newIntTypeNode(1, getSysType(g, body.info, tyBool))
|
||||
|
||||
92
compiler/layeredtable.nim
Normal file
92
compiler/layeredtable.nim
Normal file
@@ -0,0 +1,92 @@
|
||||
import std/[tables]
|
||||
import ast, astalgo
|
||||
|
||||
type
|
||||
LayeredIdTableObj* {.acyclic.} = object
|
||||
## stack of type binding contexts implemented as a linked list
|
||||
topLayer*: TypeMapping
|
||||
## the mappings on the current layer
|
||||
nextLayer*: ref LayeredIdTableObj
|
||||
## the parent type binding context, possibly `nil`
|
||||
previousLen*: int
|
||||
## total length of the bindings up to the parent layer,
|
||||
## used to track if new bindings were added
|
||||
|
||||
const useRef = not defined(gcDestructors)
|
||||
# implementation detail, only arc/orc doesn't cause issues when
|
||||
# using LayeredIdTable as an object and not a ref
|
||||
|
||||
when useRef:
|
||||
type LayeredIdTable* = ref LayeredIdTableObj
|
||||
else:
|
||||
type LayeredIdTable* = LayeredIdTableObj
|
||||
|
||||
proc initLayeredTypeMap*(pt: sink TypeMapping = initTypeMapping()): LayeredIdTable =
|
||||
result = LayeredIdTable(topLayer: pt, nextLayer: nil)
|
||||
|
||||
proc shallowCopy*(pt: LayeredIdTable): LayeredIdTable {.inline.} =
|
||||
## copies only the type bindings of the current layer, but not any parent layers,
|
||||
## useful for write-only bindings
|
||||
result = LayeredIdTable(topLayer: pt.topLayer, nextLayer: pt.nextLayer, previousLen: pt.previousLen)
|
||||
#copyIdTable(result.topLayer, pt.topLayer)
|
||||
|
||||
proc currentLen*(pt: LayeredIdTable): int =
|
||||
## the sum of the cached total binding count of the parents and
|
||||
## the current binding count, just used to track if bindings were added
|
||||
pt.previousLen + pt.topLayer.counter
|
||||
|
||||
proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable =
|
||||
result = LayeredIdTable(topLayer: initTypeMapping(), previousLen: pt.currentLen)
|
||||
when useRef:
|
||||
result.nextLayer = pt
|
||||
else:
|
||||
new(result.nextLayer)
|
||||
result.nextLayer[] = pt
|
||||
|
||||
proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
|
||||
when useRef:
|
||||
pt = pt.nextLayer
|
||||
else:
|
||||
when defined(gcDestructors):
|
||||
pt = pt.nextLayer[]
|
||||
else:
|
||||
# workaround refc
|
||||
let tmp = pt.nextLayer[]
|
||||
pt = tmp
|
||||
|
||||
iterator pairs*(pt: LayeredIdTable): (ItemId, PType) =
|
||||
var tm = pt
|
||||
while true:
|
||||
for (k, v) in idTablePairs(tm.topLayer):
|
||||
yield (k, v)
|
||||
if tm.nextLayer == nil:
|
||||
break
|
||||
tm.setToPreviousLayer
|
||||
|
||||
proc lookup(typeMap: ref LayeredIdTableObj, key: ItemId): PType =
|
||||
result = nil
|
||||
var tm = typeMap
|
||||
while tm != nil:
|
||||
result = getOrDefault(tm.topLayer, key)
|
||||
if result != nil: return
|
||||
tm = tm.nextLayer
|
||||
|
||||
template lookup*(typeMap: ref LayeredIdTableObj, key: PType): PType =
|
||||
## recursively looks up binding of `key` in all parent layers
|
||||
lookup(typeMap, key.itemId)
|
||||
|
||||
when not useRef:
|
||||
proc lookup(typeMap: LayeredIdTableObj, key: ItemId): PType {.inline.} =
|
||||
result = getOrDefault(typeMap.topLayer, key)
|
||||
if result == nil and typeMap.nextLayer != nil:
|
||||
result = lookup(typeMap.nextLayer, key)
|
||||
|
||||
template lookup*(typeMap: LayeredIdTableObj, key: PType): PType =
|
||||
lookup(typeMap, key.itemId)
|
||||
|
||||
proc put(typeMap: var LayeredIdTable, key: ItemId, value: PType) {.inline.} =
|
||||
typeMap.topLayer[key] = value
|
||||
|
||||
template put*(typeMap: var LayeredIdTable, key, value: PType) =
|
||||
## binds `key` to `value` only in current layer
|
||||
put(typeMap, key.itemId, value)
|
||||
@@ -40,7 +40,7 @@ template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
|
||||
info: TLineInfo; idgen: IdGenerator): PSym
|
||||
|
||||
proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
idgen: IdGenerator)
|
||||
@@ -49,7 +49,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
|
||||
result = newNodeI(nkBracketExpr, a.info, 2)
|
||||
result[0] = a
|
||||
result[1] = i
|
||||
result.typ = elemType
|
||||
result.typ() = elemType
|
||||
|
||||
proc destructorOverridden(g: ModuleGraph; t: PType): bool =
|
||||
let op = getAttachedOp(g, t, attachedDestructor)
|
||||
@@ -68,7 +68,7 @@ proc dotField(x: PNode, f: PSym): PNode =
|
||||
else:
|
||||
result[0] = x
|
||||
result[1] = newSymNode(f, x.info)
|
||||
result.typ = f.typ
|
||||
result.typ() = f.typ
|
||||
|
||||
proc newAsgnStmt(le, ri: PNode): PNode =
|
||||
result = newNodeI(nkAsgn, le.info, 2)
|
||||
@@ -88,10 +88,10 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
elif c.kind == attachedDestructor and c.addMemReset:
|
||||
let call = genBuiltin(c, mDefault, "default", x)
|
||||
call.typ = t
|
||||
call.typ() = t
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif c.kind == attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc genAddr(c: var TLiftCtx; x: PNode): PNode =
|
||||
if x.kind == nkHiddenDeref:
|
||||
@@ -105,7 +105,7 @@ proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
|
||||
result = newNodeI(nkWhileStmt, c.info, 2)
|
||||
let cmp = genBuiltin(c, mLtI, "<", i)
|
||||
cmp.add genLen(c.g, dest)
|
||||
cmp.typ = getSysType(c.g, c.info, tyBool)
|
||||
cmp.typ() = getSysType(c.g, c.info, tyBool)
|
||||
result[0] = cmp
|
||||
result[1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
@@ -127,10 +127,10 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
|
||||
dotExpr.add newSymNode(field)
|
||||
|
||||
let offsetOf = genBuiltin(c, mOffsetOf, "offsetof", dotExpr)
|
||||
offsetOf.typ = intType
|
||||
offsetOf.typ() = intType
|
||||
|
||||
let minusExpr = genBuiltin(c, mSubI, "-", castExpr1)
|
||||
minusExpr.typ = intType
|
||||
minusExpr.typ() = intType
|
||||
minusExpr.add offsetOf
|
||||
|
||||
let objPtr = makePtrType(objType.owner, objType, c.idgen)
|
||||
@@ -148,7 +148,7 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
if sfNeverRaises notin op.flags:
|
||||
c.canRaise = true
|
||||
if c.addMemReset:
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "`=wasMoved`", x))
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "wasMoved", x))
|
||||
else:
|
||||
result = destroy
|
||||
|
||||
@@ -168,7 +168,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
defaultOp(c, f.typ, body, x.dotField(f), b)
|
||||
else:
|
||||
if enforceWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x.dotField(f))
|
||||
fillBody(c, f.typ, body, x.dotField(f), b)
|
||||
of nkNilLit: discard
|
||||
of nkRecCase:
|
||||
@@ -218,17 +218,38 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
|
||||
c.filterDiscriminator = oldfilterDiscriminator
|
||||
of nkRecList:
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
# destroys in reverse order #24719
|
||||
if c.kind == attachedDestructor:
|
||||
for i in countdown(n.len-1, 0):
|
||||
fillBodyObj(c, n[i], body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
else:
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
else:
|
||||
illFormedAstLocal(n, c.g.config)
|
||||
|
||||
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
if t.baseClass != nil:
|
||||
let obj = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
obj.add newNodeI(nkEmpty, c.info)
|
||||
obj.add x
|
||||
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, obj, y)
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
template fillBase =
|
||||
if t.baseClass != nil:
|
||||
let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
dest.add newNodeI(nkEmpty, c.info)
|
||||
dest.add x
|
||||
var src = y
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
src.add newNodeI(nkEmpty, c.info)
|
||||
src.add y
|
||||
|
||||
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
|
||||
template fillFields =
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
if c.kind == attachedDestructor:
|
||||
# destroys in reverse order #24719
|
||||
fillFields()
|
||||
fillBase()
|
||||
else:
|
||||
fillBase()
|
||||
fillFields()
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
var hasCase = isCaseObj(t.n)
|
||||
@@ -259,7 +280,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
# because the wasMoved(dest) call would zero out src, if dest aliases src.
|
||||
var cond = newTree(nkCall, newSymNode(c.g.getSysMagic(c.info, "==", mEqRef)),
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
cond.typ() = getSysType(c.g, x.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
temp.typ = x.typ
|
||||
@@ -271,7 +292,8 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
#body.add newAsgnStmt(blob, x)
|
||||
|
||||
var wasMovedCall = newNodeI(nkCall, c.info)
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "`=wasMoved`", mWasMoved)))
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
|
||||
|
||||
wasMovedCall.add x # mWasMoved does not take the address
|
||||
body.add wasMovedCall
|
||||
|
||||
@@ -290,7 +312,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
|
||||
proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
|
||||
result = newIntLit(g, info, ord value)
|
||||
result.typ = getSysType(g, info, tyBool)
|
||||
result.typ() = getSysType(g, info, tyBool)
|
||||
|
||||
proc getCycleParam(c: TLiftCtx): PNode =
|
||||
assert c.kind in {attachedAsgn, attachedDup}
|
||||
@@ -539,18 +561,18 @@ proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
# don't call genAddr(c, x) here:
|
||||
result = genBuiltin(c, mNewSeq, "newSeq", x)
|
||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
lenCall.typ() = getSysType(c.g, x.info, tyInt)
|
||||
result.add lenCall
|
||||
|
||||
proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c, mLengthStr, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
lenCall.typ() = getSysType(c.g, x.info, tyInt)
|
||||
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
|
||||
result.add lenCall
|
||||
|
||||
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
lenCall.typ() = getSysType(c.g, x.info, tyInt)
|
||||
var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
|
||||
op = instantiateGeneric(c, op, t, t)
|
||||
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
|
||||
@@ -573,7 +595,7 @@ proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
|
||||
)
|
||||
cond.typ = getSysType(c.g, c.info, tyBool)
|
||||
cond.typ() = getSysType(c.g, c.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
|
||||
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
@@ -606,7 +628,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if canFormAcycle(c.g, t.elemType):
|
||||
# follow all elements:
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
|
||||
@@ -644,7 +666,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if op == nil:
|
||||
return # protect from recursion
|
||||
body.add newHookCall(c, op, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
# XXX: replace these with assertions.
|
||||
let op = getAttachedOp(c.g, t, c.kind)
|
||||
@@ -666,7 +688,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
of attachedTrace:
|
||||
discard "strings are atomic and have no inner elements that are to trace"
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc cyclicType*(g: ModuleGraph, t: PType): bool =
|
||||
case t.kind
|
||||
@@ -714,7 +736,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isFinal(elemType):
|
||||
addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
|
||||
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
alignOf.typ() = getSysType(c.g, c.info, tyInt)
|
||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf)
|
||||
else:
|
||||
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
|
||||
@@ -724,7 +746,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isCyclic:
|
||||
if isFinal(elemType):
|
||||
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||
typInfo.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
|
||||
else:
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
|
||||
@@ -732,7 +754,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x)
|
||||
else:
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
cond.typ() = getSysType(c.g, x.info, tyBool)
|
||||
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
@@ -759,13 +781,13 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isCyclic:
|
||||
if isFinal(elemType):
|
||||
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||
typInfo.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y)
|
||||
else:
|
||||
# If the ref is polymorphic we have to account for this
|
||||
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)
|
||||
@@ -780,7 +802,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
## Closures are really like refs except they always use a virtual destructor
|
||||
## and we need to do the refcounting only on the ref field which we call 'xenv':
|
||||
let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
xenv.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc
|
||||
let tmp =
|
||||
@@ -796,7 +818,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isCyclic: "nimDecRefIsLastCyclicDyn"
|
||||
else: "nimDecRefIsLast"
|
||||
let cond = callCodegenProc(c.g, decRefProc, c.info, tmp)
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
cond.typ() = getSysType(c.g, x.info, tyBool)
|
||||
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
@@ -808,7 +830,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yenv.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
if isCyclic:
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
|
||||
body.add newAsgnStmt(x, y)
|
||||
@@ -820,7 +842,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yenv.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
if isCyclic:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
|
||||
@@ -832,7 +854,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace:
|
||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
@@ -860,7 +882,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)
|
||||
@@ -872,7 +894,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isFinal(elemType):
|
||||
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
|
||||
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
alignOf.typ() = getSysType(c.g, c.info, tyInt)
|
||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf)
|
||||
else:
|
||||
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
|
||||
@@ -888,21 +910,21 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, x, actions)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if c.kind == attachedDeepCopy:
|
||||
# a big problem is that we don't know the environment's type here, so we
|
||||
# have to go through some indirection; we delegate this to the codegen:
|
||||
let call = newNodeI(nkCall, c.info, 2)
|
||||
call.typ = t
|
||||
call.typ() = t
|
||||
call[0] = newSymNode(createMagic(c.g, c.idgen, "deepCopy", mDeepCopy))
|
||||
call[1] = y
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
xx.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
# we 'nil' y out afterwards so we *need* to take over its reference
|
||||
@@ -911,13 +933,13 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
of attachedDestructor:
|
||||
@@ -928,11 +950,11 @@ 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)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
xx.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
var actions = newNodeI(nkStmtList, c.info)
|
||||
#discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
|
||||
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
|
||||
@@ -946,7 +968,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, xx, actions)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case t.kind
|
||||
@@ -996,9 +1018,13 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# 'selectedGC' here to determine if we have the new runtime.
|
||||
discard considerUserDefinedOp(c, t, body, x, y)
|
||||
elif tfHasAsgn in t.flags:
|
||||
# seqs with elements using custom hooks in refc
|
||||
if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
|
||||
body.add newSeqCall(c, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
if c.kind == attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
else:
|
||||
forallElements(c, t, body, x, y)
|
||||
else:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyString:
|
||||
@@ -1015,9 +1041,11 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of {attachedAsgn, attachedSink, attachedDup}:
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
elif tfUnion in t.flags: # bug #25236
|
||||
defaultOp(c, t, body, x, y)
|
||||
else:
|
||||
if c.kind == attachedDup:
|
||||
var op2 = getAttachedOp(c.g, t, attachedAsgn)
|
||||
@@ -1040,7 +1068,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
else:
|
||||
discard "cannot copy openArray"
|
||||
|
||||
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
|
||||
tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
|
||||
tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
|
||||
@@ -1057,9 +1085,7 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ.elementType
|
||||
if getAttachedOp(g, baseType, kind) == nil:
|
||||
# TODO: fixme `isDistinct` is a fix for #23552; remove it after
|
||||
# `-d:nimPreviewNonVarDestructor` becomes the default
|
||||
discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
|
||||
discard produceSym(g, c, baseType, kind, info, idgen)
|
||||
result = getAttachedOp(g, baseType, kind)
|
||||
setAttachedOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
@@ -1098,7 +1124,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache
|
||||
incl result.flags, sfGeneratedOp
|
||||
|
||||
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym =
|
||||
if kind == attachedDup:
|
||||
return symDupPrototype(g, typ, owner, kind, info, idgen)
|
||||
|
||||
@@ -1109,7 +1135,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
idgen, result, info)
|
||||
|
||||
if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)):
|
||||
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence})):
|
||||
dest.typ = typ
|
||||
else:
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
@@ -1146,18 +1172,18 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
|
||||
let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ = xx.typ
|
||||
xx.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ() = xx.typ
|
||||
body.add newAsgnStmt(xx, yy)
|
||||
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
if typ.kind == tyDistinct:
|
||||
return produceSymDistinctType(g, c, typ, kind, info, idgen)
|
||||
|
||||
result = getAttachedOp(g, typ, kind)
|
||||
if result == nil:
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen)
|
||||
|
||||
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
@@ -1191,7 +1217,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, attachedDup} and not lacksMTypeField(typ):
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(typ):
|
||||
# bug #19205: Do not forget to also copy the hidden type field:
|
||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
||||
|
||||
@@ -1201,6 +1227,8 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
result.ast[pragmasPos].add newTree(nkExprColonExpr,
|
||||
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))
|
||||
|
||||
if kind == attachedDestructor:
|
||||
incl result.options, optQuirky
|
||||
completePartialOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
|
||||
@@ -1260,7 +1288,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
|
||||
else:
|
||||
localError(g.config, info, "unresolved generic parameter")
|
||||
|
||||
proc isTrival*(s: PSym): bool {.inline.} =
|
||||
proc isTrivial*(s: PSym): bool {.inline.} =
|
||||
s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
|
||||
|
||||
proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
@@ -1270,6 +1298,10 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
## The later 'injectdestructors' pass depends on it.
|
||||
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
|
||||
incl orig.flags, tfCheckedForDestructor
|
||||
# for user defined generic destructors:
|
||||
let origRoot = genericRoot(orig)
|
||||
if origRoot != nil:
|
||||
incl origRoot.flags, tfGenericHasDestructor
|
||||
|
||||
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
|
||||
@@ -1311,8 +1343,8 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
if canon != orig:
|
||||
setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
|
||||
|
||||
if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
|
||||
#or not isTrival(orig.assignment) or
|
||||
# not isTrival(orig.sink):
|
||||
if not isTrivial(getAttachedOp(g, orig, attachedDestructor)):
|
||||
#or not isTrivial(orig.assignment) or
|
||||
# not isTrivial(orig.sink):
|
||||
orig.flags.incl tfHasAsgn
|
||||
# ^ XXX Breaks IC!
|
||||
|
||||
@@ -32,12 +32,12 @@ proc interestingVar(s: PSym): bool {.inline.} =
|
||||
proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode =
|
||||
let field = addUniqueField(c.objType, s, c.cache, c.idgen)
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ = c.objType
|
||||
deref.typ() = c.objType
|
||||
deref.add(newSymNode(c.partialParam, info))
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add(deref)
|
||||
result.add(newSymNode(field))
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc liftLocals(n: PNode; i: int; c: var Ctx) =
|
||||
let it = n[i]
|
||||
|
||||
@@ -92,8 +92,9 @@ type
|
||||
warnStmtListLambda = "StmtListLambda",
|
||||
warnBareExcept = "BareExcept",
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnGenericsIgnoredInjection = "GenericsIgnoredInjection",
|
||||
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
|
||||
warnStdPrefix = "StdPrefix"
|
||||
warnUnknownNotes = "UnknownNotes"
|
||||
warnUser = "User",
|
||||
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
|
||||
# hints
|
||||
@@ -109,9 +110,9 @@ type
|
||||
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
|
||||
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintUnknownRaises = "UnknownRaises",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
hintUnknownHint = "UnknownHint"
|
||||
|
||||
const
|
||||
MsgKindToStr*: array[TMsgKind, string] = [
|
||||
@@ -198,8 +199,9 @@ const
|
||||
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
|
||||
warnBareExcept: "$1",
|
||||
warnImplicitDefaultValue: "$1",
|
||||
warnGenericsIgnoredInjection: "$1",
|
||||
warnIgnoredSymbolInjection: "$1",
|
||||
warnStdPrefix: "$1 needs the 'std' prefix",
|
||||
warnUnknownNotes: "$1",
|
||||
warnUser: "$1",
|
||||
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
|
||||
hintSuccess: "operation successful: $#",
|
||||
@@ -235,9 +237,9 @@ const
|
||||
hintUser: "$1",
|
||||
hintUserRaw: "$1",
|
||||
hintExtendedContext: "$1",
|
||||
hintUnknownRaises: "$1 is a forward declaration without explicit .raises, assuming it can raise anything",
|
||||
hintMsgOrigin: "$1",
|
||||
hintDeclaredLoc: "$1",
|
||||
hintUnknownHint: "unknown hint: $1"
|
||||
hintDeclaredLoc: "$1"
|
||||
]
|
||||
|
||||
const
|
||||
@@ -268,6 +270,7 @@ const
|
||||
NotesVerbosity* = computeNotesVerbosity()
|
||||
errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
|
||||
errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
|
||||
errFloatToString* = "cannot convert '$1' to '$2'"
|
||||
|
||||
type
|
||||
TFileInfo* = object
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import std/strutils
|
||||
from std/sugar import dup
|
||||
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages, modulegraphs
|
||||
export packages
|
||||
|
||||
const
|
||||
@@ -97,7 +97,7 @@ template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind)
|
||||
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
|
||||
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
|
||||
sym.kind != skResult and # ignore `result`
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
@@ -138,7 +138,7 @@ template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
|
||||
## Check symbol uses match their definition's style.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
|
||||
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
|
||||
@@ -154,5 +154,5 @@ template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragma
|
||||
## Note: This only applies to builtin pragmas, not user pragmas.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
(sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)): # ignore foreign packages
|
||||
checkPragmaUseImpl(ctx.config, info, w, pragmaName)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
pathutils
|
||||
|
||||
import std/strutils
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
|
||||
@@ -68,6 +68,7 @@ when not declared(readLineFromStdin):
|
||||
# fallback implementation:
|
||||
proc readLineFromStdin(prompt: string, line: var string): bool =
|
||||
stdout.write(prompt)
|
||||
stdout.flushFile()
|
||||
result = readLine(stdin, line)
|
||||
if not result:
|
||||
stdout.write("\n")
|
||||
@@ -85,6 +86,47 @@ const
|
||||
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
|
||||
'|', '%', '&', '$', '@', '~', ','}
|
||||
AdditionalLineContinuationOprs = {'#', ':', '='}
|
||||
LineContinuationTokens = [
|
||||
"let", "var", "const", "type", # section
|
||||
"object", "tuple",
|
||||
# from ./layouter.oprSet
|
||||
"div", "mod", "shl", "shr", "in", "notin", "is",
|
||||
"isnot", "not", "of", "as", "from", "..", "and", "or", "xor",
|
||||
] # must be all `nimIdentNormalized`-ed
|
||||
|
||||
proc eqIdent(a, bNormalized: string): bool =
|
||||
a.nimIdentNormalize == bNormalized
|
||||
|
||||
proc endsWithIdent(s, subs: string): bool =
|
||||
let le = subs.len
|
||||
if le > s.len: return false
|
||||
s[^le .. ^1].eqIdent subs
|
||||
|
||||
proc continuesWithIdent(s, subs: string, start: int): bool =
|
||||
s.substr(start, start+subs.high).eqIdent subs
|
||||
|
||||
proc endsWithIdent(s, subs: string, endIdx: var int): bool =
|
||||
endIdx.dec subs.len
|
||||
result = s.continuesWithIdent(subs, endIdx+1)
|
||||
|
||||
proc containsObjectOf(x: string): bool =
|
||||
const sep = ' '
|
||||
var idx = x.rfind(sep)
|
||||
if idx == -1: return
|
||||
template eatWord(word) =
|
||||
while x[idx] == sep: idx.dec
|
||||
result = x.endsWithIdent(word, idx)
|
||||
if not result: return
|
||||
eatWord "of"
|
||||
eatWord "object"
|
||||
result = true
|
||||
|
||||
proc endsWithLineContinuationToken(x: string): bool =
|
||||
result = false
|
||||
for tok in LineContinuationTokens:
|
||||
if x.endsWithIdent(tok):
|
||||
return true
|
||||
result = x.containsObjectOf
|
||||
|
||||
proc endsWithOpr*(x: string): bool =
|
||||
result = x.endsWith(LineContinuationOprs)
|
||||
@@ -92,7 +134,9 @@ proc endsWithOpr*(x: string): bool =
|
||||
proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
|
||||
result = inTripleString or line.len > 0 and (
|
||||
line[0] == ' ' or
|
||||
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
|
||||
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) or
|
||||
line.endsWithLineContinuationToken()
|
||||
)
|
||||
|
||||
proc countTriples(s: string): int =
|
||||
result = 0
|
||||
@@ -108,7 +152,10 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
|
||||
s.rd = 0
|
||||
var line = newStringOfCap(120)
|
||||
var triples = 0
|
||||
while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
|
||||
while true:
|
||||
if not readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
|
||||
# now readLineFromStdin meets EOF (ctrl-D/Z) or ctrl-C
|
||||
quit()
|
||||
s.s.add(line)
|
||||
s.s.add("\n")
|
||||
inc triples, countTriples(line)
|
||||
|
||||
@@ -50,7 +50,7 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
case x.kind
|
||||
of nkIdent: id.add(x.ident.s)
|
||||
of nkSym: id.add(x.sym.name.s)
|
||||
of nkSymChoices:
|
||||
of nkSymChoices, nkOpenSym:
|
||||
if x[0].kind == nkSym:
|
||||
id.add(x[0].sym.name.s)
|
||||
else:
|
||||
@@ -58,7 +58,7 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
|
||||
else: handleError(n, origin)
|
||||
result = getIdent(c.cache, id)
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
if n[0].kind == nkSym:
|
||||
result = n[0].sym.name
|
||||
else:
|
||||
@@ -75,10 +75,13 @@ proc addUniqueSym*(scope: PScope, s: PSym): PSym =
|
||||
proc openScope*(c: PContext): PScope {.discardable.} =
|
||||
result = PScope(parent: c.currentScope,
|
||||
symbols: initStrTable(),
|
||||
depthLevel: c.scopeDepth + 1)
|
||||
depthLevel: c.scopeDepth + 1,
|
||||
optionStackLen: c.optionStack.len)
|
||||
c.currentScope = result
|
||||
|
||||
proc rawCloseScope*(c: PContext) =
|
||||
if c.currentScope.optionStackLen >= 1:
|
||||
c.optionStack.setLen(c.currentScope.optionStackLen)
|
||||
c.currentScope = c.currentScope.parent
|
||||
|
||||
proc closeScope*(c: PContext) =
|
||||
@@ -219,7 +222,14 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
|
||||
if i == limit: return
|
||||
inc i
|
||||
|
||||
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
proc searchImportsAll*(c: PContext, s: PIdent, filter: TSymKinds, holding: var seq[PSym]) =
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
holding.add s
|
||||
|
||||
proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
@@ -229,14 +239,12 @@ proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKi
|
||||
result.add candidate
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
|
||||
proc searchScopesAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = searchScopes(c,s,filter)
|
||||
if result.len == 0:
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
searchImportsAll(c, s, filter, result)
|
||||
|
||||
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
@@ -250,11 +258,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
|
||||
if result.len == 0:
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
searchImportsAll(c, s, filter, result)
|
||||
|
||||
proc cmpScopes*(ctx: PContext, s: PSym): int =
|
||||
# Do not return a negative number
|
||||
@@ -384,7 +388,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
|
||||
if sym.name.id == ord(wUnderscore): return
|
||||
let conflict = scope.addUniqueSym(sym)
|
||||
if conflict != nil:
|
||||
if sym.kind == skModule and conflict.kind == skModule:
|
||||
if sym.kind == skModule and conflict.kind == skModule and not c.config.isDefined("nimPreviewDuplicateModuleError"):
|
||||
# e.g.: import foo; import foo
|
||||
# xxx we could refine this by issuing a different hint for the case
|
||||
# where a duplicate import happens inside an include.
|
||||
@@ -561,23 +565,33 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
|
||||
var amb: bool = false
|
||||
discard errorUseQualifier(c, info, s, amb)
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
|
||||
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
|
||||
proc ambiguousIdentifierMsg*(candidates: seq[PSym], prefix = "use one of", indent = 0): string =
|
||||
result = ""
|
||||
for i in 0 ..< indent:
|
||||
result.add(' ')
|
||||
result.add "ambiguous identifier: '" & candidates[0].name.s & "'"
|
||||
var i = 0
|
||||
for candidate in candidates:
|
||||
if i == 0: err.add " -- $1 the following:\n" % prefix
|
||||
else: err.add "\n"
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
err.add ": " & typeToString(candidate.typ)
|
||||
if i == 0: result.add " -- $1 the following:\n" % prefix
|
||||
else: result.add "\n"
|
||||
for i in 0 ..< indent:
|
||||
result.add(' ')
|
||||
result.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
result.add ": " & typeToString(candidate.typ)
|
||||
inc i
|
||||
localError(c.config, info, errGenerated, err)
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
|
||||
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates))
|
||||
|
||||
proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string =
|
||||
var candidates = newSeq[PSym](choices.len)
|
||||
let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate"
|
||||
for i, n in choices:
|
||||
candidates[i] = n.sym
|
||||
errorUseQualifier(c, info, candidates, prefix)
|
||||
result = ambiguousIdentifierMsg(candidates, prefix, indent)
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
|
||||
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices))
|
||||
|
||||
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
|
||||
var err: string
|
||||
@@ -630,9 +644,10 @@ type
|
||||
|
||||
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
|
||||
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind]): seq[PSym] =
|
||||
result = searchInScopesFilterBy(c, ident, filter)
|
||||
if result.len == 0:
|
||||
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
|
||||
includePureEnum = false): seq[PSym] =
|
||||
result = selectFromScopesElseAll(c, ident, filter)
|
||||
if skEnumField in filter and (result.len == 0 or includePureEnum):
|
||||
result.add allPureEnumFields(c, ident)
|
||||
|
||||
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
@@ -665,24 +680,33 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
c.isAmbiguous = amb
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkOpenSym:
|
||||
result = qualifiedLookUp(c, n[0], flags)
|
||||
of nkDotExpr:
|
||||
result = nil
|
||||
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
|
||||
if m != nil and m.kind == skModule:
|
||||
var ident: PIdent = nil
|
||||
if n[1].kind == nkIdent:
|
||||
ident = n[1].ident
|
||||
elif n[1].kind == nkAccQuoted:
|
||||
if n[1].kind == nkAccQuoted:
|
||||
ident = considerQuotedIdent(c, n[1])
|
||||
else:
|
||||
# this includes sym and symchoice nodes, but since we are looking in
|
||||
# a module, it shouldn't matter what was captured
|
||||
ident = n[1].getPIdent
|
||||
if ident != nil:
|
||||
if m == c.module:
|
||||
result = strTableGet(c.topLevelScope.symbols, ident)
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
result = initIdentIter(ti, c.topLevelScope.symbols, ident)
|
||||
if result != nil and nextIdentIter(ti, c.topLevelScope.symbols) != nil:
|
||||
# another symbol exists with same name
|
||||
c.isAmbiguous = true
|
||||
else:
|
||||
var amb: bool = false
|
||||
if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
|
||||
var amb: bool = false
|
||||
result = errorUseQualifier(c, n.info, m, amb)
|
||||
else:
|
||||
result = someSym(c.graph, m, ident)
|
||||
result = someSymAmb(c.graph, m, ident, amb)
|
||||
if amb: c.isAmbiguous = true
|
||||
if result == nil and checkUndeclared in flags:
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
|
||||
elif n[1].kind == nkSym:
|
||||
@@ -701,6 +725,10 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
if result != nil and result.kind == skStub: loadStub(result)
|
||||
|
||||
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
if n.kind == nkOpenSym:
|
||||
# maybe the logic in semexprs should be mirrored here instead
|
||||
# for now it only seems this is called for `pickSym` in `getTypeIdent`
|
||||
return initOverloadIter(o, c, n[0])
|
||||
o.importIdx = -1
|
||||
o.marked = initIntSet()
|
||||
case n.kind
|
||||
|
||||
@@ -174,12 +174,12 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
|
||||
# returns a[].field as a node
|
||||
assert field.kind == skField
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ = a.typ.skipTypes(abstractInst)[0]
|
||||
deref.typ() = a.typ.skipTypes(abstractInst)[0]
|
||||
deref.add a
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add deref
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc rawDirectAccess*(obj, field: PSym): PNode =
|
||||
# returns a.field as a node
|
||||
@@ -187,7 +187,7 @@ proc rawDirectAccess*(obj, field: PSym): PNode =
|
||||
result = newNodeI(nkDotExpr, field.info)
|
||||
result.add newSymNode(obj)
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc lookupInRecord(n: PNode, id: ItemId): PSym =
|
||||
result = nil
|
||||
@@ -250,12 +250,12 @@ proc newDotExpr*(obj, b: PSym): PNode =
|
||||
assert field != nil, b.name.s
|
||||
result.add newSymNode(obj)
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
|
||||
# returns a[].b as a node
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ = a.typ.skipTypes(abstractInst).elementType
|
||||
deref.typ() = a.typ.skipTypes(abstractInst).elementType
|
||||
var t = deref.typ.skipTypes(abstractInst)
|
||||
var field: PSym
|
||||
while true:
|
||||
@@ -273,12 +273,12 @@ proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add deref
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
|
||||
# returns a[].b as a node
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ = a.typ.skipTypes(abstractInst).elementType
|
||||
deref.typ() = a.typ.skipTypes(abstractInst).elementType
|
||||
var t = deref.typ.skipTypes(abstractInst)
|
||||
var field: PSym
|
||||
let bb = getIdent(cache, b)
|
||||
@@ -297,7 +297,7 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): P
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add deref
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc getFieldFromObj*(t: PType; v: PSym): PSym =
|
||||
assert v.kind != skField
|
||||
@@ -320,7 +320,7 @@ proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
|
||||
proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
|
||||
result = newNodeI(nkAddr, n.info, 1)
|
||||
result[0] = n
|
||||
result.typ = newType(typeKind, idgen, n.typ.owner)
|
||||
result.typ() = newType(typeKind, idgen, n.typ.owner)
|
||||
result.typ.rawAddSon(n.typ)
|
||||
|
||||
proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
|
||||
@@ -344,18 +344,18 @@ proc callCodegenProc*(g: ModuleGraph; name: string;
|
||||
if optionalArgs != nil:
|
||||
for i in 1..<optionalArgs.len-2:
|
||||
result.add optionalArgs[i]
|
||||
result.typ = sym.typ.returnType
|
||||
result.typ() = sym.typ.returnType
|
||||
|
||||
proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
|
||||
result = nkIntLit.newIntNode(value)
|
||||
result.typ = getSysType(g, info, tyInt)
|
||||
result.typ() = getSysType(g, info, tyInt)
|
||||
|
||||
proc genHigh*(g: ModuleGraph; n: PNode): PNode =
|
||||
if skipTypes(n.typ, abstractVar).kind == tyArray:
|
||||
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar))))
|
||||
else:
|
||||
result = newNodeI(nkCall, n.info, 2)
|
||||
result.typ = getSysType(g, n.info, tyInt)
|
||||
result.typ() = getSysType(g, n.info, tyInt)
|
||||
result[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
|
||||
result[1] = n
|
||||
|
||||
@@ -364,7 +364,7 @@ proc genLen*(g: ModuleGraph; n: PNode): PNode =
|
||||
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1))
|
||||
else:
|
||||
result = newNodeI(nkCall, n.info, 2)
|
||||
result.typ = getSysType(g, n.info, tyInt)
|
||||
result.typ() = getSysType(g, n.info, tyInt)
|
||||
result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
|
||||
result[1] = n
|
||||
|
||||
|
||||
@@ -166,4 +166,4 @@ proc makeAddr*(n: PNode; idgen: IdGenerator): PNode =
|
||||
result = n
|
||||
else:
|
||||
result = newTree(nkHiddenAddr, n)
|
||||
result.typ = makePtrType(n.typ, idgen)
|
||||
result.typ() = makePtrType(n.typ.skipTypes({tySink}), idgen)
|
||||
|
||||
@@ -22,8 +22,6 @@ import
|
||||
modules,
|
||||
modulegraphs, lineinfos, pathutils, vmprofiler
|
||||
|
||||
# ensure NIR compiles:
|
||||
import nir / nir
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
@@ -48,9 +46,6 @@ proc writeDepsFile(g: ModuleGraph) =
|
||||
f.writeLine(toFullPath(g.config, k))
|
||||
f.close()
|
||||
|
||||
proc writeNinjaFile(g: ModuleGraph) =
|
||||
discard "to implement"
|
||||
|
||||
proc writeCMakeDepsFile(conf: ConfigRef) =
|
||||
## write a list of C files for build systems like CMake.
|
||||
## only updated when the C file list changes.
|
||||
@@ -122,6 +117,22 @@ when not defined(leanCompiler):
|
||||
else: raiseAssert $ext
|
||||
compilePipelineProject(graph)
|
||||
|
||||
proc commandCompileToNif(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
extccomp.initVars(conf)
|
||||
if conf.symbolFiles == disabledSf:
|
||||
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
|
||||
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
|
||||
# nothing changed
|
||||
graph.config.notes = graph.config.mainPackageNotes
|
||||
return
|
||||
|
||||
if not extccomp.ccHasSaneOverflow(conf):
|
||||
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
|
||||
|
||||
setPipeLinePass(graph, NifgenPass)
|
||||
compilePipelineProject(graph)
|
||||
|
||||
proc commandCompileToC(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
extccomp.initVars(conf)
|
||||
@@ -161,26 +172,6 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||
if optGenCDeps in graph.config.globalOptions:
|
||||
writeCMakeDepsFile(conf)
|
||||
|
||||
proc commandCompileToNir(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
extccomp.initVars(conf)
|
||||
if conf.symbolFiles == disabledSf:
|
||||
if {optRun, optForceFullMake} * conf.globalOptions == {optRun}:
|
||||
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
|
||||
# nothing changed
|
||||
graph.config.notes = graph.config.mainPackageNotes
|
||||
return
|
||||
|
||||
if not extccomp.ccHasSaneOverflow(conf):
|
||||
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
|
||||
|
||||
if conf.symbolFiles == disabledSf:
|
||||
setPipeLinePass(graph, NirPass)
|
||||
else:
|
||||
setPipeLinePass(graph, SemPass)
|
||||
compilePipelineProject(graph)
|
||||
writeNinjaFile(graph)
|
||||
|
||||
proc commandJsonScript(graph: ModuleGraph) =
|
||||
extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
|
||||
|
||||
@@ -197,16 +188,13 @@ proc commandCompileToJS(graph: ModuleGraph) =
|
||||
if optGenScript in conf.globalOptions:
|
||||
writeDepsFile(graph)
|
||||
|
||||
proc commandInteractive(graph: ModuleGraph; useNir: bool) =
|
||||
proc commandInteractive(graph: ModuleGraph) =
|
||||
graph.config.setErrorMaxHighMaybe
|
||||
initDefines(graph.config.symbols)
|
||||
if useNir:
|
||||
defineSymbol(graph.config.symbols, "noSignalHandler")
|
||||
else:
|
||||
defineSymbol(graph.config.symbols, "nimscript")
|
||||
defineSymbol(graph.config.symbols, "nimscript")
|
||||
# note: seems redundant with -d:nimHasLibFFI
|
||||
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
|
||||
setPipeLinePass(graph, if useNir: NirReplPass else: InterpreterPass)
|
||||
setPipeLinePass(graph, InterpreterPass)
|
||||
compilePipelineSystemModule(graph)
|
||||
if graph.config.commandArgs.len > 0:
|
||||
discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
|
||||
@@ -285,7 +273,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
if conf.exc == excNone: conf.exc = excSetjmp
|
||||
of backendCpp:
|
||||
if conf.exc == excNone: conf.exc = excCpp
|
||||
of backendObjc: discard
|
||||
of backendObjc, backendNif: discard
|
||||
of backendJs:
|
||||
if conf.hcrOn:
|
||||
# XXX: At the moment, system.nim cannot be compiled in JS mode
|
||||
@@ -293,8 +281,6 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
# and it has added this define implictly, so we must undo that here.
|
||||
# A better solution might be to fix system.nim
|
||||
undefSymbol(conf.symbols, "useNimRtl")
|
||||
of backendNir:
|
||||
if conf.exc == excNone: conf.exc = excGoto
|
||||
of backendInvalid: raiseAssert "unreachable"
|
||||
|
||||
proc compileToBackend() =
|
||||
@@ -305,7 +291,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of backendCpp: commandCompileToC(graph)
|
||||
of backendObjc: commandCompileToC(graph)
|
||||
of backendJs: commandCompileToJS(graph)
|
||||
of backendNir: commandCompileToNir(graph)
|
||||
of backendNif: commandCompileToNif(graph)
|
||||
of backendInvalid: raiseAssert "unreachable"
|
||||
|
||||
template docLikeCmd(body) =
|
||||
@@ -444,7 +430,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
wantMainModule(conf)
|
||||
commandView(graph)
|
||||
#msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
|
||||
of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir"))
|
||||
of cmdInteractive: commandInteractive(graph)
|
||||
of cmdNimscript:
|
||||
if conf.projectIsCmd or conf.projectIsStdin: discard
|
||||
elif not fileExists(conf.projectFull):
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## represents a complete Nim project. Single modules can either be kept in RAM
|
||||
## or stored in a rod-file.
|
||||
|
||||
import std/[intsets, tables, hashes, strtabs, algorithm]
|
||||
import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
|
||||
import ../dist/checksums/src/checksums/md5
|
||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
|
||||
import ic / [packed_ast, ic]
|
||||
@@ -60,10 +60,9 @@ type
|
||||
SemPass
|
||||
JSgenPass
|
||||
CgenPass
|
||||
NifgenPass
|
||||
EvalPass
|
||||
InterpreterPass
|
||||
NirPass
|
||||
NirReplPass
|
||||
GenDependPass
|
||||
Docgen2TexPass
|
||||
Docgen2JsonPass
|
||||
@@ -90,6 +89,7 @@ type
|
||||
suggestMode*: bool # whether we are in nimsuggest mode or not.
|
||||
invalidTransitiveClosure: bool
|
||||
interactive*: bool
|
||||
withinSystem*: bool # in system.nim or a module imported by system.nim
|
||||
inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
|
||||
# first module that included it
|
||||
importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
|
||||
@@ -137,6 +137,8 @@ type
|
||||
|
||||
cachedFiles*: StringTableRef
|
||||
|
||||
procGlobals*: seq[PNode]
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
idgen*: IdGenerator
|
||||
PPassContext* = ref TPassContext
|
||||
@@ -220,7 +222,7 @@ proc isCachedModule(g: ModuleGraph; module: int): bool {.inline.} =
|
||||
proc isCachedModule*(g: ModuleGraph; m: PSym): bool {.inline.} =
|
||||
isCachedModule(g, m.position)
|
||||
|
||||
proc simulateCachedModule(g: ModuleGraph; moduleSym: PSym; m: PackedModuleWriter) =
|
||||
proc simulateCachedModule(g: ModuleGraph; moduleSym: PSym; m: PackedModule) =
|
||||
when false:
|
||||
echo "simulating ", moduleSym.name.s, " ", moduleSym.position
|
||||
simulateLoadedModule(g.packed, g.config, g.cache, moduleSym, m)
|
||||
@@ -230,7 +232,7 @@ proc initEncoder*(g: ModuleGraph; module: PSym) =
|
||||
if id >= g.encoders.len:
|
||||
setLen g.encoders, id+1
|
||||
ic.initEncoder(g.encoders[id],
|
||||
g.packed[id].toDisk, module, g.config, g.startupPackedConfig)
|
||||
g.packed[id].fromDisk, module, g.config, g.startupPackedConfig)
|
||||
|
||||
type
|
||||
ModuleIter* = object
|
||||
@@ -276,6 +278,25 @@ proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
|
||||
else:
|
||||
result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
|
||||
|
||||
proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym =
|
||||
let importHidden = optImportHidden in m.options
|
||||
if isCachedModule(g, m):
|
||||
result = nil
|
||||
for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden):
|
||||
if result == nil:
|
||||
# set result to the first symbol
|
||||
result = s
|
||||
else:
|
||||
# another symbol found
|
||||
amb = true
|
||||
break
|
||||
else:
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
|
||||
if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
|
||||
# another symbol exists with same name
|
||||
amb = true
|
||||
|
||||
proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
|
||||
result = someSym(g, g.systemModule, name)
|
||||
|
||||
@@ -311,7 +332,7 @@ proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
|
||||
t.inst = result
|
||||
assert result != nil
|
||||
|
||||
proc resolveAttachedOp(g: ModuleGraph; t: var LazySym): PSym =
|
||||
proc resolveAttachedOp*(g: ModuleGraph; t: var LazySym): PSym =
|
||||
result = t.sym
|
||||
if result == nil:
|
||||
result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
|
||||
@@ -351,8 +372,8 @@ proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttached
|
||||
if g.config.symbolFiles != disabledSf:
|
||||
assert module < g.encoders.len
|
||||
assert isActive(g.encoders[module])
|
||||
toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].toDisk)
|
||||
#storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].toDisk)
|
||||
toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
|
||||
#storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk)
|
||||
|
||||
iterator getDispatchers*(g: ModuleGraph): PSym =
|
||||
for i in g.dispatchers.mitems:
|
||||
@@ -436,10 +457,10 @@ template getPContext(): untyped =
|
||||
else: c.c
|
||||
|
||||
when defined(nimsuggest):
|
||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
||||
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
|
||||
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
|
||||
else:
|
||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
||||
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
|
||||
template onDef*(info: TLineInfo; s: PSym) = discard
|
||||
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
|
||||
|
||||
@@ -454,6 +475,49 @@ proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic):
|
||||
proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym =
|
||||
result = createMagic(g, g.idgen, name, m)
|
||||
|
||||
proc uniqueModuleName*(conf: ConfigRef; m: PSym): string =
|
||||
## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
|
||||
## so that it is useful as a C identifier snippet.
|
||||
let fid = FileIndex(m.position)
|
||||
let path = AbsoluteFile toFullPath(conf, fid)
|
||||
var isLib = false
|
||||
var rel = ""
|
||||
if path.string.startsWith(conf.libpath.string):
|
||||
isLib = true
|
||||
rel = relativeTo(path, conf.libpath).string
|
||||
else:
|
||||
rel = relativeTo(path, conf.projectPath).string
|
||||
|
||||
if not isLib and not belongsToProjectPackage(conf, m):
|
||||
# special handlings for nimble packages
|
||||
when DirSep == '\\':
|
||||
let rel2 = replace(rel, '\\', '/')
|
||||
else:
|
||||
let rel2 = rel
|
||||
const pkgs2 = "pkgs2/"
|
||||
var start = rel2.find(pkgs2)
|
||||
if start >= 0:
|
||||
start += pkgs2.len
|
||||
start += skipUntil(rel2, {'/'}, start)
|
||||
if start+1 < rel2.len:
|
||||
rel = "pkg/" & rel2[start+1..<rel.len] # strips paths
|
||||
|
||||
let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
|
||||
result = newStringOfCap(trunc)
|
||||
for i in 0..<trunc:
|
||||
let c = rel[i]
|
||||
case c
|
||||
of 'a'..'z', '0'..'9':
|
||||
result.add c
|
||||
of {os.DirSep, os.AltSep}:
|
||||
result.add 'Z' # because it looks a bit like '/'
|
||||
of '.':
|
||||
result.add 'O' # a circle
|
||||
else:
|
||||
# We mangle upper letters too so that there cannot
|
||||
# be clashes with our special meanings of 'Z' and 'O'
|
||||
result.addInt ord(c)
|
||||
|
||||
proc registerModule*(g: ModuleGraph; m: PSym) =
|
||||
assert m != nil
|
||||
assert m.kind == skModule
|
||||
@@ -465,7 +529,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
|
||||
setLen(g.packed.pm, m.position + 1)
|
||||
|
||||
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
|
||||
uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position))))
|
||||
uniqueName: rope(uniqueModuleName(g.config, m)))
|
||||
initStrTables(g, m)
|
||||
|
||||
proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
|
||||
@@ -555,14 +619,14 @@ proc rememberEmittedTypeInfo*(g: ModuleGraph; m: FileIndex; ti: string) =
|
||||
if g.config.symbolFiles != disabledSf:
|
||||
#assert g.encoders[m.int32].isActive
|
||||
assert g.packed[m.int32].status != stored
|
||||
g.packed[m.int32].toDisk.emittedTypeInfo.add ti
|
||||
g.packed[m.int32].fromDisk.emittedTypeInfo.add ti
|
||||
#echo "added typeinfo ", m.int32, " ", ti, " suspicious ", not g.encoders[m.int32].isActive
|
||||
|
||||
proc rememberFlag*(g: ModuleGraph; m: PSym; flag: ModuleBackendFlag) =
|
||||
if g.config.symbolFiles != disabledSf:
|
||||
#assert g.encoders[m.int32].isActive
|
||||
assert g.packed[m.position].status != stored
|
||||
g.packed[m.position].toDisk.backendFlags.incl flag
|
||||
g.packed[m.position].fromDisk.backendFlags.incl flag
|
||||
|
||||
proc closeRodFile*(g: ModuleGraph; m: PSym) =
|
||||
if g.config.symbolFiles in {readOnlySf, v2Sf}:
|
||||
@@ -571,14 +635,14 @@ proc closeRodFile*(g: ModuleGraph; m: PSym) =
|
||||
# not depend on the hard disk contents!
|
||||
let mint = m.position
|
||||
saveRodFile(toRodFile(g.config, AbsoluteFile toFullPath(g.config, FileIndex(mint))),
|
||||
g.encoders[mint], g.packed[mint].toDisk)
|
||||
g.encoders[mint], g.packed[mint].fromDisk)
|
||||
g.packed[mint].status = stored
|
||||
|
||||
elif g.config.symbolFiles == stressTest:
|
||||
# debug code, but maybe a good idea for production? Could reduce the compiler's
|
||||
# memory consumption considerably at the cost of more loads from disk.
|
||||
let mint = m.position
|
||||
simulateCachedModule(g, m, g.packed[mint].toDisk)
|
||||
simulateCachedModule(g, m, g.packed[mint].fromDisk)
|
||||
g.packed[mint].status = loaded
|
||||
|
||||
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
|
||||
@@ -617,7 +681,6 @@ proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
||||
if m != nil:
|
||||
g.suggestSymbols.del(fileIdx)
|
||||
g.suggestErrors.del(fileIdx)
|
||||
g.resetForBackend
|
||||
incl m.flags, sfDirty
|
||||
|
||||
proc unmarkAllDirty*(g: ModuleGraph) =
|
||||
@@ -680,8 +743,6 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
|
||||
proc configComplete*(g: ModuleGraph) =
|
||||
rememberStartupConfig(g.startupPackedConfig, g.config)
|
||||
|
||||
from std/strutils import repeat, `%`
|
||||
|
||||
proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
|
||||
let conf = graph.config
|
||||
let isNimscript = conf.isDefined("nimscript")
|
||||
|
||||
@@ -79,6 +79,10 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
|
||||
else:
|
||||
result = fileInfoIdx(conf, fullPath)
|
||||
|
||||
type
|
||||
SelectedBase = enum
|
||||
FromProject, FromSearchPath, FromNimblePath
|
||||
|
||||
proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
|
||||
## Mangle a relative module path to avoid path and symbol collisions.
|
||||
##
|
||||
@@ -87,9 +91,27 @@ proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
|
||||
##
|
||||
## Example:
|
||||
## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar`
|
||||
"@m" & relativeTo(path, conf.projectPath).string.multiReplace(
|
||||
var best = relativeTo(path, conf.projectPath).string
|
||||
var selectedBase = FromProject
|
||||
for x in conf.searchPaths:
|
||||
let other = relativeTo(path, x).string
|
||||
if other.len < best.len:
|
||||
best = other
|
||||
selectedBase = FromSearchPath
|
||||
for x in conf.nimblePaths:
|
||||
let other = relativeTo(path, x).string
|
||||
if other.len < best.len:
|
||||
best = other
|
||||
selectedBase = FromNimblePath
|
||||
let prefix =
|
||||
case selectedBase
|
||||
of FromProject: "@m"
|
||||
of FromSearchPath: "@p"
|
||||
of FromNimblePath: "@n"
|
||||
|
||||
prefix & best.multiReplace(
|
||||
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
|
||||
|
||||
proc demangleModuleName*(path: string): string =
|
||||
## Demangle a relative module path.
|
||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})
|
||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@p": "", "@n": "", "@c": ":"})
|
||||
|
||||
@@ -25,7 +25,7 @@ template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
|
||||
|
||||
proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
|
||||
let packSym = getPackage(graph, fileIdx)
|
||||
result.owner = packSym
|
||||
setOwner(result, packSym)
|
||||
result.position = int fileIdx
|
||||
|
||||
proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
|
||||
@@ -629,7 +629,7 @@ proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "
|
||||
message(conf, info, warnDeprecated, msg)
|
||||
|
||||
proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
|
||||
if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
|
||||
if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return
|
||||
writeContext(conf, info)
|
||||
liMessage(conf, info, errInternal, errMsg, doAbort, info2)
|
||||
|
||||
@@ -669,31 +669,6 @@ template listMsg(title, r) =
|
||||
proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
|
||||
proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
|
||||
|
||||
proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string =
|
||||
## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
|
||||
## so that it is useful as a C identifier snippet.
|
||||
let path = AbsoluteFile toFullPath(conf, fid)
|
||||
let rel =
|
||||
if path.string.startsWith(conf.libpath.string):
|
||||
relativeTo(path, conf.libpath).string
|
||||
else:
|
||||
relativeTo(path, conf.projectPath).string
|
||||
let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
|
||||
result = newStringOfCap(trunc)
|
||||
for i in 0..<trunc:
|
||||
let c = rel[i]
|
||||
case c
|
||||
of 'a'..'z':
|
||||
result.add c
|
||||
of {os.DirSep, os.AltSep}:
|
||||
result.add 'Z' # because it looks a bit like '/'
|
||||
of '.':
|
||||
result.add 'O' # a circle
|
||||
else:
|
||||
# We mangle upper letters and digits too so that there cannot
|
||||
# be clashes with our special meanings of 'Z' and 'O'
|
||||
result.addInt ord(c)
|
||||
|
||||
proc genSuccessX*(conf: ConfigRef) =
|
||||
let mem =
|
||||
when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2017 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the generation of ``.ndi`` files for better debugging
|
||||
## support of Nim code. "ndi" stands for "Nim debug info".
|
||||
|
||||
import ast, msgs, ropes, options, pathutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
type
|
||||
NdiFile* = object
|
||||
enabled: bool
|
||||
f: File
|
||||
buf: string
|
||||
filename: AbsoluteFile
|
||||
syms: seq[PSym]
|
||||
|
||||
proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
|
||||
f.buf.setLen 0
|
||||
f.buf.addInt s.info.line.int
|
||||
f.buf.add "\t"
|
||||
f.buf.addInt s.info.col.int
|
||||
f.f.write(s.name.s, "\t")
|
||||
f.f.writeRope(s.loc.r)
|
||||
f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
|
||||
|
||||
template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
|
||||
if f.enabled: f.syms.add s
|
||||
|
||||
proc open*(f: var NdiFile; filename: AbsoluteFile; conf: ConfigRef) =
|
||||
f.enabled = not filename.isEmpty
|
||||
if f.enabled:
|
||||
f.filename = filename
|
||||
f.buf = newStringOfCap(20)
|
||||
|
||||
proc close*(f: var NdiFile, conf: ConfigRef) =
|
||||
if f.enabled:
|
||||
f.f = open(f.filename.string, fmWrite, 8000)
|
||||
doAssert f.f != nil, f.filename.string
|
||||
for s in f.syms:
|
||||
doWrite(f, s, conf)
|
||||
close(f.f)
|
||||
f.syms.reset
|
||||
f.filename.reset
|
||||
1690
compiler/nifgen.nim
Normal file
1690
compiler/nifgen.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -919,7 +919,7 @@ proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
|
||||
newSymNode(op, r.info),
|
||||
l,
|
||||
r)
|
||||
result.typ = newType(tyBool, ctx.idgen, nil)
|
||||
result.typ() = newType(tyBool, ctx.idgen, nil)
|
||||
|
||||
proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
|
||||
var cache = newIdentCache()
|
||||
@@ -929,7 +929,7 @@ proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
|
||||
result = nkPrefix.newTree(
|
||||
newSymNode(op, node.info),
|
||||
node)
|
||||
result.typ = newType(tyBool, ctx.idgen, nil)
|
||||
result.typ() = newType(tyBool, ctx.idgen, nil)
|
||||
|
||||
proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
|
||||
infix(ctx, l, r, mEqRef)
|
||||
|
||||
@@ -4,12 +4,16 @@ hint[XDeclaredButNotUsed]:off
|
||||
|
||||
define:booting
|
||||
define:nimcore
|
||||
define:nimPreviewFloatRoundtrip
|
||||
define:nimPreviewSlimSystem
|
||||
define:nimPreviewCstringConversion
|
||||
define:nimPreviewProcConversion
|
||||
define:nimPreviewRangeDefault
|
||||
define:nimPreviewNonVarDestructor
|
||||
define:nimPreviewCheckedClose
|
||||
define:nimPreviewAsmSemSymbol
|
||||
define:nimPreviewCStringComparisons
|
||||
define:nimPreviewDuplicateModuleError
|
||||
|
||||
threads:off
|
||||
|
||||
#import:"$projectpath/testability"
|
||||
|
||||
@@ -116,9 +116,9 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
|
||||
conf.backend = backendC
|
||||
|
||||
if conf.selectedGC == gcUnselected:
|
||||
if conf.backend in {backendC, backendCpp, backendObjc, backendNir} or
|
||||
(conf.cmd == cmdInteractive and isDefined(conf, "nir")) or
|
||||
(conf.cmd in cmdDocLike and conf.backend != backendJs):
|
||||
if conf.backend in {backendC, backendCpp, backendObjc} or
|
||||
(conf.cmd in cmdDocLike and conf.backend != backendJs) or
|
||||
conf.cmd == cmdGendepend:
|
||||
initOrcDefines(conf)
|
||||
|
||||
mainCommand(graph)
|
||||
|
||||
@@ -207,7 +207,6 @@ proc parseAssignment(L: var Lexer, tok: var Token;
|
||||
checkSymbol(L, tok)
|
||||
val.add($tok)
|
||||
confTok(L, tok, config, condStack)
|
||||
config.currentConfigDir = parentDir(filename.string)
|
||||
if percent:
|
||||
processSwitch(s, strtabs.`%`(val, config.configVars,
|
||||
{useEnvironment, useEmpty}), passPP, info, config)
|
||||
@@ -249,6 +248,8 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
|
||||
setDefaultLibpath(conf)
|
||||
template readConfigFile(path) =
|
||||
let configPath = path
|
||||
conf.currentConfigDir = configPath.splitFile.dir.string
|
||||
setConfigVar(conf, "selfDir", conf.currentConfigDir)
|
||||
if readConfigFile(configPath, cache, conf):
|
||||
conf.configFiles.add(configPath)
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
|
||||
elemType = settype[0]
|
||||
first = firstOrd(conf, elemType).toInt64
|
||||
result = newNodeI(nkCurly, info)
|
||||
result.typ = settype
|
||||
result.typ() = settype
|
||||
result.info = info
|
||||
e = 0
|
||||
while e < s.len * ElemSize:
|
||||
@@ -101,7 +101,7 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
|
||||
result.add aa
|
||||
else:
|
||||
n = newNodeI(nkRange, info)
|
||||
n.typ = elemType
|
||||
n.typ() = elemType
|
||||
n.add aa
|
||||
let bb = newIntTypeNode(b + first, elemType)
|
||||
bb.info = info
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,983 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# We produce C code as a list of tokens.
|
||||
|
||||
import std / [assertions, syncio, tables, intsets, formatfloat]
|
||||
from std / strutils import toOctal
|
||||
import .. / ic / [bitabs, rodfiles]
|
||||
import nirtypes, nirinsts, nirfiles
|
||||
import ../../dist/checksums/src/checksums/md5
|
||||
|
||||
type
|
||||
Token = LitId # indexing into the tokens BiTable[string]
|
||||
|
||||
PredefinedToken = enum
|
||||
IgnoreMe = "<unused>"
|
||||
EmptyToken = ""
|
||||
CurlyLe = "{"
|
||||
CurlyRi = "}"
|
||||
ParLe = "("
|
||||
ParRi = ")"
|
||||
BracketLe = "["
|
||||
BracketRi = "]"
|
||||
NewLine = "\n"
|
||||
Semicolon = ";"
|
||||
Comma = ", "
|
||||
Space = " "
|
||||
Colon = ": "
|
||||
Dot = "."
|
||||
Arrow = "->"
|
||||
Star = "*"
|
||||
Amp = "&"
|
||||
AsgnOpr = " = "
|
||||
ScopeOpr = "::"
|
||||
ConstKeyword = "const "
|
||||
StaticKeyword = "static "
|
||||
ExternKeyword = "extern "
|
||||
WhileKeyword = "while "
|
||||
IfKeyword = "if ("
|
||||
ElseKeyword = "else "
|
||||
SwitchKeyword = "switch "
|
||||
CaseKeyword = "case "
|
||||
DefaultKeyword = "default:"
|
||||
BreakKeyword = "break"
|
||||
NullPtr = "nullptr"
|
||||
IfNot = "if (!("
|
||||
ReturnKeyword = "return "
|
||||
TypedefStruct = "typedef struct "
|
||||
TypedefUnion = "typedef union "
|
||||
IncludeKeyword = "#include "
|
||||
|
||||
proc fillTokenTable(tab: var BiTable[string]) =
|
||||
for e in EmptyToken..high(PredefinedToken):
|
||||
let id = tab.getOrIncl $e
|
||||
assert id == LitId(e), $(id, " ", ord(e))
|
||||
|
||||
type
|
||||
GeneratedCode* = object
|
||||
m: NirModule
|
||||
includes: seq[LitId]
|
||||
includedHeaders: IntSet
|
||||
data: seq[LitId]
|
||||
protos: seq[LitId]
|
||||
code: seq[LitId]
|
||||
init: seq[LitId]
|
||||
tokens: BiTable[string]
|
||||
emittedStrings: IntSet
|
||||
needsPrefix: IntSet
|
||||
generatedTypes: IntSet
|
||||
mangledModules: Table[LitId, LitId]
|
||||
|
||||
proc initGeneratedCode*(m: sink NirModule): GeneratedCode =
|
||||
result = GeneratedCode(m: m, code: @[], tokens: initBiTable[string]())
|
||||
fillTokenTable(result.tokens)
|
||||
|
||||
proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} =
|
||||
g.code.add Token(t)
|
||||
|
||||
proc add*(g: var GeneratedCode; s: string) {.inline.} =
|
||||
g.code.add g.tokens.getOrIncl(s)
|
||||
|
||||
proc mangleModuleName(c: var GeneratedCode; key: LitId): LitId =
|
||||
result = c.mangledModules.getOrDefault(key, LitId(0))
|
||||
if result == LitId(0):
|
||||
let u {.cursor.} = c.m.lit.strings[key]
|
||||
var last = u.len - len(".nim") - 1
|
||||
var start = last
|
||||
while start >= 0 and u[start] != '/': dec start
|
||||
var sum = getMD5(u)
|
||||
sum.setLen(8)
|
||||
let dest = u.substr(start+1, last) & sum
|
||||
result = c.tokens.getOrIncl(dest)
|
||||
c.mangledModules[key] = result
|
||||
|
||||
type
|
||||
CppFile = object
|
||||
f: File
|
||||
|
||||
proc write(f: var CppFile; s: string) = write(f.f, s)
|
||||
proc write(f: var CppFile; c: char) = write(f.f, c)
|
||||
|
||||
proc writeTokenSeq(f: var CppFile; s: seq[Token]; c: GeneratedCode) =
|
||||
var indent = 0
|
||||
for i in 0..<s.len:
|
||||
let x = s[i]
|
||||
case x
|
||||
of Token(CurlyLe):
|
||||
inc indent
|
||||
write f, c.tokens[x]
|
||||
write f, "\n"
|
||||
for i in 1..indent*2: write f, ' '
|
||||
of Token(CurlyRi):
|
||||
dec indent
|
||||
write f, c.tokens[x]
|
||||
if i+1 < s.len and s[i+1] == Token(CurlyRi):
|
||||
discard
|
||||
else:
|
||||
write f, "\n"
|
||||
for i in 1..indent*2: write f, ' '
|
||||
of Token(Semicolon):
|
||||
write f, c.tokens[x]
|
||||
if i+1 < s.len and s[i+1] == Token(CurlyRi):
|
||||
discard "no newline before }"
|
||||
else:
|
||||
write f, "\n"
|
||||
for i in 1..indent*2: write f, ' '
|
||||
of Token(NewLine):
|
||||
write f, c.tokens[x]
|
||||
for i in 1..indent*2: write f, ' '
|
||||
else:
|
||||
write f, c.tokens[x]
|
||||
|
||||
|
||||
# Type graph
|
||||
|
||||
type
|
||||
TypeList = object
|
||||
processed: IntSet
|
||||
s: seq[(TypeId, PredefinedToken)]
|
||||
|
||||
proc add(dest: var TypeList; elem: TypeId; decl: PredefinedToken) =
|
||||
if not containsOrIncl(dest.processed, int(elem)):
|
||||
dest.s.add (elem, decl)
|
||||
|
||||
type
|
||||
TypeOrder = object
|
||||
forwardedDecls, ordered: TypeList
|
||||
typeImpls: Table[string, TypeId]
|
||||
lookedAt: IntSet
|
||||
|
||||
proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId)
|
||||
|
||||
proc recordDependency(types: TypeGraph; lit: Literals; c: var TypeOrder; parent, child: TypeId) =
|
||||
var ch = child
|
||||
var viaPointer = false
|
||||
while true:
|
||||
case types[ch].kind
|
||||
of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
|
||||
viaPointer = true
|
||||
ch = elementType(types, ch)
|
||||
of LastArrayTy:
|
||||
ch = elementType(types, ch)
|
||||
else:
|
||||
break
|
||||
|
||||
case types[ch].kind
|
||||
of ObjectTy, UnionTy:
|
||||
let decl = if types[ch].kind == ObjectTy: TypedefStruct else: TypedefUnion
|
||||
let obj = c.typeImpls.getOrDefault(lit.strings[types[ch].litId])
|
||||
if viaPointer:
|
||||
c.forwardedDecls.add obj, decl
|
||||
else:
|
||||
if not containsOrIncl(c.lookedAt, obj.int):
|
||||
traverseObject(types, lit, c, obj)
|
||||
c.ordered.add obj, decl
|
||||
of ArrayTy:
|
||||
if viaPointer:
|
||||
c.forwardedDecls.add ch, TypedefStruct
|
||||
else:
|
||||
if not containsOrIncl(c.lookedAt, ch.int):
|
||||
traverseObject(types, lit, c, ch)
|
||||
c.ordered.add ch, TypedefStruct
|
||||
else:
|
||||
discard "uninteresting type as we only focus on the required struct declarations"
|
||||
|
||||
proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId) =
|
||||
for x in sons(types, t):
|
||||
case types[x].kind
|
||||
of FieldDecl:
|
||||
recordDependency types, lit, c, t, x.firstSon
|
||||
of ObjectTy:
|
||||
# inheritance
|
||||
recordDependency types, lit, c, t, x
|
||||
else: discard
|
||||
|
||||
proc traverseTypes(types: TypeGraph; lit: Literals; c: var TypeOrder) =
|
||||
for t in allTypes(types):
|
||||
if types[t].kind in {ObjectDecl, UnionDecl}:
|
||||
assert types[t.firstSon].kind == NameVal
|
||||
c.typeImpls[lit.strings[types[t.firstSon].litId]] = t
|
||||
|
||||
for t in allTypesIncludingInner(types):
|
||||
case types[t].kind
|
||||
of ObjectDecl, UnionDecl:
|
||||
traverseObject types, lit, c, t
|
||||
let decl = if types[t].kind == ObjectDecl: TypedefStruct else: TypedefUnion
|
||||
c.ordered.add t, decl
|
||||
of ArrayTy:
|
||||
traverseObject types, lit, c, t
|
||||
c.ordered.add t, TypedefStruct
|
||||
else: discard
|
||||
|
||||
when false:
|
||||
template emitType(s: string) = c.types.add c.tokens.getOrIncl(s)
|
||||
template emitType(t: Token) = c.types.add t
|
||||
template emitType(t: PredefinedToken) = c.types.add Token(t)
|
||||
|
||||
proc genType(g: var GeneratedCode; types: TypeGraph; lit: Literals; t: TypeId; name = "") =
|
||||
template maybeAddName =
|
||||
if name != "":
|
||||
g.add Space
|
||||
g.add name
|
||||
|
||||
template atom(s: string) =
|
||||
g.add s
|
||||
maybeAddName()
|
||||
case types[t].kind
|
||||
of VoidTy: atom "void"
|
||||
of IntTy: atom "NI" & $types[t].integralBits
|
||||
of UIntTy: atom "NU" & $types[t].integralBits
|
||||
of FloatTy: atom "NF" & $types[t].integralBits
|
||||
of BoolTy: atom "NB" & $types[t].integralBits
|
||||
of CharTy: atom "NC" & $types[t].integralBits
|
||||
of ObjectTy, UnionTy, NameVal, AnnotationVal:
|
||||
atom lit.strings[types[t].litId]
|
||||
of VarargsTy:
|
||||
g.add "..."
|
||||
of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
|
||||
genType g, types, lit, elementType(types, t)
|
||||
g.add Star
|
||||
maybeAddName()
|
||||
of ArrayTy:
|
||||
genType g, types, lit, arrayName(types, t)
|
||||
maybeAddName()
|
||||
of LastArrayTy:
|
||||
genType g, types, lit, elementType(types, t)
|
||||
maybeAddName()
|
||||
g.add BracketLe
|
||||
g.add BracketRi
|
||||
of ProcTy:
|
||||
let (retType, callConv) = returnType(types, t)
|
||||
genType g, types, lit, retType
|
||||
g.add Space
|
||||
g.add ParLe
|
||||
genType g, types, lit, callConv
|
||||
g.add Star # "(*fn)"
|
||||
maybeAddName()
|
||||
g.add ParRi
|
||||
g.add ParLe
|
||||
var i = 0
|
||||
for ch in params(types, t):
|
||||
if i > 0: g.add Comma
|
||||
genType g, types, lit, ch
|
||||
inc i
|
||||
g.add ParRi
|
||||
of ObjectDecl, UnionDecl:
|
||||
atom lit.strings[types[t.firstSon].litId]
|
||||
of IntVal, SizeVal, AlignVal, OffsetVal, FieldDecl:
|
||||
#raiseAssert "did not expect: " & $types[t].kind
|
||||
g.add "BUG "
|
||||
atom $types[t].kind
|
||||
|
||||
proc generateTypes(g: var GeneratedCode; types: TypeGraph; lit: Literals; c: TypeOrder) =
|
||||
for (t, declKeyword) in c.forwardedDecls.s:
|
||||
let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
|
||||
let s {.cursor.} = lit.strings[types[name].litId]
|
||||
g.add declKeyword
|
||||
g.add s
|
||||
g.add Space
|
||||
g.add s
|
||||
g.add Semicolon
|
||||
|
||||
for (t, declKeyword) in c.ordered.s:
|
||||
let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
|
||||
let litId = types[name].litId
|
||||
if not g.generatedTypes.containsOrIncl(litId.int):
|
||||
let s {.cursor.} = lit.strings[litId]
|
||||
g.add declKeyword
|
||||
g.add CurlyLe
|
||||
if types[t].kind == ArrayTy:
|
||||
genType g, types, lit, elementType(types, t), "a"
|
||||
g.add BracketLe
|
||||
g.add $arrayLen(types, t)
|
||||
g.add BracketRi
|
||||
g.add Semicolon
|
||||
else:
|
||||
var i = 0
|
||||
for x in sons(types, t):
|
||||
case types[x].kind
|
||||
of FieldDecl:
|
||||
genType g, types, lit, x.firstSon, "F" & $i
|
||||
g.add Semicolon
|
||||
inc i
|
||||
of ObjectTy:
|
||||
genType g, types, lit, x, "P"
|
||||
g.add Semicolon
|
||||
else: discard
|
||||
g.add CurlyRi
|
||||
g.add s
|
||||
g.add Semicolon
|
||||
|
||||
# Procs
|
||||
|
||||
proc toCChar*(c: char; result: var string) {.inline.} =
|
||||
case c
|
||||
of '\0'..'\x1F', '\x7F'..'\xFF':
|
||||
result.add '\\'
|
||||
result.add toOctal(c)
|
||||
of '\'', '\"', '\\', '?':
|
||||
result.add '\\'
|
||||
result.add c
|
||||
else:
|
||||
result.add c
|
||||
|
||||
proc makeCString(s: string): string =
|
||||
result = newStringOfCap(s.len + 10)
|
||||
result.add('"')
|
||||
for c in s: toCChar(c, result)
|
||||
result.add('"')
|
||||
|
||||
template emitData(s: string) = c.data.add c.tokens.getOrIncl(s)
|
||||
template emitData(t: Token) = c.data.add t
|
||||
template emitData(t: PredefinedToken) = c.data.add Token(t)
|
||||
|
||||
proc genStrLit(c: var GeneratedCode; lit: Literals; litId: LitId): Token =
|
||||
result = Token(c.tokens.getOrIncl "QStr" & $litId)
|
||||
if not containsOrIncl(c.emittedStrings, int(litId)):
|
||||
let s {.cursor.} = lit.strings[litId]
|
||||
emitData "static const struct "
|
||||
emitData CurlyLe
|
||||
emitData "NI cap"
|
||||
emitData Semicolon
|
||||
emitData "NC8 data"
|
||||
emitData BracketLe
|
||||
emitData $s.len
|
||||
emitData "+1"
|
||||
emitData BracketRi
|
||||
emitData Semicolon
|
||||
emitData CurlyRi
|
||||
emitData result
|
||||
emitData AsgnOpr
|
||||
emitData CurlyLe
|
||||
emitData $s.len
|
||||
emitData " | NIM_STRLIT_FLAG"
|
||||
emitData Comma
|
||||
emitData makeCString(s)
|
||||
emitData CurlyRi
|
||||
emitData Semicolon
|
||||
|
||||
proc genIntLit(c: var GeneratedCode; lit: Literals; litId: LitId) =
|
||||
let i = lit.numbers[litId]
|
||||
if i > low(int32) and i <= high(int32):
|
||||
c.add $i
|
||||
elif i == low(int32):
|
||||
# Nim has the same bug for the same reasons :-)
|
||||
c.add "(-2147483647 -1)"
|
||||
elif i > low(int64):
|
||||
c.add "IL64("
|
||||
c.add $i
|
||||
c.add ")"
|
||||
else:
|
||||
c.add "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc gen(c: var GeneratedCode; t: Tree; n: NodePos)
|
||||
|
||||
proc genDisplayName(c: var GeneratedCode; symId: SymId) =
|
||||
let displayName = c.m.symnames[symId]
|
||||
if displayName != LitId(0):
|
||||
c.add "/*"
|
||||
c.add c.m.lit.strings[displayName]
|
||||
c.add "*/"
|
||||
|
||||
proc genSymDef(c: var GeneratedCode; t: Tree; n: NodePos) =
|
||||
if t[n].kind == SymDef:
|
||||
let symId = t[n].symId
|
||||
c.needsPrefix.incl symId.int
|
||||
genDisplayName c, symId
|
||||
gen c, t, n
|
||||
|
||||
proc genGlobal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
|
||||
c.add annotation
|
||||
let m: string
|
||||
if t[name].kind == SymDef:
|
||||
let symId = t[name].symId
|
||||
m = c.tokens[mangleModuleName(c, c.m.namespace)] & "__" & $symId
|
||||
genDisplayName c, symId
|
||||
else:
|
||||
assert t[name].kind == ModuleSymUse
|
||||
let (x, y) = sons2(t, name)
|
||||
m = c.tokens[mangleModuleName(c, t[x].litId)] & "__" & $t[y].immediateVal
|
||||
genType c, c.m.types, c.m.lit, t[typ].typeId, m
|
||||
|
||||
proc genLocal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
|
||||
assert t[name].kind == SymDef
|
||||
c.add annotation
|
||||
let symId = t[name].symId
|
||||
genType c, c.m.types, c.m.lit, t[typ].typeId, "q" & $symId
|
||||
genDisplayName c, symId
|
||||
|
||||
proc genProcDecl(c: var GeneratedCode; t: Tree; n: NodePos; isExtern: bool) =
|
||||
let signatureBegin = c.code.len
|
||||
let name = n.firstSon
|
||||
|
||||
var prc = n.firstSon
|
||||
next t, prc
|
||||
|
||||
while true:
|
||||
case t[prc].kind
|
||||
of PragmaPair:
|
||||
let (x, y) = sons2(t, prc)
|
||||
let key = cast[PragmaKey](t[x].rawOperand)
|
||||
case key
|
||||
of HeaderImport:
|
||||
let lit = t[y].litId
|
||||
let headerAsStr {.cursor.} = c.m.lit.strings[lit]
|
||||
let header = c.tokens.getOrIncl(headerAsStr)
|
||||
# headerAsStr can be empty, this has the semantics of the `nodecl` pragma:
|
||||
if headerAsStr.len > 0 and not c.includedHeaders.containsOrIncl(int header):
|
||||
if headerAsStr[0] == '#':
|
||||
discard "skip the #include"
|
||||
else:
|
||||
c.includes.add Token(IncludeKeyword)
|
||||
c.includes.add header
|
||||
c.includes.add Token NewLine
|
||||
# do not generate code for importc'ed procs:
|
||||
return
|
||||
of DllImport:
|
||||
let lit = t[y].litId
|
||||
raiseAssert "cannot eval: " & c.m.lit.strings[lit]
|
||||
else: discard
|
||||
of PragmaId: discard
|
||||
else: break
|
||||
next t, prc
|
||||
|
||||
var resultDeclPos = NodePos(-1)
|
||||
if t[prc].kind == SummonResult:
|
||||
resultDeclPos = prc
|
||||
gen c, t, prc.firstSon
|
||||
next t, prc
|
||||
else:
|
||||
c.add "void"
|
||||
c.add Space
|
||||
genSymDef c, t, name
|
||||
c.add ParLe
|
||||
var params = 0
|
||||
while t[prc].kind == SummonParam:
|
||||
if params > 0: c.add Comma
|
||||
let (typ, sym) = sons2(t, prc)
|
||||
genLocal c, t, sym, typ, ""
|
||||
next t, prc
|
||||
inc params
|
||||
if params == 0:
|
||||
c.add "void"
|
||||
c.add ParRi
|
||||
|
||||
for i in signatureBegin ..< c.code.len:
|
||||
c.protos.add c.code[i]
|
||||
c.protos.add Token Semicolon
|
||||
|
||||
if isExtern:
|
||||
c.code.setLen signatureBegin
|
||||
else:
|
||||
c.add CurlyLe
|
||||
if resultDeclPos.int >= 0:
|
||||
gen c, t, resultDeclPos
|
||||
for ch in sonsRest(t, n, prc):
|
||||
gen c, t, ch
|
||||
c.add CurlyRi
|
||||
|
||||
template triop(opr) =
|
||||
let (typ, a, b) = sons3(t, n)
|
||||
c.add ParLe
|
||||
c.add ParLe
|
||||
gen c, t, typ
|
||||
c.add ParRi
|
||||
gen c, t, a
|
||||
c.add opr
|
||||
gen c, t, b
|
||||
c.add ParRi
|
||||
|
||||
template cmpop(opr) =
|
||||
let (_, a, b) = sons3(t, n)
|
||||
c.add ParLe
|
||||
gen c, t, a
|
||||
c.add opr
|
||||
gen c, t, b
|
||||
c.add ParRi
|
||||
|
||||
template binaryop(opr) =
|
||||
let (typ, a) = sons2(t, n)
|
||||
c.add ParLe
|
||||
c.add ParLe
|
||||
gen c, t, typ
|
||||
c.add ParRi
|
||||
c.add opr
|
||||
gen c, t, a
|
||||
c.add ParRi
|
||||
|
||||
template checkedBinaryop(opr) =
|
||||
let (typ, labIdx, a, b) = sons4(t, n)
|
||||
let bits = integralBits(c.m.types[t[typ].typeId])
|
||||
let lab = t[labIdx].label
|
||||
|
||||
c.add (opr & $bits)
|
||||
c.add ParLe
|
||||
c.gen t, a
|
||||
c.add Comma
|
||||
c.gen t, b
|
||||
c.add Comma
|
||||
c.add "L" & $lab.int
|
||||
c.add ParRi
|
||||
|
||||
proc genNumberConv(c: var GeneratedCode; t: Tree; n: NodePos) =
|
||||
let (typ, arg) = sons2(t, n)
|
||||
if t[arg].kind == IntVal:
|
||||
let litId = t[arg].litId
|
||||
c.add ParLe
|
||||
c.add ParLe
|
||||
gen c, t, typ
|
||||
c.add ParRi
|
||||
case c.m.types[t[typ].typeId].kind
|
||||
of UIntTy:
|
||||
let x = cast[uint64](c.m.lit.numbers[litId])
|
||||
c.add $x
|
||||
of FloatTy:
|
||||
let x = cast[float64](c.m.lit.numbers[litId])
|
||||
c.add $x
|
||||
else:
|
||||
gen c, t, arg
|
||||
c.add ParRi
|
||||
else:
|
||||
binaryop ""
|
||||
|
||||
template moveToDataSection(body: untyped) =
|
||||
let oldLen = c.code.len
|
||||
body
|
||||
for i in oldLen ..< c.code.len:
|
||||
c.data.add c.code[i]
|
||||
setLen c.code, oldLen
|
||||
|
||||
proc gen(c: var GeneratedCode; t: Tree; n: NodePos) =
|
||||
case t[n].kind
|
||||
of Nop:
|
||||
discard "nothing to emit"
|
||||
of ImmediateVal:
|
||||
c.add $t[n].immediateVal
|
||||
of IntVal:
|
||||
genIntLit c, c.m.lit, t[n].litId
|
||||
of StrVal:
|
||||
c.code.add genStrLit(c, c.m.lit, t[n].litId)
|
||||
of Typed:
|
||||
genType c, c.m.types, c.m.lit, t[n].typeId
|
||||
of SymDef, SymUse:
|
||||
let s = t[n].symId
|
||||
if c.needsPrefix.contains(s.int):
|
||||
c.code.add mangleModuleName(c, c.m.namespace)
|
||||
c.add "__"
|
||||
c.add $s
|
||||
else:
|
||||
# XXX Use proper names here
|
||||
c.add "q"
|
||||
c.add $s
|
||||
|
||||
of ModuleSymUse:
|
||||
let (x, y) = sons2(t, n)
|
||||
let u = mangleModuleName(c, t[x].litId)
|
||||
let s = t[y].immediateVal
|
||||
c.code.add u
|
||||
c.add "__"
|
||||
c.add $s
|
||||
|
||||
of NilVal:
|
||||
c.add NullPtr
|
||||
of LoopLabel:
|
||||
c.add WhileKeyword
|
||||
c.add ParLe
|
||||
c.add "1"
|
||||
c.add ParRi
|
||||
c.add CurlyLe
|
||||
of GotoLoop:
|
||||
c.add CurlyRi
|
||||
of Label:
|
||||
let lab = t[n].label
|
||||
c.add "L"
|
||||
c.add $lab.int
|
||||
c.add Colon
|
||||
c.add Semicolon
|
||||
of Goto:
|
||||
let lab = t[n].label
|
||||
c.add "goto L"
|
||||
c.add $lab.int
|
||||
c.add Semicolon
|
||||
of CheckedGoto:
|
||||
discard "XXX todo"
|
||||
of ArrayConstr:
|
||||
c.add CurlyLe
|
||||
c.add ".a = "
|
||||
c.add CurlyLe
|
||||
var i = 0
|
||||
for ch in sonsFrom1(t, n):
|
||||
if i > 0: c.add Comma
|
||||
c.gen t, ch
|
||||
inc i
|
||||
c.add CurlyRi
|
||||
c.add CurlyRi
|
||||
of ObjConstr:
|
||||
c.add CurlyLe
|
||||
var i = 0
|
||||
for ch in sonsFrom1(t, n):
|
||||
if i mod 2 == 0:
|
||||
if i > 0: c.add Comma
|
||||
c.add ".F" & $t[ch].immediateVal
|
||||
c.add AsgnOpr
|
||||
else:
|
||||
c.gen t, ch
|
||||
inc i
|
||||
c.add CurlyRi
|
||||
of Ret:
|
||||
c.add ReturnKeyword
|
||||
c.gen t, n.firstSon
|
||||
c.add Semicolon
|
||||
of Select:
|
||||
c.add SwitchKeyword
|
||||
c.add ParLe
|
||||
let (_, selector) = sons2(t, n)
|
||||
c.gen t, selector
|
||||
c.add ParRi
|
||||
c.add CurlyLe
|
||||
for ch in sonsFromN(t, n, 2):
|
||||
c.gen t, ch
|
||||
c.add CurlyRi
|
||||
of SelectPair:
|
||||
let (le, ri) = sons2(t, n)
|
||||
c.gen t, le
|
||||
c.gen t, ri
|
||||
of SelectList:
|
||||
for ch in sons(t, n):
|
||||
c.gen t, ch
|
||||
of SelectValue:
|
||||
c.add CaseKeyword
|
||||
c.gen t, n.firstSon
|
||||
c.add Colon
|
||||
of SelectRange:
|
||||
let (le, ri) = sons2(t, n)
|
||||
c.add CaseKeyword
|
||||
c.gen t, le
|
||||
c.add " ... "
|
||||
c.gen t, ri
|
||||
c.add Colon
|
||||
of ForeignDecl:
|
||||
c.data.add LitId(ExternKeyword)
|
||||
c.gen t, n.firstSon
|
||||
of SummonGlobal:
|
||||
moveToDataSection:
|
||||
let (typ, sym) = sons2(t, n)
|
||||
c.genGlobal t, sym, typ, ""
|
||||
c.add Semicolon
|
||||
of SummonThreadLocal:
|
||||
moveToDataSection:
|
||||
let (typ, sym) = sons2(t, n)
|
||||
c.genGlobal t, sym, typ, "__thread "
|
||||
c.add Semicolon
|
||||
of SummonConst:
|
||||
moveToDataSection:
|
||||
let (typ, sym, val) = sons3(t, n)
|
||||
c.genGlobal t, sym, typ, "const "
|
||||
c.add AsgnOpr
|
||||
c.gen t, val
|
||||
c.add Semicolon
|
||||
of Summon, SummonResult:
|
||||
let (typ, sym) = sons2(t, n)
|
||||
c.genLocal t, sym, typ, ""
|
||||
c.add Semicolon
|
||||
|
||||
of SummonParam:
|
||||
raiseAssert "SummonParam should have been handled in genProc"
|
||||
of Kill:
|
||||
discard "we don't care about Kill instructions"
|
||||
of AddrOf:
|
||||
let (_, arg) = sons2(t, n)
|
||||
c.add "&"
|
||||
gen c, t, arg
|
||||
of DerefArrayAt:
|
||||
let (_, a, i) = sons3(t, n)
|
||||
gen c, t, a
|
||||
c.add BracketLe
|
||||
gen c, t, i
|
||||
c.add BracketRi
|
||||
of ArrayAt:
|
||||
let (_, a, i) = sons3(t, n)
|
||||
gen c, t, a
|
||||
c.add Dot
|
||||
c.add "a"
|
||||
c.add BracketLe
|
||||
gen c, t, i
|
||||
c.add BracketRi
|
||||
of FieldAt:
|
||||
let (_, a, b) = sons3(t, n)
|
||||
gen c, t, a
|
||||
let field = t[b].immediateVal
|
||||
c.add Dot
|
||||
c.add "F" & $field
|
||||
of DerefFieldAt:
|
||||
let (_, a, b) = sons3(t, n)
|
||||
gen c, t, a
|
||||
let field = t[b].immediateVal
|
||||
c.add Arrow
|
||||
c.add "F" & $field
|
||||
of Load:
|
||||
let (_, arg) = sons2(t, n)
|
||||
c.add ParLe
|
||||
c.add "*"
|
||||
gen c, t, arg
|
||||
c.add ParRi
|
||||
of Store:
|
||||
raiseAssert "Assumption was that Store is unused!"
|
||||
of Asgn:
|
||||
let (_, dest, src) = sons3(t, n)
|
||||
gen c, t, dest
|
||||
c.add AsgnOpr
|
||||
gen c, t, src
|
||||
c.add Semicolon
|
||||
of CheckedRange:
|
||||
c.add "nimCheckRange"
|
||||
c.add ParLe
|
||||
let (_, gotoInstr, x, a, b) = sons5(t, n)
|
||||
gen c, t, x
|
||||
c.add Comma
|
||||
gen c, t, a
|
||||
c.add Comma
|
||||
gen c, t, b
|
||||
c.add Comma
|
||||
c.add "L" & $t[gotoInstr].label.int
|
||||
c.add ParRi
|
||||
of CheckedIndex:
|
||||
c.add "nimCheckIndex"
|
||||
c.add ParLe
|
||||
let (gotoInstr, x, a) = sons3(t, n)
|
||||
gen c, t, x
|
||||
c.add Comma
|
||||
gen c, t, a
|
||||
c.add Comma
|
||||
c.add "L" & $t[gotoInstr].label.int
|
||||
c.add ParRi
|
||||
of Call, IndirectCall:
|
||||
let (typ, fn) = sons2(t, n)
|
||||
gen c, t, fn
|
||||
c.add ParLe
|
||||
var i = 0
|
||||
for ch in sonsFromN(t, n, 2):
|
||||
if i > 0: c.add Comma
|
||||
gen c, t, ch
|
||||
inc i
|
||||
c.add ParRi
|
||||
if c.m.types[t[typ].typeId].kind == VoidTy:
|
||||
c.add Semicolon
|
||||
of CheckedCall, CheckedIndirectCall:
|
||||
let (typ, gotoInstr, fn) = sons3(t, n)
|
||||
gen c, t, fn
|
||||
c.add ParLe
|
||||
var i = 0
|
||||
for ch in sonsFromN(t, n, 3):
|
||||
if i > 0: c.add Comma
|
||||
gen c, t, ch
|
||||
inc i
|
||||
c.add ParRi
|
||||
if c.m.types[t[typ].typeId].kind == VoidTy:
|
||||
c.add Semicolon
|
||||
|
||||
of CheckedAdd: checkedBinaryop "nimAddInt"
|
||||
of CheckedSub: checkedBinaryop "nimSubInt"
|
||||
of CheckedMul: checkedBinaryop "nimMulInt"
|
||||
of CheckedDiv: checkedBinaryop "nimDivInt"
|
||||
of CheckedMod: checkedBinaryop "nimModInt"
|
||||
of Add: triop " + "
|
||||
of Sub: triop " - "
|
||||
of Mul: triop " * "
|
||||
of Div: triop " / "
|
||||
of Mod: triop " % "
|
||||
of BitShl: triop " << "
|
||||
of BitShr: triop " >> "
|
||||
of BitAnd: triop " & "
|
||||
of BitOr: triop " | "
|
||||
of BitXor: triop " ^ "
|
||||
of BitNot: binaryop " ~ "
|
||||
of BoolNot: binaryop " !"
|
||||
of Eq: cmpop " == "
|
||||
of Le: cmpop " <= "
|
||||
of Lt: cmpop " < "
|
||||
of Cast: binaryop ""
|
||||
of NumberConv: genNumberConv c, t, n
|
||||
of CheckedObjConv: binaryop ""
|
||||
of ObjConv: binaryop ""
|
||||
of Emit: raiseAssert "cannot interpret: Emit"
|
||||
of ProcDecl: genProcDecl c, t, n, false
|
||||
of ForeignProcDecl: genProcDecl c, t, n, true
|
||||
of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc:
|
||||
c.add "cannot interpret: " & $t[n].kind
|
||||
|
||||
const
|
||||
Prelude = """
|
||||
/* GENERATED CODE. DO NOT EDIT. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define NB8 bool
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
|
||||
// see #13798: to avoid conflicts for code emitting `#include <stdbool.h>`
|
||||
#define NB8 _Bool
|
||||
#else
|
||||
typedef unsigned char NB8; // best effort
|
||||
#endif
|
||||
|
||||
typedef unsigned char NC8;
|
||||
|
||||
typedef float NF32;
|
||||
typedef double NF64;
|
||||
#if defined(__BORLANDC__) || defined(_MSC_VER)
|
||||
typedef signed char NI8;
|
||||
typedef signed short int NI16;
|
||||
typedef signed int NI32;
|
||||
typedef __int64 NI64;
|
||||
/* XXX: Float128? */
|
||||
typedef unsigned char NU8;
|
||||
typedef unsigned short int NU16;
|
||||
typedef unsigned int NU32;
|
||||
typedef unsigned __int64 NU64;
|
||||
#elif defined(HAVE_STDINT_H)
|
||||
#ifndef USE_NIM_NAMESPACE
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
typedef int8_t NI8;
|
||||
typedef int16_t NI16;
|
||||
typedef int32_t NI32;
|
||||
typedef int64_t NI64;
|
||||
typedef uint8_t NU8;
|
||||
typedef uint16_t NU16;
|
||||
typedef uint32_t NU32;
|
||||
typedef uint64_t NU64;
|
||||
#elif defined(HAVE_CSTDINT)
|
||||
#ifndef USE_NIM_NAMESPACE
|
||||
# include <cstdint>
|
||||
#endif
|
||||
typedef std::int8_t NI8;
|
||||
typedef std::int16_t NI16;
|
||||
typedef std::int32_t NI32;
|
||||
typedef std::int64_t NI64;
|
||||
typedef std::uint8_t NU8;
|
||||
typedef std::uint16_t NU16;
|
||||
typedef std::uint32_t NU32;
|
||||
typedef std::uint64_t NU64;
|
||||
#else
|
||||
/* Unknown compiler/version, do our best */
|
||||
#ifdef __INT8_TYPE__
|
||||
typedef __INT8_TYPE__ NI8;
|
||||
#else
|
||||
typedef signed char NI8;
|
||||
#endif
|
||||
#ifdef __INT16_TYPE__
|
||||
typedef __INT16_TYPE__ NI16;
|
||||
#else
|
||||
typedef signed short int NI16;
|
||||
#endif
|
||||
#ifdef __INT32_TYPE__
|
||||
typedef __INT32_TYPE__ NI32;
|
||||
#else
|
||||
typedef signed int NI32;
|
||||
#endif
|
||||
#ifdef __INT64_TYPE__
|
||||
typedef __INT64_TYPE__ NI64;
|
||||
#else
|
||||
typedef long long int NI64;
|
||||
#endif
|
||||
/* XXX: Float128? */
|
||||
#ifdef __UINT8_TYPE__
|
||||
typedef __UINT8_TYPE__ NU8;
|
||||
#else
|
||||
typedef unsigned char NU8;
|
||||
#endif
|
||||
#ifdef __UINT16_TYPE__
|
||||
typedef __UINT16_TYPE__ NU16;
|
||||
#else
|
||||
typedef unsigned short int NU16;
|
||||
#endif
|
||||
#ifdef __UINT32_TYPE__
|
||||
typedef __UINT32_TYPE__ NU32;
|
||||
#else
|
||||
typedef unsigned int NU32;
|
||||
#endif
|
||||
#ifdef __UINT64_TYPE__
|
||||
typedef __UINT64_TYPE__ NU64;
|
||||
#else
|
||||
typedef unsigned long long int NU64;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef NIM_INTBITS
|
||||
# if NIM_INTBITS == 64
|
||||
typedef NI64 NI;
|
||||
typedef NU64 NU;
|
||||
# elif NIM_INTBITS == 32
|
||||
typedef NI32 NI;
|
||||
typedef NU32 NU;
|
||||
# elif NIM_INTBITS == 16
|
||||
typedef NI16 NI;
|
||||
typedef NU16 NU;
|
||||
# elif NIM_INTBITS == 8
|
||||
typedef NI8 NI;
|
||||
typedef NU8 NU;
|
||||
# else
|
||||
# error "invalid bit width for int"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
|
||||
|
||||
#define nimAddInt64(a, b, L) ({long long int res; if(__builtin_saddll_overflow(a, b, &res)) goto L; res})
|
||||
#define nimSubInt64(a, b, L) ({long long int res; if(__builtin_ssubll_overflow(a, b, &res)) goto L; res})
|
||||
#define nimMulInt64(a, b, L) ({long long int res; if(__builtin_smulll_overflow(a, b, &res)) goto L; res})
|
||||
|
||||
#define nimAddInt32(a, b, L) ({long int res; if(__builtin_sadd_overflow(a, b, &res)) goto L; res})
|
||||
#define nimSubInt32(a, b, L) ({long int res; if(__builtin_ssub_overflow(a, b, &res)) goto L; res})
|
||||
#define nimMulInt32(a, b, L) ({long int res; if(__builtin_smul_overflow(a, b, &res)) goto L; res})
|
||||
|
||||
#define nimCheckRange(x, a, b, L) ({if (x < a || x > b) goto L; x})
|
||||
#define nimCheckIndex(x, a, L) ({if (x >= a) goto L; x})
|
||||
|
||||
"""
|
||||
|
||||
proc traverseCode(c: var GeneratedCode) =
|
||||
const AllowedInToplevelC = {SummonConst, SummonGlobal, SummonThreadLocal,
|
||||
ProcDecl, ForeignDecl, ForeignProcDecl}
|
||||
var i = NodePos(0)
|
||||
while i.int < c.m.code.len:
|
||||
let oldLen = c.code.len
|
||||
let moveToInitSection = c.m.code[NodePos(i)].kind notin AllowedInToplevelC
|
||||
|
||||
gen c, c.m.code, NodePos(i)
|
||||
next c.m.code, i
|
||||
|
||||
if moveToInitSection:
|
||||
for i in oldLen ..< c.code.len:
|
||||
c.init.add c.code[i]
|
||||
setLen c.code, oldLen
|
||||
|
||||
proc generateCode*(inp, outp: string) =
|
||||
var c = initGeneratedCode(load(inp))
|
||||
|
||||
var co = TypeOrder()
|
||||
traverseTypes(c.m.types, c.m.lit, co)
|
||||
|
||||
generateTypes(c, c.m.types, c.m.lit, co)
|
||||
let typeDecls = move c.code
|
||||
|
||||
traverseCode c
|
||||
var f = CppFile(f: open(outp, fmWrite))
|
||||
f.write "#define NIM_INTBITS " & $c.m.intbits & "\n"
|
||||
f.write Prelude
|
||||
writeTokenSeq f, c.includes, c
|
||||
writeTokenSeq f, typeDecls, c
|
||||
writeTokenSeq f, c.data, c
|
||||
writeTokenSeq f, c.protos, c
|
||||
writeTokenSeq f, c.code, c
|
||||
if c.init.len > 0:
|
||||
f.write "void __attribute__((constructor)) init(void) {"
|
||||
writeTokenSeq f, c.init, c
|
||||
f.write "}\n\n"
|
||||
f.f.close
|
||||
@@ -1,105 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Nim Intermediate Representation, designed to capture all of Nim's semantics without losing too much
|
||||
## precious information. Can easily be translated into C. And to JavaScript, hopefully.
|
||||
|
||||
from std/os import addFileExt, `/`, createDir
|
||||
|
||||
import std / assertions
|
||||
import ".." / [ast, modulegraphs, renderer, transf, options, msgs, lineinfos]
|
||||
import nirtypes, nirinsts, ast2ir, nirlineinfos, nirfiles, nirvm
|
||||
|
||||
import ".." / ic / [rodfiles, bitabs]
|
||||
|
||||
type
|
||||
PCtx* = ref object of TPassContext
|
||||
m: ModuleCon
|
||||
c: ProcCon
|
||||
oldErrorCount: int
|
||||
bytecode: Bytecode
|
||||
|
||||
proc newCtx*(module: PSym; g: ModuleGraph; idgen: IdGenerator): PCtx =
|
||||
var lit = Literals()
|
||||
var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
|
||||
var m = initModuleCon(g, g.config, idgen, module, nirm)
|
||||
m.noModularity = true
|
||||
PCtx(m: m, c: initProcCon(m, nil, g.config), idgen: idgen, bytecode: initBytecode(nirm))
|
||||
|
||||
proc refresh*(c: PCtx; module: PSym; idgen: IdGenerator) =
|
||||
#c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module, c.m.nirm)
|
||||
#c.m.noModularity = true
|
||||
c.c = initProcCon(c.m, nil, c.m.graph.config)
|
||||
c.idgen = idgen
|
||||
|
||||
proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) =
|
||||
if graph.repl.isNil:
|
||||
graph.repl = newCtx(module, graph, idgen)
|
||||
#registerAdditionalOps(PCtx graph.repl)
|
||||
else:
|
||||
refresh(PCtx graph.repl, module, idgen)
|
||||
|
||||
proc setupNirReplGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
setupGlobalCtx(module, graph, idgen)
|
||||
result = PCtx graph.repl
|
||||
|
||||
proc evalStmt(c: PCtx; n: PNode) =
|
||||
let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
|
||||
let pc = genStmt(c.c, n)
|
||||
#var res = ""
|
||||
#toString c.m.nirm.code, NodePos(pc), c.m.nirm.lit.strings, c.m.nirm.lit.numbers, c.m.symnames, res
|
||||
#res.add "\n--------------------------\n"
|
||||
#toString res, c.m.types.g
|
||||
if pc.int < c.m.nirm.code.len:
|
||||
c.bytecode.interactive = c.m.graph.interactive
|
||||
execCode c.bytecode, c.m.nirm.code, pc
|
||||
#echo res
|
||||
|
||||
proc runCode*(c: PPassContext; n: PNode): PNode =
|
||||
let c = PCtx(c)
|
||||
# don't eval errornous code:
|
||||
if c.oldErrorCount == c.m.graph.config.errorCounter:
|
||||
evalStmt(c, n)
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
else:
|
||||
result = n
|
||||
c.oldErrorCount = c.m.graph.config.errorCounter
|
||||
|
||||
type
|
||||
NirPassContext* = ref object of TPassContext
|
||||
m: ModuleCon
|
||||
c: ProcCon
|
||||
|
||||
proc openNirBackend*(g: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
var lit = Literals()
|
||||
var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
|
||||
let m = initModuleCon(g, g.config, idgen, module, nirm)
|
||||
NirPassContext(m: m, c: initProcCon(m, nil, g.config), idgen: idgen)
|
||||
|
||||
proc gen(c: NirPassContext; n: PNode) =
|
||||
let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
|
||||
let pc = genStmt(c.c, n)
|
||||
|
||||
proc nirBackend*(c: PPassContext; n: PNode): PNode =
|
||||
gen(NirPassContext(c), n)
|
||||
result = n
|
||||
|
||||
proc closeNirBackend*(c: PPassContext; finalNode: PNode) =
|
||||
discard nirBackend(c, finalNode)
|
||||
|
||||
let c = NirPassContext(c)
|
||||
let nimcache = getNimcacheDir(c.c.config).string
|
||||
createDir nimcache
|
||||
let outp = nimcache / c.m.module.name.s.addFileExt("nir")
|
||||
#c.m.nirm.code = move c.c.code
|
||||
try:
|
||||
store c.m.nirm[], outp
|
||||
echo "created: ", outp
|
||||
except IOError:
|
||||
rawMessage(c.c.config, errFatal, "serialization failed: " & outp)
|
||||
@@ -1,52 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Nir Compiler.
|
||||
|
||||
import ".." / ic / [bitabs, rodfiles]
|
||||
import nirinsts, nirtypes, nirlineinfos, nirfiles, cir
|
||||
|
||||
proc view(filename: string) =
|
||||
let m = load(filename)
|
||||
var res = ""
|
||||
allTreesToString m.code, m.lit.strings, m.lit.numbers, m.symnames, res
|
||||
res.add "\n# TYPES\n"
|
||||
nirtypes.toString res, m.types
|
||||
echo res
|
||||
|
||||
import std / [syncio, parseopt]
|
||||
|
||||
proc writeHelp =
|
||||
echo """Usage: nirc view|c <file.nir>"""
|
||||
quit 0
|
||||
|
||||
proc main =
|
||||
var inp = ""
|
||||
var cmd = ""
|
||||
for kind, key, val in getopt():
|
||||
case kind
|
||||
of cmdArgument:
|
||||
if cmd.len == 0: cmd = key
|
||||
elif inp.len == 0: inp = key
|
||||
else: quit "Error: too many arguments"
|
||||
of cmdLongOption, cmdShortOption:
|
||||
case key
|
||||
of "help", "h": writeHelp()
|
||||
of "version", "v": stdout.write "1.0\n"
|
||||
of cmdEnd: discard
|
||||
if inp.len == 0:
|
||||
quit "Error: no input file specified"
|
||||
case cmd
|
||||
of "", "view":
|
||||
view inp
|
||||
of "c":
|
||||
let outp = inp & ".c"
|
||||
cir.generateCode inp, outp
|
||||
|
||||
main()
|
||||
@@ -1,83 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import ".." / ic / [bitabs, rodfiles]
|
||||
import nirinsts, nirtypes, nirlineinfos
|
||||
|
||||
type
|
||||
NirModule* = object
|
||||
code*: Tree
|
||||
man*: LineInfoManager
|
||||
types*: TypeGraph
|
||||
lit*: Literals
|
||||
namespace*: LitId
|
||||
intbits*: uint32
|
||||
symnames*: SymNames
|
||||
|
||||
proc load*(filename: string): NirModule =
|
||||
let lit = Literals()
|
||||
result = NirModule(lit: lit, types: initTypeGraph(lit))
|
||||
var r = rodfiles.open(filename)
|
||||
try:
|
||||
r.loadHeader(nirCookie)
|
||||
r.loadSection stringsSection
|
||||
r.load result.lit.strings
|
||||
|
||||
r.loadSection numbersSection
|
||||
r.load result.lit.numbers
|
||||
|
||||
r.loadSection bodiesSection
|
||||
r.load result.code
|
||||
|
||||
r.loadSection typesSection
|
||||
r.load result.types
|
||||
|
||||
r.loadSection sideChannelSection
|
||||
r.load result.man
|
||||
|
||||
r.loadSection namespaceSection
|
||||
r.loadPrim result.namespace
|
||||
r.loadPrim result.intbits
|
||||
|
||||
r.loadSection symnamesSection
|
||||
r.load result.symnames
|
||||
|
||||
finally:
|
||||
r.close()
|
||||
|
||||
proc store*(m: NirModule; outp: string) =
|
||||
var r = rodfiles.create(outp)
|
||||
try:
|
||||
r.storeHeader(nirCookie)
|
||||
r.storeSection stringsSection
|
||||
r.store m.lit.strings
|
||||
|
||||
r.storeSection numbersSection
|
||||
r.store m.lit.numbers
|
||||
|
||||
r.storeSection bodiesSection
|
||||
r.store m.code
|
||||
|
||||
r.storeSection typesSection
|
||||
r.store m.types
|
||||
|
||||
r.storeSection sideChannelSection
|
||||
r.store m.man
|
||||
|
||||
r.storeSection namespaceSection
|
||||
r.storePrim m.namespace
|
||||
r.storePrim m.intbits
|
||||
|
||||
r.storeSection symnamesSection
|
||||
r.store m.symnames
|
||||
|
||||
finally:
|
||||
r.close()
|
||||
if r.err != ok:
|
||||
raise newException(IOError, "could store into: " & outp)
|
||||
@@ -1,582 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## NIR instructions. Somewhat inspired by LLVM's instructions.
|
||||
|
||||
import std / [assertions, hashes]
|
||||
import .. / ic / [bitabs, rodfiles]
|
||||
import nirlineinfos, nirtypes
|
||||
|
||||
const
|
||||
NirVersion = 1
|
||||
nirCookie* = [byte(0), byte('N'), byte('I'), byte('R'),
|
||||
byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(NirVersion)]
|
||||
|
||||
type
|
||||
SymId* = distinct int
|
||||
|
||||
proc `$`*(s: SymId): string {.borrow.}
|
||||
proc hash*(s: SymId): Hash {.borrow.}
|
||||
proc `==`*(a, b: SymId): bool {.borrow.}
|
||||
|
||||
type
|
||||
Opcode* = enum
|
||||
Nop,
|
||||
ImmediateVal,
|
||||
IntVal,
|
||||
StrVal,
|
||||
SymDef,
|
||||
SymUse,
|
||||
Typed, # with type ID
|
||||
PragmaId, # with Pragma ID, possible values: see PragmaKey enum
|
||||
NilVal,
|
||||
Label,
|
||||
Goto,
|
||||
CheckedGoto,
|
||||
LoopLabel,
|
||||
GotoLoop, # last atom
|
||||
|
||||
ModuleSymUse, # `"module".x`
|
||||
|
||||
ArrayConstr,
|
||||
ObjConstr,
|
||||
Ret,
|
||||
Yld,
|
||||
|
||||
Select,
|
||||
SelectPair, # ((values...), Label)
|
||||
SelectList, # (values...)
|
||||
SelectValue, # (value)
|
||||
SelectRange, # (valueA..valueB)
|
||||
ForeignDecl, # Can wrap SummonGlobal, SummonThreadLocal, SummonConst
|
||||
SummonGlobal,
|
||||
SummonThreadLocal,
|
||||
Summon, # x = Summon Typed <Type ID>; x begins to live
|
||||
SummonResult,
|
||||
SummonParam,
|
||||
SummonConst,
|
||||
Kill, # `Kill x`: scope end for `x`
|
||||
|
||||
AddrOf,
|
||||
ArrayAt, # a[i]
|
||||
DerefArrayAt, # a[i] where `a` is a PtrArray; `a[][i]`
|
||||
FieldAt, # obj.field
|
||||
DerefFieldAt, # obj[].field
|
||||
|
||||
Load, # a[]
|
||||
Store, # a[] = b
|
||||
Asgn, # a = b
|
||||
SetExc,
|
||||
TestExc,
|
||||
|
||||
CheckedRange,
|
||||
CheckedIndex,
|
||||
|
||||
Call,
|
||||
IndirectCall,
|
||||
CheckedCall, # call that can raise
|
||||
CheckedIndirectCall, # call that can raise
|
||||
CheckedAdd, # with overflow checking etc.
|
||||
CheckedSub,
|
||||
CheckedMul,
|
||||
CheckedDiv,
|
||||
CheckedMod,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
BitShl,
|
||||
BitShr,
|
||||
BitAnd,
|
||||
BitOr,
|
||||
BitXor,
|
||||
BitNot,
|
||||
BoolNot,
|
||||
Eq,
|
||||
Le,
|
||||
Lt,
|
||||
Cast,
|
||||
NumberConv,
|
||||
CheckedObjConv,
|
||||
ObjConv,
|
||||
TestOf,
|
||||
Emit,
|
||||
ProcDecl,
|
||||
ForeignProcDecl,
|
||||
PragmaPair
|
||||
|
||||
type
|
||||
PragmaKey* = enum
|
||||
FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall,
|
||||
CoreName,
|
||||
ExternName,
|
||||
HeaderImport,
|
||||
DllImport,
|
||||
DllExport,
|
||||
ObjExport
|
||||
|
||||
const
|
||||
LastAtomicValue = GotoLoop
|
||||
|
||||
OpcodeBits = 8'u32
|
||||
OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
|
||||
|
||||
ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal}
|
||||
|
||||
ValueProducing* = {
|
||||
ImmediateVal,
|
||||
IntVal,
|
||||
StrVal,
|
||||
SymUse,
|
||||
NilVal,
|
||||
ModuleSymUse,
|
||||
ArrayConstr,
|
||||
ObjConstr,
|
||||
CheckedAdd,
|
||||
CheckedSub,
|
||||
CheckedMul,
|
||||
CheckedDiv,
|
||||
CheckedMod,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
BitShl,
|
||||
BitShr,
|
||||
BitAnd,
|
||||
BitOr,
|
||||
BitXor,
|
||||
BitNot,
|
||||
BoolNot,
|
||||
Eq,
|
||||
Le,
|
||||
Lt,
|
||||
Cast,
|
||||
NumberConv,
|
||||
CheckedObjConv,
|
||||
ObjConv,
|
||||
AddrOf,
|
||||
Load,
|
||||
ArrayAt,
|
||||
DerefArrayAt,
|
||||
FieldAt,
|
||||
DerefFieldAt,
|
||||
TestOf
|
||||
}
|
||||
|
||||
type
|
||||
Instr* = object # 8 bytes
|
||||
x: uint32
|
||||
info*: PackedLineInfo
|
||||
|
||||
template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask)
|
||||
template operand(n: Instr): uint32 = (n.x shr OpcodeBits)
|
||||
|
||||
template rawOperand*(n: Instr): uint32 = (n.x shr OpcodeBits)
|
||||
|
||||
template toX(k: Opcode; operand: uint32): uint32 =
|
||||
uint32(k) or (operand shl OpcodeBits)
|
||||
|
||||
template toX(k: Opcode; operand: LitId): uint32 =
|
||||
uint32(k) or (operand.uint32 shl OpcodeBits)
|
||||
|
||||
type
|
||||
Tree* = object
|
||||
nodes: seq[Instr]
|
||||
|
||||
Values* = object
|
||||
numbers: BiTable[int64]
|
||||
strings: BiTable[string]
|
||||
|
||||
type
|
||||
PatchPos* = distinct int
|
||||
NodePos* = distinct int
|
||||
|
||||
const
|
||||
InvalidPatchPos* = PatchPos(-1)
|
||||
|
||||
proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
|
||||
|
||||
proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos =
|
||||
result = PatchPos tree.nodes.len
|
||||
tree.nodes.add Instr(x: toX(kind, 1'u32), info: info)
|
||||
|
||||
proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
|
||||
proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
|
||||
|
||||
proc patch*(tree: var Tree; pos: PatchPos) =
|
||||
let pos = pos.int
|
||||
let k = tree.nodes[pos].kind
|
||||
assert k > LastAtomicValue
|
||||
let distance = int32(tree.nodes.len - pos)
|
||||
assert distance > 0
|
||||
tree.nodes[pos].x = toX(k, cast[uint32](distance))
|
||||
|
||||
template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) =
|
||||
let pos = prepare(tree, info, kind)
|
||||
body
|
||||
patch(tree, pos)
|
||||
|
||||
template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
|
||||
let pos = prepare(tree, info, kind)
|
||||
tree.addTyped info, typ
|
||||
body
|
||||
patch(tree, pos)
|
||||
|
||||
proc len*(tree: Tree): int {.inline.} = tree.nodes.len
|
||||
|
||||
template rawSpan(n: Instr): int = int(operand(n))
|
||||
|
||||
proc nextChild(tree: Tree; pos: var int) {.inline.} =
|
||||
if tree.nodes[pos].kind > LastAtomicValue:
|
||||
assert tree.nodes[pos].operand > 0'u32
|
||||
inc pos, tree.nodes[pos].rawSpan
|
||||
else:
|
||||
inc pos
|
||||
|
||||
proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos)
|
||||
|
||||
template firstSon*(n: NodePos): NodePos = NodePos(n.int+1)
|
||||
|
||||
template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2)
|
||||
|
||||
iterator sons*(tree: Tree; n: NodePos): NodePos =
|
||||
var pos = n.int
|
||||
assert tree.nodes[pos].kind > LastAtomicValue
|
||||
let last = pos + tree.nodes[pos].rawSpan
|
||||
inc pos
|
||||
while pos < last:
|
||||
yield NodePos pos
|
||||
nextChild tree, pos
|
||||
|
||||
iterator sonsFrom1*(tree: Tree; n: NodePos): NodePos =
|
||||
var pos = n.int
|
||||
assert tree.nodes[pos].kind > LastAtomicValue
|
||||
let last = pos + tree.nodes[pos].rawSpan
|
||||
inc pos
|
||||
nextChild tree, pos
|
||||
while pos < last:
|
||||
yield NodePos pos
|
||||
nextChild tree, pos
|
||||
|
||||
iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos =
|
||||
var pos = n.int
|
||||
assert tree.nodes[pos].kind > LastAtomicValue
|
||||
let last = pos + tree.nodes[pos].rawSpan
|
||||
inc pos
|
||||
for i in 1..toSkip:
|
||||
nextChild tree, pos
|
||||
while pos < last:
|
||||
yield NodePos pos
|
||||
nextChild tree, pos
|
||||
|
||||
template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int]
|
||||
|
||||
iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos =
|
||||
var pos = n.int
|
||||
assert tree[parent].kind > LastAtomicValue
|
||||
let last = parent.int + tree[parent].rawSpan
|
||||
while pos < last:
|
||||
yield NodePos pos
|
||||
nextChild tree, pos
|
||||
|
||||
proc span(tree: Tree; pos: int): int {.inline.} =
|
||||
if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
|
||||
|
||||
proc copyTree*(dest: var Tree; src: Tree) =
|
||||
let pos = 0
|
||||
let L = span(src, pos)
|
||||
let d = dest.nodes.len
|
||||
dest.nodes.setLen(d + L)
|
||||
assert L > 0
|
||||
for i in 0..<L:
|
||||
dest.nodes[d+i] = src.nodes[pos+i]
|
||||
|
||||
proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) {.inline.} =
|
||||
assert(not isAtom(tree, n.int))
|
||||
let a = n.int+1
|
||||
let b = a + span(tree, a)
|
||||
result = (NodePos a, NodePos b)
|
||||
|
||||
proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) {.inline.} =
|
||||
assert(not isAtom(tree, n.int))
|
||||
let a = n.int+1
|
||||
let b = a + span(tree, a)
|
||||
let c = b + span(tree, b)
|
||||
result = (NodePos a, NodePos b, NodePos c)
|
||||
|
||||
proc sons4*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos) {.inline.} =
|
||||
assert(not isAtom(tree, n.int))
|
||||
let a = n.int+1
|
||||
let b = a + span(tree, a)
|
||||
let c = b + span(tree, b)
|
||||
let d = c + span(tree, c)
|
||||
result = (NodePos a, NodePos b, NodePos c, NodePos d)
|
||||
|
||||
proc sons5*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos, NodePos) {.inline.} =
|
||||
assert(not isAtom(tree, n.int))
|
||||
let a = n.int+1
|
||||
let b = a + span(tree, a)
|
||||
let c = b + span(tree, b)
|
||||
let d = c + span(tree, c)
|
||||
let e = d + span(tree, d)
|
||||
result = (NodePos a, NodePos b, NodePos c, NodePos d, NodePos e)
|
||||
|
||||
proc typeId*(ins: Instr): TypeId {.inline.} =
|
||||
assert ins.kind == Typed
|
||||
result = TypeId(ins.operand)
|
||||
|
||||
proc symId*(ins: Instr): SymId {.inline.} =
|
||||
assert ins.kind in {SymUse, SymDef}
|
||||
result = SymId(ins.operand)
|
||||
|
||||
proc immediateVal*(ins: Instr): int {.inline.} =
|
||||
assert ins.kind == ImmediateVal
|
||||
result = cast[int](ins.operand)
|
||||
|
||||
proc litId*(ins: Instr): LitId {.inline.} =
|
||||
assert ins.kind in {StrVal, IntVal}
|
||||
result = LitId(ins.operand)
|
||||
|
||||
|
||||
type
|
||||
LabelId* = distinct int
|
||||
|
||||
proc `==`*(a, b: LabelId): bool {.borrow.}
|
||||
proc hash*(a: LabelId): Hash {.borrow.}
|
||||
|
||||
proc label*(ins: Instr): LabelId {.inline.} =
|
||||
assert ins.kind in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
|
||||
result = LabelId(ins.operand)
|
||||
|
||||
proc newLabel*(labelGen: var int): LabelId {.inline.} =
|
||||
result = LabelId labelGen
|
||||
inc labelGen
|
||||
|
||||
proc newLabels*(labelGen: var int; n: int): LabelId {.inline.} =
|
||||
result = LabelId labelGen
|
||||
inc labelGen, n
|
||||
|
||||
proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId =
|
||||
assert k in {Label, LoopLabel}
|
||||
result = LabelId labelGen
|
||||
t.nodes.add Instr(x: toX(k, uint32(result)), info: info)
|
||||
inc labelGen
|
||||
|
||||
proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) =
|
||||
assert k in {Goto, GotoLoop, CheckedGoto}
|
||||
t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
|
||||
|
||||
proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} =
|
||||
assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
|
||||
t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
|
||||
|
||||
proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
|
||||
t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info)
|
||||
|
||||
proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
|
||||
t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
|
||||
|
||||
proc addNop*(t: var Tree; info: PackedLineInfo) {.inline.} =
|
||||
t.nodes.add Instr(x: toX(Nop, 0'u32), info: info)
|
||||
|
||||
proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} =
|
||||
assert typ.int >= 0
|
||||
t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
|
||||
|
||||
proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} =
|
||||
assert typ.int >= 0
|
||||
assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam, SummonResult}
|
||||
let x = prepare(t, info, opc)
|
||||
t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
|
||||
t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
|
||||
patch t, x
|
||||
|
||||
proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) =
|
||||
assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
|
||||
t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
|
||||
|
||||
proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) =
|
||||
t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info)
|
||||
|
||||
proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
|
||||
buildTyped t, info, NumberConv, typ:
|
||||
t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info)
|
||||
|
||||
proc boolVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; b: bool) =
|
||||
buildTyped t, info, NumberConv, Bool8Id:
|
||||
t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(ord b))), info: info)
|
||||
|
||||
proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
|
||||
t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info)
|
||||
|
||||
proc addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) =
|
||||
t.nodes.add Instr(x: toX(StrVal, uint32(s)), info: info)
|
||||
|
||||
proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) =
|
||||
buildTyped t, info, NumberConv, typ:
|
||||
t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info)
|
||||
|
||||
proc store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes
|
||||
proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes
|
||||
|
||||
proc escapeToNimLit*(s: string; result: var string) =
|
||||
result.add '"'
|
||||
for c in items s:
|
||||
if c < ' ' or int(c) >= 128:
|
||||
result.add '\\'
|
||||
result.addInt int(c)
|
||||
elif c == '\\':
|
||||
result.add r"\\"
|
||||
elif c == '\n':
|
||||
result.add r"\n"
|
||||
elif c == '\r':
|
||||
result.add r"\r"
|
||||
elif c == '\t':
|
||||
result.add r"\t"
|
||||
else:
|
||||
result.add c
|
||||
result.add '"'
|
||||
|
||||
type
|
||||
SymNames* = object
|
||||
s: seq[LitId]
|
||||
|
||||
proc `[]=`*(t: var SymNames; key: SymId; val: LitId) =
|
||||
let k = int(key)
|
||||
if k >= t.s.len: t.s.setLen k+1
|
||||
t.s[k] = val
|
||||
|
||||
proc `[]`*(t: SymNames; key: SymId): LitId =
|
||||
let k = int(key)
|
||||
if k < t.s.len: result = t.s[k]
|
||||
else: result = LitId(0)
|
||||
|
||||
template localName(s: SymId): string =
|
||||
let name = names[s]
|
||||
if name != LitId(0):
|
||||
strings[name]
|
||||
else:
|
||||
$s.int
|
||||
|
||||
proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s)
|
||||
proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s)
|
||||
|
||||
proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64];
|
||||
names: SymNames;
|
||||
r: var string; nesting = 0) =
|
||||
if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}:
|
||||
r.add ' '
|
||||
|
||||
case t[pos].kind
|
||||
of Nop: r.add "Nop"
|
||||
of ImmediateVal:
|
||||
r.add $t[pos].operand
|
||||
of IntVal:
|
||||
r.add "IntVal "
|
||||
r.add $integers[LitId t[pos].operand]
|
||||
of StrVal:
|
||||
escapeToNimLit(strings[LitId t[pos].operand], r)
|
||||
of SymDef:
|
||||
r.add "SymDef "
|
||||
r.add localName(SymId t[pos].operand)
|
||||
of SymUse:
|
||||
r.add "SymUse "
|
||||
r.add localName(SymId t[pos].operand)
|
||||
of PragmaId:
|
||||
r.add $cast[PragmaKey](t[pos].operand)
|
||||
of Typed:
|
||||
r.add "T<"
|
||||
r.add $t[pos].operand
|
||||
r.add ">"
|
||||
of NilVal:
|
||||
r.add "NilVal"
|
||||
of Label:
|
||||
# undo the nesting:
|
||||
var spaces = r.len-1
|
||||
while spaces >= 0 and r[spaces] == ' ': dec spaces
|
||||
r.setLen spaces+1
|
||||
r.add "\n L"
|
||||
r.add $t[pos].operand
|
||||
of Goto, CheckedGoto, LoopLabel, GotoLoop:
|
||||
r.add $t[pos].kind
|
||||
r.add " L"
|
||||
r.add $t[pos].operand
|
||||
else:
|
||||
r.add $t[pos].kind
|
||||
r.add "{\n"
|
||||
for i in 0..<(nesting+1)*2: r.add ' '
|
||||
for p in sons(t, pos):
|
||||
toString t, p, strings, integers, names, r, nesting+1
|
||||
r.add "\n"
|
||||
for i in 0..<nesting*2: r.add ' '
|
||||
r.add "}"
|
||||
|
||||
proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64];
|
||||
names: SymNames;
|
||||
r: var string) =
|
||||
var i = 0
|
||||
while i < t.len:
|
||||
toString t, NodePos(i), strings, integers, names, r
|
||||
nextChild t, i
|
||||
|
||||
type
|
||||
Value* = distinct Tree
|
||||
|
||||
proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} =
|
||||
assert k in ValueProducing - ValueProducingAtoms
|
||||
result = prepare(Tree(dest), info, k)
|
||||
|
||||
proc patch*(dest: var Value; pos: PatchPos) {.inline.} =
|
||||
patch(Tree(dest), pos)
|
||||
|
||||
proc localToValue*(info: PackedLineInfo; s: SymId): Value =
|
||||
result = Value(Tree())
|
||||
Tree(result).addSymUse info, s
|
||||
|
||||
proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0
|
||||
|
||||
proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0
|
||||
|
||||
proc extractTemp*(v: Value): SymId =
|
||||
if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse:
|
||||
result = SymId(Tree(v)[NodePos 0].operand)
|
||||
else:
|
||||
result = SymId(-1)
|
||||
|
||||
proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src)
|
||||
|
||||
proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) =
|
||||
assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
|
||||
Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
|
||||
|
||||
template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) =
|
||||
let pos = prepare(Tree(tree), info, kind)
|
||||
body
|
||||
patch(tree, pos)
|
||||
|
||||
proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} =
|
||||
addTyped(Tree(t), info, typ)
|
||||
|
||||
template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
|
||||
let pos = prepare(tree, info, kind)
|
||||
tree.addTyped info, typ
|
||||
body
|
||||
patch(tree, pos)
|
||||
|
||||
proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
|
||||
addStrVal(Tree(t), strings, info, s)
|
||||
|
||||
proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) =
|
||||
addNilVal Tree(t), info, typ
|
||||
|
||||
proc addIntVal*(t: var Value; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
|
||||
addIntVal Tree(t), integers, info, typ, x
|
||||
@@ -1,104 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Management of slots. Similar to "register allocation"
|
||||
## in lower level languages.
|
||||
|
||||
import std / [assertions, tables]
|
||||
import nirtypes, nirinsts
|
||||
|
||||
type
|
||||
SlotManagerFlag* = enum
|
||||
ReuseTemps,
|
||||
ReuseVars
|
||||
SlotKind* = enum
|
||||
Temp, Perm
|
||||
SlotManager* = object # "register allocator"
|
||||
live: Table[SymId, (SlotKind, TypeId)]
|
||||
dead: Table[TypeId, seq[SymId]]
|
||||
flags: set[SlotManagerFlag]
|
||||
inScope: seq[SymId]
|
||||
|
||||
proc initSlotManager*(flags: set[SlotManagerFlag]): SlotManager {.inline.} =
|
||||
SlotManager(flags: flags)
|
||||
|
||||
proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag; k: SlotKind;
|
||||
symIdgen: var int32): SymId {.inline.} =
|
||||
if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0:
|
||||
result = m.dead[t].pop()
|
||||
else:
|
||||
inc symIdgen
|
||||
result = SymId(symIdgen)
|
||||
m.inScope.add result
|
||||
m.live[result] = (k, t)
|
||||
|
||||
proc allocTemp*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} =
|
||||
result = allocRaw(m, t, ReuseTemps, Temp, symIdgen)
|
||||
|
||||
proc allocVar*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} =
|
||||
result = allocRaw(m, t, ReuseVars, Perm, symIdgen)
|
||||
|
||||
proc freeLoc*(m: var SlotManager; s: SymId) =
|
||||
let t = m.live.getOrDefault(s)
|
||||
assert t[1].int != 0
|
||||
m.live.del s
|
||||
m.dead.mgetOrPut(t[1], @[]).add s
|
||||
|
||||
proc freeTemp*(m: var SlotManager; s: SymId) =
|
||||
let t = m.live.getOrDefault(s)
|
||||
if t[1].int != 0 and t[0] == Temp:
|
||||
m.live.del s
|
||||
m.dead.mgetOrPut(t[1], @[]).add s
|
||||
|
||||
iterator stillAlive*(m: SlotManager): (SymId, TypeId) =
|
||||
for k, v in pairs(m.live):
|
||||
yield (k, v[1])
|
||||
|
||||
proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s][1]
|
||||
|
||||
proc openScope*(m: var SlotManager) =
|
||||
m.inScope.add SymId(-1) # add marker
|
||||
|
||||
proc closeScope*(m: var SlotManager) =
|
||||
var i = m.inScope.len - 1
|
||||
while i >= 0:
|
||||
if m.inScope[i] == SymId(-1):
|
||||
m.inScope.setLen i
|
||||
break
|
||||
dec i
|
||||
|
||||
when isMainModule:
|
||||
var symIdgen: int32
|
||||
var m = initSlotManager({ReuseTemps})
|
||||
|
||||
var g = initTypeGraph(Literals())
|
||||
|
||||
let a = g.openType ArrayTy
|
||||
g.addBuiltinType Int8Id
|
||||
g.addArrayLen 5
|
||||
let finalArrayType = finishType(g, a)
|
||||
|
||||
let obj = g.openType ObjectDecl
|
||||
g.addName "MyType"
|
||||
|
||||
g.addField "p", finalArrayType, 0
|
||||
let objB = finishType(g, obj)
|
||||
|
||||
let x = m.allocTemp(objB, symIdgen)
|
||||
assert x.int == 0
|
||||
|
||||
let y = m.allocTemp(objB, symIdgen)
|
||||
assert y.int == 1
|
||||
|
||||
let z = m.allocTemp(Int8Id, symIdgen)
|
||||
assert z.int == 2
|
||||
|
||||
m.freeLoc y
|
||||
let y2 = m.allocTemp(objB, symIdgen)
|
||||
assert y2.int == 1
|
||||
@@ -1,475 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Type system for NIR. Close to C's type system but without its quirks.
|
||||
|
||||
import std / [assertions, hashes]
|
||||
import .. / ic / [bitabs, rodfiles]
|
||||
|
||||
type
|
||||
NirTypeKind* = enum
|
||||
VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal,
|
||||
IntVal, SizeVal, AlignVal, OffsetVal,
|
||||
AnnotationVal,
|
||||
ObjectTy,
|
||||
UnionTy,
|
||||
VarargsTy, # the `...` in a C prototype; also the last "atom"
|
||||
APtrTy, # pointer to aliasable memory
|
||||
UPtrTy, # pointer to unique/unaliasable memory
|
||||
AArrayPtrTy, # pointer to array of aliasable memory
|
||||
UArrayPtrTy, # pointer to array of unique/unaliasable memory
|
||||
ArrayTy,
|
||||
LastArrayTy, # array of unspecified size as a last field inside an object
|
||||
ProcTy,
|
||||
ObjectDecl,
|
||||
UnionDecl,
|
||||
FieldDecl
|
||||
|
||||
const
|
||||
TypeKindBits = 8'u32
|
||||
TypeKindMask = (1'u32 shl TypeKindBits) - 1'u32
|
||||
|
||||
type
|
||||
TypeNode* = object # 4 bytes
|
||||
x: uint32
|
||||
|
||||
template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask)
|
||||
template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits)
|
||||
|
||||
proc integralBits*(n: TypeNode): int {.inline.} =
|
||||
# Number of bits in the IntTy, etc. Only valid for integral types.
|
||||
assert n.kind in {IntTy, UIntTy, FloatTy, BoolTy, CharTy}
|
||||
result = int(n.operand)
|
||||
|
||||
template toX(k: NirTypeKind; operand: uint32): uint32 =
|
||||
uint32(k) or (operand shl TypeKindBits)
|
||||
|
||||
template toX(k: NirTypeKind; operand: LitId): uint32 =
|
||||
uint32(k) or (operand.uint32 shl TypeKindBits)
|
||||
|
||||
type
|
||||
TypeId* = distinct int
|
||||
|
||||
proc `==`*(a, b: TypeId): bool {.borrow.}
|
||||
proc hash*(a: TypeId): Hash {.borrow.}
|
||||
|
||||
type
|
||||
Literals* = ref object
|
||||
strings*: BiTable[string]
|
||||
numbers*: BiTable[int64]
|
||||
|
||||
TypeGraph* = object
|
||||
nodes: seq[TypeNode]
|
||||
lit: Literals
|
||||
|
||||
const
|
||||
VoidId* = TypeId 0
|
||||
Bool8Id* = TypeId 1
|
||||
Char8Id* = TypeId 2
|
||||
Int8Id* = TypeId 3
|
||||
Int16Id* = TypeId 4
|
||||
Int32Id* = TypeId 5
|
||||
Int64Id* = TypeId 6
|
||||
UInt8Id* = TypeId 7
|
||||
UInt16Id* = TypeId 8
|
||||
UInt32Id* = TypeId 9
|
||||
UInt64Id* = TypeId 10
|
||||
Float32Id* = TypeId 11
|
||||
Float64Id* = TypeId 12
|
||||
VoidPtrId* = TypeId 13
|
||||
LastBuiltinId* = 13
|
||||
|
||||
proc initTypeGraph*(lit: Literals): TypeGraph =
|
||||
result = TypeGraph(nodes: @[
|
||||
TypeNode(x: toX(VoidTy, 0'u32)),
|
||||
TypeNode(x: toX(BoolTy, 8'u32)),
|
||||
TypeNode(x: toX(CharTy, 8'u32)),
|
||||
TypeNode(x: toX(IntTy, 8'u32)),
|
||||
TypeNode(x: toX(IntTy, 16'u32)),
|
||||
TypeNode(x: toX(IntTy, 32'u32)),
|
||||
TypeNode(x: toX(IntTy, 64'u32)),
|
||||
TypeNode(x: toX(UIntTy, 8'u32)),
|
||||
TypeNode(x: toX(UIntTy, 16'u32)),
|
||||
TypeNode(x: toX(UIntTy, 32'u32)),
|
||||
TypeNode(x: toX(UIntTy, 64'u32)),
|
||||
TypeNode(x: toX(FloatTy, 32'u32)),
|
||||
TypeNode(x: toX(FloatTy, 64'u32)),
|
||||
TypeNode(x: toX(APtrTy, 2'u32)),
|
||||
TypeNode(x: toX(VoidTy, 0'u32))
|
||||
], lit: lit)
|
||||
assert result.nodes.len == LastBuiltinId+2
|
||||
|
||||
type
|
||||
TypePatchPos* = distinct int
|
||||
|
||||
const
|
||||
InvalidTypePatchPos* = TypePatchPos(-1)
|
||||
LastAtomicValue = VarargsTy
|
||||
|
||||
proc isValid(p: TypePatchPos): bool {.inline.} = p.int != -1
|
||||
|
||||
proc prepare(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
|
||||
result = TypePatchPos tree.nodes.len
|
||||
tree.nodes.add TypeNode(x: toX(kind, 1'u32))
|
||||
|
||||
proc isAtom(tree: TypeGraph; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
|
||||
proc isAtom(tree: TypeGraph; pos: TypeId): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
|
||||
|
||||
proc patch(tree: var TypeGraph; pos: TypePatchPos) =
|
||||
let pos = pos.int
|
||||
let k = tree.nodes[pos].kind
|
||||
assert k > LastAtomicValue
|
||||
let distance = int32(tree.nodes.len - pos)
|
||||
assert distance > 0
|
||||
tree.nodes[pos].x = toX(k, cast[uint32](distance))
|
||||
|
||||
proc len*(tree: TypeGraph): int {.inline.} = tree.nodes.len
|
||||
|
||||
template rawSpan(n: TypeNode): int = int(operand(n))
|
||||
|
||||
proc nextChild(tree: TypeGraph; pos: var int) {.inline.} =
|
||||
if tree.nodes[pos].kind > LastAtomicValue:
|
||||
assert tree.nodes[pos].operand > 0'u32
|
||||
inc pos, tree.nodes[pos].rawSpan
|
||||
else:
|
||||
inc pos
|
||||
|
||||
iterator sons*(tree: TypeGraph; n: TypeId): TypeId =
|
||||
var pos = n.int
|
||||
assert tree.nodes[pos].kind > LastAtomicValue
|
||||
let last = pos + tree.nodes[pos].rawSpan
|
||||
inc pos
|
||||
while pos < last:
|
||||
yield TypeId pos
|
||||
nextChild tree, pos
|
||||
|
||||
template `[]`*(t: TypeGraph; n: TypeId): TypeNode = t.nodes[n.int]
|
||||
|
||||
proc elementType*(tree: TypeGraph; n: TypeId): TypeId {.inline.} =
|
||||
assert tree[n].kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ArrayTy, LastArrayTy}
|
||||
result = TypeId(n.int+1)
|
||||
|
||||
proc litId*(n: TypeNode): LitId {.inline.} =
|
||||
assert n.kind in {NameVal, IntVal, SizeVal, AlignVal, OffsetVal, AnnotationVal, ObjectTy, UnionTy}
|
||||
result = LitId(n.operand)
|
||||
|
||||
proc kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind
|
||||
|
||||
proc span(tree: TypeGraph; pos: int): int {.inline.} =
|
||||
if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
|
||||
|
||||
proc sons2(tree: TypeGraph; n: TypeId): (TypeId, TypeId) =
|
||||
assert(not isAtom(tree, n.int))
|
||||
let a = n.int+1
|
||||
let b = a + span(tree, a)
|
||||
result = (TypeId a, TypeId b)
|
||||
|
||||
proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) =
|
||||
assert(not isAtom(tree, n.int))
|
||||
let a = n.int+1
|
||||
let b = a + span(tree, a)
|
||||
let c = b + span(tree, b)
|
||||
result = (TypeId a, TypeId b, TypeId c)
|
||||
|
||||
proc arrayName*(tree: TypeGraph; n: TypeId): TypeId {.inline.} =
|
||||
assert tree[n].kind == ArrayTy
|
||||
let (_, _, c) = sons3(tree, n)
|
||||
result = c
|
||||
|
||||
proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt =
|
||||
assert tree[n].kind == ArrayTy
|
||||
let (_, b) = sons2(tree, n)
|
||||
result = tree.lit.numbers[LitId tree[b].operand]
|
||||
|
||||
proc returnType*(tree: TypeGraph; n: TypeId): (TypeId, TypeId) =
|
||||
# Returns the positions of the return type + calling convention.
|
||||
var pos = n.int
|
||||
assert tree.nodes[pos].kind == ProcTy
|
||||
let a = n.int+1
|
||||
let b = a + span(tree, a)
|
||||
result = (TypeId b, TypeId a) # not a typo, order is reversed
|
||||
|
||||
iterator params*(tree: TypeGraph; n: TypeId): TypeId =
|
||||
var pos = n.int
|
||||
assert tree.nodes[pos].kind == ProcTy
|
||||
let last = pos + tree.nodes[pos].rawSpan
|
||||
inc pos
|
||||
nextChild tree, pos
|
||||
nextChild tree, pos
|
||||
while pos < last:
|
||||
yield TypeId pos
|
||||
nextChild tree, pos
|
||||
|
||||
proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
|
||||
assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy,
|
||||
ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl,
|
||||
FieldDecl}
|
||||
result = prepare(tree, kind)
|
||||
|
||||
template typeInvariant(p: TypePatchPos) =
|
||||
when false:
|
||||
if tree[TypeId(p)].kind == FieldDecl:
|
||||
var k = 0
|
||||
for ch in sons(tree, TypeId(p)):
|
||||
inc k
|
||||
assert k > 2, "damn! " & $k
|
||||
|
||||
proc sealType*(tree: var TypeGraph; p: TypePatchPos) =
|
||||
patch tree, p
|
||||
typeInvariant(p)
|
||||
|
||||
proc finishType*(tree: var TypeGraph; p: TypePatchPos): TypeId =
|
||||
# Search for an existing instance of this type in
|
||||
# order to reduce memory consumption:
|
||||
patch tree, p
|
||||
typeInvariant(p)
|
||||
|
||||
let s = span(tree, p.int)
|
||||
var i = 0
|
||||
while i < p.int:
|
||||
if tree.nodes[i].x == tree.nodes[p.int].x:
|
||||
var isMatch = true
|
||||
for j in 1..<s:
|
||||
if tree.nodes[j+i].x == tree.nodes[j+p.int].x:
|
||||
discard "still a match"
|
||||
else:
|
||||
isMatch = false
|
||||
break
|
||||
if isMatch:
|
||||
if p.int+s == tree.len:
|
||||
setLen tree.nodes, p.int
|
||||
return TypeId(i)
|
||||
nextChild tree, i
|
||||
result = TypeId(p)
|
||||
|
||||
proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId =
|
||||
assert kind in {ObjectTy, UnionTy}
|
||||
let content = TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
|
||||
for i in 0..<tree.len:
|
||||
if tree.nodes[i].x == content.x:
|
||||
return TypeId(i)
|
||||
result = TypeId tree.nodes.len
|
||||
tree.nodes.add content
|
||||
|
||||
proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) =
|
||||
assert kind in {ObjectTy, UnionTy}
|
||||
tree.nodes.add TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
|
||||
|
||||
proc getTypeTag*(tree: TypeGraph; t: TypeId): string =
|
||||
assert tree[t].kind in {ObjectTy, UnionTy}
|
||||
result = tree.lit.strings[LitId tree[t].operand]
|
||||
|
||||
proc addVarargs*(tree: var TypeGraph) =
|
||||
tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32))
|
||||
|
||||
proc getFloat128Type*(tree: var TypeGraph): TypeId =
|
||||
result = TypeId tree.nodes.len
|
||||
tree.nodes.add TypeNode(x: toX(FloatTy, 128'u32))
|
||||
|
||||
proc addBuiltinType*(g: var TypeGraph; id: TypeId) =
|
||||
g.nodes.add g[id]
|
||||
|
||||
template firstSon*(n: TypeId): TypeId = TypeId(n.int+1)
|
||||
|
||||
proc addType*(g: var TypeGraph; t: TypeId) =
|
||||
# We cannot simply copy `*Decl` nodes. We have to introduce `*Ty` nodes instead:
|
||||
if g[t].kind in {ObjectDecl, UnionDecl}:
|
||||
assert g[t.firstSon].kind == NameVal
|
||||
let name = LitId g[t.firstSon].operand
|
||||
if g[t].kind == ObjectDecl:
|
||||
g.nodes.add TypeNode(x: toX(ObjectTy, name))
|
||||
else:
|
||||
g.nodes.add TypeNode(x: toX(UnionTy, name))
|
||||
else:
|
||||
let pos = t.int
|
||||
let L = span(g, pos)
|
||||
let d = g.nodes.len
|
||||
g.nodes.setLen(d + L)
|
||||
assert L > 0
|
||||
for i in 0..<L:
|
||||
g.nodes[d+i] = g.nodes[pos+i]
|
||||
|
||||
proc addArrayLen*(g: var TypeGraph; len: int64) =
|
||||
g.nodes.add TypeNode(x: toX(IntVal, g.lit.numbers.getOrIncl(len)))
|
||||
|
||||
proc addSize*(g: var TypeGraph; s: int64) =
|
||||
g.nodes.add TypeNode(x: toX(SizeVal, g.lit.numbers.getOrIncl(s)))
|
||||
|
||||
proc addOffset*(g: var TypeGraph; offset: int64) =
|
||||
g.nodes.add TypeNode(x: toX(OffsetVal, g.lit.numbers.getOrIncl(offset)))
|
||||
|
||||
proc addAlign*(g: var TypeGraph; a: int64) =
|
||||
g.nodes.add TypeNode(x: toX(AlignVal, g.lit.numbers.getOrIncl(a)))
|
||||
|
||||
proc addName*(g: var TypeGraph; name: string) =
|
||||
g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
|
||||
|
||||
proc addAnnotation*(g: var TypeGraph; name: string) =
|
||||
g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
|
||||
|
||||
proc addField*(g: var TypeGraph; name: string; typ: TypeId; offset: int64) =
|
||||
let f = g.openType FieldDecl
|
||||
g.addType typ
|
||||
g.addOffset offset
|
||||
g.addName name
|
||||
sealType(g, f)
|
||||
|
||||
proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
|
||||
let f = g.openType APtrTy
|
||||
g.addType t
|
||||
result = finishType(g, f)
|
||||
|
||||
proc arrayPtrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
|
||||
let f = g.openType AArrayPtrTy
|
||||
g.addType t
|
||||
result = finishType(g, f)
|
||||
|
||||
proc store*(r: var RodFile; g: TypeGraph) =
|
||||
storeSeq r, g.nodes
|
||||
|
||||
proc load*(r: var RodFile; g: var TypeGraph) =
|
||||
loadSeq r, g.nodes
|
||||
|
||||
proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
|
||||
case g[i].kind
|
||||
of VoidTy: dest.add "void"
|
||||
of IntTy:
|
||||
dest.add "i"
|
||||
dest.addInt g[i].operand
|
||||
of UIntTy:
|
||||
dest.add "u"
|
||||
dest.addInt g[i].operand
|
||||
of FloatTy:
|
||||
dest.add "f"
|
||||
dest.addInt g[i].operand
|
||||
of BoolTy:
|
||||
dest.add "b"
|
||||
dest.addInt g[i].operand
|
||||
of CharTy:
|
||||
dest.add "c"
|
||||
dest.addInt g[i].operand
|
||||
of NameVal, AnnotationVal:
|
||||
dest.add g.lit.strings[LitId g[i].operand]
|
||||
of IntVal, SizeVal, AlignVal, OffsetVal:
|
||||
dest.add $g[i].kind
|
||||
dest.add ' '
|
||||
dest.add $g.lit.numbers[LitId g[i].operand]
|
||||
of VarargsTy:
|
||||
dest.add "..."
|
||||
of APtrTy:
|
||||
dest.add "aptr["
|
||||
toString(dest, g, g.elementType(i))
|
||||
dest.add "]"
|
||||
of UPtrTy:
|
||||
dest.add "uptr["
|
||||
toString(dest, g, g.elementType(i))
|
||||
dest.add "]"
|
||||
of AArrayPtrTy:
|
||||
dest.add "aArrayPtr["
|
||||
toString(dest, g, g.elementType(i))
|
||||
dest.add "]"
|
||||
of UArrayPtrTy:
|
||||
dest.add "uArrayPtr["
|
||||
toString(dest, g, g.elementType(i))
|
||||
dest.add "]"
|
||||
of ArrayTy:
|
||||
dest.add "Array["
|
||||
let (elems, len, name) = g.sons3(i)
|
||||
toString(dest, g, elems)
|
||||
dest.add ", "
|
||||
toString(dest, g, len)
|
||||
dest.add ", "
|
||||
toString(dest, g, name)
|
||||
dest.add "]"
|
||||
of LastArrayTy:
|
||||
# array of unspecified size as a last field inside an object
|
||||
dest.add "LastArrayTy["
|
||||
toString(dest, g, g.elementType(i))
|
||||
dest.add "]"
|
||||
of ObjectTy:
|
||||
dest.add "object "
|
||||
dest.add g.lit.strings[LitId g[i].operand]
|
||||
of UnionTy:
|
||||
dest.add "union "
|
||||
dest.add g.lit.strings[LitId g[i].operand]
|
||||
of ProcTy:
|
||||
dest.add "proc["
|
||||
for t in sons(g, i):
|
||||
dest.add ' '
|
||||
toString(dest, g, t)
|
||||
dest.add "]"
|
||||
of ObjectDecl:
|
||||
dest.add "object["
|
||||
for t in sons(g, i):
|
||||
toString(dest, g, t)
|
||||
dest.add '\n'
|
||||
dest.add "]"
|
||||
of UnionDecl:
|
||||
dest.add "union["
|
||||
for t in sons(g, i):
|
||||
toString(dest, g, t)
|
||||
dest.add '\n'
|
||||
dest.add "]"
|
||||
of FieldDecl:
|
||||
dest.add "field["
|
||||
for t in sons(g, i):
|
||||
toString(dest, g, t)
|
||||
dest.add ' '
|
||||
dest.add "]"
|
||||
|
||||
when false:
|
||||
let (typ, offset, name) = g.sons3(i)
|
||||
toString(dest, g, typ)
|
||||
dest.add ' '
|
||||
toString(dest, g, offset)
|
||||
dest.add ' '
|
||||
toString(dest, g, name)
|
||||
|
||||
proc toString*(dest: var string; g: TypeGraph) =
|
||||
var i = 0
|
||||
while i < g.len:
|
||||
dest.add "T<"
|
||||
dest.addInt i
|
||||
dest.add "> "
|
||||
toString(dest, g, TypeId i)
|
||||
dest.add '\n'
|
||||
nextChild g, i
|
||||
|
||||
iterator allTypes*(g: TypeGraph; start = 0): TypeId =
|
||||
var i = start
|
||||
while i < g.len:
|
||||
yield TypeId i
|
||||
nextChild g, i
|
||||
|
||||
iterator allTypesIncludingInner*(g: TypeGraph; start = 0): TypeId =
|
||||
var i = start
|
||||
while i < g.len:
|
||||
yield TypeId i
|
||||
inc i
|
||||
|
||||
proc `$`(g: TypeGraph): string =
|
||||
result = ""
|
||||
toString(result, g)
|
||||
|
||||
when isMainModule:
|
||||
var g = initTypeGraph(Literals())
|
||||
|
||||
let a = g.openType ArrayTy
|
||||
g.addBuiltinType Int8Id
|
||||
g.addArrayLen 5
|
||||
g.addName "SomeArray"
|
||||
let finalArrayType = finishType(g, a)
|
||||
|
||||
let obj = g.openType ObjectDecl
|
||||
g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl("MyType")))
|
||||
|
||||
g.addField "p", finalArrayType, 0
|
||||
sealType(g, obj)
|
||||
|
||||
echo g
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,200 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## included from ast2ir.nim
|
||||
|
||||
#[
|
||||
|
||||
case s
|
||||
of "abc", "abbd":
|
||||
echo 1
|
||||
of "hah":
|
||||
echo 2
|
||||
of "gah":
|
||||
echo 3
|
||||
|
||||
# we produce code like this:
|
||||
|
||||
if s[0] <= 'a':
|
||||
if s == "abc: goto L1
|
||||
elif s == "abbd": goto L1
|
||||
else:
|
||||
if s[2] <= 'h':
|
||||
if s == "hah": goto L2
|
||||
elif s == "gah": goto L3
|
||||
goto afterCase
|
||||
|
||||
L1:
|
||||
echo 1
|
||||
goto afterCase
|
||||
L2:
|
||||
echo 2
|
||||
goto afterCase
|
||||
L3:
|
||||
echo 3
|
||||
goto afterCase
|
||||
|
||||
afterCase: ...
|
||||
|
||||
]#
|
||||
|
||||
# We split the set of strings into 2 sets of roughly the same size.
|
||||
# The condition used for splitting is a (position, char) tuple.
|
||||
# Every string of length > position for which s[position] <= char is in one
|
||||
# set else it is in the other set.
|
||||
|
||||
from std/sequtils import addUnique
|
||||
|
||||
type
|
||||
Key = (LitId, LabelId)
|
||||
|
||||
proc splitValue(strings: BiTable[string]; a: openArray[Key]; position: int): (char, float) =
|
||||
var cand: seq[char] = @[]
|
||||
for t in items a:
|
||||
let s = strings[t[0]]
|
||||
if s.len > position: cand.addUnique s[position]
|
||||
|
||||
result = ('\0', -1.0)
|
||||
for disc in items cand:
|
||||
var hits = 0
|
||||
for t in items a:
|
||||
let s = strings[t[0]]
|
||||
if s.len > position and s[position] <= disc:
|
||||
inc hits
|
||||
# the split is the better, the more `hits` is close to `a.len / 2`:
|
||||
let grade = 100000.0 - abs(hits.float - a.len.float / 2.0)
|
||||
if grade > result[1]:
|
||||
result = (disc, grade)
|
||||
|
||||
proc tryAllPositions(strings: BiTable[string]; a: openArray[Key]): (char, int) =
|
||||
var m = 0
|
||||
for t in items a:
|
||||
m = max(m, strings[t[0]].len)
|
||||
|
||||
result = ('\0', -1)
|
||||
var best = -1.0
|
||||
for i in 0 ..< m:
|
||||
let current = splitValue(strings, a, i)
|
||||
if current[1] > best:
|
||||
best = current[1]
|
||||
result = (current[0], i)
|
||||
|
||||
type
|
||||
SearchKind = enum
|
||||
LinearSearch, SplitSearch
|
||||
SearchResult* = object
|
||||
case kind: SearchKind
|
||||
of LinearSearch:
|
||||
a: seq[Key]
|
||||
of SplitSearch:
|
||||
span: int
|
||||
best: (char, int)
|
||||
|
||||
proc emitLinearSearch(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
|
||||
var d = SearchResult(kind: LinearSearch, a: @[])
|
||||
for x in a: d.a.add x
|
||||
dest.add d
|
||||
|
||||
proc split(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
|
||||
if a.len <= 4:
|
||||
emitLinearSearch strings, a, dest
|
||||
else:
|
||||
let best = tryAllPositions(strings, a)
|
||||
var groupA: seq[Key] = @[]
|
||||
var groupB: seq[Key] = @[]
|
||||
for t in items a:
|
||||
let s = strings[t[0]]
|
||||
if s.len > best[1] and s[best[1]] <= best[0]:
|
||||
groupA.add t
|
||||
else:
|
||||
groupB.add t
|
||||
if groupA.len == 0 or groupB.len == 0:
|
||||
emitLinearSearch strings, a, dest
|
||||
else:
|
||||
let toPatch = dest.len
|
||||
dest.add SearchResult(kind: SplitSearch, span: 1, best: best)
|
||||
split strings, groupA, dest
|
||||
split strings, groupB, dest
|
||||
let dist = dest.len - toPatch
|
||||
assert dist > 0
|
||||
dest[toPatch].span = dist
|
||||
|
||||
proc toProblemDescription(c: var ProcCon; n: PNode): (seq[Key], LabelId) =
|
||||
result = (@[], newLabels(c.labelGen, n.len))
|
||||
assert n.kind == nkCaseStmt
|
||||
for i in 1..<n.len:
|
||||
let it = n[i]
|
||||
let thisBranch = LabelId(result[1].int + i - 1)
|
||||
if it.kind == nkOfBranch:
|
||||
for j in 0..<it.len-1:
|
||||
assert it[j].kind in {nkStrLit..nkTripleStrLit}
|
||||
result[0].add (c.lit.strings.getOrIncl(it[j].strVal), thisBranch)
|
||||
|
||||
proc decodeSolution(c: var ProcCon; dest: var Tree; s: seq[SearchResult]; i: int;
|
||||
selector: Value; info: PackedLineInfo) =
|
||||
case s[i].kind
|
||||
of SplitSearch:
|
||||
let thenA = i+1
|
||||
let elseA = thenA + (if s[thenA].kind == LinearSearch: 1 else: s[thenA].span)
|
||||
let best = s[i].best
|
||||
|
||||
let tmp = getTemp(c, Bool8Id, info)
|
||||
buildTyped dest, info, Asgn, Bool8Id:
|
||||
dest.copyTree tmp
|
||||
buildTyped dest, info, Call, Bool8Id:
|
||||
c.addUseCodegenProc dest, "nimStrAtLe", info
|
||||
dest.copyTree selector
|
||||
dest.addIntVal c.lit.numbers, info, c.m.nativeIntId, best[1]
|
||||
dest.addIntVal c.lit.numbers, info, Char8Id, best[0].int
|
||||
|
||||
template then() =
|
||||
c.decodeSolution dest, s, thenA, selector, info
|
||||
template otherwise() =
|
||||
c.decodeSolution dest, s, elseA, selector, info
|
||||
buildIfThenElse tmp, then, otherwise
|
||||
freeTemp c, tmp
|
||||
|
||||
of LinearSearch:
|
||||
let tmp = getTemp(c, Bool8Id, info)
|
||||
for x in s[i].a:
|
||||
buildTyped dest, info, Asgn, Bool8Id:
|
||||
dest.copyTree tmp
|
||||
buildTyped dest, info, Call, Bool8Id:
|
||||
c.addUseCodegenProc dest, "eqStrings", info
|
||||
dest.copyTree selector
|
||||
dest.addStrLit info, x[0]
|
||||
buildIf tmp:
|
||||
c.code.gotoLabel info, Goto, x[1]
|
||||
freeTemp c, tmp
|
||||
|
||||
proc genStringCase(c: var ProcCon; n: PNode; d: var Value) =
|
||||
let (problem, firstBranch) = toProblemDescription(c, n)
|
||||
var solution: seq[SearchResult] = @[]
|
||||
split c.lit.strings, problem, solution
|
||||
|
||||
# XXX Todo move complex case selector into a temporary.
|
||||
let selector = c.genx(n[0])
|
||||
|
||||
let info = toLineInfo(c, n.info)
|
||||
decodeSolution c, c.code, solution, 0, selector, info
|
||||
|
||||
let lend = newLabel(c.labelGen)
|
||||
c.code.addLabel info, Goto, lend
|
||||
for i in 1..<n.len:
|
||||
let it = n[i]
|
||||
let thisBranch = LabelId(firstBranch.int + i - 1)
|
||||
c.code.addLabel info, Label, thisBranch
|
||||
if it.kind == nkOfBranch:
|
||||
gen(c, it.lastSon, d)
|
||||
c.code.addLabel info, Goto, lend
|
||||
else:
|
||||
gen(c, it.lastSon, d)
|
||||
|
||||
c.code.addLabel info, Label, lend
|
||||
freeTemp c, selector
|
||||
@@ -1,525 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import std / [assertions, tables, sets]
|
||||
import ".." / [ast, types, options, sighashes, modulegraphs]
|
||||
import nirtypes
|
||||
|
||||
type
|
||||
TypesCon* = object
|
||||
processed: Table[ItemId, TypeId]
|
||||
processedByName: Table[string, TypeId]
|
||||
recursionCheck: HashSet[ItemId]
|
||||
conf: ConfigRef
|
||||
stringType: TypeId
|
||||
|
||||
proc initTypesCon*(conf: ConfigRef): TypesCon =
|
||||
TypesCon(conf: conf, stringType: TypeId(-1))
|
||||
|
||||
proc mangle(c: var TypesCon; t: PType): string =
|
||||
result = $sighashes.hashType(t, c.conf)
|
||||
|
||||
template cached(c: var TypesCon; t: PType; body: untyped) =
|
||||
result = c.processed.getOrDefault(t.itemId)
|
||||
if result.int == 0:
|
||||
body
|
||||
c.processed[t.itemId] = result
|
||||
|
||||
template cachedByName(c: var TypesCon; t: PType; body: untyped) =
|
||||
let key = mangle(c, t)
|
||||
result = c.processedByName.getOrDefault(key)
|
||||
if result.int == 0:
|
||||
body
|
||||
c.processedByName[key] = result
|
||||
|
||||
proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId
|
||||
|
||||
proc collectFieldTypes(c: var TypesCon; g: var TypeGraph; n: PNode; dest: var Table[ItemId, TypeId]) =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
collectFieldTypes(c, g, n[i], dest)
|
||||
of nkRecCase:
|
||||
assert(n[0].kind == nkSym)
|
||||
collectFieldTypes(c, g, n[0], dest)
|
||||
for i in 1..<n.len:
|
||||
case n[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
collectFieldTypes c, g, lastSon(n[i]), dest
|
||||
else: discard
|
||||
of nkSym:
|
||||
dest[n.sym.itemId] = typeToIr(c, g, n.sym.typ)
|
||||
else:
|
||||
assert false, "unknown node kind: " & $n.kind
|
||||
|
||||
proc objectToIr(c: var TypesCon; g: var TypeGraph; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
objectToIr(c, g, n[i], fieldTypes, unionId)
|
||||
of nkRecCase:
|
||||
assert(n[0].kind == nkSym)
|
||||
objectToIr(c, g, n[0], fieldTypes, unionId)
|
||||
let u = openType(g, UnionDecl)
|
||||
g.addName "u_" & $unionId
|
||||
inc unionId
|
||||
for i in 1..<n.len:
|
||||
case n[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
let subObj = openType(g, ObjectDecl)
|
||||
g.addName "uo_" & $unionId & "_" & $i
|
||||
objectToIr c, g, lastSon(n[i]), fieldTypes, unionId
|
||||
sealType(g, subObj)
|
||||
else: discard
|
||||
sealType(g, u)
|
||||
of nkSym:
|
||||
g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset
|
||||
else:
|
||||
assert false, "unknown node kind: " & $n.kind
|
||||
|
||||
proc objectToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
|
||||
if t.baseClass != nil:
|
||||
# ensure we emitted the base type:
|
||||
discard typeToIr(c, g, t.baseClass)
|
||||
|
||||
var unionId = 0
|
||||
var fieldTypes = initTable[ItemId, TypeId]()
|
||||
collectFieldTypes c, g, t.n, fieldTypes
|
||||
let obj = openType(g, ObjectDecl)
|
||||
g.addName mangle(c, t)
|
||||
g.addSize c.conf.getSize(t)
|
||||
g.addAlign c.conf.getAlign(t)
|
||||
|
||||
if t.baseClass != nil:
|
||||
g.addNominalType(ObjectTy, mangle(c, t.baseClass))
|
||||
else:
|
||||
g.addBuiltinType VoidId # object does not inherit
|
||||
if not lacksMTypeField(t):
|
||||
let f2 = g.openType FieldDecl
|
||||
let voidPtr = openType(g, APtrTy)
|
||||
g.addBuiltinType(VoidId)
|
||||
sealType(g, voidPtr)
|
||||
g.addOffset 0 # type field is always at offset 0
|
||||
g.addName "m_type"
|
||||
sealType(g, f2) # FieldDecl
|
||||
|
||||
objectToIr c, g, t.n, fieldTypes, unionId
|
||||
result = finishType(g, obj)
|
||||
|
||||
proc objectHeaderToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
|
||||
result = g.nominalType(ObjectTy, mangle(c, t))
|
||||
|
||||
proc tupleToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
|
||||
var fieldTypes = newSeq[TypeId](t.len)
|
||||
for i in 0..<t.len:
|
||||
fieldTypes[i] = typeToIr(c, g, t[i])
|
||||
let obj = openType(g, ObjectDecl)
|
||||
g.addName mangle(c, t)
|
||||
g.addSize c.conf.getSize(t)
|
||||
g.addAlign c.conf.getAlign(t)
|
||||
|
||||
var accum = OffsetAccum(maxAlign: 1)
|
||||
for i in 0..<t.len:
|
||||
let child = t[i]
|
||||
g.addField "f_" & $i, fieldTypes[i], accum.offset
|
||||
|
||||
computeSizeAlign(c.conf, child)
|
||||
accum.align(child.align)
|
||||
accum.inc(int32(child.size))
|
||||
result = finishType(g, obj)
|
||||
|
||||
proc procToIr(c: var TypesCon; g: var TypeGraph; t: PType; addEnv = false): TypeId =
|
||||
var fieldTypes = newSeq[TypeId](0)
|
||||
for i in 0..<t.len:
|
||||
if t[i] == nil or not isCompileTimeOnly(t[i]):
|
||||
fieldTypes.add typeToIr(c, g, t[i])
|
||||
let obj = openType(g, ProcTy)
|
||||
|
||||
case t.callConv
|
||||
of ccNimCall, ccFastCall, ccClosure: g.addAnnotation "__fastcall"
|
||||
of ccStdCall: g.addAnnotation "__stdcall"
|
||||
of ccCDecl: g.addAnnotation "__cdecl"
|
||||
of ccSafeCall: g.addAnnotation "__safecall"
|
||||
of ccSysCall: g.addAnnotation "__syscall"
|
||||
of ccInline: g.addAnnotation "__inline"
|
||||
of ccNoInline: g.addAnnotation "__noinline"
|
||||
of ccThisCall: g.addAnnotation "__thiscall"
|
||||
of ccNoConvention, ccMember: g.addAnnotation ""
|
||||
|
||||
for i in 0..<fieldTypes.len:
|
||||
g.addType fieldTypes[i]
|
||||
|
||||
if addEnv:
|
||||
let a = openType(g, APtrTy)
|
||||
g.addBuiltinType(VoidId)
|
||||
sealType(g, a)
|
||||
|
||||
if tfVarargs in t.flags:
|
||||
g.addVarargs()
|
||||
result = finishType(g, obj)
|
||||
|
||||
proc nativeInt(c: TypesCon): TypeId =
|
||||
case c.conf.target.intSize
|
||||
of 2: result = Int16Id
|
||||
of 4: result = Int32Id
|
||||
else: result = Int64Id
|
||||
|
||||
proc openArrayPayloadType*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
|
||||
let e = elementType(t)
|
||||
let elementType = typeToIr(c, g, e)
|
||||
let arr = g.openType AArrayPtrTy
|
||||
g.addType elementType
|
||||
result = finishType(g, arr) # LastArrayTy
|
||||
|
||||
proc openArrayToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
|
||||
# object (a: ArrayPtr[T], len: int)
|
||||
let e = elementType(t)
|
||||
let mangledBase = mangle(c, e)
|
||||
let typeName = "NimOpenArray" & mangledBase
|
||||
|
||||
let elementType = typeToIr(c, g, e)
|
||||
#assert elementType.int >= 0, typeToString(t)
|
||||
|
||||
let p = openType(g, ObjectDecl)
|
||||
g.addName typeName
|
||||
g.addSize c.conf.target.ptrSize*2
|
||||
g.addAlign c.conf.target.ptrSize
|
||||
|
||||
let f = g.openType FieldDecl
|
||||
let arr = g.openType AArrayPtrTy
|
||||
g.addType elementType
|
||||
sealType(g, arr) # LastArrayTy
|
||||
g.addOffset 0
|
||||
g.addName "data"
|
||||
sealType(g, f) # FieldDecl
|
||||
|
||||
g.addField "len", c.nativeInt, c.conf.target.ptrSize
|
||||
|
||||
result = finishType(g, p) # ObjectDecl
|
||||
|
||||
proc strPayloadType(c: var TypesCon; g: var TypeGraph): (string, TypeId) =
|
||||
result = ("NimStrPayload", TypeId(-1))
|
||||
let p = openType(g, ObjectDecl)
|
||||
g.addName result[0]
|
||||
g.addSize c.conf.target.ptrSize*2
|
||||
g.addAlign c.conf.target.ptrSize
|
||||
|
||||
g.addField "cap", c.nativeInt, 0
|
||||
|
||||
let f = g.openType FieldDecl
|
||||
let arr = g.openType LastArrayTy
|
||||
g.addBuiltinType Char8Id
|
||||
result[1] = finishType(g, arr) # LastArrayTy
|
||||
g.addOffset c.conf.target.ptrSize # comes after the len field
|
||||
g.addName "data"
|
||||
sealType(g, f) # FieldDecl
|
||||
|
||||
sealType(g, p)
|
||||
|
||||
proc strPayloadPtrType*(c: var TypesCon; g: var TypeGraph): (TypeId, TypeId) =
|
||||
let (mangled, arrayType) = strPayloadType(c, g)
|
||||
let ffp = g.openType APtrTy
|
||||
g.addNominalType ObjectTy, mangled
|
||||
result = (finishType(g, ffp), arrayType) # APtrTy
|
||||
|
||||
proc stringToIr(c: var TypesCon; g: var TypeGraph): TypeId =
|
||||
#[
|
||||
|
||||
NimStrPayload = object
|
||||
cap: int
|
||||
data: UncheckedArray[char]
|
||||
|
||||
NimStringV2 = object
|
||||
len: int
|
||||
p: ptr NimStrPayload
|
||||
|
||||
]#
|
||||
let payload = strPayloadType(c, g)
|
||||
|
||||
let str = openType(g, ObjectDecl)
|
||||
g.addName "NimStringV2"
|
||||
g.addSize c.conf.target.ptrSize*2
|
||||
g.addAlign c.conf.target.ptrSize
|
||||
|
||||
g.addField "len", c.nativeInt, 0
|
||||
|
||||
let fp = g.openType FieldDecl
|
||||
let ffp = g.openType APtrTy
|
||||
g.addNominalType ObjectTy, "NimStrPayload"
|
||||
sealType(g, ffp) # APtrTy
|
||||
g.addOffset c.conf.target.ptrSize # comes after 'len' field
|
||||
g.addName "p"
|
||||
sealType(g, fp) # FieldDecl
|
||||
|
||||
result = finishType(g, str) # ObjectDecl
|
||||
|
||||
proc seqPayloadType(c: var TypesCon; g: var TypeGraph; t: PType): (string, TypeId) =
|
||||
#[
|
||||
NimSeqPayload[T] = object
|
||||
cap: int
|
||||
data: UncheckedArray[T]
|
||||
]#
|
||||
let e = elementType(t)
|
||||
result = (mangle(c, e), TypeId(-1))
|
||||
let payloadName = "NimSeqPayload" & result[0]
|
||||
|
||||
let elementType = typeToIr(c, g, e)
|
||||
|
||||
let p = openType(g, ObjectDecl)
|
||||
g.addName payloadName
|
||||
g.addSize c.conf.target.intSize
|
||||
g.addAlign c.conf.target.intSize
|
||||
|
||||
g.addField "cap", c.nativeInt, 0
|
||||
|
||||
let f = g.openType FieldDecl
|
||||
let arr = g.openType LastArrayTy
|
||||
g.addType elementType
|
||||
# DO NOT USE `finishType` here as it is an inner type. This is subtle and we
|
||||
# probably need an even better API here.
|
||||
sealType(g, arr)
|
||||
result[1] = TypeId(arr)
|
||||
|
||||
g.addOffset c.conf.target.ptrSize
|
||||
g.addName "data"
|
||||
sealType(g, f) # FieldDecl
|
||||
|
||||
sealType(g, p)
|
||||
|
||||
proc seqPayloadPtrType*(c: var TypesCon; g: var TypeGraph; t: PType): (TypeId, TypeId) =
|
||||
let (mangledBase, arrayType) = seqPayloadType(c, g, t)
|
||||
let ffp = g.openType APtrTy
|
||||
g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
|
||||
result = (finishType(g, ffp), arrayType) # APtrTy
|
||||
|
||||
proc seqToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
|
||||
#[
|
||||
NimSeqV2*[T] = object
|
||||
len: int
|
||||
p: ptr NimSeqPayload[T]
|
||||
]#
|
||||
let (mangledBase, _) = seqPayloadType(c, g, t)
|
||||
|
||||
let sq = openType(g, ObjectDecl)
|
||||
g.addName "NimSeqV2" & mangledBase
|
||||
g.addSize c.conf.getSize(t)
|
||||
g.addAlign c.conf.getAlign(t)
|
||||
|
||||
g.addField "len", c.nativeInt, 0
|
||||
|
||||
let fp = g.openType FieldDecl
|
||||
let ffp = g.openType APtrTy
|
||||
g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
|
||||
sealType(g, ffp) # APtrTy
|
||||
g.addOffset c.conf.target.ptrSize
|
||||
g.addName "p"
|
||||
sealType(g, fp) # FieldDecl
|
||||
|
||||
result = finishType(g, sq) # ObjectDecl
|
||||
|
||||
|
||||
proc closureToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
|
||||
# struct {fn(args, void* env), env}
|
||||
# typedef struct {$n" &
|
||||
# "N_NIMCALL_PTR($2, ClP_0) $3;$n" &
|
||||
# "void* ClE_0;$n} $1;$n"
|
||||
let mangledBase = mangle(c, t)
|
||||
let typeName = "NimClosure" & mangledBase
|
||||
|
||||
let procType = procToIr(c, g, t, addEnv=true)
|
||||
|
||||
let p = openType(g, ObjectDecl)
|
||||
g.addName typeName
|
||||
g.addSize c.conf.getSize(t)
|
||||
g.addAlign c.conf.getAlign(t)
|
||||
|
||||
let f = g.openType FieldDecl
|
||||
g.addType procType
|
||||
g.addOffset 0
|
||||
g.addName "ClP_0"
|
||||
sealType(g, f) # FieldDecl
|
||||
|
||||
let f2 = g.openType FieldDecl
|
||||
let voidPtr = openType(g, APtrTy)
|
||||
g.addBuiltinType(VoidId)
|
||||
sealType(g, voidPtr)
|
||||
|
||||
g.addOffset c.conf.target.ptrSize
|
||||
g.addName "ClE_0"
|
||||
sealType(g, f2) # FieldDecl
|
||||
|
||||
result = finishType(g, p) # ObjectDecl
|
||||
|
||||
proc bitsetBasetype*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
|
||||
let s = int(getSize(c.conf, t))
|
||||
case s
|
||||
of 1: result = UInt8Id
|
||||
of 2: result = UInt16Id
|
||||
of 4: result = UInt32Id
|
||||
of 8: result = UInt64Id
|
||||
else: result = UInt8Id
|
||||
|
||||
proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
|
||||
if t == nil: return VoidId
|
||||
case t.kind
|
||||
of tyInt:
|
||||
case int(getSize(c.conf, t))
|
||||
of 2: result = Int16Id
|
||||
of 4: result = Int32Id
|
||||
else: result = Int64Id
|
||||
of tyInt8: result = Int8Id
|
||||
of tyInt16: result = Int16Id
|
||||
of tyInt32: result = Int32Id
|
||||
of tyInt64: result = Int64Id
|
||||
of tyFloat:
|
||||
case int(getSize(c.conf, t))
|
||||
of 4: result = Float32Id
|
||||
else: result = Float64Id
|
||||
of tyFloat32: result = Float32Id
|
||||
of tyFloat64: result = Float64Id
|
||||
of tyFloat128: result = getFloat128Type(g)
|
||||
of tyUInt:
|
||||
case int(getSize(c.conf, t))
|
||||
of 2: result = UInt16Id
|
||||
of 4: result = UInt32Id
|
||||
else: result = UInt64Id
|
||||
of tyUInt8: result = UInt8Id
|
||||
of tyUInt16: result = UInt16Id
|
||||
of tyUInt32: result = UInt32Id
|
||||
of tyUInt64: result = UInt64Id
|
||||
of tyBool: result = Bool8Id
|
||||
of tyChar: result = Char8Id
|
||||
of tyVoid: result = VoidId
|
||||
of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange:
|
||||
result = typeToIr(c, g, t.skipModifier)
|
||||
of tyEnum:
|
||||
if firstOrd(c.conf, t) < 0:
|
||||
result = Int32Id
|
||||
else:
|
||||
case int(getSize(c.conf, t))
|
||||
of 1: result = UInt8Id
|
||||
of 2: result = UInt16Id
|
||||
of 4: result = Int32Id
|
||||
of 8: result = Int64Id
|
||||
else: result = Int32Id
|
||||
of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
|
||||
if t.len > 0:
|
||||
result = typeToIr(c, g, t.skipModifier)
|
||||
else:
|
||||
result = TypeId(-1)
|
||||
of tyFromExpr:
|
||||
if t.n != nil and t.n.typ != nil:
|
||||
result = typeToIr(c, g, t.n.typ)
|
||||
else:
|
||||
result = TypeId(-1)
|
||||
of tyArray:
|
||||
cached(c, t):
|
||||
var n = toInt64(lengthOrd(c.conf, t))
|
||||
if n <= 0: n = 1 # make an array of at least one element
|
||||
let elemType = typeToIr(c, g, t.elementType)
|
||||
let a = openType(g, ArrayTy)
|
||||
g.addType(elemType)
|
||||
g.addArrayLen n
|
||||
g.addName mangle(c, t)
|
||||
result = finishType(g, a)
|
||||
of tyPtr, tyRef:
|
||||
cached(c, t):
|
||||
let e = t.elementType
|
||||
if e.kind == tyUncheckedArray:
|
||||
let elemType = typeToIr(c, g, e.elementType)
|
||||
let a = openType(g, AArrayPtrTy)
|
||||
g.addType(elemType)
|
||||
result = finishType(g, a)
|
||||
else:
|
||||
let elemType = typeToIr(c, g, t.elementType)
|
||||
let a = openType(g, APtrTy)
|
||||
g.addType(elemType)
|
||||
result = finishType(g, a)
|
||||
of tyVar, tyLent:
|
||||
cached(c, t):
|
||||
let e = t.elementType
|
||||
if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
# skip the modifier, `var openArray` is a (ptr, len) pair too:
|
||||
result = typeToIr(c, g, e)
|
||||
else:
|
||||
let elemType = typeToIr(c, g, e)
|
||||
let a = openType(g, APtrTy)
|
||||
g.addType(elemType)
|
||||
result = finishType(g, a)
|
||||
of tySet:
|
||||
let s = int(getSize(c.conf, t))
|
||||
case s
|
||||
of 1: result = UInt8Id
|
||||
of 2: result = UInt16Id
|
||||
of 4: result = UInt32Id
|
||||
of 8: result = UInt64Id
|
||||
else:
|
||||
# array[U8, s]
|
||||
cached(c, t):
|
||||
let a = openType(g, ArrayTy)
|
||||
g.addType(UInt8Id)
|
||||
g.addArrayLen s
|
||||
g.addName mangle(c, t)
|
||||
result = finishType(g, a)
|
||||
of tyPointer, tyNil:
|
||||
# tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim
|
||||
let a = openType(g, APtrTy)
|
||||
g.addBuiltinType(VoidId)
|
||||
result = finishType(g, a)
|
||||
of tyObject:
|
||||
# Objects are special as they can be recursive in Nim. This is easily solvable.
|
||||
# We check if we are already "processing" t. If so, we produce `ObjectTy`
|
||||
# instead of `ObjectDecl`.
|
||||
cached(c, t):
|
||||
if not c.recursionCheck.containsOrIncl(t.itemId):
|
||||
result = objectToIr(c, g, t)
|
||||
else:
|
||||
result = objectHeaderToIr(c, g, t)
|
||||
of tyTuple:
|
||||
cachedByName(c, t):
|
||||
result = tupleToIr(c, g, t)
|
||||
of tyProc:
|
||||
cached(c, t):
|
||||
if t.callConv == ccClosure:
|
||||
result = closureToIr(c, g, t)
|
||||
else:
|
||||
result = procToIr(c, g, t)
|
||||
of tyVarargs, tyOpenArray:
|
||||
cached(c, t):
|
||||
result = openArrayToIr(c, g, t)
|
||||
of tyString:
|
||||
if c.stringType.int < 0:
|
||||
result = stringToIr(c, g)
|
||||
c.stringType = result
|
||||
else:
|
||||
result = c.stringType
|
||||
of tySequence:
|
||||
cachedByName(c, t):
|
||||
result = seqToIr(c, g, t)
|
||||
of tyCstring:
|
||||
cached(c, t):
|
||||
let a = openType(g, AArrayPtrTy)
|
||||
g.addBuiltinType Char8Id
|
||||
result = finishType(g, a)
|
||||
of tyUncheckedArray:
|
||||
# We already handled the `ptr UncheckedArray` in a special way.
|
||||
cached(c, t):
|
||||
let elemType = typeToIr(c, g, t.elementType)
|
||||
let a = openType(g, LastArrayTy)
|
||||
g.addType(elemType)
|
||||
result = finishType(g, a)
|
||||
of tyUntyped, tyTyped:
|
||||
# this avoids a special case for system.echo which is not a generic but
|
||||
# uses `varargs[typed]`:
|
||||
result = VoidId
|
||||
of tyNone, tyEmpty, tyTypeDesc,
|
||||
tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
|
||||
tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
|
||||
result = TypeId(-1)
|
||||
@@ -204,6 +204,7 @@ type
|
||||
nkModuleRef # for .rod file support: A (moduleId, itemId) pair
|
||||
nkReplayAction # for .rod file support: A replay action
|
||||
nkNilRodNode # for .rod file support: a 'nil' PNode
|
||||
nkOpenSym # container for captured sym that can be overriden by local symbols
|
||||
|
||||
const
|
||||
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
||||
|
||||
@@ -28,11 +28,14 @@ type
|
||||
hasReturn, hasBreak: bool
|
||||
label: PSym # can be nil
|
||||
parent: ptr BasicBlock
|
||||
symToDel: seq[PNode]
|
||||
|
||||
Con = object
|
||||
somethingTodo: bool
|
||||
inFinally: int
|
||||
|
||||
proc invalidateWasMoved(c: var BasicBlock; x: PNode)
|
||||
|
||||
proc nestedBlock(parent: var BasicBlock; kind: TNodeKind): BasicBlock =
|
||||
BasicBlock(wasMovedLocs: @[], kind: kind, hasReturn: false, hasBreak: false,
|
||||
label: nil, parent: addr(parent))
|
||||
@@ -62,6 +65,10 @@ proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} =
|
||||
if this.hasReturn:
|
||||
parent.wasMovedLocs.setLen 0
|
||||
parent.hasReturn = true
|
||||
elif this.symToDel.len > 0:
|
||||
parent.symToDel = this.symToDel
|
||||
for i in this.symToDel:
|
||||
invalidateWasMoved(parent, i)
|
||||
|
||||
proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool =
|
||||
result = false
|
||||
@@ -149,6 +156,7 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
|
||||
# any usage of the location before destruction implies we
|
||||
# cannot elide the 'wasMoved(x)':
|
||||
b.invalidateWasMoved n
|
||||
b.symToDel.add n
|
||||
|
||||
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
|
||||
@@ -25,7 +25,7 @@ const
|
||||
useEffectSystem* = true
|
||||
useWriteTracking* = false
|
||||
hasFFI* = defined(nimHasLibFFI)
|
||||
copyrightYear* = "2024"
|
||||
copyrightYear* = "2025"
|
||||
|
||||
nimEnableCovariance* = defined(nimEnableCovariance)
|
||||
|
||||
@@ -139,7 +139,7 @@ type
|
||||
backendCpp = "cpp"
|
||||
backendJs = "js"
|
||||
backendObjc = "objc"
|
||||
backendNir = "nir"
|
||||
backendNif = "nif"
|
||||
# backendNimscript = "nimscript" # this could actually work
|
||||
# backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
|
||||
|
||||
@@ -147,7 +147,6 @@ type
|
||||
cmdNone # not yet processed command
|
||||
cmdUnknown # command unmapped
|
||||
cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS,
|
||||
cmdCompileToNir,
|
||||
cmdCrun # compile and run in nimache
|
||||
cmdTcc # run the project via TCC backend
|
||||
cmdCheck # semantic checking for whole project
|
||||
@@ -173,15 +172,15 @@ type
|
||||
cmdNop
|
||||
cmdJsonscript # compile a .json build file
|
||||
# old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
|
||||
cmdCompileToNif
|
||||
|
||||
const
|
||||
cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
|
||||
cmdCompileToJS, cmdCrun, cmdCompileToNir}
|
||||
cmdCompileToJS, cmdCrun, cmdCompileToNif}
|
||||
cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
|
||||
cmdCtags, cmdBuildindex}
|
||||
|
||||
type
|
||||
NimVer* = tuple[major: int, minor: int, patch: int]
|
||||
TStringSeq* = seq[string]
|
||||
TGCMode* = enum # the selected GC
|
||||
gcUnselected = "unselected"
|
||||
@@ -225,11 +224,14 @@ type
|
||||
strictEffects,
|
||||
unicodeOperators, # deadcode
|
||||
flexibleOptionalParams,
|
||||
strictDefs,
|
||||
strictDefs, # deadcode
|
||||
strictCaseObjects,
|
||||
inferGenericTypes,
|
||||
genericsOpenSym,
|
||||
openSym, # remove nfDisabledOpenSym when this is default
|
||||
# alternative to above:
|
||||
genericsOpenSym
|
||||
vtables
|
||||
typeBoundOps
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
@@ -248,13 +250,15 @@ type
|
||||
## Useful for libraries that rely on local passC
|
||||
jsNoLambdaLifting
|
||||
## Old transformation for closures in JS backend
|
||||
noPanicOnExcept
|
||||
## don't panic on bare except
|
||||
|
||||
SymbolFilesOption* = enum
|
||||
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
|
||||
|
||||
TSystemCC* = enum
|
||||
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
|
||||
ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl
|
||||
ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc
|
||||
|
||||
ExceptionSystem* = enum
|
||||
excNone, # no exception system selected yet
|
||||
@@ -370,7 +374,6 @@ type
|
||||
arguments*: string ## the arguments to be passed to the program that
|
||||
## should be run
|
||||
ideCmd*: IdeCmd
|
||||
oldNewlines*: bool
|
||||
cCompiler*: TSystemCC # the used compiler
|
||||
modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs
|
||||
cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline
|
||||
@@ -384,6 +387,7 @@ type
|
||||
warnCounter*: int
|
||||
errorMax*: int
|
||||
maxLoopIterationsVM*: int ## VM: max iterations of all loops
|
||||
maxCallDepthVM*: int ## VM: max call depth
|
||||
isVmTrace*: bool
|
||||
configVars*: StringTableRef
|
||||
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
|
||||
@@ -397,12 +401,12 @@ type
|
||||
outDir*: AbsoluteDir
|
||||
jsonBuildFile*: AbsoluteFile
|
||||
prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
|
||||
nimStdlibVersion*: NimVer
|
||||
dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef
|
||||
dllOverrides*, moduleOverrides*, cfileSpecificOptions*: StringTableRef
|
||||
projectName*: string # holds a name like 'nim'
|
||||
projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
|
||||
projectFull*: AbsoluteFile # projectPath/projectName
|
||||
projectIsStdin*: bool # whether we're compiling from stdin
|
||||
stdinFile*: AbsoluteFile # Filename to use in messages for stdin
|
||||
lastMsgWasDot*: set[StdOrrKind] # the last compiler message was a single '.'
|
||||
projectMainIdx*: FileIndex # the canonical path id of the main module
|
||||
projectMainIdx2*: FileIndex # consider merging with projectMainIdx
|
||||
@@ -410,7 +414,6 @@ type
|
||||
commandArgs*: seq[string] # any arguments after the main command
|
||||
commandLine*: string
|
||||
extraCmds*: seq[string] # for writeJsonBuildInstructions
|
||||
keepComments*: bool # whether the parser needs to keep comments
|
||||
implicitImports*: seq[string] # modules that are to be implicitly imported
|
||||
implicitIncludes*: seq[string] # modules that are to be implicitly included
|
||||
docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
|
||||
@@ -451,16 +454,6 @@ type
|
||||
clientProcessId*: int
|
||||
|
||||
|
||||
proc parseNimVersion*(a: string): NimVer =
|
||||
# could be moved somewhere reusable
|
||||
result = default(NimVer)
|
||||
if a.len > 0:
|
||||
let b = a.split(".")
|
||||
assert b.len == 3, a
|
||||
template fn(i) = result[i] = b[i].parseInt # could be optimized if needed
|
||||
fn(0)
|
||||
fn(1)
|
||||
fn(2)
|
||||
|
||||
proc assignIfDefault*[T](result: var T, val: T, def = default(T)) =
|
||||
## if `result` was already assigned to a value (that wasn't `def`), this is a noop.
|
||||
@@ -590,11 +583,11 @@ proc newConfigRef*(): ConfigRef =
|
||||
projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
|
||||
projectFull: AbsoluteFile"", # projectPath/projectName
|
||||
projectIsStdin: false, # whether we're compiling from stdin
|
||||
stdinFile: AbsoluteFile"stdinfile",
|
||||
projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
|
||||
command: "", # the main command (e.g. cc, check, scan, etc)
|
||||
commandArgs: @[], # any arguments after the main command
|
||||
commandLine: "",
|
||||
keepComments: true, # whether the parser needs to keep comments
|
||||
implicitImports: @[], # modules that are to be implicitly imported
|
||||
implicitIncludes: @[], # modules that are to be implicitly included
|
||||
docSeeSrcUrl: "",
|
||||
@@ -612,6 +605,7 @@ proc newConfigRef*(): ConfigRef =
|
||||
arguments: "",
|
||||
suggestMaxResults: 10_000,
|
||||
maxLoopIterationsVM: 10_000_000,
|
||||
maxCallDepthVM: 2_000,
|
||||
vmProfileData: newProfileData(),
|
||||
spellSuggestMax: spellSuggestSecretSauce,
|
||||
currentConfigDir: ""
|
||||
@@ -635,12 +629,6 @@ proc newPartialConfigRef*(): ConfigRef =
|
||||
proc cppDefine*(c: ConfigRef; define: string) =
|
||||
c.cppDefines.incl define
|
||||
|
||||
proc getStdlibVersion*(conf: ConfigRef): NimVer =
|
||||
if conf.nimStdlibVersion == (0,0,0):
|
||||
let s = conf.symbols.getOrDefault("nimVersion", "")
|
||||
conf.nimStdlibVersion = s.parseNimVersion
|
||||
result = conf.nimStdlibVersion
|
||||
|
||||
proc isDefined*(conf: ConfigRef; symbol: string): bool =
|
||||
if conf.symbols.hasKey(symbol):
|
||||
result = true
|
||||
|
||||
@@ -51,3 +51,11 @@ func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
conf.mainPackageId == sym.getPackageId
|
||||
|
||||
func belongsToProjectPackageMaybeNil*(conf: ConfigRef, sym: PSym): bool =
|
||||
## Return whether the symbol belongs to the project's package.
|
||||
## Returns `false` if `sym` is nil.
|
||||
##
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
sym != nil and conf.mainPackageId == sym.getPackageId
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import ast, types, msgs, idents, renderer, wordrecg, trees,
|
||||
options
|
||||
|
||||
import std/strutils
|
||||
import std/[strutils, assertions]
|
||||
|
||||
# we precompile the pattern here for efficiency into some internal
|
||||
# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
|
||||
@@ -216,6 +216,11 @@ proc exprRoot*(n: PNode; allowCalls = true): PSym =
|
||||
else:
|
||||
break
|
||||
|
||||
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult
|
||||
|
||||
proc isLentableBranch(owner: PSym, n: PNode): bool =
|
||||
result = isAssignable(owner, n) in {arLentValue, arAddressableConst, arLentValue}
|
||||
|
||||
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
## 'owner' can be nil!
|
||||
result = arNone
|
||||
@@ -266,7 +271,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
|
||||
{tyOpenArray, tyTuple, tyObject}:
|
||||
result = isAssignable(owner, n[1])
|
||||
elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct):
|
||||
elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
|
||||
# types that are equal modulo distinction preserve l-value:
|
||||
result = isAssignable(owner, n[1])
|
||||
of nkHiddenDeref:
|
||||
@@ -283,8 +288,15 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result = isAssignable(owner, n[0])
|
||||
of nkCallKinds:
|
||||
# builtin slice keeps lvalue-ness:
|
||||
if getMagic(n) in {mArrGet, mSlice}:
|
||||
let m = getMagic(n)
|
||||
if m == mSlice:
|
||||
# builtin slice keeps l-value-ness
|
||||
# except for pointers because slice dereferences
|
||||
if n[1].typ.kind == tyPtr:
|
||||
result = arLValue
|
||||
else:
|
||||
result = isAssignable(owner, n[1])
|
||||
elif m == mArrGet:
|
||||
result = isAssignable(owner, n[1])
|
||||
elif n.typ != nil:
|
||||
case n.typ.kind
|
||||
@@ -301,6 +313,35 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
# nkVarTy denotes an lvalue, but the example above is the only
|
||||
# possible code which will get us here
|
||||
result = arLValue
|
||||
of nkIfExpr, nkIfStmt:
|
||||
# allow 'if' expressions to be lent if all branches are lentable
|
||||
for branch in n:
|
||||
if branch.len == 2:
|
||||
if not isLentableBranch(owner, branch[1]):
|
||||
return
|
||||
elif branch.len == 1:
|
||||
if not isLentableBranch(owner, branch[0]):
|
||||
return
|
||||
else:
|
||||
raiseAssert "Malformed `if` statement in isAssignable"
|
||||
result = arLentValue
|
||||
of nkCaseStmt:
|
||||
# allow 'case' expressions to be lent if all branches are lentable
|
||||
for i in 1 ..< n.len:
|
||||
let branch = n[i]
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
if not isLentableBranch(owner, branch[^1]):
|
||||
return
|
||||
of nkElifBranch:
|
||||
if not isLentableBranch(owner, branch[1]):
|
||||
return
|
||||
of nkElse:
|
||||
if not isLentableBranch(owner, branch[0]):
|
||||
return
|
||||
else:
|
||||
raiseAssert "Malformed `case` statement in isAssignable"
|
||||
result = arLentValue
|
||||
else:
|
||||
discard
|
||||
|
||||
|
||||
@@ -638,8 +638,9 @@ proc semiStmtList(p: var Parser, result: PNode) =
|
||||
getTok(p)
|
||||
if p.tok.tokType == tkParRi:
|
||||
break
|
||||
elif not (sameInd(p) or realInd(p)):
|
||||
parMessage(p, errInvalidIndentation)
|
||||
# ignore indent:
|
||||
#elif not (sameOrNoInd(p) or realInd(p)):
|
||||
# parMessage(p, errInvalidIndentation)
|
||||
let a = complexOrSimpleStmt(p)
|
||||
if a.kind == nkEmpty:
|
||||
parMessage(p, errExprExpected, p.tok)
|
||||
@@ -699,10 +700,12 @@ proc parsePar(p: var Parser): PNode =
|
||||
asgn.add b
|
||||
result.add(asgn)
|
||||
if p.tok.tokType == tkSemiColon:
|
||||
getTok(p)
|
||||
semiStmtList(p, result)
|
||||
elif p.tok.tokType == tkSemiColon:
|
||||
# stmt context:
|
||||
result.add(a)
|
||||
getTok(p)
|
||||
semiStmtList(p, result)
|
||||
else:
|
||||
a = colonOrEquals(p, a)
|
||||
@@ -1153,7 +1156,10 @@ proc parseParamList(p: var Parser, retColon = true): PNode =
|
||||
parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'")
|
||||
break
|
||||
else:
|
||||
parMessage(p, "expected closing ')'")
|
||||
if p.tok.tokType in tokKeywordLow..tokKeywordHigh:
|
||||
parMessage(p, errGenerated, "'" & $p.tok.ident.s & "' is a keyword and cannot be used as a parameter name")
|
||||
else:
|
||||
parMessage(p, "expected closing ')'")
|
||||
break
|
||||
result.add(a)
|
||||
if p.tok.tokType notin {tkComma, tkSemiColon}: break
|
||||
@@ -1401,7 +1407,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode =
|
||||
result = primarySuffix(p, result, baseInd, mode)
|
||||
|
||||
proc binaryNot(p: var Parser; a: PNode): PNode =
|
||||
if p.tok.tokType == tkNot:
|
||||
if p.tok.tokType == tkNot and p.tok.indent < 0:
|
||||
let notOpr = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
optInd(p, notOpr)
|
||||
@@ -2107,12 +2113,28 @@ proc parseObjectCase(p: var Parser): PNode =
|
||||
#| objectBranches = objectBranch (IND{=} objectBranch)*
|
||||
#| (IND{=} 'elif' expr colcom objectPart)*
|
||||
#| (IND{=} 'else' colcom objectPart)?
|
||||
#| objectCase = 'case' declColonEquals ':'? COMMENT?
|
||||
#| objectCase = 'case' (declColonEquals / pragma)? ':'? COMMENT?
|
||||
#| (IND{>} objectBranches DED
|
||||
#| | IND{=} objectBranches)
|
||||
result = newNodeP(nkRecCase, p)
|
||||
getTokNoInd(p)
|
||||
var a = parseIdentColonEquals(p, {withPragma})
|
||||
getTok(p)
|
||||
if p.tok.tokType != tkOf:
|
||||
# of case will be handled later
|
||||
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
|
||||
var a: PNode
|
||||
if p.tok.tokType in {tkSymbol, tkAccent}:
|
||||
a = parseIdentColonEquals(p, {withPragma})
|
||||
else:
|
||||
a = newNodeP(nkIdentDefs, p)
|
||||
if p.tok.tokType == tkCurlyDotLe:
|
||||
var prag = newNodeP(nkPragmaExpr, p)
|
||||
prag.add(p.emptyNode)
|
||||
prag.add(parsePragma(p))
|
||||
a.add(prag)
|
||||
else:
|
||||
a.add(p.emptyNode)
|
||||
a.add(p.emptyNode)
|
||||
a.add(p.emptyNode)
|
||||
result.add(a)
|
||||
if p.tok.tokType == tkColon: getTok(p)
|
||||
flexComment(p, result)
|
||||
|
||||
@@ -42,10 +42,7 @@ proc makePass*(open: TPassOpen = nil,
|
||||
process: TPassProcess = nil,
|
||||
close: TPassClose = nil,
|
||||
isFrontend = false): TPass =
|
||||
result.open = open
|
||||
result.close = close
|
||||
result.process = process
|
||||
result.isFrontend = isFrontend
|
||||
result = (open, process, close, isFrontend)
|
||||
|
||||
const
|
||||
maxPasses = 10
|
||||
@@ -100,8 +97,8 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
||||
stream: PLLStream): bool {.discardable.} =
|
||||
if graph.stopCompile(): return true
|
||||
var
|
||||
p: Parser
|
||||
a: TPassContextArray
|
||||
p: Parser = default(Parser)
|
||||
a: TPassContextArray = default(TPassContextArray)
|
||||
s: PLLStream
|
||||
fileIdx = module.fileIdx
|
||||
prepareConfigNotes(graph, module)
|
||||
|
||||
@@ -77,7 +77,7 @@ proc inSymChoice(sc, x: PNode): bool =
|
||||
result = false
|
||||
for i in 0..<sc.len:
|
||||
if sc[i].sym == x.sym: return true
|
||||
elif sc.kind == nkOpenSymChoice:
|
||||
elif sc.kind in {nkOpenSymChoice, nkOpenSym}:
|
||||
# same name suffices for open sym choices!
|
||||
result = sc[0].sym.name.id == x.sym.name.id
|
||||
else:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user