mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 10:53:40 +00:00
Compare commits
410 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce7c6f4f33 | ||
|
|
b0074121ec | ||
|
|
3713994ef1 | ||
|
|
75bd2d0688 | ||
|
|
eedfcbeb30 | ||
|
|
fe72db98c1 | ||
|
|
a70b4712fc | ||
|
|
76d834c182 | ||
|
|
72fcda1b35 | ||
|
|
fd9d2f5b82 | ||
|
|
d3f7fb3100 | ||
|
|
4acc7a5e18 | ||
|
|
e334aad202 | ||
|
|
67d7a7c124 | ||
|
|
206bbbd940 | ||
|
|
cc4c9251f0 | ||
|
|
8754469f49 | ||
|
|
e2ef322754 | ||
|
|
4fe17a2d87 | ||
|
|
cc887c23f4 | ||
|
|
acd09cec43 | ||
|
|
c8094176f1 | ||
|
|
3c66401dee | ||
|
|
ff46fcfd24 | ||
|
|
e941ee15be | ||
|
|
5f119f0cdf | ||
|
|
0636bd2a04 | ||
|
|
2499336fef | ||
|
|
1ba16876f1 | ||
|
|
3a180df0d8 | ||
|
|
9d65507d20 | ||
|
|
78003e9f7b | ||
|
|
27381cc602 | ||
|
|
dd9cb0425c | ||
|
|
894eee03a5 | ||
|
|
4b8b8fb9d7 | ||
|
|
3214174f06 | ||
|
|
8bb9823fde | ||
|
|
40ee3a950e | ||
|
|
74c4b23e0d | ||
|
|
7180ce6faa | ||
|
|
fd1d008cce | ||
|
|
8bc8347f02 | ||
|
|
33bef682dd | ||
|
|
bfd4b9b7a4 | ||
|
|
7d2fe8086f | ||
|
|
a78834c26c | ||
|
|
7cbf0ee53c | ||
|
|
f86f2928a7 | ||
|
|
5f09bf11de | ||
|
|
25ce25bc42 | ||
|
|
7503bed717 | ||
|
|
b7041b250c | ||
|
|
06d00dfcf6 | ||
|
|
baa8af92af | ||
|
|
f09b612f64 | ||
|
|
7d0cdfc79d | ||
|
|
ae117bd7bc | ||
|
|
b79e603702 | ||
|
|
da2ba43dca | ||
|
|
b5ef14ceda | ||
|
|
dc62ee00df | ||
|
|
b6a8dcd922 | ||
|
|
37965bd591 | ||
|
|
c2b4d8a968 | ||
|
|
8d254a5945 | ||
|
|
4856beae70 | ||
|
|
568a637f0f | ||
|
|
8b421d2203 | ||
|
|
a5237a5053 | ||
|
|
d0a8637872 | ||
|
|
7821aa94eb | ||
|
|
b1e5dd605a | ||
|
|
87ab9b8ac2 | ||
|
|
c18e599f53 | ||
|
|
d1aa568de3 | ||
|
|
352c82af89 | ||
|
|
22149422ee | ||
|
|
d6f7625626 | ||
|
|
3e8dfde2f9 | ||
|
|
3f183f6769 | ||
|
|
1100f550dc | ||
|
|
02eb396f45 | ||
|
|
796aa78562 | ||
|
|
1cc1451ca3 | ||
|
|
dd37bb88fe | ||
|
|
a88bce6106 | ||
|
|
8a4ee2b84f | ||
|
|
7b834b94da | ||
|
|
179ae267e9 | ||
|
|
5d872321b3 | ||
|
|
ff1881a4c1 | ||
|
|
702f8342ff | ||
|
|
92e505577e | ||
|
|
1ce954a0cf | ||
|
|
76e6130f64 | ||
|
|
83455cf1d5 | ||
|
|
be99f2fed8 | ||
|
|
cbcf6c5be6 | ||
|
|
e4db9bffba | ||
|
|
ecc7e3d41d | ||
|
|
22896b3a95 | ||
|
|
eaf0e7ff60 | ||
|
|
5935c3bfa9 | ||
|
|
2114414099 | ||
|
|
3788aa0a99 | ||
|
|
e98c98b46c | ||
|
|
51723bace8 | ||
|
|
a90e687d26 | ||
|
|
4b42170022 | ||
|
|
2eff34f08a | ||
|
|
ba51e7c4d8 | ||
|
|
d259099ef0 | ||
|
|
915cd5b28b | ||
|
|
16bbffcb77 | ||
|
|
73af7c60a1 | ||
|
|
7bdcaada13 | ||
|
|
e77e129a05 | ||
|
|
d0b3b7e1a7 | ||
|
|
2735fd2bf5 | ||
|
|
8d1f03e1da | ||
|
|
d83eb88eb9 | ||
|
|
10907cc4a7 | ||
|
|
c00e8e71e0 | ||
|
|
a073e225c8 | ||
|
|
140d641443 | ||
|
|
cc779a42ce | ||
|
|
26a4b137c6 | ||
|
|
d7544ec3db | ||
|
|
767a901267 | ||
|
|
36f8cccda4 | ||
|
|
d6bc8699b9 | ||
|
|
7db4376cda | ||
|
|
a85c635da8 | ||
|
|
3e18c48cda | ||
|
|
3455623500 | ||
|
|
8daac1b061 | ||
|
|
7071e3a080 | ||
|
|
98075688f3 | ||
|
|
5abf9a1d77 | ||
|
|
ab053fd56a | ||
|
|
598de35d45 | ||
|
|
a88b275bc3 | ||
|
|
e57b207ff8 | ||
|
|
b9951e8c87 | ||
|
|
9ad0ada8e4 | ||
|
|
30eb2b394e | ||
|
|
6a1694b28b | ||
|
|
8f5451832e | ||
|
|
cd65b5e5f8 | ||
|
|
80a6005f55 | ||
|
|
cd72be29c6 | ||
|
|
1425654e9d | ||
|
|
1463d811fb | ||
|
|
d372a0030d | ||
|
|
a6e729ed5d | ||
|
|
c5f2d84ac7 | ||
|
|
24e5e9875c | ||
|
|
4e977767a7 | ||
|
|
df589bcce1 | ||
|
|
e690ff35a2 | ||
|
|
e3f4c3d417 | ||
|
|
efcb6b4b11 | ||
|
|
5eee3e06fc | ||
|
|
dbbd2c8002 | ||
|
|
59e49ded2d | ||
|
|
4bf12a086d | ||
|
|
b1bfba9a31 | ||
|
|
a798356838 | ||
|
|
1d7170f0ed | ||
|
|
eaa8c7ca7a | ||
|
|
5f8e08031d | ||
|
|
c2a14cb3fc | ||
|
|
b3a62931cd | ||
|
|
b213da9937 | ||
|
|
6eb3e3134e | ||
|
|
1e2d98a3e0 | ||
|
|
55b5068d20 | ||
|
|
c17e48c9c5 | ||
|
|
11713ef018 | ||
|
|
b522d1fe24 | ||
|
|
dd94d98110 | ||
|
|
2f399807cb | ||
|
|
5cecd81a05 | ||
|
|
526e48b2ed | ||
|
|
a16f126614 | ||
|
|
8b815cc857 | ||
|
|
d43672833a | ||
|
|
fcb8461efa | ||
|
|
871cd4b9db | ||
|
|
fa78d937dc | ||
|
|
b302b3102e | ||
|
|
eb3aa70ffa | ||
|
|
72a2fe5125 | ||
|
|
e582f1ccb3 | ||
|
|
0e1a699937 | ||
|
|
494b5486ba | ||
|
|
33817f2c30 | ||
|
|
f44c49c5bd | ||
|
|
3dd5e11480 | ||
|
|
716ff93161 | ||
|
|
1ac5aa951d | ||
|
|
d112874078 | ||
|
|
61da33f31b | ||
|
|
e15aa20059 | ||
|
|
8d7b7ff861 | ||
|
|
d389310bb9 | ||
|
|
1ae6deda61 | ||
|
|
6d38eafda1 | ||
|
|
845be91df5 | ||
|
|
fa003a00ee | ||
|
|
45f1b19942 | ||
|
|
96917755cd | ||
|
|
8a7a776034 | ||
|
|
e33d96ad4e | ||
|
|
5a2cd98867 | ||
|
|
a1b310d186 | ||
|
|
f97c0cced0 | ||
|
|
2c964268f8 | ||
|
|
a715aeb716 | ||
|
|
d57200bc6f | ||
|
|
fbb9ce4d5c | ||
|
|
76735147b3 | ||
|
|
c2d297cc3c | ||
|
|
4886534ed3 | ||
|
|
2ed16bf2d4 | ||
|
|
c3d043e980 | ||
|
|
0cdca3a0cd | ||
|
|
5e22f6ca58 | ||
|
|
f8d538f4ad | ||
|
|
8efdc313a3 | ||
|
|
49cc3c5746 | ||
|
|
865a2f895e | ||
|
|
82e77a13f3 | ||
|
|
fca71c4bb4 | ||
|
|
3d4ec68e54 | ||
|
|
54bd380011 | ||
|
|
55905604da | ||
|
|
fd64402e47 | ||
|
|
c696ec92db | ||
|
|
4bc45bb6bc | ||
|
|
9f35ede1a8 | ||
|
|
51fa2fada1 | ||
|
|
aed04c6405 | ||
|
|
d566d21730 | ||
|
|
5c4c8c44dc | ||
|
|
47f1ab7b97 | ||
|
|
710801d880 | ||
|
|
39d0eb3a58 | ||
|
|
71dde8f7cf | ||
|
|
683a1213db | ||
|
|
3ae3050b80 | ||
|
|
bd829c093d | ||
|
|
5e20e935dd | ||
|
|
106c7d2afb | ||
|
|
755a15d8d4 | ||
|
|
a5a6a4d39e | ||
|
|
3120c6e839 | ||
|
|
0d255b4ea1 | ||
|
|
e9067c03c6 | ||
|
|
999d29d2a0 | ||
|
|
08d37c2d7e | ||
|
|
4f78a4dd3e | ||
|
|
bb82d46ef1 | ||
|
|
567900be76 | ||
|
|
cb899ccf25 | ||
|
|
710fd14393 | ||
|
|
499ad5b63c | ||
|
|
8ab6cf1ca8 | ||
|
|
577ffbc57c | ||
|
|
e918a762f7 | ||
|
|
d56000eaff | ||
|
|
cc7f76a247 | ||
|
|
adf3e3ceb5 | ||
|
|
344feb0dd1 | ||
|
|
dc6058fb9b | ||
|
|
bbf32118a2 | ||
|
|
b2cb5f55ac | ||
|
|
7083d397f3 | ||
|
|
3ddacd90d1 | ||
|
|
cb3e5d3f28 | ||
|
|
12f2c36aca | ||
|
|
a12cb273b3 | ||
|
|
1bbbb2f46b | ||
|
|
28cef86a09 | ||
|
|
9ac5666271 | ||
|
|
70ab7a53f3 | ||
|
|
70d771d1a9 | ||
|
|
45aa0a4725 | ||
|
|
d4c1cc7a34 | ||
|
|
131a0328b5 | ||
|
|
7335495db7 | ||
|
|
24f3272db3 | ||
|
|
3911c90d7b | ||
|
|
7c44af4e22 | ||
|
|
d49d7ba90c | ||
|
|
26f3dfff75 | ||
|
|
271632a72d | ||
|
|
0d02bee23f | ||
|
|
c39a0139fc | ||
|
|
06464fe2ff | ||
|
|
8eaf98d64f | ||
|
|
2a4755ddf2 | ||
|
|
814e929eeb | ||
|
|
6f00b46c4b | ||
|
|
665480372e | ||
|
|
2fa314a89d | ||
|
|
bd63124a00 | ||
|
|
b67d0a64b4 | ||
|
|
09109c55c9 | ||
|
|
c1745b5924 | ||
|
|
a7394440b9 | ||
|
|
42e4190e1a | ||
|
|
36b339bbcf | ||
|
|
fb2cc06557 | ||
|
|
ac66f6ce7a | ||
|
|
f4d99c8d56 | ||
|
|
d097028307 | ||
|
|
7fbf42960b | ||
|
|
526dbf0cf2 | ||
|
|
1c19d5c72e | ||
|
|
413395866f | ||
|
|
3ad16028aa | ||
|
|
b47747d318 | ||
|
|
5667fbfbf3 | ||
|
|
d4b58b0b06 | ||
|
|
ef356cfd4b | ||
|
|
e374759f29 | ||
|
|
0de70dc7f3 | ||
|
|
2f15b67fd9 | ||
|
|
e03667e920 | ||
|
|
11c048b06f | ||
|
|
22de3ae53e | ||
|
|
3be2514683 | ||
|
|
57658b685c | ||
|
|
5f6ff1f051 | ||
|
|
7e6f4723c3 | ||
|
|
4a1151d6b4 | ||
|
|
9994b019c5 | ||
|
|
bb930aef2d | ||
|
|
be66aedb9e | ||
|
|
4bc305db1e | ||
|
|
fdb1f4451d | ||
|
|
02497c9533 | ||
|
|
0acdce502c | ||
|
|
fe1d4a6232 | ||
|
|
c4c44d10df | ||
|
|
55bb60a56b | ||
|
|
ef8b8317ff | ||
|
|
fff127bec7 | ||
|
|
7b5289a1a4 | ||
|
|
3a0d09c1c5 | ||
|
|
3ab6012c81 | ||
|
|
add124e07a | ||
|
|
8834f3e74d | ||
|
|
489dd89a45 | ||
|
|
ba9effc8a1 | ||
|
|
0d93fdf607 | ||
|
|
b8cec0c714 | ||
|
|
4793fc0fc1 | ||
|
|
e1b44666b0 | ||
|
|
194d21a6e4 | ||
|
|
1b80f7126a | ||
|
|
1dc85802e2 | ||
|
|
9e081579bb | ||
|
|
30dad5dfa1 | ||
|
|
6d32066055 | ||
|
|
86e5545c25 | ||
|
|
f4e80d51ff | ||
|
|
1db305af1c | ||
|
|
805b4e2dc2 | ||
|
|
e8e9948223 | ||
|
|
75441dcdc3 | ||
|
|
d30565f810 | ||
|
|
5c916b645b | ||
|
|
9ad184841e | ||
|
|
7536d7f94a | ||
|
|
ee0d3f9b34 | ||
|
|
d3cf815dbe | ||
|
|
b477c2adcc | ||
|
|
78d4aa3d01 | ||
|
|
855f5c8b81 | ||
|
|
7ab0f3beec | ||
|
|
ba8c9a9d1e | ||
|
|
87c1200c93 | ||
|
|
d46e71bea3 | ||
|
|
4cf2658235 | ||
|
|
5fa48e97b6 | ||
|
|
562fb0d14b | ||
|
|
320ecc69a0 | ||
|
|
106cf7a9f7 | ||
|
|
7221c6f156 | ||
|
|
4a7cdaecb4 | ||
|
|
81d58d2cc8 | ||
|
|
1080f5eba2 | ||
|
|
18e62ad136 | ||
|
|
f03aa1d0b5 | ||
|
|
ef63d47ecd | ||
|
|
0a58af8cbf | ||
|
|
8359f9683a | ||
|
|
c3425c05e9 | ||
|
|
3fa378533a | ||
|
|
4850bb5a59 | ||
|
|
037f536e7e | ||
|
|
287fbc5fb5 | ||
|
|
f553288db3 | ||
|
|
e0e386314f | ||
|
|
1de6a7a023 | ||
|
|
d1db4c29fa | ||
|
|
a488067a41 |
6
.github/workflows/ci_bench.yml
vendored
6
.github/workflows/ci_bench.yml
vendored
@@ -21,10 +21,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 16.x'
|
||||
uses: actions/setup-node@v3
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
|
||||
2
.github/workflows/ci_docs.yml
vendored
2
.github/workflows/ci_docs.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
os: macos-11
|
||||
os: macos-13
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
9
.github/workflows/ci_packages.yml
vendored
9
.github/workflows/ci_packages.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-11]
|
||||
os: [ubuntu-20.04, macos-13]
|
||||
cpu: [amd64]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
@@ -32,10 +32,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 16.x'
|
||||
uses: actions/setup-node@v3
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
@@ -48,6 +48,7 @@ jobs:
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
# XXX can't find boehm and gtk on macos 13
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
|
||||
6
.github/workflows/ci_publish.yml
vendored
6
.github/workflows/ci_publish.yml
vendored
@@ -21,10 +21,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 16.x'
|
||||
uses: actions/setup-node@v3
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
|
||||
@@ -29,10 +29,10 @@ jobs:
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-11'
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-11'
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
@@ -130,6 +130,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
|
||||
|
||||
104
changelog.md
104
changelog.md
@@ -3,6 +3,9 @@
|
||||
|
||||
## 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.
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
@@ -11,6 +14,12 @@
|
||||
|
||||
[//]: # "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.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
|
||||
@@ -21,10 +30,101 @@
|
||||
|
||||
|
||||
|
||||
- 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
|
||||
|
||||
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
|
||||
`sink` for optimization purposes.
|
||||
|
||||
- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises.
|
||||
- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fulfill its promises.
|
||||
|
||||
- The `{.this.}` pragma, deprecated since 0.19, has been removed.
|
||||
- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
|
||||
|
||||
197
compiler/ast.nim
197
compiler/ast.nim
@@ -10,7 +10,7 @@
|
||||
# abstract syntax tree + symbol table
|
||||
|
||||
import
|
||||
lineinfos, hashes, options, ropes, idents, int128, tables
|
||||
lineinfos, hashes, options, ropes, idents, int128, tables, wordrecg
|
||||
from strutils import toLowerAscii
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
@@ -227,11 +227,12 @@ 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
|
||||
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
TSymFlag* = enum # 51 flags!
|
||||
TSymFlag* = enum # 52 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
|
||||
@@ -315,6 +316,7 @@ type
|
||||
sfVirtual # proc is a C++ virtual function
|
||||
sfByCopy # param is marked as pass bycopy
|
||||
sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl
|
||||
sfWasGenSym # symbol was 'gensym'ed
|
||||
|
||||
TSymFlags* = set[TSymFlag]
|
||||
|
||||
@@ -516,6 +518,9 @@ type
|
||||
nfFirstWrite # this node is a first write
|
||||
nfHasComment # node has a comment
|
||||
nfSkipFieldChecking # node skips field visable checking
|
||||
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)
|
||||
@@ -589,6 +594,7 @@ type
|
||||
tfEffectSystemWorkaround
|
||||
tfIsOutParam
|
||||
tfSendable
|
||||
tfImplicitStatic
|
||||
|
||||
TTypeFlags* = set[TTypeFlag]
|
||||
|
||||
@@ -638,7 +644,7 @@ const
|
||||
skError* = skUnknown
|
||||
|
||||
var
|
||||
eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam, tfSendable}
|
||||
eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam}
|
||||
## type flags that are essential for type equality.
|
||||
## This is now a variable because for emulation of version:1.0 we
|
||||
## might exclude {tfGcSafe, tfNoSideEffect}.
|
||||
@@ -898,6 +904,7 @@ type
|
||||
info*: TLineInfo
|
||||
when defined(nimsuggest):
|
||||
endInfo*: TLineInfo
|
||||
hasUserSpecifiedType*: bool # used for determining whether to display inlay type hints
|
||||
owner*: PSym
|
||||
flags*: TSymFlags
|
||||
ast*: PNode # syntax tree of proc, iterator, etc.:
|
||||
@@ -1086,7 +1093,8 @@ const
|
||||
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
|
||||
nfFromTemplate, nfDefaultRefsParam,
|
||||
nfExecuteOnReload, nfLastRead,
|
||||
nfFirstWrite, nfSkipFieldChecking}
|
||||
nfFirstWrite, nfSkipFieldChecking,
|
||||
nfDisabledOpenSym}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
@@ -1102,7 +1110,7 @@ const
|
||||
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
||||
nkCommand, nkCallStrLit, nkHiddenCallConv}
|
||||
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
|
||||
nkClosedSymChoice}
|
||||
nkClosedSymChoice, nkOpenSym}
|
||||
|
||||
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
|
||||
nkLiterals* = {nkCharLit..nkTripleStrLit}
|
||||
@@ -1130,6 +1138,7 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
|
||||
of nkSym: a.sym.name
|
||||
of nkIdent: a.ident
|
||||
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
|
||||
of nkOpenSym: getPIdent(a.sons[0])
|
||||
else: nil
|
||||
|
||||
const
|
||||
@@ -1249,8 +1258,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:
|
||||
@@ -1448,6 +1460,9 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||
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
|
||||
@@ -1456,7 +1471,42 @@ proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = castToInt64(intVal)
|
||||
|
||||
proc lastSon*(n: Indexable): Indexable = n.sons[^1]
|
||||
proc lastSon*(n: Indexable): Indexable {.inline.} = n.sons[^1]
|
||||
template setLastSon*(n: PNode, s: PNode) = n.sons[^1] = s
|
||||
|
||||
template firstSon*(n: PNode): PNode = n.sons[0]
|
||||
template secondSon*(n: PNode): PNode = n.sons[1]
|
||||
|
||||
template hasSon*(n: PNode): bool = n.len > 0
|
||||
template has2Sons*(n: PNode): bool = n.len > 1
|
||||
|
||||
proc replaceFirstSon*(n, newson: PNode) {.inline.} =
|
||||
n.sons[0] = newson
|
||||
|
||||
proc replaceSon*(n: PNode; i: int; newson: PNode) {.inline.} =
|
||||
n.sons[i] = newson
|
||||
|
||||
proc last*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc elementType*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
proc skipModifier*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc indexType*(n: PType): PType {.inline.} = n.sons[0]
|
||||
proc baseClass*(n: PType): PType {.inline.} = n.sons[0]
|
||||
|
||||
proc base*(t: PType): PType {.inline.} =
|
||||
result = t.sons[0]
|
||||
|
||||
proc returnType*(n: PType): PType {.inline.} = n.sons[0]
|
||||
proc setReturnType*(n, r: PType) {.inline.} = n.sons[0] = r
|
||||
proc setIndexType*(n, idx: PType) {.inline.} = n.sons[0] = idx
|
||||
|
||||
proc firstParamType*(n: PType): PType {.inline.} = n.sons[1]
|
||||
proc firstGenericParam*(n: PType): PType {.inline.} = n.sons[1]
|
||||
|
||||
proc typeBodyImpl*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc genericHead*(n: PType): PType {.inline.} = n.sons[0]
|
||||
|
||||
proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||
## Used throughout the compiler code to test whether a type tree contains or
|
||||
@@ -1464,7 +1514,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||
## last child nodes of a type tree need to be searched. This is a really hot
|
||||
## path within the compiler!
|
||||
result = t
|
||||
while result.kind in kinds: result = lastSon(result)
|
||||
while result.kind in kinds: result = last(result)
|
||||
|
||||
proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
|
||||
let kind = skipTypes(typ, abstractVarRange).kind
|
||||
@@ -1523,6 +1573,107 @@ proc `$`*(s: PSym): string =
|
||||
else:
|
||||
result = "<nil>"
|
||||
|
||||
when false:
|
||||
iterator items*(t: PType): PType =
|
||||
for i in 0..<t.sons.len: yield t.sons[i]
|
||||
|
||||
iterator pairs*(n: PType): tuple[i: int, n: PType] =
|
||||
for i in 0..<n.sons.len: yield (i, n.sons[i])
|
||||
|
||||
when true:
|
||||
proc len*(n: PType): int {.inline.} =
|
||||
result = n.sons.len
|
||||
|
||||
proc sameTupleLengths*(a, b: PType): bool {.inline.} =
|
||||
result = a.sons.len == b.sons.len
|
||||
|
||||
iterator tupleTypePairs*(a, b: PType): (int, PType, PType) =
|
||||
for i in 0 ..< a.sons.len:
|
||||
yield (i, a.sons[i], b.sons[i])
|
||||
|
||||
iterator underspecifiedPairs*(a, b: PType; start = 0; without = 0): (PType, PType) =
|
||||
# XXX Figure out with what typekinds this is called.
|
||||
for i in start ..< min(a.sons.len, b.sons.len) + without:
|
||||
yield (a.sons[i], b.sons[i])
|
||||
|
||||
proc signatureLen*(t: PType): int {.inline.} =
|
||||
result = t.sons.len
|
||||
|
||||
proc paramsLen*(t: PType): int {.inline.} =
|
||||
result = t.sons.len - 1
|
||||
|
||||
proc genericParamsLen*(t: PType): int {.inline.} =
|
||||
assert t.kind == tyGenericInst
|
||||
result = t.sons.len - 2 # without 'head' and 'body'
|
||||
|
||||
proc genericInvocationParamsLen*(t: PType): int {.inline.} =
|
||||
assert t.kind == tyGenericInvocation
|
||||
result = t.sons.len - 1 # without 'head'
|
||||
|
||||
proc kidsLen*(t: PType): int {.inline.} =
|
||||
result = t.sons.len
|
||||
|
||||
proc genericParamHasConstraints*(t: PType): bool {.inline.} = t.sons.len > 0
|
||||
|
||||
proc hasElementType*(t: PType): bool {.inline.} = t.sons.len > 0
|
||||
proc isEmptyTupleType*(t: PType): bool {.inline.} = t.sons.len == 0
|
||||
proc isSingletonTupleType*(t: PType): bool {.inline.} = t.sons.len == 1
|
||||
|
||||
proc genericConstraint*(t: PType): PType {.inline.} = t.sons[0]
|
||||
|
||||
iterator genericInstParams*(t: PType): (bool, PType) =
|
||||
for i in 1..<t.sons.len-1:
|
||||
yield (i!=1, t.sons[i])
|
||||
|
||||
iterator genericInstParamPairs*(a, b: PType): (int, PType, PType) =
|
||||
for i in 1..<min(a.sons.len, b.sons.len)-1:
|
||||
yield (i-1, a.sons[i], b.sons[i])
|
||||
|
||||
iterator genericInvocationParams*(t: PType): (bool, PType) =
|
||||
for i in 1..<t.sons.len:
|
||||
yield (i!=1, t.sons[i])
|
||||
|
||||
iterator genericInvocationAndBodyElements*(a, b: PType): (PType, PType) =
|
||||
for i in 1..<a.sons.len:
|
||||
yield (a.sons[i], b.sons[i-1])
|
||||
|
||||
iterator genericInvocationParamPairs*(a, b: PType): (bool, PType, PType) =
|
||||
for i in 1..<a.sons.len:
|
||||
if i >= b.sons.len:
|
||||
yield (false, nil, nil)
|
||||
else:
|
||||
yield (true, a.sons[i], b.sons[i])
|
||||
|
||||
iterator genericBodyParams*(t: PType): (int, PType) =
|
||||
for i in 0..<t.sons.len-1:
|
||||
yield (i, t.sons[i])
|
||||
|
||||
iterator userTypeClassInstParams*(t: PType): (bool, PType) =
|
||||
for i in 1..<t.sons.len-1:
|
||||
yield (i!=1, t.sons[i])
|
||||
|
||||
iterator ikids*(t: PType): (int, PType) =
|
||||
for i in 0..<t.sons.len: yield (i, t.sons[i])
|
||||
|
||||
const
|
||||
FirstParamAt* = 1
|
||||
FirstGenericParamAt* = 1
|
||||
|
||||
iterator paramTypes*(t: PType): (int, PType) =
|
||||
for i in FirstParamAt..<t.sons.len: yield (i, t.sons[i])
|
||||
|
||||
iterator paramTypePairs*(a, b: PType): (PType, PType) =
|
||||
for i in FirstParamAt..<a.sons.len: yield (a.sons[i], b.sons[i])
|
||||
|
||||
template paramTypeToNodeIndex*(x: int): int = x
|
||||
|
||||
iterator kids*(t: PType): PType =
|
||||
for i in 0..<t.sons.len: yield t.sons[i]
|
||||
|
||||
iterator signature*(t: PType): PType =
|
||||
# yields return type + parameter types
|
||||
for i in 0..<t.sons.len: yield t.sons[i]
|
||||
|
||||
proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
|
||||
result = PType(kind: kind, owner: owner, size: defaultSize,
|
||||
align: defaultAlignment, itemId: id,
|
||||
@@ -1633,7 +1784,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
|
||||
result = t
|
||||
var i = maxIters
|
||||
while result.kind in kinds:
|
||||
result = lastSon(result)
|
||||
result = last(result)
|
||||
dec i
|
||||
if i == 0: return nil
|
||||
|
||||
@@ -1642,7 +1793,7 @@ proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
|
||||
result = t
|
||||
while result != nil and result.kind in kinds:
|
||||
if result.len == 0: return nil
|
||||
result = lastSon(result)
|
||||
result = last(result)
|
||||
|
||||
proc isGCedMem*(t: PType): bool {.inline.} =
|
||||
result = t.kind in {tyString, tyRef, tySequence} or
|
||||
@@ -1911,13 +2062,15 @@ proc skipGenericOwner*(s: PSym): PSym =
|
||||
## Generic instantiations are owned by their originating generic
|
||||
## symbol. This proc skips such owners and goes straight to the owner
|
||||
## of the generic itself (the module or the enclosing proc).
|
||||
result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
|
||||
result = if s.kind == skModule:
|
||||
s
|
||||
elif s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule:
|
||||
s.owner.owner
|
||||
else:
|
||||
s.owner
|
||||
|
||||
proc originatingModule*(s: PSym): PSym =
|
||||
result = s.owner
|
||||
result = s
|
||||
while result.kind != skModule: result = result.owner
|
||||
|
||||
proc isRoutine*(s: PSym): bool {.inline.} =
|
||||
@@ -1979,7 +2132,7 @@ proc toObject*(typ: PType): PType =
|
||||
## cases should be a ``tyObject``).
|
||||
## Otherwise ``typ`` is simply returned as-is.
|
||||
let t = typ.skipTypes({tyAlias, tyGenericInst})
|
||||
if t.kind == tyRef: t.lastSon
|
||||
if t.kind == tyRef: t.elementType
|
||||
else: typ
|
||||
|
||||
proc toObjectFromRefPtrGeneric*(typ: PType): PType =
|
||||
@@ -1996,7 +2149,7 @@ proc toObjectFromRefPtrGeneric*(typ: PType): PType =
|
||||
result = typ
|
||||
while true:
|
||||
case result.kind
|
||||
of tyGenericBody: result = result.lastSon
|
||||
of tyGenericBody: result = result.last
|
||||
of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0]
|
||||
# automatic dereferencing is deep, refs #18298.
|
||||
else: break
|
||||
@@ -2014,7 +2167,7 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
||||
result = true
|
||||
|
||||
proc isInfixAs*(n: PNode): bool =
|
||||
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as"
|
||||
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == ord(wAs)
|
||||
|
||||
proc skipColon*(n: PNode): PNode =
|
||||
result = n
|
||||
@@ -2022,10 +2175,12 @@ proc skipColon*(n: PNode): PNode =
|
||||
result = n[1]
|
||||
|
||||
proc findUnresolvedStatic*(n: PNode): PNode =
|
||||
# n.typ == nil: see issue #14802
|
||||
if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil:
|
||||
return n
|
||||
|
||||
if n.typ != nil and n.typ.kind == tyTypeDesc:
|
||||
let t = skipTypes(n.typ, {tyTypeDesc})
|
||||
if t.kind == tyGenericParam and t.len == 0:
|
||||
return n
|
||||
for son in n:
|
||||
let n = son.findUnresolvedStatic
|
||||
if n != nil: return n
|
||||
@@ -2063,6 +2218,12 @@ proc isClosureIterator*(typ: PType): bool {.inline.} =
|
||||
proc isClosure*(typ: PType): bool {.inline.} =
|
||||
typ.kind == tyProc and typ.callConv == ccClosure
|
||||
|
||||
proc isNimcall*(s: PSym): bool {.inline.} =
|
||||
s.typ.callConv == ccNimCall
|
||||
|
||||
proc isExplicitCallConv*(s: PSym): bool {.inline.} =
|
||||
tfExplicitCallConv in s.typ.flags
|
||||
|
||||
proc isSinkParam*(s: PSym): bool {.inline.} =
|
||||
s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)
|
||||
|
||||
|
||||
@@ -890,7 +890,7 @@ proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
|
||||
result = nextIter(ti, tab)
|
||||
|
||||
iterator items*(tab: TStrTable): PSym =
|
||||
var it: TTabIter
|
||||
var it: TTabIter = default(TTabIter)
|
||||
var s = initTabIter(it, tab)
|
||||
while s != nil:
|
||||
yield s
|
||||
@@ -1054,14 +1054,6 @@ proc iiTablePut(t: var TIITable, key, val: int) =
|
||||
iiTableRawInsert(t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
proc isAddrNode*(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkAddr, nkHiddenAddr: true
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mAddr: true
|
||||
else: false
|
||||
else: false
|
||||
|
||||
proc listSymbolNames*(symbols: openArray[PSym]): string =
|
||||
for sym in symbols:
|
||||
if result.len > 0:
|
||||
|
||||
@@ -24,6 +24,12 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
||||
result.add "]"
|
||||
|
||||
proc addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
result.add " [$1" % typ.kind.toHumanStr
|
||||
if typ.sym != nil:
|
||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
||||
result.add "]"
|
||||
|
||||
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ proc canRaiseDisp(p: BProc; n: PNode): bool =
|
||||
|
||||
proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
|
||||
proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
|
||||
result = false
|
||||
var n = le
|
||||
while true:
|
||||
# do NOT follow nkHiddenDeref here!
|
||||
@@ -82,6 +83,10 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if typ[0] != nil:
|
||||
var flags: TAssignmentFlags = {}
|
||||
if typ[0].kind in {tyOpenArray, tyVarargs}:
|
||||
# perhaps generate no temp if the call doesn't have side effects
|
||||
flags.incl needTempForOpenArray
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if params.len != 0: pl.add(", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
@@ -129,7 +134,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
genAssignment(p, d, list, flags) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
@@ -137,7 +142,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, tmp, list, {}) # no need for deep copying
|
||||
genAssignment(p, tmp, list, flags) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
else:
|
||||
@@ -145,12 +150,18 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
line(p, cpsStmts, pl)
|
||||
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
|
||||
while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}:
|
||||
x = x[0]
|
||||
while true:
|
||||
case x.kind
|
||||
of {nkAddr, nkHiddenAddr, nkHiddenDeref}:
|
||||
x = x[0]
|
||||
of nkHiddenStdConv:
|
||||
x = x[1]
|
||||
else:
|
||||
break
|
||||
if x.kind == nkSym and x.sym.kind == skParam:
|
||||
result = false
|
||||
else:
|
||||
@@ -161,12 +172,15 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||
initLocExpr(p, q[1], a)
|
||||
initLocExpr(p, q[2], b)
|
||||
initLocExpr(p, q[3], c)
|
||||
# bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
|
||||
# are mapped into ctPtrToArray, the dereference of which is skipped
|
||||
# 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)
|
||||
genBoundsCheck(p, a, b, c, ty)
|
||||
if prepareForMutation:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr})
|
||||
let dest = getTypeDesc(p.module, destType)
|
||||
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
||||
case ty.kind
|
||||
@@ -301,18 +315,33 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
|
||||
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
|
||||
n.kind == nkHiddenAddr:
|
||||
initLocExprSingleUse(p, n[0], a)
|
||||
# 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
|
||||
initLocExprSingleUse(p, n, a)
|
||||
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:
|
||||
initLocExprSingleUse(p, n, a)
|
||||
if param.typ.kind in abstractPtrs:
|
||||
let typ = skipTypes(param.typ, abstractPtrs)
|
||||
if typ.sym != nil and sfImportc in typ.sym.flags:
|
||||
a.r = "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
|
||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
||||
#assert result != nil
|
||||
|
||||
@@ -394,9 +423,11 @@ 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:
|
||||
|
||||
@@ -285,15 +285,23 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
|
||||
[addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfoV1(p.module, dest.t, dest.lode.info)])
|
||||
|
||||
proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) =
|
||||
proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) =
|
||||
assert d.k != locNone
|
||||
# getTemp(p, d.t, d)
|
||||
|
||||
case a.t.skipTypes(abstractVar).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
if reifiedOpenArray(a.lode):
|
||||
linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
|
||||
[rdLoc(d), a.rdLoc])
|
||||
if needTempForOpenArray in flags:
|
||||
var tmp: TLoc
|
||||
getTemp(p, a.t, tmp)
|
||||
linefmt(p, cpsStmts, "$2 = $1; $n",
|
||||
[a.rdLoc, tmp.rdLoc])
|
||||
linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
|
||||
[rdLoc(d), tmp.rdLoc])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
|
||||
[rdLoc(d), a.rdLoc])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $2Len_0;$n",
|
||||
[rdLoc(d), a.rdLoc])
|
||||
@@ -336,7 +344,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
of tyString:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
genGenericAsgn(p, dest, src, flags)
|
||||
elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
|
||||
elif ({needToCopy, needToCopySinkParam} * flags == {} and src.storage != OnStatic) or canMove(p, src.lode, dest):
|
||||
genRefAssign(p, dest, src)
|
||||
else:
|
||||
if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
|
||||
@@ -392,7 +400,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
# open arrays are always on the stack - really? What if a sequence is
|
||||
# passed to an open array?
|
||||
if reifiedOpenArray(dest.lode):
|
||||
genOpenArrayConv(p, dest, src)
|
||||
genOpenArrayConv(p, dest, src, flags)
|
||||
elif containsGarbageCollectedRef(dest.t):
|
||||
linefmt(p, cpsStmts, # XXX: is this correct for arrays?
|
||||
"#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
|
||||
@@ -594,7 +602,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
# skipping 'range' is correct here as we'll generate a proper range check
|
||||
# later via 'chckRange'
|
||||
let t = e.typ.skipTypes(abstractRange)
|
||||
if optOverflowCheck notin p.options:
|
||||
if optOverflowCheck notin p.options or (m in {mSucc, mPred} and t.kind in {tyUInt..tyUInt64}):
|
||||
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
||||
putIntoDest(p, d, e, res)
|
||||
else:
|
||||
@@ -616,7 +624,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
if t.kind == tyInt64: prc64[m] else: prc[m])
|
||||
putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
|
||||
else:
|
||||
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
||||
let res = "($1)(($2) $3 ($4))" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
||||
putIntoDest(p, d, e, res)
|
||||
|
||||
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
@@ -824,6 +832,8 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
|
||||
#Message(e.info, warnUser, "HERE NEW &")
|
||||
elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ):
|
||||
expr(p, e[0], d)
|
||||
# bug #19497
|
||||
d.lode = e
|
||||
else:
|
||||
var a: TLoc
|
||||
initLocExpr(p, e[0], a)
|
||||
@@ -1029,8 +1039,8 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
putIntoDest(p, d, n,
|
||||
ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
|
||||
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
|
||||
let ty = skipTypes(arr.t, abstractVarRange)
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) =
|
||||
let ty = arrTyp
|
||||
case ty.kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
if reifiedOpenArray(arr.lode):
|
||||
@@ -1496,11 +1506,12 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
|
||||
initLocExpr(p, e[1], a)
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
if d.k == locNone: getTemp(p, e.typ, d, needsInit=false)
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3));$n",
|
||||
[d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
|
||||
getSeqPayloadType(p.module, seqtype),
|
||||
])
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, e.typ, d, needsInit=false) # bug #22560
|
||||
putIntoDest(p, d, e, ropecg(p.module,
|
||||
"($1)#nimNewSeqOfCap($2, $3)", [
|
||||
getTypeDesc(p.module, seqtype),
|
||||
@@ -1550,6 +1561,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
|
||||
var tmp: TLoc
|
||||
var r: Rope
|
||||
let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc} or nfAllFieldsSet notin e.flags
|
||||
if useTemp:
|
||||
getTemp(p, t, tmp)
|
||||
r = rdLoc(tmp)
|
||||
@@ -1558,10 +1570,13 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
t = t.lastSon.skipTypes(abstractInstOwned)
|
||||
r = "(*$1)" % [r]
|
||||
gcUsage(p.config, e)
|
||||
else:
|
||||
elif needsZeroMem:
|
||||
constructLoc(p, tmp)
|
||||
else:
|
||||
genObjectInit(p, cpsStmts, t, tmp, constructObj)
|
||||
else:
|
||||
resetLoc(p, d)
|
||||
if needsZeroMem: resetLoc(p, d)
|
||||
else: genObjectInit(p, cpsStmts, d.t, d, if isRef: constructRefObj else: constructObj)
|
||||
r = rdLoc(d)
|
||||
discard getTypeDesc(p.module, t)
|
||||
let ty = getUniqueType(t)
|
||||
@@ -1874,11 +1889,11 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
initLocExpr(p, a[2], b)
|
||||
initLocExpr(p, a[3], c)
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, m, b, c)
|
||||
genBoundsCheck(p, m, b, c, skipTypes(m.t, abstractVarRange))
|
||||
if op == mHigh:
|
||||
putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)", [rdLoc(b), rdLoc(c)]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)]))
|
||||
else:
|
||||
putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1)+1)", [rdLoc(b), rdLoc(c)]))
|
||||
else:
|
||||
if not reifiedOpenArray(a):
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
|
||||
@@ -2120,7 +2135,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mCard:
|
||||
var a: TLoc
|
||||
initLocExpr(p, e[1], a)
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [addrLoc(p.config, a), size]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [rdCharLoc(a), size]))
|
||||
of mLtSet, mLeSet:
|
||||
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter
|
||||
initLocExpr(p, e[1], a)
|
||||
@@ -2363,12 +2378,31 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
if op == nil:
|
||||
resetLoc(p, a)
|
||||
else:
|
||||
let addrExp = makeAddr(n[1], p.module.idgen)
|
||||
let wasMovedCall = newTreeI(nkCall, n.info, newSymNode(op), addrExp)
|
||||
genCall(p, wasMovedCall, d)
|
||||
var b: TLoc
|
||||
initLocExpr(p, newSymNode(op), b)
|
||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
||||
of tyOpenArray, tyVarargs: # todo fixme generated `wasMoved` hooks for
|
||||
# openarrays, but it probably shouldn't?
|
||||
var s: string
|
||||
if reifiedOpenArray(a.lode):
|
||||
if a.t.kind in {tyVar, tyLent}:
|
||||
s = "$1->Field0, $1->Field1" % [rdLoc(a)]
|
||||
else:
|
||||
s = "$1.Field0, $1.Field1" % [rdLoc(a)]
|
||||
else:
|
||||
s = "$1, $1Len_0" % [rdLoc(a)]
|
||||
linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), s])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)])
|
||||
else:
|
||||
let flags = if not canMove(p, n[1], d): {needToCopy} else: {}
|
||||
genAssignment(p, d, a, flags)
|
||||
if n[1].kind == nkSym and isSinkParam(n[1].sym):
|
||||
var tmp: TLoc
|
||||
getTemp(p, n[1].typ.skipTypes({tySink}), tmp)
|
||||
genAssignment(p, tmp, a, {needToCopySinkParam})
|
||||
genAssignment(p, d, tmp, {})
|
||||
resetLoc(p, tmp)
|
||||
else:
|
||||
genAssignment(p, d, a, {})
|
||||
resetLoc(p, a)
|
||||
|
||||
proc genDestroy(p: BProc; n: PNode) =
|
||||
@@ -2607,7 +2641,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mDeepCopy:
|
||||
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
|
||||
localError(p.config, e.info,
|
||||
"for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
|
||||
"for --mm:arc|atomicArc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
|
||||
|
||||
var a, b: TLoc
|
||||
let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
|
||||
@@ -3282,7 +3316,8 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
||||
getNullValueAux(p, t, it, constOrNil, result, count, isConst, info)
|
||||
of nkRecCase:
|
||||
getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info)
|
||||
if count > 0: result.add ", "
|
||||
var res = ""
|
||||
if count > 0: res.add ", "
|
||||
var branch = Zero
|
||||
if constOrNil != nil:
|
||||
## find kind value, default is zero if not specified
|
||||
@@ -3296,18 +3331,21 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
||||
break
|
||||
|
||||
let selectedBranch = caseObjDefaultBranch(obj, branch)
|
||||
result.add "{"
|
||||
res.add "{"
|
||||
var countB = 0
|
||||
let b = lastSon(obj[selectedBranch])
|
||||
# designated initilization is the only way to init non first element of unions
|
||||
# branches are allowed to have no members (b.len == 0), in this case they don't need initializer
|
||||
if b.kind == nkRecList and not isEmptyCaseObjectBranch(b):
|
||||
result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
|
||||
getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info)
|
||||
result.add "}"
|
||||
res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
|
||||
getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info)
|
||||
res.add "}"
|
||||
elif b.kind == nkSym:
|
||||
result.add "." & mangleRecFieldName(p.module, b.sym) & " = "
|
||||
getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info)
|
||||
res.add "." & mangleRecFieldName(p.module, b.sym) & " = "
|
||||
getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info)
|
||||
else:
|
||||
return
|
||||
result.add res
|
||||
result.add "}"
|
||||
|
||||
of nkSym:
|
||||
|
||||
@@ -83,7 +83,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
|
||||
else:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
of tyChar, tyBool, tyEnum, tyInt..tyUInt64:
|
||||
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
|
||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
@@ -97,7 +97,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
else:
|
||||
doAssert false, "unexpected set type kind"
|
||||
of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
||||
tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs,
|
||||
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
|
||||
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
|
||||
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}:
|
||||
|
||||
@@ -971,8 +971,11 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
||||
hasDefault = true
|
||||
exprBlock(p, branch.lastSon, d)
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
|
||||
lineF(p, cpsStmts, "default: __assume(0);$n", [])
|
||||
if not hasDefault:
|
||||
if hasBuiltinUnreachable in CC[p.config.cCompiler].props:
|
||||
lineF(p, cpsStmts, "default: __builtin_unreachable();$n", [])
|
||||
elif hasAssume in CC[p.config.cCompiler].props:
|
||||
lineF(p, cpsStmts, "default: __assume(0);$n", [])
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
if lend != "": fixLabel(p, lend)
|
||||
|
||||
|
||||
@@ -54,13 +54,31 @@ proc mangleField(m: BModule; name: PIdent): string =
|
||||
if isKeyword(name):
|
||||
result.add "_0"
|
||||
|
||||
proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
|
||||
result = "_Z" # Common prefix in Itanium ABI
|
||||
result.add encodeSym(m, s, makeUnique)
|
||||
if s.typ.len > 1: #we dont care about the return param
|
||||
for i in 1..<s.typ.len:
|
||||
if s.typ[i].isNil: continue
|
||||
result.add encodeType(m, s.typ[i])
|
||||
|
||||
if result in m.g.mangledPrcs:
|
||||
result = mangleProc(m, s, true)
|
||||
else:
|
||||
m.g.mangledPrcs.incl(result)
|
||||
|
||||
proc fillBackendName(m: BModule; s: PSym) =
|
||||
if s.loc.r == "":
|
||||
var result = s.name.s.mangle.rope
|
||||
result.add "__"
|
||||
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
||||
result.add "_u"
|
||||
result.addInt s.itemId.item # s.disamb #
|
||||
var result: Rope
|
||||
if not m.compileToCpp and s.kind in routineKinds and optCDebug in m.g.config.globalOptions and
|
||||
m.g.config.symbolFiles == disabledSf:
|
||||
result = mangleProc(m, s, false).rope
|
||||
else:
|
||||
result = s.name.s.mangle.rope
|
||||
result.add "__"
|
||||
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
||||
result.add "_u"
|
||||
result.addInt s.itemId.item # s.disamb #
|
||||
if m.hcrOn:
|
||||
result.add '_'
|
||||
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
|
||||
@@ -252,9 +270,15 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
|
||||
{tyVar, tyLent, tyRef, tyPtr})
|
||||
of ctStruct:
|
||||
let t = skipTypes(rettype, typedescInst)
|
||||
if rettype.isImportedCppType or t.isImportedCppType: return false
|
||||
result = containsGarbageCollectedRef(t) or
|
||||
(t.kind == tyObject and not isObjLackingTypeField(t))
|
||||
if rettype.isImportedCppType or t.isImportedCppType or
|
||||
(typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc}):
|
||||
# prevents nrvo for cdecl procs; # bug #23401
|
||||
result = false
|
||||
else:
|
||||
result = containsGarbageCollectedRef(t) or
|
||||
(t.kind == tyObject and not isObjLackingTypeField(t)) or
|
||||
(getSize(conf, rettype) == szUnknownSize and (t.sym == nil or sfImportc notin t.sym.flags))
|
||||
|
||||
else: result = false
|
||||
|
||||
const
|
||||
@@ -642,6 +666,13 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope =
|
||||
result = rope(mangleField(m, field.name))
|
||||
if result == "": internalError(m.config, field.info, "mangleRecFieldName")
|
||||
|
||||
proc hasCppCtor(m: BModule; typ: PType): bool =
|
||||
result = false
|
||||
if m.compileToCpp and typ != nil and typ.itemId in m.g.graph.memberProcsPerType:
|
||||
for prc in m.g.graph.memberProcsPerType[typ.itemId]:
|
||||
if sfConstructor in prc.flags:
|
||||
return true
|
||||
|
||||
proc genRecordFieldsAux(m: BModule; n: PNode,
|
||||
rectype: PType,
|
||||
check: var IntSet; result: var Rope; unionPrefix = "") =
|
||||
@@ -706,7 +737,7 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
|
||||
else:
|
||||
# don't use fieldType here because we need the
|
||||
# tyGenericInst for C++ template support
|
||||
if fieldType.isOrHasImportedCppType():
|
||||
if fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ):
|
||||
result.addf("\t$1$3 $2{};$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias])
|
||||
else:
|
||||
result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias])
|
||||
@@ -729,7 +760,7 @@ proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope =
|
||||
genMemberProcHeader(m, prc, header, false, true)
|
||||
result.addf "$1;$n", [header]
|
||||
if isCtorGen and not isDefaultCtorGen:
|
||||
var ch: IntSet
|
||||
var ch: IntSet = default(IntSet)
|
||||
result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)]
|
||||
|
||||
proc fillObjectFields*(m: BModule; typ: PType) =
|
||||
@@ -1404,7 +1435,7 @@ proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo
|
||||
else:
|
||||
genTypeInfoAuxBase(m, typ, origType, name, rope("0"), info)
|
||||
var tmp = getNimNode(m)
|
||||
if not isImportedType(typ):
|
||||
if (not isImportedType(typ)) or tfCompleteStruct in typ.flags:
|
||||
genObjectFields(m, typ, origType, typ.n, tmp, info)
|
||||
m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), tmp])
|
||||
var t = typ[0]
|
||||
@@ -1563,7 +1594,7 @@ proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TType
|
||||
))
|
||||
)
|
||||
else:
|
||||
let addrOf = newNodeIT(nkAddr, info, theProc.typ[1])
|
||||
let addrOf = newNodeIT(nkHiddenAddr, info, theProc.typ[1])
|
||||
addrOf.add newDeref(newTreeIT(
|
||||
nkCast, info, castType, newNodeIT(nkType, info, castType),
|
||||
newSymNode(dest)
|
||||
@@ -1909,8 +1940,11 @@ proc genTypeSection(m: BModule, n: PNode) =
|
||||
if len(n[i]) == 0: continue
|
||||
if n[i][0].kind != nkPragmaExpr: continue
|
||||
for p in 0..<n[i][0].len:
|
||||
if (n[i][0][p].kind != nkSym): continue
|
||||
if sfExportc in n[i][0][p].sym.flags:
|
||||
discard getTypeDescAux(m, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind))
|
||||
if (n[i][0][p].kind notin {nkSym, nkPostfix}): continue
|
||||
var s = n[i][0][p]
|
||||
if s.kind == nkPostfix:
|
||||
s = n[i][0][p][1]
|
||||
if {sfExportc, sfCompilerProc} * s.sym.flags == {sfExportc}:
|
||||
discard getTypeDescAux(m, s.typ, intSet, descKindFromSymKind(s.sym.kind))
|
||||
if m.g.generatedHeader != nil:
|
||||
discard getTypeDescAux(m.g.generatedHeader, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind))
|
||||
discard getTypeDescAux(m.g.generatedHeader, s.typ, intSet, descKindFromSymKind(s.sym.kind))
|
||||
|
||||
@@ -13,6 +13,8 @@ import
|
||||
ast, types, hashes, strutils, msgs, wordrecg,
|
||||
platform, trees, options, cgendata
|
||||
|
||||
import std/[hashes, strutils, formatfloat]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
@@ -121,10 +123,10 @@ proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
|
||||
proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
||||
var pt = skipTypes(s.typ, typedescInst)
|
||||
assert skResult != s.kind
|
||||
|
||||
|
||||
#note precedence: params override types
|
||||
if optByRef in s.options: return true
|
||||
elif sfByCopy in s.flags: return false
|
||||
elif sfByCopy in s.flags: return false
|
||||
elif tfByRef in pt.flags: return true
|
||||
elif tfByCopy in pt.flags: return false
|
||||
case pt.kind
|
||||
@@ -132,6 +134,9 @@ 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):
|
||||
@@ -148,3 +153,64 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
||||
result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
|
||||
pt.kind == tySet and mapSetType(conf, pt) == ctArray)
|
||||
|
||||
proc encodeName*(name: string): string =
|
||||
result = mangle(name)
|
||||
result = $result.len & result
|
||||
|
||||
proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
|
||||
result = if name == "": s.name.s else: name
|
||||
result.add "__"
|
||||
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
||||
result.add "_u"
|
||||
result.add $s.itemId.item
|
||||
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
|
||||
#Module::Type
|
||||
var name = s.name.s
|
||||
if makeUnique:
|
||||
name = makeUnique(m, s, name)
|
||||
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
|
||||
|
||||
proc elementType*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc encodeType*(m: BModule; t: PType): string =
|
||||
result = ""
|
||||
var kindName = ($t.kind)[2..^1]
|
||||
kindName[0] = toLower($kindName[0])[0]
|
||||
case t.kind
|
||||
of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
|
||||
result = encodeSym(m, t.sym)
|
||||
of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
|
||||
result = encodeName(t[0].sym.name.s)
|
||||
result.add "I"
|
||||
for i in 1..<t.len - 1:
|
||||
result.add encodeType(m, t[i])
|
||||
result.add "E"
|
||||
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
result =
|
||||
case t.kind:
|
||||
of tySequence: encodeName("seq")
|
||||
else: encodeName(kindName)
|
||||
result.add "I"
|
||||
for i in 0..<t.len:
|
||||
let s = t[i]
|
||||
if s.isNil: continue
|
||||
result.add encodeType(m, s)
|
||||
result.add "E"
|
||||
of tyRange:
|
||||
var val = "range_"
|
||||
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
|
||||
val.addFloat t.n[0].floatVal
|
||||
val.add "_"
|
||||
val.addFloat t.n[1].floatVal
|
||||
else:
|
||||
val.add $t.n[0].intVal & "_" & $t.n[1].intVal
|
||||
result = encodeName(val)
|
||||
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
|
||||
result = encodeName(kindName)
|
||||
of tyAlias, tyInferred, tyOwned:
|
||||
result = encodeType(m, t.elementType)
|
||||
else:
|
||||
assert false, "encodeType " & $t.kind
|
||||
|
||||
|
||||
@@ -30,6 +30,12 @@ import strutils except `%`, addf # collides with ropes.`%`
|
||||
from ic / ic import ModuleBackendFlag
|
||||
import dynlib
|
||||
|
||||
const
|
||||
# we use some ASCII control characters to insert directives that will be converted to real code in a postprocessing pass
|
||||
postprocessDirStart = '\1'
|
||||
postprocessDirSep = '\31'
|
||||
postprocessDirEnd = '\23'
|
||||
|
||||
when not declared(dynlib.libCandidates):
|
||||
proc libCandidates(s: string, dest: var seq[string]) =
|
||||
## given a library name pattern `s` write possible library names to `dest`.
|
||||
@@ -266,24 +272,28 @@ proc safeLineNm(info: TLineInfo): int =
|
||||
result = toLinenumber(info)
|
||||
if result < 0: result = 0 # negative numbers are not allowed in #line
|
||||
|
||||
proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
|
||||
proc genPostprocessDir(field1, field2, field3: string): string =
|
||||
result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd
|
||||
|
||||
proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) =
|
||||
assert line >= 0
|
||||
if optLineDir in conf.options and line > 0:
|
||||
r.addf("\n#line $2 $1\n",
|
||||
[rope(makeSingleLineCString(filename)), rope(line)])
|
||||
if fileIdx == InvalidFileIdx:
|
||||
r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
|
||||
else:
|
||||
r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
|
||||
|
||||
proc genCLineDir(r: var Rope, filename: string, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
|
||||
proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
|
||||
assert line >= 0
|
||||
if optLineDir in p.config.options and line > 0:
|
||||
if lastFileIndex == info.fileIndex:
|
||||
r.addf("\n#line $1\n", [rope(line)])
|
||||
if fileIdx == InvalidFileIdx:
|
||||
r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
|
||||
else:
|
||||
r.addf("\n#line $2 $1\n",
|
||||
[rope(makeSingleLineCString(filename)), rope(line)])
|
||||
r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
|
||||
|
||||
proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
|
||||
if optLineDir in conf.options:
|
||||
genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
|
||||
genCLineDir(r, info.fileIndex, info.safeLineNm, conf)
|
||||
|
||||
proc freshLineInfo(p: BProc; info: TLineInfo): bool =
|
||||
if p.lastLineInfo.line != info.line or
|
||||
@@ -296,7 +306,7 @@ proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) =
|
||||
if optLineDir in conf.options:
|
||||
let lastFileIndex = p.lastLineInfo.fileIndex
|
||||
if freshLineInfo(p, info):
|
||||
genCLineDir(r, toFullPath(conf, info), info.safeLineNm, p, info, lastFileIndex)
|
||||
genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex)
|
||||
|
||||
proc genLineDir(p: BProc, t: PNode) =
|
||||
let line = t.info.safeLineNm
|
||||
@@ -306,16 +316,11 @@ proc genLineDir(p: BProc, t: PNode) =
|
||||
let lastFileIndex = p.lastLineInfo.fileIndex
|
||||
let freshLine = freshLineInfo(p, t.info)
|
||||
if freshLine:
|
||||
genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p, t.info, lastFileIndex)
|
||||
genCLineDir(p.s(cpsStmts), t.info.fileIndex, line, p, t.info, lastFileIndex)
|
||||
if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
|
||||
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
|
||||
if freshLine:
|
||||
if lastFileIndex == t.info.fileIndex:
|
||||
linefmt(p, cpsStmts, "nimln_($1);\n",
|
||||
[line])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "nimlf_($1, $2);\n",
|
||||
[line, quotedFilename(p.config, t.info)])
|
||||
line(p, cpsStmts, genPostprocessDir("nimln", $line, $t.info.fileIndex.int32))
|
||||
|
||||
proc accessThreadLocalVar(p: BProc, s: PSym)
|
||||
proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
|
||||
@@ -401,6 +406,8 @@ proc rdCharLoc(a: TLoc): Rope =
|
||||
type
|
||||
TAssignmentFlag = enum
|
||||
needToCopy
|
||||
needToCopySinkParam
|
||||
needTempForOpenArray
|
||||
TAssignmentFlags = set[TAssignmentFlag]
|
||||
|
||||
proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
|
||||
@@ -986,11 +993,19 @@ proc closureSetup(p: BProc, prc: PSym) =
|
||||
linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
|
||||
[rdLoc(env.loc), getTypeDesc(p.module, env.typ)])
|
||||
|
||||
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
|
||||
nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} +
|
||||
declarativeDefs
|
||||
|
||||
proc containsResult(n: PNode): bool =
|
||||
result = false
|
||||
case n.kind
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkFormalParams:
|
||||
of succ(nkEmpty)..pred(nkSym), succ(nkSym)..nkNilLit, harmless:
|
||||
discard
|
||||
of nkReturnStmt:
|
||||
for i in 0..<n.len:
|
||||
if containsResult(n[i]): return true
|
||||
result = n.len > 0 and n[0].kind == nkEmpty
|
||||
of nkSym:
|
||||
if n.sym.kind == skResult:
|
||||
result = true
|
||||
@@ -998,10 +1013,6 @@ proc containsResult(n: PNode): bool =
|
||||
for i in 0..<n.len:
|
||||
if containsResult(n[i]): return true
|
||||
|
||||
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
|
||||
nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} +
|
||||
declarativeDefs
|
||||
|
||||
proc easyResultAsgn(n: PNode): PNode =
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
@@ -1021,7 +1032,7 @@ proc easyResultAsgn(n: PNode): PNode =
|
||||
type
|
||||
InitResultEnum = enum Unknown, InitSkippable, InitRequired
|
||||
|
||||
proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
|
||||
# Exceptions coming from calls don't have not be considered here:
|
||||
#
|
||||
# proc bar(): string = raise newException(...)
|
||||
@@ -1036,7 +1047,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
# echo "a was not written to"
|
||||
#
|
||||
template allPathsInBranch(it) =
|
||||
let a = allPathsAsgnResult(it)
|
||||
let a = allPathsAsgnResult(p, it)
|
||||
case a
|
||||
of InitRequired: return InitRequired
|
||||
of InitSkippable: discard
|
||||
@@ -1048,14 +1059,20 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
for it in n:
|
||||
result = allPathsAsgnResult(it)
|
||||
result = allPathsAsgnResult(p, it)
|
||||
if result != Unknown: return result
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
if n[0].kind == nkSym and n[0].sym.kind == skResult:
|
||||
if not containsResult(n[1]): result = InitSkippable
|
||||
if not containsResult(n[1]):
|
||||
if allPathsAsgnResult(p, n[1]) == InitRequired:
|
||||
result = InitRequired
|
||||
else:
|
||||
result = InitSkippable
|
||||
else: result = InitRequired
|
||||
elif containsResult(n):
|
||||
result = InitRequired
|
||||
else:
|
||||
result = allPathsAsgnResult(p, n[1])
|
||||
of nkReturnStmt:
|
||||
if n.len > 0:
|
||||
if n[0].kind == nkEmpty and result != InitSkippable:
|
||||
@@ -1064,7 +1081,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
# initialized. This avoids cases like #9286 where this heuristic lead to
|
||||
# wrong code being generated.
|
||||
result = InitRequired
|
||||
else: result = allPathsAsgnResult(n[0])
|
||||
else: result = allPathsAsgnResult(p, n[0])
|
||||
of nkIfStmt, nkIfExpr:
|
||||
var exhaustive = false
|
||||
result = InitSkippable
|
||||
@@ -1090,9 +1107,9 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
of nkWhileStmt:
|
||||
# some dubious code can assign the result in the 'while'
|
||||
# condition and that would be fine. Everything else isn't:
|
||||
result = allPathsAsgnResult(n[0])
|
||||
result = allPathsAsgnResult(p, n[0])
|
||||
if result == Unknown:
|
||||
result = allPathsAsgnResult(n[1])
|
||||
result = allPathsAsgnResult(p, n[1])
|
||||
# we cannot assume that the 'while' loop is really executed at least once:
|
||||
if result == InitSkippable: result = Unknown
|
||||
of harmless:
|
||||
@@ -1117,9 +1134,21 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
allPathsInBranch(n[0])
|
||||
for i in 1..<n.len:
|
||||
if n[i].kind == nkFinally:
|
||||
result = allPathsAsgnResult(n[i].lastSon)
|
||||
result = allPathsAsgnResult(p, n[i].lastSon)
|
||||
else:
|
||||
allPathsInBranch(n[i].lastSon)
|
||||
of nkCallKinds:
|
||||
if canRaiseDisp(p, n[0]):
|
||||
result = InitRequired
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
allPathsInBranch(n[i])
|
||||
of nkRaiseStmt:
|
||||
result = InitRequired
|
||||
of nkChckRangeF, nkChckRange64, nkChckRange:
|
||||
# TODO: more checks might need to be covered like overflow, indexDefect etc.
|
||||
# bug #22852
|
||||
result = InitRequired
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
allPathsInBranch(n[i])
|
||||
@@ -1175,7 +1204,13 @@ proc genProcAux*(m: BModule, prc: PSym) =
|
||||
# declare the result symbol:
|
||||
assignLocalVar(p, resNode)
|
||||
assert(res.loc.r != "")
|
||||
initLocalVar(p, res, immediateAsgn=false)
|
||||
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
|
||||
allPathsAsgnResult(p, procBody) == InitSkippable:
|
||||
# In an ideal world the codegen could rely on injectdestructors doing its job properly
|
||||
# and then the analysis step would not be required.
|
||||
discard "result init optimized out"
|
||||
else:
|
||||
initLocalVar(p, res, immediateAsgn=false)
|
||||
returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
|
||||
elif sfConstructor in prc.flags:
|
||||
fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
|
||||
@@ -1189,7 +1224,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
|
||||
# global is either 'nil' or points to valid memory and so the RC operation
|
||||
# succeeds without touching not-initialized memory.
|
||||
if sfNoInit in prc.flags: discard
|
||||
elif allPathsAsgnResult(procBody) == InitSkippable: discard
|
||||
elif allPathsAsgnResult(p, procBody) == InitSkippable: discard
|
||||
else:
|
||||
resetLoc(p, res.loc)
|
||||
if skipTypes(res.typ, abstractInst).kind == tyArray:
|
||||
@@ -1775,7 +1810,7 @@ proc genDatInitCode(m: BModule) =
|
||||
|
||||
# we don't want to break into such init code - could happen if a line
|
||||
# directive from a function written by the user spills after itself
|
||||
genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
|
||||
genCLineDir(prc, InvalidFileIdx, 999999, m.config)
|
||||
|
||||
for i in cfsTypeInit1..cfsDynLibInit:
|
||||
if m.s[i].len != 0:
|
||||
@@ -1816,7 +1851,7 @@ proc genInitCode(m: BModule) =
|
||||
[rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname]
|
||||
# we don't want to break into such init code - could happen if a line
|
||||
# directive from a function written by the user spills after itself
|
||||
genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
|
||||
genCLineDir(prc, InvalidFileIdx, 999999, m.config)
|
||||
if m.typeNodes > 0:
|
||||
if m.hcrOn:
|
||||
appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName])
|
||||
@@ -1880,13 +1915,13 @@ proc genInitCode(m: BModule) =
|
||||
if beforeRetNeeded in m.initProc.flags:
|
||||
prc.add("\tBeforeRet_: ;\n")
|
||||
|
||||
if sfMainModule in m.module.flags and m.config.exc == excGoto:
|
||||
if m.config.exc == excGoto:
|
||||
if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
|
||||
m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
|
||||
|
||||
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
|
||||
prc.add(deinitFrame(m.initProc))
|
||||
elif sfMainModule in m.module.flags and m.config.exc == excGoto:
|
||||
elif m.config.exc == excGoto:
|
||||
if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
|
||||
m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
|
||||
|
||||
@@ -1931,6 +1966,40 @@ proc genInitCode(m: BModule) =
|
||||
|
||||
registerModuleToMain(m.g, m)
|
||||
|
||||
proc postprocessCode(conf: ConfigRef, r: var Rope) =
|
||||
# find the first directive
|
||||
var f = r.find(postprocessDirStart)
|
||||
if f == -1:
|
||||
return
|
||||
|
||||
var
|
||||
nimlnDirLastF = ""
|
||||
|
||||
var res: Rope = r.substr(0, f - 1)
|
||||
while f != -1:
|
||||
var
|
||||
e = r.find(postprocessDirEnd, f + 1)
|
||||
dir = r.substr(f + 1, e - 1).split(postprocessDirSep)
|
||||
case dir[0]
|
||||
of "nimln":
|
||||
if dir[2] == nimlnDirLastF:
|
||||
res.add("nimln_(" & dir[1] & ");")
|
||||
else:
|
||||
res.add("nimlf_(" & dir[1] & ", " & quotedFilename(conf, dir[2].parseInt.FileIndex) & ");")
|
||||
nimlnDirLastF = dir[2]
|
||||
else:
|
||||
raiseAssert "unexpected postprocess directive"
|
||||
|
||||
# find the next directive
|
||||
f = r.find(postprocessDirStart, e + 1)
|
||||
# copy the code until the next directive
|
||||
if f != -1:
|
||||
res.add(r.substr(e + 1, f - 1))
|
||||
else:
|
||||
res.add(r.substr(e + 1))
|
||||
|
||||
r = res
|
||||
|
||||
proc genModule(m: BModule, cfile: Cfile): Rope =
|
||||
var moduleIsEmpty = true
|
||||
|
||||
@@ -1959,9 +2028,17 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
|
||||
if m.config.cppCustomNamespace.len > 0:
|
||||
closeNamespaceNim(result)
|
||||
|
||||
if optLineDir in m.config.options:
|
||||
var srcFileDefs = ""
|
||||
for fi in 0..m.config.m.fileInfos.high:
|
||||
srcFileDefs.add("#define FX_" & $fi & " " & makeSingleLineCString(toFullPath(m.config, fi.FileIndex)) & "\n")
|
||||
result = srcFileDefs & result
|
||||
|
||||
if moduleIsEmpty:
|
||||
result = ""
|
||||
|
||||
postprocessCode(m.config, result)
|
||||
|
||||
proc initProcOptions(m: BModule): TOptions =
|
||||
let opts = m.config.options
|
||||
if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
|
||||
|
||||
@@ -135,6 +135,7 @@ type
|
||||
# unconditionally...
|
||||
# nimtvDeps is VERY hard to cache because it's
|
||||
# not a list of IDs nor can it be made to be one.
|
||||
mangledPrcs*: HashSet[string]
|
||||
|
||||
TCGen = object of PPassContext # represents a C source file
|
||||
s*: TCFileSections # sections of the C file
|
||||
@@ -148,7 +149,7 @@ type
|
||||
typeABICache*: HashSet[SigHash] # cache for ABI checks; reusing typeCache
|
||||
# would be ideal but for some reason enums
|
||||
# don't seem to get cached so it'd generate
|
||||
# 1 ABI check per occurence in code
|
||||
# 1 ABI check per occurrence in code
|
||||
forwTypeCache*: TypeCache # cache for forward declarations of types
|
||||
declaredThings*: IntSet # things we have declared in this .c file
|
||||
declaredProtos*: IntSet # prototypes we have declared in this .c file
|
||||
@@ -196,6 +197,8 @@ proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
result = BProc(
|
||||
prc: prc,
|
||||
module: module,
|
||||
optionsStack: if module.initProc != nil: module.initProc.optionsStack
|
||||
else: @[],
|
||||
options: if prc != nil: prc.options
|
||||
else: module.config.options,
|
||||
blocks: @[initBlock()],
|
||||
|
||||
@@ -95,7 +95,8 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
|
||||
return No
|
||||
if result == Yes:
|
||||
# check for return type:
|
||||
if not sameTypeOrNil(a.typ[0], b.typ[0]):
|
||||
# ignore flags of return types; # bug #22673
|
||||
if not sameTypeOrNil(a.typ[0], b.typ[0], {IgnoreFlags}):
|
||||
if b.typ[0] != nil and b.typ[0].kind == tyUntyped:
|
||||
# infer 'auto' from the base to make it consistent:
|
||||
b.typ[0] = a.typ[0]
|
||||
|
||||
@@ -55,6 +55,11 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi
|
||||
if conf.cmd == cmdNimscript:
|
||||
incl(conf.globalOptions, optWasNimscript)
|
||||
loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
|
||||
# restores `conf.notes` after loading config files
|
||||
# because it has overwrites the notes when compiling the system module which
|
||||
# is a foreign module compared to the project
|
||||
if conf.cmd in cmdBackends:
|
||||
conf.notes = conf.mainPackageNotes
|
||||
|
||||
if not self.suggestMode:
|
||||
let scriptFile = conf.projectFull.changeFileExt("nims")
|
||||
|
||||
@@ -205,7 +205,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
|
||||
let x = findStr(noteMin, noteMax, id, errUnknown)
|
||||
if x != errUnknown: notes = {TNoteKind(x)}
|
||||
else: localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
else:
|
||||
if isSomeHint:
|
||||
message(conf, info, hintUnknownHint, id)
|
||||
else:
|
||||
localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
case id.normalize
|
||||
of "all": # other note groups would be easy to support via additional cases
|
||||
notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax}
|
||||
|
||||
@@ -314,7 +314,7 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invocation: PType): bool =
|
||||
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
|
||||
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fullfill the
|
||||
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
|
||||
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
|
||||
## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.)
|
||||
## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the
|
||||
|
||||
@@ -143,7 +143,6 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasTemplateRedefinitionPragma")
|
||||
defineSymbol("nimHasCstringCase")
|
||||
defineSymbol("nimHasCallsitePragma")
|
||||
defineSymbol("nimHasAmbiguousEnumHint")
|
||||
|
||||
defineSymbol("nimHasWarnCastSizes") # deadcode
|
||||
defineSymbol("nimHasOutParams")
|
||||
@@ -158,3 +157,9 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimAllowNonVarDestructor")
|
||||
defineSymbol("nimHasQuirky")
|
||||
defineSymbol("nimHasEnsureMove")
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
defineSymbol("nimHasGenericsOpenSym2")
|
||||
defineSymbol("nimHasNolineTooLong")
|
||||
defineSymbol("nimHasGenericsOpenSym3")
|
||||
|
||||
@@ -821,7 +821,7 @@ proc getName(n: PNode): string =
|
||||
result = "`"
|
||||
for i in 0..<n.len: result.add(getName(n[i]))
|
||||
result = "`"
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getName(n[0])
|
||||
else:
|
||||
result = ""
|
||||
@@ -839,7 +839,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
|
||||
@@ -853,7 +853,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
|
||||
@@ -1723,7 +1723,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string =
|
||||
"moduledesc", d.modDescFinal, "date", getDateStr(), "time", getClockStr(),
|
||||
"content", content, "author", d.meta[metaAuthor],
|
||||
"version", esc(d.target, d.meta[metaVersion]), "analytics", d.analytics,
|
||||
"deprecationMsg", d.modDeprecationMsg]
|
||||
"deprecationMsg", d.modDeprecationMsg, "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
|
||||
else:
|
||||
code = content
|
||||
result = code
|
||||
@@ -1907,7 +1907,7 @@ proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"")
|
||||
"title", "Index",
|
||||
"subtitle", "", "tableofcontents", "", "moduledesc", "",
|
||||
"date", getDateStr(), "time", getClockStr(),
|
||||
"content", content, "author", "", "version", "", "analytics", ""]
|
||||
"content", content, "author", "", "version", "", "analytics", "", "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
|
||||
# no analytics because context is not available
|
||||
|
||||
try:
|
||||
|
||||
@@ -423,8 +423,8 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
|
||||
internalAssert conf, call[0].kind == nkPtrLit
|
||||
|
||||
var cif: TCif
|
||||
var sig: ParamList
|
||||
var cif: TCif = default(TCif)
|
||||
var sig: ParamList = default(ParamList)
|
||||
# use the arguments' types for varargs support:
|
||||
for i in 1..<call.len:
|
||||
sig[i-1] = mapType(conf, call[i].typ)
|
||||
@@ -463,8 +463,8 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||
info: TLineInfo): PNode =
|
||||
internalAssert conf, fn.kind == nkPtrLit
|
||||
|
||||
var cif: TCif
|
||||
var sig: ParamList
|
||||
var cif: TCif = default(TCif)
|
||||
var sig: ParamList = default(ParamList)
|
||||
for i in 0..len-1:
|
||||
var aTyp = args[i+start].typ
|
||||
if aTyp.isNil:
|
||||
|
||||
@@ -56,6 +56,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
# internalAssert c.config, false
|
||||
idTablePut(c.mapping, s, x)
|
||||
if sfGenSym in s.flags:
|
||||
# TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID)
|
||||
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
|
||||
if c.instLines: actual.info else: templ.info)
|
||||
else:
|
||||
|
||||
@@ -19,7 +19,7 @@ import std/[os, osproc, streams, sequtils, times, strtabs, json, jsonutils, suga
|
||||
import std / strutils except addf
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
import std/[syncio, assertions]
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
@@ -33,6 +33,7 @@ type
|
||||
hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax
|
||||
hasDeclspec, # CC has __declspec(X)
|
||||
hasAttribute, # CC has __attribute__((X))
|
||||
hasBuiltinUnreachable # CC has __builtin_unreachable
|
||||
TInfoCCProps* = set[TInfoCCProp]
|
||||
TInfoCC* = tuple[
|
||||
name: string, # the short name of the compiler
|
||||
@@ -95,7 +96,7 @@ compiler gcc:
|
||||
produceAsm: gnuAsmListing,
|
||||
cppXsupport: "-std=gnu++17 -funsigned-char",
|
||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||
hasAttribute})
|
||||
hasAttribute, hasBuiltinUnreachable})
|
||||
|
||||
# GNU C and C++ Compiler
|
||||
compiler nintendoSwitchGCC:
|
||||
@@ -122,7 +123,7 @@ compiler nintendoSwitchGCC:
|
||||
produceAsm: gnuAsmListing,
|
||||
cppXsupport: "-std=gnu++17 -funsigned-char",
|
||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||
hasAttribute})
|
||||
hasAttribute, hasBuiltinUnreachable})
|
||||
|
||||
# LLVM Frontend for GCC/G++
|
||||
compiler llvmGcc:
|
||||
@@ -992,7 +993,7 @@ type BuildCache = object
|
||||
depfiles: seq[(string, string)]
|
||||
nimexe: string
|
||||
|
||||
proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
||||
proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
|
||||
var linkFiles = collect(for it in conf.externalToLink:
|
||||
var it = it
|
||||
if conf.noAbsolutePaths: it = it.extractFilename
|
||||
@@ -1013,10 +1014,14 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
||||
currentDir: getCurrentDir())
|
||||
if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
|
||||
bcache.cmdline = conf.commandLine
|
||||
bcache.depfiles = collect(for it in conf.m.fileInfos:
|
||||
for it in conf.m.fileInfos:
|
||||
let path = it.fullPath.string
|
||||
if isAbsolute(path): # TODO: else?
|
||||
(path, $secureHashFile(path)))
|
||||
if path in deps:
|
||||
bcache.depfiles.add (path, deps[path])
|
||||
else: # backup for configs etc.
|
||||
bcache.depfiles.add (path, $secureHashFile(path))
|
||||
|
||||
bcache.nimexe = hashNimExe()
|
||||
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
|
||||
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
|
||||
|
||||
@@ -1047,8 +1047,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
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
# This include implements the high level optimization pass.
|
||||
# included from sem.nim
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
proc hlo(c: PContext, n: PNode): PNode
|
||||
|
||||
proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
||||
|
||||
@@ -421,7 +421,7 @@ proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pac
|
||||
## add a remote symbol reference to the tree
|
||||
let info = n.info.toPackedInfo(c, m)
|
||||
ir.nodes.add PackedNode(kind: nkModuleRef, operand: 3.int32, # spans 3 nodes in total
|
||||
typeId: storeTypeLater(n.typ, c, m), info: info)
|
||||
typeId: storeTypeLater(n.typ, c, m), info: info, flags: n.flags)
|
||||
ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
|
||||
operand: toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
|
||||
ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
|
||||
@@ -768,6 +768,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.nodes[n.int].operand))
|
||||
if result.typ == nil:
|
||||
result.typ = result.sym.typ
|
||||
of directIntLit:
|
||||
result.intVal = tree.nodes[n.int].operand
|
||||
of externIntLit:
|
||||
@@ -782,6 +784,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
assert n2.kind == nkInt32Lit
|
||||
transitionNoneToSym(result)
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
|
||||
if result.typ == nil:
|
||||
result.typ = result.sym.typ
|
||||
else:
|
||||
for n0 in sonsReadonly(tree, n):
|
||||
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
|
||||
|
||||
@@ -141,7 +141,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
|
||||
# for an enumeration we have to add all identifiers
|
||||
if multiImport:
|
||||
# for a overloadable syms add all overloaded routines
|
||||
var it: ModuleIter
|
||||
var it: ModuleIter = default(ModuleIter)
|
||||
var e = initModuleIter(it, c.graph, fromMod, s.name)
|
||||
while e != nil:
|
||||
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
|
||||
@@ -304,7 +304,15 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
var prefix = ""
|
||||
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
|
||||
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
|
||||
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
|
||||
|
||||
proc suggestMod(n: PNode; s: PSym) =
|
||||
if n.kind == nkImportAs:
|
||||
suggestMod(n[0], realModule)
|
||||
elif n.kind == nkInfix:
|
||||
suggestMod(n[2], s)
|
||||
else:
|
||||
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
|
||||
suggestMod(n, result)
|
||||
importStmtResult.add newSymNode(result, n.info)
|
||||
#newStrNode(toFullPath(c.config, f), n.info)
|
||||
|
||||
@@ -344,9 +352,11 @@ proc evalImport*(c: PContext, n: PNode): PNode =
|
||||
imp[lastPos] = x[1]
|
||||
impAs[1] = imp
|
||||
impAs[2] = x[2]
|
||||
impAs.info = x[2].info
|
||||
impMod(c, impAs, result)
|
||||
else:
|
||||
imp[lastPos] = x
|
||||
imp.info = x.info
|
||||
impMod(c, imp, result)
|
||||
else:
|
||||
impMod(c, it, result)
|
||||
|
||||
@@ -79,11 +79,11 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
|
||||
proc nestedScope(parent: var Scope; body: PNode): Scope =
|
||||
Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
|
||||
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode
|
||||
|
||||
type
|
||||
MoveOrCopyFlag = enum
|
||||
IsDecl, IsExplicitSink
|
||||
IsDecl, IsExplicitSink, IsReturn
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode
|
||||
|
||||
@@ -161,7 +161,8 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
||||
result = false
|
||||
|
||||
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
|
||||
if not hasDestructor(c, n.typ): return true
|
||||
# bug #23354; an object type could have a non-trival assignements when it is passed to a sink parameter
|
||||
if not hasDestructor(c, n.typ) and (n.typ.kind != tyObject or isTrival(getAttachedOp(c.graph, n.typ, attachedAsgn))): return true
|
||||
|
||||
let m = skipConvDfa(n)
|
||||
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
|
||||
@@ -185,7 +186,9 @@ proc isCursor(n: PNode): bool =
|
||||
template isUnpackedTuple(n: PNode): bool =
|
||||
## we move out all elements of unpacked tuples,
|
||||
## hence unpacked tuples themselves don't need to be destroyed
|
||||
(n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple)
|
||||
## except it's already a cursor
|
||||
(n.kind == nkSym and n.sym.kind == skTemp and
|
||||
n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
|
||||
|
||||
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFromCopy = false) =
|
||||
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
|
||||
@@ -270,7 +273,7 @@ proc deepAliases(dest, ri: PNode): bool =
|
||||
proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
|
||||
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
|
||||
isNoInit(dest):
|
||||
isNoInit(dest) or IsReturn in flags:
|
||||
# optimize sink call into a bitwise memcopy
|
||||
result = newTree(nkFastAsgn, dest, ri)
|
||||
else:
|
||||
@@ -293,7 +296,7 @@ proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFla
|
||||
proc isCriticalLink(dest: PNode): bool {.inline.} =
|
||||
#[
|
||||
Lins's idea that only "critical" links can introduce a cycle. This is
|
||||
critical for the performance gurantees that we strive for: If you
|
||||
critical for the performance guarantees that we strive for: If you
|
||||
traverse a data structure, no tracing will be performed at all.
|
||||
ORC is about this promise: The GC only touches the memory that the
|
||||
mutator touches too.
|
||||
@@ -310,9 +313,10 @@ 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:
|
||||
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
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))
|
||||
|
||||
@@ -405,7 +409,10 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
||||
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
# generate: (let tmp = v; reset(v); tmp)
|
||||
if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0:
|
||||
assert n.kind != nkSym or not hasDestructor(c, n.sym.typ)
|
||||
assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) or
|
||||
(n.typ.kind == tyPtr and n.sym.typ.kind == tyRef)
|
||||
# bug #23505; transformed by `transf`: addr (deref ref) -> ptr
|
||||
# we know it's really a pointer; so here we assign it directly
|
||||
result = copyTree(n)
|
||||
else:
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
@@ -435,24 +442,25 @@ proc isCapturedVar(n: PNode): bool =
|
||||
|
||||
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
let tmp = c.getTemp(s, n.typ, n.info)
|
||||
if hasDestructor(c, n.typ):
|
||||
let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
|
||||
let tmp = c.getTemp(s, nTyp, n.info)
|
||||
if hasDestructor(c, nTyp):
|
||||
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let op = getAttachedOp(c.graph, typ, attachedDup)
|
||||
if op != nil and tfHasOwned notin typ.flags:
|
||||
if sfError in op.flags:
|
||||
c.checkForErrorPragma(n.typ, n, "=dup")
|
||||
c.checkForErrorPragma(nTyp, n, "=dup")
|
||||
else:
|
||||
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
|
||||
if copyOp != nil and sfError in copyOp.flags and
|
||||
sfOverridden notin op.flags:
|
||||
c.checkForErrorPragma(n.typ, n, "=dup", inferredFromCopy = true)
|
||||
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
|
||||
|
||||
let src = p(n, c, s, normal)
|
||||
var newCall = newTreeIT(nkCall, src.info, src.typ,
|
||||
newSymNode(op),
|
||||
src)
|
||||
c.finishCopy(newCall, n, isFromSink = true)
|
||||
c.finishCopy(newCall, n, {}, isFromSink = true)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
newCall
|
||||
@@ -461,9 +469,9 @@ 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 n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
message(c.graph.config, n.info, hintPerformance,
|
||||
("passing '$1' to a sink parameter introduces an implicit copy; " &
|
||||
"if possible, rearrange your program's control flow to prevent it") % $n)
|
||||
@@ -472,8 +480,8 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
|
||||
else:
|
||||
if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
assert(not containsManagedMemory(n.typ))
|
||||
if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
assert(not containsManagedMemory(nTyp))
|
||||
if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
|
||||
result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
|
||||
# Since we know somebody will take over the produced copy, there is
|
||||
@@ -489,7 +497,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:
|
||||
@@ -587,7 +595,9 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
|
||||
# tricky because you would have to intercept moveOrCopy at a certain point
|
||||
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
|
||||
tmp.sym.flags = tmpFlags
|
||||
let cpy = if hasDestructor(c, ret.typ):
|
||||
let cpy = if hasDestructor(c, ret.typ) and
|
||||
ret.typ.kind notin {tyOpenArray, tyVarargs}:
|
||||
# bug #23247 we don't own the data, so it's harmful to destroy it
|
||||
s.parent[].final.add c.genDestroy(tmp)
|
||||
moveOrCopy(tmp, ret, c, s, {IsDecl})
|
||||
else:
|
||||
@@ -745,7 +755,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
|
||||
@@ -760,7 +770,19 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||
result.add copyNode(n[0])
|
||||
s.needsTry = true
|
||||
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode =
|
||||
template isCustomDestructor(c: Con, t: PType): bool =
|
||||
hasDestructor(c, t) and
|
||||
getAttachedOp(c.graph, t, attachedDestructor) != nil and
|
||||
sfOverridden in getAttachedOp(c.graph, t, attachedDestructor).flags
|
||||
|
||||
proc hasCustomDestructor(c: Con, t: PType): bool =
|
||||
result = isCustomDestructor(c, t)
|
||||
var obj = t
|
||||
while obj.len > 0 and obj[0] != nil:
|
||||
obj = skipTypes(obj[0], abstractPtrs)
|
||||
result = result or isCustomDestructor(c, obj)
|
||||
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode =
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
|
||||
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}:
|
||||
template process(child, s): untyped = p(child, c, s, mode)
|
||||
@@ -801,6 +823,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
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)
|
||||
@@ -850,7 +875,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result[i][1] = p(n[i][1], c, s, m)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, m)
|
||||
if mode == normal and isRefConstr:
|
||||
if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
@@ -896,7 +921,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result[0] = p(n[0], c, s, normal)
|
||||
if canRaise(n[0]): s.needsTry = true
|
||||
if mode == normal:
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
if result.typ != nil and result.typ.kind notin {tyOpenArray, tyVarargs}:
|
||||
# Returns of openarray types shouldn't be destroyed
|
||||
# bug #19435; # bug #23247
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
of nkDiscardStmt: # Small optimization
|
||||
result = shallowCopy(n)
|
||||
if n[0].kind != nkEmpty:
|
||||
@@ -944,7 +972,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
||||
cycleCheck(n, c)
|
||||
assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn}
|
||||
let flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
|
||||
var flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
|
||||
if inReturn:
|
||||
flags.incl(IsReturn)
|
||||
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
|
||||
elif isDiscriminantField(n[0]):
|
||||
result = c.genDiscriminantAsgn(s, n)
|
||||
@@ -1028,7 +1058,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
of nkReturnStmt:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = p(n[i], c, s, mode)
|
||||
result[i] = p(n[i], c, s, mode, inReturn=true)
|
||||
s.needsTry = true
|
||||
of nkCast:
|
||||
result = shallowCopy(n)
|
||||
@@ -1134,7 +1164,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):
|
||||
@@ -1142,7 +1172,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:
|
||||
@@ -1162,7 +1192,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
@@ -1182,7 +1212,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) =
|
||||
|
||||
@@ -10,7 +10,7 @@ Platforms: """
|
||||
macosx: i386;amd64;powerpc64;arm64
|
||||
solaris: i386;amd64;sparc;sparc64
|
||||
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
|
||||
netbsd: i386;amd64
|
||||
netbsd: i386;amd64;arm64
|
||||
openbsd: i386;amd64;arm;arm64
|
||||
dragonfly: i386;amd64
|
||||
crossos: amd64
|
||||
|
||||
@@ -50,6 +50,7 @@ type
|
||||
graph: ModuleGraph
|
||||
config: ConfigRef
|
||||
sigConflicts: CountTable[SigHash]
|
||||
initProc: PProc
|
||||
|
||||
BModule = ref TJSGen
|
||||
TJSTypeKind = enum # necessary JS "types"
|
||||
@@ -158,6 +159,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
|
||||
options: TOptions): PProc =
|
||||
result = PProc(
|
||||
blocks: @[],
|
||||
optionsStack: if module.initProc != nil: module.initProc.optionsStack
|
||||
else: @[],
|
||||
options: options,
|
||||
module: module,
|
||||
procDef: procDef,
|
||||
@@ -893,7 +896,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
|
||||
p.body.add("++excHandler;\L")
|
||||
var tmpFramePtr = rope"F"
|
||||
lineF(p, "try {$n", [])
|
||||
var a: TCompRes
|
||||
var a: TCompRes = default(TCompRes)
|
||||
gen(p, n[0], a)
|
||||
moveInto(p, a, r)
|
||||
var generalCatchBranchExists = false
|
||||
@@ -1195,7 +1198,7 @@ proc needsNoCopy(p: PProc; y: PNode): bool =
|
||||
return y.kind in nodeKindsNeedNoCopy or
|
||||
((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and
|
||||
(skipTypes(y.typ, abstractInst).kind in
|
||||
{tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned} + IntegralTypes))
|
||||
{tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned, tyOpenArray} + IntegralTypes))
|
||||
|
||||
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||
var a, b: TCompRes
|
||||
@@ -1949,7 +1952,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
result = putToSeq("null", indirect)
|
||||
of tySequence, tyString:
|
||||
result = putToSeq("[]", indirect)
|
||||
of tyCstring, tyProc:
|
||||
of tyCstring, tyProc, tyOpenArray:
|
||||
result = putToSeq("null", indirect)
|
||||
of tyStatic:
|
||||
if t.n != nil:
|
||||
@@ -3036,6 +3039,7 @@ proc processJSCodeGen*(b: PPassContext, n: PNode): PNode =
|
||||
if m.module == nil: internalError(m.config, n.info, "myProcess")
|
||||
let globals = PGlobals(m.graph.backend)
|
||||
var p = newInitProc(globals, m)
|
||||
m.initProc = p
|
||||
p.unique = globals.unique
|
||||
genModule(p, n)
|
||||
p.g.code.add(p.locals)
|
||||
|
||||
@@ -258,8 +258,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||
let iter = n.sym
|
||||
assert iter.isIterator
|
||||
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
result = newNodeIT(nkStmtListExpr, n.info, iter.typ)
|
||||
let hp = getHiddenParam(g, iter)
|
||||
var env: PNode
|
||||
if owner.isIterator:
|
||||
@@ -293,23 +292,26 @@ proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner:
|
||||
|
||||
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
let s = n.sym
|
||||
let isEnv = s.name.id == getIdent(g.cache, ":env").id
|
||||
if illegalCapture(s):
|
||||
localError(g.config, n.info,
|
||||
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
|
||||
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
|
||||
" Consider using a <ref $2> which can be captured.") %
|
||||
[s.name.s, typeToString(s.typ), g.config$s.info])
|
||||
elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags):
|
||||
elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
|
||||
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
|
||||
[s.name.s, owner.name.s, $owner.typ.callConv])
|
||||
incl(owner.typ.flags, tfCapturesEnv)
|
||||
owner.typ.callConv = ccClosure
|
||||
if not isEnv:
|
||||
owner.typ.callConv = ccClosure
|
||||
|
||||
type
|
||||
DetectionPass = object
|
||||
processed, capturedVars: IntSet
|
||||
ownerToType: Table[int, PType]
|
||||
somethingToDo: bool
|
||||
inTypeOf: bool
|
||||
graph: ModuleGraph
|
||||
idgen: IdGenerator
|
||||
|
||||
@@ -413,6 +415,9 @@ Consider:
|
||||
|
||||
"""
|
||||
|
||||
proc isTypeOf(n: PNode): bool =
|
||||
n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
|
||||
|
||||
proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
|
||||
var cp = getEnvParam(fn)
|
||||
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
|
||||
@@ -444,12 +449,15 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
let body = transformBody(c.graph, c.idgen, s, useCache)
|
||||
detectCapturedVars(body, s, c)
|
||||
let ow = s.skipGenericOwner
|
||||
let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
|
||||
let interested = interestingVar(s)
|
||||
if ow == owner:
|
||||
if owner.isIterator:
|
||||
c.somethingToDo = true
|
||||
addClosureParam(c, owner, n.info)
|
||||
if interestingIterVar(s):
|
||||
if not c.capturedVars.containsOrIncl(s.id):
|
||||
if not c.capturedVars.contains(s.id):
|
||||
if not c.inTypeOf: c.capturedVars.incl(s.id)
|
||||
let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
|
||||
@@ -458,7 +466,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
else:
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
# direct or indirect dependency:
|
||||
elif (innerProc and not s.isIterator and s.typ.callConv == ccClosure) or interestingVar(s):
|
||||
elif innerClosure or interested:
|
||||
discard """
|
||||
proc outer() =
|
||||
var x: int
|
||||
@@ -475,10 +483,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
addClosureParam(c, owner, n.info)
|
||||
#echo "capturing ", n.info
|
||||
# variable 's' is actually captured:
|
||||
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
|
||||
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
if interestingVar(s):
|
||||
if not c.capturedVars.contains(s.id):
|
||||
if not c.inTypeOf: c.capturedVars.incl(s.id)
|
||||
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
# create required upFields:
|
||||
var w = owner.skipGenericOwner
|
||||
if isInnerProc(w) or owner.isIterator:
|
||||
@@ -510,9 +520,14 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
detectCapturedVars(n[namePos], owner, c)
|
||||
of nkReturnStmt:
|
||||
detectCapturedVars(n[0], owner, c)
|
||||
of nkIdentDefs:
|
||||
detectCapturedVars(n[^1], owner, c)
|
||||
else:
|
||||
if n.isCallExpr and n[0].isTypeOf:
|
||||
c.inTypeOf = true
|
||||
for i in 0..<n.len:
|
||||
detectCapturedVars(n[i], owner, c)
|
||||
c.inTypeOf = false
|
||||
|
||||
type
|
||||
LiftingPass = object
|
||||
@@ -792,6 +807,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
of nkTypeOfExpr:
|
||||
result = n
|
||||
else:
|
||||
if n.isCallExpr and n[0].isTypeOf:
|
||||
return
|
||||
if owner.isIterator:
|
||||
if nfLL in n.flags:
|
||||
# special case 'when nimVm' due to bug #3636:
|
||||
@@ -971,12 +988,15 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
# gather vars in a tuple:
|
||||
var v2 = newNodeI(nkLetSection, body.info)
|
||||
var vpart = newNodeI(if body.len == 3: nkIdentDefs else: nkVarTuple, body.info)
|
||||
for i in 0..<body.len-2:
|
||||
if body[i].kind == nkSym:
|
||||
body[i].sym.transitionToLet()
|
||||
vpart.add body[i]
|
||||
if body.len == 3 and body[0].kind == nkVarTuple:
|
||||
vpart = body[0] # fixes for (i,j) in walk() # bug #15924
|
||||
else:
|
||||
for i in 0..<body.len-2:
|
||||
if body[i].kind == nkSym:
|
||||
body[i].sym.transitionToLet()
|
||||
vpart.add body[i]
|
||||
|
||||
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
|
||||
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
|
||||
if not env.isNil:
|
||||
call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info)
|
||||
vpart.add call
|
||||
|
||||
@@ -39,7 +39,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): PSym
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
|
||||
|
||||
proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
idgen: IdGenerator)
|
||||
@@ -156,17 +156,18 @@ proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
result.add(newSymNode(op))
|
||||
result.add genAddr(c, x)
|
||||
|
||||
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
|
||||
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if c.filterDiscriminator != nil: return
|
||||
let f = n.sym
|
||||
let b = if c.kind == attachedTrace: y else: y.dotField(f)
|
||||
if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
|
||||
c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or
|
||||
if (sfCursor in f.flags and c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or
|
||||
enforceDefaultOp:
|
||||
defaultOp(c, f.typ, body, x.dotField(f), b)
|
||||
else:
|
||||
if enforceWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
|
||||
fillBody(c, f.typ, body, x.dotField(f), b)
|
||||
of nkNilLit: discard
|
||||
of nkRecCase:
|
||||
@@ -205,7 +206,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
|
||||
branch[^1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
fillBodyObj(c, n[i].lastSon, branch[^1], x, y,
|
||||
enforceDefaultOp = localEnforceDefaultOp)
|
||||
enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn)
|
||||
if branch[^1].len == 0: inc emptyBranches
|
||||
caseStmt.add(branch)
|
||||
if emptyBranches != n.len-1:
|
||||
@@ -216,13 +217,16 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
|
||||
fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
|
||||
c.filterDiscriminator = oldfilterDiscriminator
|
||||
of nkRecList:
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp)
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
else:
|
||||
illFormedAstLocal(n, c.g.config)
|
||||
|
||||
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
if t.len > 0 and t[0] != nil:
|
||||
fillBody(c, skipTypes(t[0], abstractPtrs), body, x, y)
|
||||
let obj = newNodeIT(nkHiddenSubConv, c.info, t[0])
|
||||
obj.add newNodeI(nkEmpty, c.info)
|
||||
obj.add x
|
||||
fillBody(c, skipTypes(t[0], abstractPtrs), body, obj, y)
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
@@ -279,6 +283,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
c.kind = attachedDestructor
|
||||
fillBodyObjTImpl(c, t, body, blob, y)
|
||||
c.kind = prevKind
|
||||
|
||||
else:
|
||||
fillBodyObjTImpl(c, t, body, x, y)
|
||||
|
||||
@@ -542,6 +547,14 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
else:
|
||||
body.sons.setLen counterIdx
|
||||
|
||||
proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
var cond = callCodegenProc(c.g, "sameSeqPayload", c.info,
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
|
||||
)
|
||||
cond.typ = getSysType(c.g, c.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
|
||||
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
of attachedDup:
|
||||
@@ -549,10 +562,13 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedAsgn, attachedDeepCopy:
|
||||
# we generate:
|
||||
# if x.p == y.p:
|
||||
# return
|
||||
# setLen(dest, y.len)
|
||||
# var i = 0
|
||||
# while i < y.len: dest[i] = y[i]; inc(i)
|
||||
# This is usually more efficient than a destroy/create pair.
|
||||
checkSelfAssignment(c, t, body, x, y)
|
||||
body.add setLenSeqCall(c, t, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedSink:
|
||||
@@ -1020,7 +1036,9 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ[0]
|
||||
if getAttachedOp(g, baseType, kind) == nil:
|
||||
discard produceSym(g, c, baseType, kind, info, idgen)
|
||||
# TODO: fixme `isDistinct` is a fix for #23552; remove it after
|
||||
# `-d:nimPreviewNonVarDestructor` becomes the default
|
||||
discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
|
||||
result = getAttachedOp(g, baseType, kind)
|
||||
setAttachedOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
@@ -1059,7 +1077,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): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
|
||||
if kind == attachedDup:
|
||||
return symDupPrototype(g, typ, owner, kind, info, idgen)
|
||||
|
||||
@@ -1068,8 +1086,8 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
|
||||
let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
|
||||
idgen, result, info)
|
||||
|
||||
if kind == attachedDestructor and typ.kind in {tyRef, tyString, tySequence} and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
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)):
|
||||
dest.typ = typ
|
||||
else:
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
@@ -1111,13 +1129,13 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(xx, yy)
|
||||
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
|
||||
if typ.kind == tyDistinct:
|
||||
return produceSymDistinctType(g, c, typ, kind, info, idgen)
|
||||
|
||||
result = getAttachedOp(g, typ, kind)
|
||||
if result == nil:
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen)
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
|
||||
|
||||
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
@@ -1155,14 +1173,20 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
# bug #19205: Do not forget to also copy the hidden type field:
|
||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
||||
|
||||
if not a.canRaise: incl result.flags, sfNeverRaises
|
||||
if not a.canRaise:
|
||||
incl result.flags, sfNeverRaises
|
||||
result.ast[pragmasPos] = newNodeI(nkPragma, info)
|
||||
result.ast[pragmasPos].add newTree(nkExprColonExpr,
|
||||
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))
|
||||
|
||||
completePartialOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
|
||||
proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
assert(typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject)
|
||||
result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen)
|
||||
# discrimantor assignments needs pointers to destroy fields; alas, we cannot use non-var destructor here
|
||||
result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen, isDiscriminant = true)
|
||||
var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
a.asgnForType = typ
|
||||
@@ -1214,7 +1238,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
|
||||
else:
|
||||
localError(g.config, info, "unresolved generic parameter")
|
||||
|
||||
proc isTrival(s: PSym): bool {.inline.} =
|
||||
proc isTrival*(s: PSym): bool {.inline.} =
|
||||
s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
|
||||
|
||||
proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
|
||||
@@ -91,6 +91,7 @@ type
|
||||
warnStmtListLambda = "StmtListLambda",
|
||||
warnBareExcept = "BareExcept",
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
|
||||
warnUser = "User",
|
||||
# hints
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||
@@ -104,10 +105,10 @@ type
|
||||
hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency",
|
||||
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
|
||||
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
|
||||
hintAmbiguousEnum = "AmbiguousEnum",
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
hintUnknownHint = "UnknownHint"
|
||||
|
||||
const
|
||||
MsgKindToStr*: array[TMsgKind, string] = [
|
||||
@@ -185,7 +186,7 @@ const
|
||||
warnAnyEnumConv: "$1",
|
||||
warnHoleEnumConv: "$1",
|
||||
warnCstringConv: "$1",
|
||||
warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; this will become a compile time error in the future",
|
||||
warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; Use a `cast` operation like `cast[cstring](x)`; this will become a compile time error in the future",
|
||||
warnEffect: "$1",
|
||||
warnCastSizes: "$1", # deadcode
|
||||
warnAboveMaxSizeSet: "$1",
|
||||
@@ -194,6 +195,7 @@ const
|
||||
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
|
||||
warnBareExcept: "$1",
|
||||
warnImplicitDefaultValue: "$1",
|
||||
warnIgnoredSymbolInjection: "$1",
|
||||
warnUser: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
@@ -225,12 +227,12 @@ const
|
||||
hintGCStats: "$1",
|
||||
hintGlobalVar: "global variable declared here",
|
||||
hintExpandMacro: "expanded macro: $1",
|
||||
hintAmbiguousEnum: "$1",
|
||||
hintUser: "$1",
|
||||
hintUserRaw: "$1",
|
||||
hintExtendedContext: "$1",
|
||||
hintMsgOrigin: "$1",
|
||||
hintDeclaredLoc: "$1",
|
||||
hintUnknownHint: "unknown hint: $1"
|
||||
]
|
||||
|
||||
const
|
||||
|
||||
@@ -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
|
||||
@@ -95,7 +95,7 @@ template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind)
|
||||
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
|
||||
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(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
|
||||
@@ -136,7 +136,7 @@ template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
|
||||
## Check symbol uses match their definition's style.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(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
|
||||
@@ -152,5 +152,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)
|
||||
|
||||
@@ -48,7 +48,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:
|
||||
@@ -61,6 +61,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
result = n[0].sym.name
|
||||
else:
|
||||
handleError(n, origin)
|
||||
of nkOpenSym:
|
||||
result = considerQuotedIdent(c, n[0], origin)
|
||||
else:
|
||||
handleError(n, origin)
|
||||
|
||||
@@ -135,7 +137,7 @@ proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule;
|
||||
return result
|
||||
|
||||
iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym =
|
||||
var ti: ModuleIter
|
||||
var ti: ModuleIter = default(ModuleIter)
|
||||
var candidate = initIdentIter(ti, marked, im, name, g)
|
||||
while candidate != nil:
|
||||
yield candidate
|
||||
@@ -148,7 +150,7 @@ iterator importedItems*(c: PContext; name: PIdent): PSym =
|
||||
yield s
|
||||
|
||||
proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
|
||||
var ti: TIdentIter
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
result = @[]
|
||||
var res = initIdentIter(ti, c.pureEnumFields, name)
|
||||
while res != nil:
|
||||
@@ -220,7 +222,7 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
|
||||
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
@@ -238,7 +240,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
result = @[]
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
@@ -254,8 +256,63 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
proc cmpScopes*(ctx: PContext, s: PSym): int =
|
||||
# Do not return a negative number
|
||||
if s.originatingModule == ctx.module:
|
||||
result = 2
|
||||
var owner = s
|
||||
while true:
|
||||
owner = owner.skipGenericOwner
|
||||
if owner.kind == skModule: break
|
||||
inc result
|
||||
else:
|
||||
result = 1
|
||||
|
||||
proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool =
|
||||
result = false
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
var scopeHasCandidate = false
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
if scopeHasCandidate:
|
||||
# 2 candidates in same scope, ambiguous
|
||||
return true
|
||||
else:
|
||||
scopeHasCandidate = true
|
||||
sym = candidate
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
if scopeHasCandidate:
|
||||
# scope had a candidate but wasn't ambiguous
|
||||
return false
|
||||
|
||||
var importsHaveCandidate = false
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
if importsHaveCandidate:
|
||||
# 2 candidates among imports, ambiguous
|
||||
return true
|
||||
else:
|
||||
importsHaveCandidate = true
|
||||
sym = s
|
||||
if importsHaveCandidate:
|
||||
# imports had a candidate but wasn't ambiguous
|
||||
return false
|
||||
|
||||
proc errorSym*(c: PContext, ident: PIdent, info: TLineInfo): PSym =
|
||||
## creates an error symbol to avoid cascading errors (for IDE support)
|
||||
result = newSym(skError, ident, c.idgen, getCurrOwner(c), info, {})
|
||||
result.typ = errorType(c)
|
||||
incl(result.flags, sfDiscardable)
|
||||
# pretend it's from the top level scope to prevent cascading errors:
|
||||
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
|
||||
c.moduleScope.addSym(result)
|
||||
|
||||
proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
var m = n
|
||||
# ensure that 'considerQuotedIdent' can't fail:
|
||||
if m.kind == nkDotExpr: m = m[1]
|
||||
@@ -263,12 +320,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
considerQuotedIdent(c, m)
|
||||
else:
|
||||
getIdent(c.cache, "err:" & renderTree(m))
|
||||
result = newSym(skError, ident, c.idgen, getCurrOwner(c), n.info, {})
|
||||
result.typ = errorType(c)
|
||||
incl(result.flags, sfDiscardable)
|
||||
# pretend it's from the top level scope to prevent cascading errors:
|
||||
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
|
||||
c.moduleScope.addSym(result)
|
||||
result = errorSym(c, ident, n.info)
|
||||
|
||||
type
|
||||
TOverloadIterMode* = enum
|
||||
@@ -295,7 +347,7 @@ proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string =
|
||||
|
||||
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
|
||||
# check if all symbols have been used and defined:
|
||||
var it: TTabIter
|
||||
var it: TTabIter = default(TTabIter)
|
||||
var s = initTabIter(it, scope.symbols)
|
||||
var missingImpls = 0
|
||||
var unusedSyms: seq[tuple[sym: PSym, key: string]]
|
||||
@@ -450,7 +502,7 @@ proc mustFixSpelling(c: PContext): bool {.inline.} =
|
||||
result = c.config.spellSuggestMax != 0 and c.compilesContextId == 0
|
||||
# don't slowdown inside compiles()
|
||||
|
||||
proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
|
||||
proc fixSpelling(c: PContext, ident: PIdent, result: var string) =
|
||||
## when we cannot find the identifier, suggest nearby spellings
|
||||
var list = initHeapQueue[SpellCandidate]()
|
||||
let name0 = ident.s.nimIdentNormalize
|
||||
@@ -505,10 +557,10 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PS
|
||||
amb = false
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
|
||||
var amb: bool
|
||||
var amb: bool = false
|
||||
discard errorUseQualifier(c, info, s, amb)
|
||||
|
||||
proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
|
||||
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
|
||||
var i = 0
|
||||
for candidate in candidates:
|
||||
@@ -531,7 +583,11 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extr
|
||||
if name == "_":
|
||||
err = "the special identifier '_' is ignored in declarations and cannot be used"
|
||||
else:
|
||||
err = "undeclared identifier: '" & name & "'" & extra
|
||||
err = "undeclared identifier: '" & name & "'"
|
||||
if "`gensym" in name:
|
||||
err.add "; if declared in a template, this identifier may be inconsistently marked inject or gensym"
|
||||
if extra.len != 0:
|
||||
err.add extra
|
||||
if c.recursiveDep.len > 0:
|
||||
err.add "\nThis might be caused by a recursive module dependency:\n"
|
||||
err.add c.recursiveDep
|
||||
@@ -539,11 +595,11 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extr
|
||||
c.recursiveDep = ""
|
||||
localError(c.config, info, errGenerated, err)
|
||||
|
||||
proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym =
|
||||
proc errorUndeclaredIdentifierHint*(c: PContext; ident: PIdent; info: TLineInfo): PSym =
|
||||
var extra = ""
|
||||
if c.mustFixSpelling: fixSpelling(c, n, ident, extra)
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s, extra)
|
||||
result = errorSym(c, n)
|
||||
if c.mustFixSpelling: fixSpelling(c, ident, extra)
|
||||
errorUndeclaredIdentifier(c, info, ident.s, extra)
|
||||
result = errorSym(c, ident, info)
|
||||
|
||||
proc lookUp*(c: PContext, n: PNode): PSym =
|
||||
# Looks up a symbol. Generates an error in case of nil.
|
||||
@@ -551,16 +607,16 @@ proc lookUp*(c: PContext, n: PNode): PSym =
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
result = searchInScopes(c, n.ident, amb)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, n.ident)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n.ident, n.info)
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkAccQuoted:
|
||||
var ident = considerQuotedIdent(c, n)
|
||||
result = searchInScopes(c, ident, amb)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, ident, n.info)
|
||||
else:
|
||||
internalError(c.config, n.info, "lookUp")
|
||||
return
|
||||
return nil
|
||||
if amb:
|
||||
#contains(c.ambiguousSymbols, result.id):
|
||||
result = errorUseQualifier(c, n.info, result, amb)
|
||||
@@ -571,36 +627,45 @@ type
|
||||
TLookupFlag* = enum
|
||||
checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
|
||||
|
||||
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:
|
||||
result.add allPureEnumFields(c, ident)
|
||||
|
||||
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
case n.kind
|
||||
of nkIdent, nkAccQuoted:
|
||||
var amb = false
|
||||
var ident = considerQuotedIdent(c, n)
|
||||
if checkModule in flags:
|
||||
result = searchInScopes(c, ident, amb)
|
||||
if result == nil:
|
||||
let candidates = allPureEnumFields(c, ident)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
if amb and checkAmbiguity in flags:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
else:
|
||||
let candidates = searchInScopesFilterBy(c, ident, allExceptModule)
|
||||
let candidates = lookUpCandidates(c, ident, allExceptModule)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
if amb and checkAmbiguity in flags:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
if result == nil:
|
||||
let candidates = allPureEnumFields(c, ident)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
if amb and checkAmbiguity in flags:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
|
||||
else:
|
||||
result = nil
|
||||
if result == nil and checkUndeclared in flags:
|
||||
result = errorUndeclaredIdentifierHint(c, n, ident)
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n.info)
|
||||
elif checkAmbiguity in flags and result != nil and amb:
|
||||
result = errorUseQualifier(c, n.info, result, amb)
|
||||
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})
|
||||
@@ -615,17 +680,17 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
result = strTableGet(c.topLevelScope.symbols, ident)
|
||||
else:
|
||||
if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
|
||||
var amb: bool
|
||||
var amb: bool = false
|
||||
result = errorUseQualifier(c, n.info, m, amb)
|
||||
else:
|
||||
result = someSym(c.graph, m, ident)
|
||||
if result == nil and checkUndeclared in flags:
|
||||
result = errorUndeclaredIdentifierHint(c, n[1], ident)
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
|
||||
elif n[1].kind == nkSym:
|
||||
result = n[1].sym
|
||||
if result.owner != nil and result.owner != m and checkUndeclared in flags:
|
||||
# dotExpr in templates can end up here
|
||||
result = errorUndeclaredIdentifierHint(c, n[1], considerQuotedIdent(c, n[1]))
|
||||
result = errorUndeclaredIdentifierHint(c, result.name, n[1].info)
|
||||
elif checkUndeclared in flags and
|
||||
n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
|
||||
localError(c.config, n[1].info, "identifier expected, but got: " &
|
||||
@@ -637,6 +702,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
|
||||
|
||||
@@ -122,25 +122,6 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
|
||||
proc newTryFinally*(body, final: PNode): PNode =
|
||||
result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
|
||||
|
||||
proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||
let value = n.lastSon
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
var temp = newSym(skTemp, getIdent(g.cache, "_"), idgen, owner, value.info, owner.options)
|
||||
var v = newNodeI(nkLetSection, value.info)
|
||||
let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
||||
|
||||
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
||||
vpart[0] = tempAsNode
|
||||
vpart[1] = newNodeI(nkTupleClassTy, value.info)
|
||||
vpart[2] = value
|
||||
v.add vpart
|
||||
result.add(v)
|
||||
|
||||
let lhs = n[0]
|
||||
for i in 0..<lhs.len:
|
||||
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i))
|
||||
|
||||
proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
# note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
|
||||
@@ -237,6 +218,9 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym
|
||||
field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
let t = skipIntLit(s.typ, idgen)
|
||||
field.typ = t
|
||||
if s.kind in {skLet, skVar, skField, skForVar}:
|
||||
#field.bitsize = s.bitsize
|
||||
field.alignment = s.alignment
|
||||
assert t.kind != tyTyped
|
||||
propagateToOwner(obj, t)
|
||||
field.position = obj.n.len
|
||||
|
||||
@@ -151,7 +151,7 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||
extccomp.callCCompiler(conf)
|
||||
# for now we do not support writing out a .json file with the build instructions when HCR is on
|
||||
if not conf.hcrOn:
|
||||
extccomp.writeJsonBuildInstructions(conf)
|
||||
extccomp.writeJsonBuildInstructions(conf, graph.cachedFiles)
|
||||
if optGenScript in graph.config.globalOptions:
|
||||
writeDepsFile(graph)
|
||||
if optGenCDeps in graph.config.globalOptions:
|
||||
@@ -195,8 +195,8 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
|
||||
var stream = llStreamOpen(f, fmRead)
|
||||
if stream != nil:
|
||||
var
|
||||
L: Lexer
|
||||
tok: Token
|
||||
L: Lexer = default(Lexer)
|
||||
tok: Token = default(Token)
|
||||
initToken(tok)
|
||||
openLexer(L, f, stream, cache, config)
|
||||
while true:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## represents a complete Nim project. Single modules can either be kept in RAM
|
||||
## or stored in a rod-file.
|
||||
|
||||
import intsets, tables, hashes
|
||||
import std/[intsets, tables, hashes, strtabs]
|
||||
import ../dist/checksums/src/checksums/md5
|
||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
|
||||
import ic / [packed_ast, ic]
|
||||
@@ -57,6 +57,7 @@ type
|
||||
SymInfoPair* = object
|
||||
sym*: PSym
|
||||
info*: TLineInfo
|
||||
isDecl*: bool
|
||||
|
||||
PipelinePass* = enum
|
||||
NonePass
|
||||
@@ -128,6 +129,8 @@ type
|
||||
idgen*: IdGenerator
|
||||
operators*: Operators
|
||||
|
||||
cachedFiles*: StringTableRef
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
idgen*: IdGenerator
|
||||
PPassContext* = ref TPassContext
|
||||
@@ -248,7 +251,7 @@ proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
|
||||
iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
|
||||
let importHidden = optImportHidden in m.options
|
||||
if isCachedModule(g, m):
|
||||
var rodIt: RodIter
|
||||
var rodIt: RodIter = default(RodIter)
|
||||
var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden)
|
||||
while r != nil:
|
||||
yield r
|
||||
@@ -269,7 +272,7 @@ proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
|
||||
result = someSym(g, g.systemModule, name)
|
||||
|
||||
iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
|
||||
var mi: ModuleIter
|
||||
var mi: ModuleIter = default(ModuleIter)
|
||||
var r = initModuleIter(mi, g, g.systemModule, name)
|
||||
while r != nil:
|
||||
yield r
|
||||
@@ -479,6 +482,7 @@ proc initModuleGraphFields(result: ModuleGraph) =
|
||||
result.symBodyHashes = initTable[int, SigHash]()
|
||||
result.operators = initOperators(result)
|
||||
result.emittedTypeInfo = initTable[string, FileIndex]()
|
||||
result.cachedFiles = newStringTable()
|
||||
|
||||
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||
result = ModuleGraph()
|
||||
|
||||
@@ -36,11 +36,18 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
|
||||
localError(n.info, "only '/' supported with $package notation")
|
||||
result = ""
|
||||
else:
|
||||
let modname = getModuleName(conf, n[2])
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = getModuleName(conf, n1)
|
||||
result.add renderTree(n0, {renderNoComments}).replace(" ")
|
||||
result.add modname
|
||||
if n0.kind in nkIdentKinds:
|
||||
let ident = n0.getPIdent
|
||||
if ident != nil and ident.s[0] == '/':
|
||||
let modname = getModuleName(conf, n[2])
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = getModuleName(conf, n1)
|
||||
result.add renderTree(n0, {renderNoComments}).replace(" ")
|
||||
result.add modname
|
||||
else:
|
||||
result = ""
|
||||
else:
|
||||
result = ""
|
||||
of nkPrefix:
|
||||
when false:
|
||||
if n[0].kind == nkIdent and n[0].ident.s == "$":
|
||||
|
||||
@@ -14,6 +14,9 @@ import
|
||||
idents, lexer, syntaxes, modulegraphs,
|
||||
lineinfos, pathutils
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
import std/strtabs
|
||||
|
||||
proc resetSystemArtifacts*(g: ModuleGraph) =
|
||||
magicsys.resetSysTypes(g)
|
||||
|
||||
@@ -42,6 +45,8 @@ proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
|
||||
result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
|
||||
graph.addDep(s, fileIdx)
|
||||
graph.addIncludeDep(s.position.FileIndex, fileIdx)
|
||||
let path = toFullPath(graph.config, fileIdx)
|
||||
graph.cachedFiles[path] = $secureHashFile(path)
|
||||
|
||||
proc wantMainModule*(conf: ConfigRef) =
|
||||
if conf.projectFull.isEmpty:
|
||||
|
||||
@@ -125,14 +125,14 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool
|
||||
conf.m.filenameToIndexTbl[canon2] = result
|
||||
|
||||
proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
|
||||
var dummy: bool
|
||||
var dummy: bool = false
|
||||
result = fileInfoIdx(conf, filename, dummy)
|
||||
|
||||
proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile; isKnownFile: var bool): FileIndex =
|
||||
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), isKnownFile)
|
||||
|
||||
proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex =
|
||||
var dummy: bool
|
||||
var dummy: bool = false
|
||||
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy)
|
||||
|
||||
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
|
||||
@@ -429,7 +429,8 @@ To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 f
|
||||
proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) =
|
||||
if msg in fatalMsgs:
|
||||
if conf.cmd == cmdIdeTools: log(s)
|
||||
quit(conf, msg)
|
||||
if conf.cmd != cmdIdeTools or msg != errFatal:
|
||||
quit(conf, msg)
|
||||
if msg >= errMin and msg <= errMax or
|
||||
(msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg):
|
||||
inc(conf.errorCounter)
|
||||
@@ -437,7 +438,11 @@ proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string,
|
||||
if conf.errorCounter >= conf.errorMax:
|
||||
# only really quit when we're not in the new 'nim check --def' mode:
|
||||
if conf.ideCmd == ideNone:
|
||||
quit(conf, msg)
|
||||
when defined(nimsuggest):
|
||||
#we need to inform the user that something went wrong when initializing NimSuggest
|
||||
raiseRecoverableError(s)
|
||||
else:
|
||||
quit(conf, msg)
|
||||
elif eh == doAbort and conf.cmd != cmdIdeTools:
|
||||
quit(conf, msg)
|
||||
elif eh == doRaise:
|
||||
@@ -553,9 +558,10 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
ignoreMsg = not conf.hasHint(msg)
|
||||
if not ignoreMsg and msg in conf.warningAsErrors:
|
||||
title = ErrorTitle
|
||||
color = ErrorColor
|
||||
else:
|
||||
title = HintTitle
|
||||
color = HintColor
|
||||
color = HintColor
|
||||
inc(conf.hintCounter)
|
||||
|
||||
let s = if isRaw: arg else: getMessageStr(msg, arg)
|
||||
@@ -643,13 +649,16 @@ template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraM
|
||||
let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
|
||||
liMessage(conf, info, msg, m, doNothing, instLoc())
|
||||
|
||||
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
|
||||
if i.fileIndex.int32 < 0:
|
||||
proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope =
|
||||
if fi.int32 < 0:
|
||||
result = makeCString "???"
|
||||
elif optExcessiveStackTrace in conf.globalOptions:
|
||||
result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
|
||||
result = conf.m.fileInfos[fi.int32].quotedFullName
|
||||
else:
|
||||
result = conf.m.fileInfos[i.fileIndex.int32].quotedName
|
||||
result = conf.m.fileInfos[fi.int32].quotedName
|
||||
|
||||
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
|
||||
quotedFilename(conf, i.fileIndex)
|
||||
|
||||
template listMsg(title, r) =
|
||||
msgWriteln(conf, title, {msgNoUnitSep})
|
||||
|
||||
@@ -9,6 +9,7 @@ define:nimPreviewSlimSystem
|
||||
define:nimPreviewCstringConversion
|
||||
define:nimPreviewProcConversion
|
||||
define:nimPreviewRangeDefault
|
||||
define:nimPreviewNonVarDestructor
|
||||
threads:off
|
||||
|
||||
#import:"$projectpath/testability"
|
||||
|
||||
@@ -40,7 +40,7 @@ proc selectUniqueSymbol*(i: Interpreter; name: string;
|
||||
assert i != nil
|
||||
assert i.mainModule != nil, "no main module selected"
|
||||
let n = getIdent(i.graph.cache, name)
|
||||
var it: ModuleIter
|
||||
var it: ModuleIter = default(ModuleIter)
|
||||
var s = initModuleIter(it, i.graph, i.mainModule, n)
|
||||
result = nil
|
||||
while s != nil:
|
||||
|
||||
@@ -279,8 +279,8 @@ proc optimize*(n: PNode): PNode =
|
||||
|
||||
Now assume 'use' raises, then we shouldn't do the 'wasMoved(s)'
|
||||
]#
|
||||
var c: Con
|
||||
var b: BasicBlock
|
||||
var c: Con = Con()
|
||||
var b: BasicBlock = default(BasicBlock)
|
||||
analyse(c, b, n)
|
||||
if c.somethingTodo:
|
||||
result = shallowCopy(n)
|
||||
|
||||
@@ -195,7 +195,7 @@ type
|
||||
IdeCmd* = enum
|
||||
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod,
|
||||
ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols,
|
||||
ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand
|
||||
ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand, ideInlayHints
|
||||
|
||||
Feature* = enum ## experimental features; DO NOT RENAME THESE!
|
||||
dotOperators,
|
||||
@@ -220,7 +220,10 @@ type
|
||||
unicodeOperators, # deadcode
|
||||
flexibleOptionalParams,
|
||||
strictDefs,
|
||||
strictCaseObjects
|
||||
strictCaseObjects,
|
||||
openSym, # remove nfDisabledOpenSym when this is default
|
||||
# alternative to above:
|
||||
genericsOpenSym
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
@@ -280,9 +283,24 @@ type
|
||||
version*: int
|
||||
endLine*: uint16
|
||||
endCol*: int
|
||||
inlayHintInfo*: SuggestInlayHint
|
||||
|
||||
Suggestions* = seq[Suggest]
|
||||
|
||||
SuggestInlayHintKind* = enum
|
||||
sihkType = "Type",
|
||||
sihkParameter = "Parameter"
|
||||
|
||||
SuggestInlayHint* = ref object
|
||||
kind*: SuggestInlayHintKind
|
||||
line*: int # Starts at 1
|
||||
column*: int # Starts at 0
|
||||
label*: string
|
||||
paddingLeft*: bool
|
||||
paddingRight*: bool
|
||||
allowInsert*: bool
|
||||
tooltip*: string
|
||||
|
||||
ProfileInfo* = object
|
||||
time*: float
|
||||
count*: int
|
||||
@@ -417,6 +435,8 @@ type
|
||||
expandNodeResult*: string
|
||||
expandPosition*: TLineInfo
|
||||
|
||||
clientProcessId*: int
|
||||
|
||||
|
||||
proc parseNimVersion*(a: string): NimVer =
|
||||
# could be moved somewhere reusable
|
||||
@@ -1057,6 +1077,7 @@ proc `$`*(c: IdeCmd): string =
|
||||
of ideRecompile: "recompile"
|
||||
of ideChanged: "changed"
|
||||
of ideType: "type"
|
||||
of ideInlayHints: "inlayHints"
|
||||
|
||||
proc floatInt64Align*(conf: ConfigRef): int16 =
|
||||
## Returns either 4 or 8 depending on reasons.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -280,8 +280,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
|
||||
|
||||
@@ -1430,6 +1430,7 @@ proc parseTypeDesc(p: var Parser, fullExpr = false): PNode =
|
||||
result = newNodeP(nkObjectTy, p)
|
||||
getTok(p)
|
||||
of tkConcept:
|
||||
result = p.emptyNode
|
||||
parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
|
||||
of tkVar: result = parseTypeDescKAux(p, nkVarTy, pmTypeDesc)
|
||||
of tkOut: result = parseTypeDescKAux(p, nkOutTy, pmTypeDesc)
|
||||
@@ -2285,7 +2286,7 @@ proc parseTypeDef(p: var Parser): PNode =
|
||||
setEndInfo()
|
||||
|
||||
proc parseVarTuple(p: var Parser): PNode =
|
||||
#| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')'
|
||||
#| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)?
|
||||
#| varTuple = varTupleLhs '=' optInd expr
|
||||
result = newNodeP(nkVarTuple, p)
|
||||
getTok(p) # skip '('
|
||||
@@ -2302,9 +2303,14 @@ proc parseVarTuple(p: var Parser): PNode =
|
||||
if p.tok.tokType != tkComma: break
|
||||
getTok(p)
|
||||
skipComment(p, a)
|
||||
result.add(p.emptyNode) # no type desc
|
||||
optPar(p)
|
||||
eat(p, tkParRi)
|
||||
if p.tok.tokType == tkColon:
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
result.add(parseTypeDesc(p, fullExpr = true))
|
||||
else:
|
||||
result.add(p.emptyNode) # no type desc
|
||||
setEndInfo()
|
||||
|
||||
proc parseVariable(p: var Parser): PNode =
|
||||
@@ -2511,22 +2517,6 @@ proc parseStmt(p: var Parser): PNode =
|
||||
if err and p.tok.tokType == tkEof: break
|
||||
setEndInfo()
|
||||
|
||||
proc parseAll(p: var Parser): PNode =
|
||||
## Parses the rest of the input stream held by the parser into a PNode.
|
||||
result = newNodeP(nkStmtList, p)
|
||||
while p.tok.tokType != tkEof:
|
||||
p.hasProgress = false
|
||||
var a = complexOrSimpleStmt(p)
|
||||
if a.kind != nkEmpty and p.hasProgress:
|
||||
result.add(a)
|
||||
else:
|
||||
parMessage(p, errExprExpected, p.tok)
|
||||
# bugfix: consume a token here to prevent an endless loop:
|
||||
getTok(p)
|
||||
if p.tok.indent != 0:
|
||||
parMessage(p, errInvalidIndentation)
|
||||
setEndInfo()
|
||||
|
||||
proc checkFirstLineIndentation*(p: var Parser) =
|
||||
if p.tok.indent != 0 and tsLeading in p.tok.spacing:
|
||||
parMessage(p, errInvalidIndentation)
|
||||
@@ -2561,6 +2551,16 @@ proc parseTopLevelStmt(p: var Parser): PNode =
|
||||
break
|
||||
setEndInfo()
|
||||
|
||||
proc parseAll*(p: var Parser): PNode =
|
||||
## Parses the rest of the input stream held by the parser into a PNode.
|
||||
result = newNodeP(nkStmtList, p)
|
||||
while true:
|
||||
let nextStmt = p.parseTopLevelStmt()
|
||||
if nextStmt.kind == nkEmpty:
|
||||
break
|
||||
result &= nextStmt
|
||||
setEndInfo()
|
||||
|
||||
proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
|
||||
filename: string = ""; line: int = 0;
|
||||
errorHandler: ErrorHandler = nil): PNode =
|
||||
|
||||
@@ -5,10 +5,12 @@ import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
|
||||
|
||||
import pipelineutils
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
when not defined(leanCompiler):
|
||||
import jsgen, docgen2
|
||||
|
||||
import std/[syncio, objectdollar, assertions, tables, strutils]
|
||||
import std/[syncio, objectdollar, assertions, tables, strutils, strtabs]
|
||||
import renderer
|
||||
import ic/replayer
|
||||
|
||||
@@ -89,7 +91,7 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
stream: PLLStream): bool =
|
||||
if graph.stopCompile(): return true
|
||||
var
|
||||
p: Parser
|
||||
p: Parser = default(Parser)
|
||||
s: PLLStream
|
||||
fileIdx = module.fileIdx
|
||||
|
||||
@@ -225,7 +227,10 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
|
||||
if result == nil:
|
||||
var cachedModules: seq[FileIndex]
|
||||
result = moduleFromRodFile(graph, fileIdx, cachedModules)
|
||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||
let path = toFullPath(graph.config, fileIdx)
|
||||
let filename = AbsoluteFile path
|
||||
if fileExists(filename): # it could be a stdinfile
|
||||
graph.cachedFiles[path] = $secureHashFile(path)
|
||||
if result == nil:
|
||||
result = newModule(graph, fileIdx)
|
||||
result.flags.incl flags
|
||||
|
||||
@@ -330,7 +330,7 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
|
||||
# {.dynlib: myGetProcAddr(...).}
|
||||
result = c.semExpr(c, n[1])
|
||||
if result.kind == nkSym and result.sym.kind == skConst:
|
||||
result = result.sym.astdef # look it up
|
||||
result = c.semConstExpr(c, result) # fold const
|
||||
if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}:
|
||||
localError(c.config, n.info, errStringLiteralExpected)
|
||||
result = newEmptyStrNode(c, n)
|
||||
@@ -468,6 +468,18 @@ proc processOption(c: PContext, n: PNode, resOptions: var TOptions) =
|
||||
# calling conventions (boring...):
|
||||
localError(c.config, n.info, "option expected")
|
||||
|
||||
proc checkPushedPragma(c: PContext, n: PNode) =
|
||||
let keyDeep = n.kind in nkPragmaCallKinds and n.len > 1
|
||||
var key = if keyDeep: n[0] else: n
|
||||
if key.kind in nkIdentKinds:
|
||||
let ident = considerQuotedIdent(c, key)
|
||||
var userPragma = strTableGet(c.userPragmas, ident)
|
||||
if userPragma == nil:
|
||||
let k = whichKeyword(ident)
|
||||
# TODO: might as well make a list which is not accepted by `push`: emit, cast etc.
|
||||
if k == wEmit:
|
||||
localError(c.config, n.info, "an 'emit' pragma cannot be pushed")
|
||||
|
||||
proc processPush(c: PContext, n: PNode, start: int) =
|
||||
if n[start-1].kind in nkPragmaCallKinds:
|
||||
localError(c.config, n.info, "'push' cannot have arguments")
|
||||
@@ -475,6 +487,7 @@ proc processPush(c: PContext, n: PNode, start: int) =
|
||||
for i in start..<n.len:
|
||||
if not tryProcessOption(c, n[i], c.config.options):
|
||||
# simply store it somewhere:
|
||||
checkPushedPragma(c, n[i])
|
||||
if x.otherPragmas.isNil:
|
||||
x.otherPragmas = newNodeI(nkPragma, n.info)
|
||||
x.otherPragmas.add n[i]
|
||||
@@ -849,6 +862,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
|
||||
# number of pragmas increase/decrease with user pragma expansion
|
||||
inc c.instCounter
|
||||
defer: dec c.instCounter
|
||||
if c.instCounter > 100:
|
||||
globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s)
|
||||
|
||||
@@ -858,7 +872,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
pragma(c, sym, userPragma.ast, validPragmas, isStatement)
|
||||
n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
|
||||
i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
|
||||
dec c.instCounter
|
||||
else:
|
||||
let k = whichKeyword(ident)
|
||||
if k in validPragmas:
|
||||
@@ -1112,13 +1125,20 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
of wFatal: fatal(c.config, it.info, expectStrLit(c, it))
|
||||
of wDefine: processDefine(c, it, sym)
|
||||
of wUndef: processUndef(c, it)
|
||||
of wCompile: processCompile(c, it)
|
||||
of wCompile:
|
||||
let m = sym.getModule()
|
||||
incl(m.flags, sfUsed)
|
||||
processCompile(c, it)
|
||||
of wLink: processLink(c, it)
|
||||
of wPassl:
|
||||
let m = sym.getModule()
|
||||
incl(m.flags, sfUsed)
|
||||
let s = expectStrLit(c, it)
|
||||
extccomp.addLinkOption(c.config, s)
|
||||
recordPragma(c, it, "passl", s)
|
||||
of wPassc:
|
||||
let m = sym.getModule()
|
||||
incl(m.flags, sfUsed)
|
||||
let s = expectStrLit(c, it)
|
||||
extccomp.addCompileOption(c.config, s)
|
||||
recordPragma(c, it, "passc", s)
|
||||
|
||||
@@ -31,7 +31,7 @@ proc equalGenericParams(procA, procB: PNode): bool =
|
||||
proc searchForProcAux(c: PContext, scope: PScope, fn: PSym): PSym =
|
||||
const flags = {ExactGenericParams, ExactTypeDescValues,
|
||||
ExactConstraints, IgnoreCC}
|
||||
var it: TIdentIter
|
||||
var it: TIdentIter = default(TIdentIter)
|
||||
result = initIdentIter(it, scope.symbols, fn.name)
|
||||
while result != nil:
|
||||
if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags):
|
||||
@@ -74,7 +74,7 @@ when false:
|
||||
proc searchForBorrowProc*(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
# Searches for the fn in the symbol table. If the parameter lists are suitable
|
||||
# for borrowing the sym in the symbol table is returned, else nil.
|
||||
var it: TIdentIter
|
||||
var it: TIdentIter = default(TIdentIter)
|
||||
for scope in walkScopes(startScope):
|
||||
result = initIdentIter(it, scope.symbols, fn.Name)
|
||||
while result != nil:
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{.used.}
|
||||
|
||||
import
|
||||
lexer, options, idents, strutils, ast, msgs, lineinfos
|
||||
lexer, options, idents, strutils, ast, msgs, lineinfos, wordrecg
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions, formatfloat]
|
||||
@@ -515,6 +515,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
if n.len > 0: result += lsub(g, n[0])
|
||||
of nkOpenSym: result = lsub(g, n[0])
|
||||
of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
|
||||
of nkTupleClassTy: result = len("tuple")
|
||||
of nkDotExpr: result = lsons(g, n) + 1
|
||||
@@ -848,7 +849,7 @@ proc gcase(g: var TSrcGen, n: PNode) =
|
||||
gsub(g, n[^1], c)
|
||||
|
||||
proc genSymSuffix(result: var string, s: PSym) {.inline.} =
|
||||
if sfGenSym in s.flags:
|
||||
if sfGenSym in s.flags and s.name.id != ord(wUnderscore):
|
||||
result.add '_'
|
||||
result.addInt s.id
|
||||
|
||||
@@ -874,7 +875,7 @@ proc gproc(g: var TSrcGen, n: PNode) =
|
||||
if renderNoPragmas notin g.flags:
|
||||
gsub(g, n[pragmasPos])
|
||||
if renderNoBody notin g.flags:
|
||||
if n[bodyPos].kind != nkEmpty:
|
||||
if n.len > bodyPos and n[bodyPos].kind != nkEmpty:
|
||||
put(g, tkSpaces, Space)
|
||||
putWithSpace(g, tkEquals, "=")
|
||||
indentNL(g)
|
||||
@@ -971,7 +972,9 @@ proc gident(g: var TSrcGen, n: PNode) =
|
||||
s.addInt localId
|
||||
if sfCursor in n.sym.flags:
|
||||
s.add "_cursor"
|
||||
elif n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp):
|
||||
elif n.kind == nkSym and (renderIds in g.flags or
|
||||
(sfGenSym in n.sym.flags and n.sym.name.id != ord(wUnderscore)) or
|
||||
n.sym.kind == skTemp):
|
||||
s.add '_'
|
||||
s.addInt n.sym.id
|
||||
when defined(debugMagics):
|
||||
@@ -1017,7 +1020,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
|
||||
proc skipHiddenNodes(n: PNode): PNode =
|
||||
result = n
|
||||
while result != nil:
|
||||
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1:
|
||||
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1:
|
||||
result = result[1]
|
||||
elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
|
||||
result.len > 0:
|
||||
@@ -1277,6 +1280,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
|
||||
else:
|
||||
gsub(g, n, 0)
|
||||
of nkOpenSym: gsub(g, n, 0)
|
||||
of nkPar, nkClosure:
|
||||
put(g, tkParLe, "(")
|
||||
gcomma(g, n, c)
|
||||
@@ -1306,10 +1310,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
put(g, tkCustomLit, n[0].strVal)
|
||||
gsub(g, n, 1)
|
||||
else:
|
||||
gsub(g, n, 0)
|
||||
for i in 0..<n.len-1:
|
||||
gsub(g, n, i)
|
||||
put(g, tkDot, ".")
|
||||
assert n.len == 2, $n.len
|
||||
accentedName(g, n[1])
|
||||
if n.len > 1:
|
||||
accentedName(g, n[^1])
|
||||
of nkBind:
|
||||
putWithSpace(g, tkBind, "bind")
|
||||
gsub(g, n, 0)
|
||||
@@ -1524,17 +1529,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
gsub(g, n[0])
|
||||
gsub(g, n[1])
|
||||
gcoms(g)
|
||||
indentNL(g)
|
||||
gsub(g, n[2])
|
||||
dedent(g)
|
||||
else:
|
||||
put(g, tkObject, "object")
|
||||
of nkRecList:
|
||||
indentNL(g)
|
||||
for i in 0..<n.len:
|
||||
optNL(g)
|
||||
gsub(g, n[i], c)
|
||||
gcoms(g)
|
||||
dedent(g)
|
||||
putNL(g)
|
||||
of nkOfInherit:
|
||||
putWithSpace(g, tkOf, "of")
|
||||
gsub(g, n, 0)
|
||||
|
||||
@@ -319,6 +319,14 @@ proc intersects(s1, s2: IntSet): bool =
|
||||
if s2.contains(a):
|
||||
return true
|
||||
|
||||
proc hasPushOrPopPragma(n: DepN): bool =
|
||||
# Checks if the tree node has some pragmas that do not
|
||||
# play well with reordering, like the push/pop pragma
|
||||
# no crossing for push/pop barrier
|
||||
let a = n.pnode
|
||||
result = a.kind == nkPragma and a[0].kind == nkIdent and
|
||||
(a[0].ident.s == "push" or a[0].ident.s == "pop")
|
||||
|
||||
proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
|
||||
# Build a dependency graph
|
||||
result = newSeqOfCap[DepN](deps.len)
|
||||
@@ -360,6 +368,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
|
||||
for dep in deps[i][0]:
|
||||
if dep in declares:
|
||||
ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
|
||||
elif hasPushOrPopPragma(nj):
|
||||
# Every node that comes after a push/pop pragma must
|
||||
# depend on it; vice versa
|
||||
if j < i:
|
||||
ni.kids.add nj
|
||||
else:
|
||||
nj.kids.add ni
|
||||
else:
|
||||
for d in declares:
|
||||
if uses.contains(d):
|
||||
@@ -393,23 +408,14 @@ proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
|
||||
proc getStrongComponents(g: var DepG): seq[seq[DepN]] =
|
||||
## Tarjan's algorithm. Performs a topological sort
|
||||
## and detects strongly connected components.
|
||||
var s: seq[DepN]
|
||||
result = @[]
|
||||
var s: seq[DepN] = @[]
|
||||
var idx = 0
|
||||
for v in g.mitems:
|
||||
if v.idx < 0:
|
||||
strongConnect(v, idx, s, result)
|
||||
|
||||
proc hasForbiddenPragma(n: PNode): bool =
|
||||
# Checks if the tree node has some pragmas that do not
|
||||
# play well with reordering, like the push/pop pragma
|
||||
for a in n:
|
||||
if a.kind == nkPragma and a[0].kind == nkIdent and
|
||||
a[0].ident.s == "push":
|
||||
return true
|
||||
|
||||
proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
|
||||
if n.hasForbiddenPragma:
|
||||
return n
|
||||
var includedFiles = initIntSet()
|
||||
let mpath = toFullPath(graph.config, module.fileIdx)
|
||||
let n = expandIncludes(graph, module, n, mpath,
|
||||
|
||||
@@ -26,7 +26,10 @@ when not defined(leanCompiler):
|
||||
import spawn
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/formatfloat
|
||||
import std/[
|
||||
formatfloat,
|
||||
assertions,
|
||||
]
|
||||
|
||||
# implementation
|
||||
|
||||
@@ -85,6 +88,11 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
|
||||
changeType(c, x, formal, check=true)
|
||||
result = arg
|
||||
result = skipHiddenSubConv(result, c.graph, c.idgen)
|
||||
# mark inserted converter as used:
|
||||
var a = result
|
||||
if a.kind == nkHiddenDeref: a = a[0]
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
markUsed(c, a.info, a[0].sym)
|
||||
|
||||
|
||||
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
@@ -205,13 +213,19 @@ proc commonType*(c: PContext; x, y: PType): PType =
|
||||
result = newType(k, nextTypeId(c.idgen), r.owner)
|
||||
result.addSonSkipIntLit(r, c.idgen)
|
||||
|
||||
proc endsInNoReturn(n: PNode): bool =
|
||||
# check if expr ends in raise exception or call of noreturn proc
|
||||
var it = n
|
||||
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
|
||||
it = it.lastSon
|
||||
result = it.kind in nkLastBlockStmts or
|
||||
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
|
||||
const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
|
||||
proc shouldCheckCaseCovered(caseTyp: PType): bool =
|
||||
result = false
|
||||
case caseTyp.kind
|
||||
of shouldChckCovered:
|
||||
result = true
|
||||
of tyRange:
|
||||
if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
|
||||
result = true
|
||||
else:
|
||||
discard
|
||||
|
||||
proc endsInNoReturn(n: PNode): bool
|
||||
|
||||
proc commonType*(c: PContext; x: PType, y: PNode): PType =
|
||||
# ignore exception raising branches in case/if expressions
|
||||
@@ -243,6 +257,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
result.owner = getCurrOwner(c)
|
||||
else:
|
||||
result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
|
||||
if find(result.name.s, '`') >= 0:
|
||||
result.flags.incl sfWasGenSym
|
||||
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
|
||||
# incl(result.flags, sfGlobal)
|
||||
when defined(nimsuggest):
|
||||
@@ -252,7 +268,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||
allowed: TSymFlags): PSym
|
||||
# identifier with visibility
|
||||
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
||||
allowed: TSymFlags): PSym
|
||||
allowed: TSymFlags, fromTopLevel = false): PSym
|
||||
|
||||
proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind;
|
||||
flags: TTypeAllowedFlags = {}) =
|
||||
@@ -351,6 +367,11 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
c.config.m.errorOutputs = {}
|
||||
c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here
|
||||
|
||||
when defined(nimsuggest):
|
||||
# Remove the error hook so nimsuggest doesn't report errors there
|
||||
let tempHook = c.graph.config.structuredErrorHook
|
||||
c.graph.config.structuredErrorHook = nil
|
||||
|
||||
try:
|
||||
result = evalConstExpr(c.module, c.idgen, c.graph, e)
|
||||
if result == nil or result.kind == nkEmpty:
|
||||
@@ -361,6 +382,10 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
except ERecoverableError:
|
||||
result = nil
|
||||
|
||||
when defined(nimsuggest):
|
||||
# Restore the error hook
|
||||
c.graph.config.structuredErrorHook = tempHook
|
||||
|
||||
c.config.errorCounter = oldErrorCount
|
||||
c.config.errorMax = oldErrorMax
|
||||
c.config.m.errorOutputs = oldErrorOutputs
|
||||
@@ -462,7 +487,13 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
# we now know the supplied arguments
|
||||
var paramTypes = newIdTable()
|
||||
for param, value in genericParamsInMacroCall(s, call):
|
||||
idTablePut(paramTypes, param.typ, value.typ)
|
||||
var givenType = value.typ
|
||||
# the sym nodes used for the supplied generic arguments for
|
||||
# templates and macros leave type nil so regular sem can handle it
|
||||
# in this case, get the type directly from the sym
|
||||
if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
|
||||
givenType = value.sym.typ
|
||||
idTablePut(paramTypes, param.typ, givenType)
|
||||
|
||||
retType = generateTypeInstance(c, paramTypes,
|
||||
macroResult.info, retType)
|
||||
@@ -470,7 +501,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
if retType.kind == tyVoid:
|
||||
result = semStmt(c, result, flags)
|
||||
else:
|
||||
result = semExpr(c, result, flags, expectedType)
|
||||
result = semExpr(c, result, flags)
|
||||
result = fitNode(c, retType, result, result.info)
|
||||
#globalError(s.info, errInvalidParamKindX, typeToString(s.typ[0]))
|
||||
dec(c.config.evalTemplateCounter)
|
||||
|
||||
@@ -68,7 +68,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# `matches` may find new symbols, so keep track of count
|
||||
var symCount = c.currentScope.symbols.counter
|
||||
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
# https://github.com/nim-lang/Nim/issues/21272
|
||||
# prevent mutation during iteration by storing them in a seq
|
||||
# luckily `initCandidateSymbols` does just that
|
||||
@@ -236,44 +236,89 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
candidates.add("\n")
|
||||
let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
|
||||
let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
|
||||
if n.len > 1 and verboseTypeMismatch in c.config.legacyFeatures:
|
||||
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
|
||||
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
|
||||
case err.firstMismatch.kind
|
||||
of kUnknownNamedParam:
|
||||
if nArg == nil:
|
||||
candidates.add("\n unknown named parameter")
|
||||
else:
|
||||
candidates.add("\n unknown named parameter: " & $nArg[0])
|
||||
of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0])
|
||||
of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param")
|
||||
of kExtraArg: candidates.add("\n extra argument given")
|
||||
of kMissingParam: candidates.add("\n missing parameter: " & nameParam)
|
||||
of kTypeMismatch, kVarNeeded:
|
||||
doAssert nArg != nil
|
||||
let wanted = err.firstMismatch.formal.typ
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
candidates.add("\n required type for " & nameParam & ": ")
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
|
||||
candidates.add "\n but expression '"
|
||||
if err.firstMismatch.kind == kVarNeeded:
|
||||
if n.len > 1:
|
||||
if verboseTypeMismatch notin c.config.legacyFeatures:
|
||||
case err.firstMismatch.kind
|
||||
of kUnknownNamedParam:
|
||||
if nArg == nil:
|
||||
candidates.add(" unknown named parameter")
|
||||
else:
|
||||
candidates.add(" unknown named parameter: " & $nArg[0])
|
||||
candidates.add "\n"
|
||||
of kAlreadyGiven:
|
||||
candidates.add(" named param already provided: " & $nArg[0])
|
||||
candidates.add "\n"
|
||||
of kPositionalAlreadyGiven:
|
||||
candidates.add(" positional param was already given as named param")
|
||||
candidates.add "\n"
|
||||
of kExtraArg:
|
||||
candidates.add(" extra argument given")
|
||||
candidates.add "\n"
|
||||
of kMissingParam:
|
||||
candidates.add(" missing parameter: " & nameParam)
|
||||
candidates.add "\n"
|
||||
of kVarNeeded:
|
||||
doAssert nArg != nil
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
candidates.add " expression '"
|
||||
candidates.add renderNotLValue(nArg)
|
||||
candidates.add "' is immutable, not 'var'"
|
||||
else:
|
||||
candidates.add renderTree(nArg)
|
||||
candidates.add "' is of type: "
|
||||
let got = nArg.typ
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, got)
|
||||
candidates.add "\n"
|
||||
of kTypeMismatch:
|
||||
doAssert nArg != nil
|
||||
let wanted = err.firstMismatch.formal.typ
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
doAssert wanted != nil
|
||||
if got != nil:
|
||||
if got.kind == tyProc and wanted.kind == tyProc:
|
||||
# These are proc mismatches so,
|
||||
# add the extra explict detail of the mismatch
|
||||
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
|
||||
let got = nArg.typ
|
||||
if got != nil and got.kind == tyProc and wanted.kind == tyProc:
|
||||
# These are proc mismatches so,
|
||||
# add the extra explict detail of the mismatch
|
||||
candidates.add " expression '"
|
||||
candidates.add renderTree(nArg)
|
||||
candidates.add "' is of type: "
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, got)
|
||||
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
|
||||
effectProblem(wanted, got, candidates, c)
|
||||
candidates.add "\n"
|
||||
of kUnknown: discard "do not break 'nim check'"
|
||||
else:
|
||||
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
|
||||
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
|
||||
case err.firstMismatch.kind
|
||||
of kUnknownNamedParam:
|
||||
if nArg == nil:
|
||||
candidates.add("\n unknown named parameter")
|
||||
else:
|
||||
candidates.add("\n unknown named parameter: " & $nArg[0])
|
||||
of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0])
|
||||
of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param")
|
||||
of kExtraArg: candidates.add("\n extra argument given")
|
||||
of kMissingParam: candidates.add("\n missing parameter: " & nameParam)
|
||||
of kTypeMismatch, kVarNeeded:
|
||||
doAssert nArg != nil
|
||||
let wanted = err.firstMismatch.formal.typ
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
candidates.add("\n required type for " & nameParam & ": ")
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
|
||||
candidates.add "\n but expression '"
|
||||
if err.firstMismatch.kind == kVarNeeded:
|
||||
candidates.add renderNotLValue(nArg)
|
||||
candidates.add "' is immutable, not 'var'"
|
||||
else:
|
||||
candidates.add renderTree(nArg)
|
||||
candidates.add "' is of type: "
|
||||
let got = nArg.typ
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, got)
|
||||
doAssert wanted != nil
|
||||
if got != nil:
|
||||
if got.kind == tyProc and wanted.kind == tyProc:
|
||||
# These are proc mismatches so,
|
||||
# add the extra explict detail of the mismatch
|
||||
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
|
||||
effectProblem(wanted, got, candidates, c)
|
||||
|
||||
of kUnknown: discard "do not break 'nim check'"
|
||||
candidates.add "\n"
|
||||
of kUnknown: discard "do not break 'nim check'"
|
||||
candidates.add "\n"
|
||||
if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and
|
||||
n.kind == nkCommand:
|
||||
maybeWrongSpace = true
|
||||
@@ -354,7 +399,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||
|
||||
proc bracketNotFoundError(c: PContext; n: PNode) =
|
||||
var errors: CandidateErrors = @[]
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
let headSymbol = n[0]
|
||||
var symx = initOverloadIter(o, c, headSymbol)
|
||||
while symx != nil:
|
||||
@@ -376,7 +421,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
|
||||
# also avoid slowdowns in evaluating `compiles(expr)`.
|
||||
discard
|
||||
else:
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var sym = initOverloadIter(o, c, f)
|
||||
while sym != nil:
|
||||
result &= "\n found $1" % [getSymRepr(c.config, sym)]
|
||||
@@ -420,7 +465,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
filter, result, alt, errors, efExplain in flags,
|
||||
errorsEnabled, flags)
|
||||
|
||||
var dummyErrors: CandidateErrors
|
||||
var dummyErrors: CandidateErrors = @[]
|
||||
template pickSpecialOp(headSymbol) =
|
||||
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
|
||||
filter, result, alt, dummyErrors, efExplain in flags,
|
||||
@@ -511,6 +556,15 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
|
||||
for i in 1..<n.len:
|
||||
instGenericConvertersArg(c, n[i], x)
|
||||
|
||||
proc markConvertersUsed*(c: PContext, n: PNode) =
|
||||
assert n.kind in nkCallKinds
|
||||
for i in 1..<n.len:
|
||||
var a = n[i]
|
||||
if a == nil: continue
|
||||
if a.kind == nkHiddenDeref: a = a[0]
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
markUsed(c, a.info, a[0].sym)
|
||||
|
||||
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
|
||||
var m = newCandidate(c, f)
|
||||
result = paramTypesMatch(m, f, a, arg, nil)
|
||||
@@ -595,12 +649,18 @@ proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
else:
|
||||
x.call.add c.graph.emptyNode
|
||||
of skType:
|
||||
x.call.add newSymNode(s, n.info)
|
||||
var tn = newSymNode(s, n.info)
|
||||
# this node will be used in template substitution,
|
||||
# pretend this is an untyped node and let regular sem handle the type
|
||||
# to prevent problems where a generic parameter is treated as a value
|
||||
tn.typ = nil
|
||||
x.call.add tn
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
markConvertersUsed(c, result)
|
||||
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
|
||||
result.typ = finalCallee.typ[0]
|
||||
updateDefaultParams(result)
|
||||
@@ -665,14 +725,18 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
|
||||
onUse(info, s)
|
||||
result = newSymNode(newInst, info)
|
||||
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
assert n.kind == nkBracketExpr
|
||||
proc setGenericParams(c: PContext, n: PNode) =
|
||||
## sems generic params in subscript expression
|
||||
for i in 1..<n.len:
|
||||
let e = semExprWithType(c, n[i])
|
||||
if e.typ == nil:
|
||||
n[i].typ = errorType(c)
|
||||
else:
|
||||
n[i].typ = e.typ.skipTypes({tyTypeDesc})
|
||||
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
assert n.kind == nkBracketExpr
|
||||
setGenericParams(c, n)
|
||||
var s = s
|
||||
var a = n[0]
|
||||
if a.kind == nkSym:
|
||||
@@ -707,17 +771,25 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
else:
|
||||
result = explicitGenericInstError(c, n)
|
||||
|
||||
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] =
|
||||
# Searches for the fn in the symbol table. If the parameter lists are suitable
|
||||
# for borrowing the sym in the symbol table is returned, else nil.
|
||||
# New approach: generate fn(x, y, z) where x, y, z have the proper types
|
||||
# and use the overloading resolution mechanism:
|
||||
const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct}
|
||||
|
||||
template getType(isDistinct: bool; t: PType):untyped =
|
||||
if isDistinct: t.baseOfDistinct(c.graph, c.idgen) else: t
|
||||
|
||||
result = default(tuple[s: PSym, state: TBorrowState])
|
||||
var call = newNodeI(nkCall, fn.info)
|
||||
var hasDistinct = false
|
||||
var isDistinct: bool
|
||||
var x: PType
|
||||
var t: PType
|
||||
call.add(newIdentNode(fn.name, fn.info))
|
||||
for i in 1..<fn.typ.n.len:
|
||||
let param = fn.typ.n[i]
|
||||
const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct}
|
||||
#[.
|
||||
# We only want the type not any modifiers such as `ptr`, `var`, `ref` ...
|
||||
# tyCompositeTypeClass is here for
|
||||
@@ -726,22 +798,31 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
proc `$`(f: Foo): string {.borrow.}
|
||||
# We want to skip the `Foo` to get `int`
|
||||
]#
|
||||
let t = skipTypes(param.typ, desiredTypes)
|
||||
if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true
|
||||
var x: PType
|
||||
t = skipTypes(param.typ, desiredTypes)
|
||||
isDistinct = t.kind == tyDistinct or param.typ.kind == tyDistinct
|
||||
if t.kind == tyGenericInvocation and t[0].lastSon.kind == tyDistinct:
|
||||
result.state = bsGeneric
|
||||
return
|
||||
if isDistinct: hasDistinct = true
|
||||
if param.typ.kind == tyVar:
|
||||
x = newTypeS(param.typ.kind, c)
|
||||
x.addSonSkipIntLit(t.baseOfDistinct(c.graph, c.idgen), c.idgen)
|
||||
x.addSonSkipIntLit(getType(isDistinct, t), c.idgen)
|
||||
else:
|
||||
x = t.baseOfDistinct(c.graph, c.idgen)
|
||||
call.add(newNodeIT(nkEmpty, fn.info, x))
|
||||
x = getType(isDistinct, t)
|
||||
var s = copySym(param.sym, c.idgen)
|
||||
s.typ = x
|
||||
s.info = param.info
|
||||
call.add(newSymNode(s))
|
||||
if hasDistinct:
|
||||
let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}
|
||||
var resolved = semOverloadedCall(c, call, call, filter, {})
|
||||
if resolved != nil:
|
||||
result = resolved[0].sym
|
||||
if not compareTypes(result.typ[0], fn.typ[0], dcEqIgnoreDistinct):
|
||||
result = nil
|
||||
elif result.magic in {mArrPut, mArrGet}:
|
||||
result.s = resolved[0].sym
|
||||
result.state = bsMatch
|
||||
if not compareTypes(result.s.typ[0], fn.typ[0], dcEqIgnoreDistinct, {IgnoreFlags}):
|
||||
result.state = bsReturnNotMatch
|
||||
elif result.s.magic in {mArrPut, mArrGet}:
|
||||
# cannot borrow these magics for now
|
||||
result = nil
|
||||
result.state = bsNotSupported
|
||||
else:
|
||||
result.state = bsNoDistinct
|
||||
|
||||
@@ -76,6 +76,7 @@ type
|
||||
efNoDiagnostics,
|
||||
efTypeAllowed # typeAllowed will be called after
|
||||
efWantNoDefaults
|
||||
efAllowSymChoice # symchoice node should not be resolved
|
||||
|
||||
TExprFlags* = set[TExprFlag]
|
||||
|
||||
@@ -168,7 +169,10 @@ type
|
||||
sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
|
||||
inUncheckedAssignSection*: int
|
||||
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
|
||||
skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance and sets.
|
||||
skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
|
||||
inTypeofContext*: int
|
||||
TBorrowState* = enum
|
||||
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
|
||||
|
||||
template config*(c: PContext): ConfigRef = c.graph.config
|
||||
|
||||
|
||||
@@ -52,19 +52,8 @@ template rejectEmptyNode(n: PNode) =
|
||||
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
rejectEmptyNode(n)
|
||||
# same as 'semExprWithType' but doesn't check for proc vars
|
||||
result = semExpr(c, n, flags + {efOperand})
|
||||
result = semExpr(c, n, flags + {efOperand, efAllowSymChoice})
|
||||
if result.typ != nil:
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
|
||||
#and tfUnresolved in result.typ.flags:
|
||||
let owner = result.typ.owner
|
||||
let err =
|
||||
# consistent error message with evaltempl/semMacroExpr
|
||||
if owner != nil and owner.kind in {skTemplate, skMacro}:
|
||||
errMissingGenericParamsForTemplate % n.renderTree
|
||||
else:
|
||||
errProcHasNoConcreteType % n.renderTree
|
||||
localError(c.config, n.info, err)
|
||||
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
|
||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||
result.typ = newTypeS(tyVoid, c)
|
||||
@@ -90,39 +79,10 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType
|
||||
# do not produce another redundant error message:
|
||||
result = errorNode(c, n)
|
||||
|
||||
proc ambiguousSymChoice(c: PContext, orig, n: PNode): PNode =
|
||||
let first = n[0].sym
|
||||
if first.kind == skEnumField:
|
||||
# choose the first resolved enum field, i.e. the latest in scope
|
||||
# to mirror behavior before overloadable enums
|
||||
if hintAmbiguousEnum in c.config.notes:
|
||||
var err = "ambiguous enum field '" & first.name.s &
|
||||
"' assumed to be of type " & typeToString(first.typ) &
|
||||
" -- use one of the following:\n"
|
||||
for child in n:
|
||||
let candidate = child.sym
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s & "\n"
|
||||
message(c.config, orig.info, hintAmbiguousEnum, err)
|
||||
result = n[0]
|
||||
else:
|
||||
var err = "ambiguous identifier '" & first.name.s &
|
||||
"' -- use one of the following:\n"
|
||||
for child in n:
|
||||
let candidate = child.sym
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
err.add ": " & typeToString(candidate.typ) & "\n"
|
||||
localError(c.config, orig.info, err)
|
||||
n.typ = errorType(c)
|
||||
result = n
|
||||
|
||||
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
result = semExprCheck(c, n, flags-{efTypeAllowed}, expectedType)
|
||||
if result.typ == nil and efInTypeof in flags:
|
||||
result.typ = c.voidType
|
||||
elif (result.typ == nil or result.typ.kind == tyNone) and
|
||||
efTypeAllowed in flags and
|
||||
result.kind == nkClosedSymChoice and result.len > 0:
|
||||
result = ambiguousSymChoice(c, n, result)
|
||||
elif result.typ == nil or result.typ == c.enforceVoidContext:
|
||||
localError(c.config, n.info, errExprXHasNoType %
|
||||
renderTree(result, {renderNoComments}))
|
||||
@@ -155,6 +115,112 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
|
||||
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode
|
||||
|
||||
proc isSymChoice(n: PNode): bool {.inline.} =
|
||||
result = n.kind in nkSymChoices
|
||||
|
||||
proc resolveSymChoice(c: PContext, n: var PNode, flags: TExprFlags = {}, expectedType: PType = nil) =
|
||||
## Attempts to resolve a symchoice `n`, `n` remains a symchoice if
|
||||
## it cannot be resolved (this is the case even when `n.len == 1`).
|
||||
if expectedType != nil:
|
||||
# resolve from type inference, see paramTypesMatch
|
||||
n = fitNode(c, expectedType, n, n.info)
|
||||
if isSymChoice(n) and efAllowSymChoice notin flags:
|
||||
# some contexts might want sym choices preserved for later disambiguation
|
||||
# in general though they are ambiguous
|
||||
let first = n[0].sym
|
||||
var foundSym: PSym = nil
|
||||
if first.kind == skEnumField and
|
||||
not isAmbiguous(c, first.name, {skEnumField}, foundSym) and
|
||||
foundSym == first:
|
||||
# choose the first resolved enum field, i.e. the latest in scope
|
||||
# to mirror behavior before overloadable enums
|
||||
n = n[0]
|
||||
|
||||
proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType,
|
||||
warnDisabled = false): PNode =
|
||||
## sem the child of an `nkOpenSym` node, that is, captured symbols that can be
|
||||
## replaced by newly injected symbols in generics. `s` must be the captured
|
||||
## symbol if the original node is an `nkSym` node; and `nil` if it is an
|
||||
## `nkOpenSymChoice`, in which case only non-overloadable injected symbols
|
||||
## will be considered.
|
||||
let isSym = n.kind == nkSym
|
||||
let ident = n.getPIdent
|
||||
assert ident != nil
|
||||
let id = newIdentNode(ident, n.info)
|
||||
c.isAmbiguous = false
|
||||
let s2 = qualifiedLookUp(c, id, {})
|
||||
# for `nkSym`, the first found symbol being different and unambiguous is
|
||||
# enough to replace the original
|
||||
# for `nkOpenSymChoice`, the first found symbol must be non-overloadable,
|
||||
# since otherwise we have to use regular `nkOpenSymChoice` functionality
|
||||
# but of the overloadable sym kinds, semExpr does not handle skModule, skMacro, skTemplate
|
||||
# as overloaded in the case where `nkIdent` finds them first
|
||||
if s2 != nil and not c.isAmbiguous and
|
||||
((isSym and s2 != n.sym) or
|
||||
(not isSym and s2.kind notin OverloadableSyms-{skModule, skMacro, skTemplate})):
|
||||
# only consider symbols defined under current proc:
|
||||
var o = s2.owner
|
||||
while o != nil:
|
||||
if o == c.p.owner:
|
||||
if not warnDisabled:
|
||||
result = semExpr(c, id, flags, expectedType)
|
||||
return
|
||||
else:
|
||||
var msg =
|
||||
"a new symbol '" & ident.s & "' has been injected during " &
|
||||
# msgContext should show what is being instantiated:
|
||||
"template or generic instantiation, however "
|
||||
if isSym:
|
||||
msg.add(
|
||||
getSymRepr(c.config, n.sym) & " captured at " &
|
||||
"the proc declaration will be used instead; " &
|
||||
"either enable --experimental:openSym to use the injected symbol, " &
|
||||
"or `bind` this captured symbol explicitly")
|
||||
else:
|
||||
msg.add(
|
||||
"overloads of " & ident.s & " will be used instead; " &
|
||||
"either enable --experimental:openSym to use the injected symbol, " &
|
||||
"or `bind` this symbol explicitly")
|
||||
message(c.config, n.info, warnIgnoredSymbolInjection, msg)
|
||||
break
|
||||
o = o.owner
|
||||
# nothing found
|
||||
n.flags.excl nfDisabledOpenSym
|
||||
if not warnDisabled and isSym:
|
||||
result = semExpr(c, n, flags, expectedType)
|
||||
else:
|
||||
result = nil
|
||||
if not isSym:
|
||||
# set symchoice node type back to None
|
||||
n.typ = newTypeS(tyNone, c)
|
||||
|
||||
proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
if n.kind == nkOpenSymChoice:
|
||||
result = semOpenSym(c, n, flags, expectedType,
|
||||
warnDisabled = nfDisabledOpenSym in n.flags and
|
||||
genericsOpenSym notin c.features)
|
||||
if result != nil:
|
||||
return
|
||||
result = n
|
||||
resolveSymChoice(c, result, flags, expectedType)
|
||||
if isSymChoice(result) and result.len == 1:
|
||||
# resolveSymChoice can leave 1 sym
|
||||
result = result[0]
|
||||
if isSymChoice(result) and efAllowSymChoice notin flags:
|
||||
var err = "ambiguous identifier: '" & result[0].sym.name.s &
|
||||
"' -- use one of the following:\n"
|
||||
for child in n:
|
||||
let candidate = child.sym
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
err.add ": " & typeToString(candidate.typ) & "\n"
|
||||
localError(c.config, n.info, err)
|
||||
n.typ = errorType(c)
|
||||
result = n
|
||||
if result.kind == nkSym:
|
||||
result = semSym(c, result, result.sym, flags)
|
||||
|
||||
proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} =
|
||||
result = copyTree(s.astdef)
|
||||
if result.isNil:
|
||||
@@ -223,7 +289,8 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
|
||||
discard "convOk"
|
||||
elif targetTyp.isOrdinalType:
|
||||
if src.kind in nkCharLit..nkUInt64Lit and
|
||||
src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp):
|
||||
src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp) and
|
||||
targetTyp.kind notin {tyUInt..tyUInt64}:
|
||||
result = convNotInRange
|
||||
elif src.kind in nkFloatLit..nkFloat64Lit and
|
||||
(classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or
|
||||
@@ -238,6 +305,9 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
|
||||
result = convNotInRange
|
||||
else:
|
||||
# we use d, s here to speed up that operation a bit:
|
||||
if d.kind == tyFromExpr:
|
||||
result = convNotLegal
|
||||
return
|
||||
case cmpTypes(c, d, s)
|
||||
of isNone, isGeneric:
|
||||
if not compareTypes(targetTyp.skipTypes(abstractVar), srcTyp.skipTypes({tyOwned}), dcEqIgnoreDistinct):
|
||||
@@ -291,9 +361,6 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
|
||||
if result and src.kind == tyNil:
|
||||
return dst.size <= conf.target.ptrSize
|
||||
|
||||
proc isSymChoice(n: PNode): bool {.inline.} =
|
||||
result = n.kind in nkSymChoices
|
||||
|
||||
proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
|
||||
# XXX: liftParamType started to perform addDecl
|
||||
# we could do that instead in semTypeNode by snooping for added
|
||||
@@ -308,7 +375,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool =
|
||||
let s = qualifiedLookUp(c, n, {})
|
||||
result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned"
|
||||
|
||||
proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
if n.len != 2:
|
||||
localError(c.config, n.info, "a type conversion takes exactly one argument")
|
||||
return n
|
||||
@@ -355,10 +422,10 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
if n[1].kind == nkExprEqExpr and
|
||||
targetType.skipTypes(abstractPtrs).kind == tyObject:
|
||||
localError(c.config, n.info, "object construction uses ':', not '='")
|
||||
var op = semExprWithType(c, n[1])
|
||||
if op.kind == nkClosedSymChoice and op.len > 0 and
|
||||
op[0].sym.kind == skEnumField: # resolves overloadedable enums
|
||||
op = ambiguousSymChoice(c, n, op)
|
||||
var op = semExprWithType(c, n[1], flags * {efDetermineType} + {efAllowSymChoice})
|
||||
if isSymChoice(op) and op[0].sym.kind notin routineKinds:
|
||||
# T(foo) disambiguation syntax only allowed for routines
|
||||
op = semSymChoice(c, op)
|
||||
if targetType.kind != tyGenericParam and targetType.isMetaType:
|
||||
let final = inferWithMetatype(c, targetType, op, true)
|
||||
result.add final
|
||||
@@ -370,7 +437,9 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
# here or needs to be overwritten too then.
|
||||
result.add op
|
||||
|
||||
if targetType.kind == tyGenericParam:
|
||||
if targetType.kind == tyGenericParam or
|
||||
(op.typ != nil and op.typ.kind == tyFromExpr and c.inGenericContext > 0):
|
||||
# expression is compiled early in a generic body
|
||||
result.typ = makeTypeFromExpr(c, copyTree(result))
|
||||
return result
|
||||
|
||||
@@ -384,7 +453,8 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple:
|
||||
op = fitNode(c, targetType, op, result.info)
|
||||
of convNotNeedeed:
|
||||
message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
||||
if efNoSem2Check notin flags:
|
||||
message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
||||
of convNotLegal:
|
||||
result = fitNode(c, result.typ, result[1], result.info)
|
||||
if result == nil:
|
||||
@@ -514,8 +584,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result.typ = n.typ
|
||||
|
||||
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
if n.len != 3:
|
||||
if n.len != 3 or n[2].kind == nkEmpty:
|
||||
localError(c.config, n.info, "'is' operator takes 2 arguments")
|
||||
return errorNode(c, n)
|
||||
|
||||
let boolType = getSysType(c.graph, n.info, tyBool)
|
||||
result = n
|
||||
@@ -549,15 +620,14 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||
lhsType = n[1].typ
|
||||
else:
|
||||
if lhsType.base.kind == tyNone or
|
||||
(c.inGenericContext > 0 and lhsType.base.containsGenericType):
|
||||
if c.inGenericContext > 0 and lhsType.base.containsGenericType:
|
||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||
return
|
||||
|
||||
result = isOpImpl(c, n, flags)
|
||||
|
||||
proc semOpAux(c: PContext, n: PNode) =
|
||||
const flags = {efDetermineType}
|
||||
const flags = {efDetermineType, efAllowSymChoice}
|
||||
for i in 1..<n.len:
|
||||
var a = n[i]
|
||||
if a.kind == nkExprEqExpr and a.len == 2:
|
||||
@@ -615,7 +685,7 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
|
||||
a.add m
|
||||
changeType(m, tup[i], check)
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if check and n.kind != nkUInt64Lit and not sameType(n.typ, newType):
|
||||
if check and n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType):
|
||||
let value = n.intVal
|
||||
if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
|
||||
localError(c.config, n.info, "cannot convert " & $value &
|
||||
@@ -899,9 +969,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
n.typ.flags.incl tfUnresolved
|
||||
|
||||
# optimization pass: not necessary for correctness of the semantic pass
|
||||
if callee.kind == skConst or
|
||||
if (callee.kind == skConst or
|
||||
{sfNoSideEffect, sfCompileTime} * callee.flags != {} and
|
||||
{sfForward, sfImportc} * callee.flags == {} and n.typ != nil:
|
||||
{sfForward, sfImportc} * callee.flags == {}) and n.typ != nil:
|
||||
|
||||
if callee.kind != skConst and
|
||||
sfCompileTime notin callee.flags and
|
||||
@@ -909,7 +979,8 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
|
||||
if callee.magic notin ctfeWhitelist: return
|
||||
|
||||
if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine:
|
||||
if callee.kind notin {skProc, skFunc, skConverter, skConst} or
|
||||
callee.isGenericRoutineStrict:
|
||||
return
|
||||
|
||||
if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return
|
||||
@@ -996,9 +1067,33 @@ proc bracketedMacro(n: PNode): PSym =
|
||||
if result.kind notin {skMacro, skTemplate}:
|
||||
result = nil
|
||||
|
||||
proc setGenericParams(c: PContext, n: PNode) =
|
||||
for i in 1..<n.len:
|
||||
n[i].typ = semTypeNode(c, n[i], nil)
|
||||
proc finishOperand(c: PContext, a: PNode): PNode =
|
||||
if a.typ.isNil:
|
||||
result = c.semOperand(c, a, {efDetermineType})
|
||||
else:
|
||||
result = a
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
|
||||
#and tfUnresolved in result.typ.flags:
|
||||
let owner = result.typ.owner
|
||||
let err =
|
||||
# consistent error message with evaltempl/semMacroExpr
|
||||
if owner != nil and owner.kind in {skTemplate, skMacro}:
|
||||
errMissingGenericParamsForTemplate % a.renderTree
|
||||
else:
|
||||
errProcHasNoConcreteType % a.renderTree
|
||||
localError(c.config, a.info, err)
|
||||
considerGenSyms(c, result)
|
||||
|
||||
proc semFinishOperands(c: PContext; n: PNode; isBracketExpr = false) =
|
||||
# this needs to be called to ensure that after overloading resolution every
|
||||
# argument has been sem'checked
|
||||
|
||||
# skip the first argument for operands of `[]` since it may be an unresolved
|
||||
# generic proc, which is handled in semMagic
|
||||
let start = 1 + ord(isBracketExpr)
|
||||
for i in start..<n.len:
|
||||
n[i] = finishOperand(c, n[i])
|
||||
|
||||
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
|
||||
@@ -1020,7 +1115,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
|
||||
of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
|
||||
of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
|
||||
else:
|
||||
semFinishOperands(c, result)
|
||||
semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut})
|
||||
activate(c, result)
|
||||
fixAbstractType(c, result)
|
||||
analyseIfAddressTakenInCall(c, result)
|
||||
@@ -1031,7 +1126,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
|
||||
not (result.typ.kind == tySequence and result.typ[0].kind == tyEmpty):
|
||||
liftTypeBoundOps(c, result.typ, n.info)
|
||||
#result = patchResolvedTypeBoundOp(c, result)
|
||||
if c.matchedConcept == nil:
|
||||
if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone):
|
||||
# don't fold calls in concepts and typeof
|
||||
result = evalAtCompileTime(c, result)
|
||||
|
||||
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
@@ -1051,7 +1147,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
else:
|
||||
n[0] = n0
|
||||
else:
|
||||
n[0] = semExpr(c, n[0], {efInCall})
|
||||
n[0] = semExpr(c, n[0], {efInCall, efAllowSymChoice})
|
||||
let t = n[0].typ
|
||||
if t != nil and t.kind in {tyVar, tyLent}:
|
||||
n[0] = newDeref(n[0])
|
||||
@@ -1069,7 +1165,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct})
|
||||
if t != nil and t.kind == tyTypeDesc:
|
||||
if n.len == 1: return semObjConstr(c, n, flags, expectedType)
|
||||
return semConv(c, n)
|
||||
return semConv(c, n, flags)
|
||||
|
||||
let nOrig = n.copyTree
|
||||
semOpAux(c, n)
|
||||
@@ -1106,10 +1202,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
msg.addDeclaredLocMaybe(c.config, typ)
|
||||
localError(c.config, n.info, msg)
|
||||
return errorNode(c, n)
|
||||
result = nil
|
||||
else:
|
||||
result = m.call
|
||||
instGenericConvertersSons(c, result, m)
|
||||
markConvertersUsed(c, result)
|
||||
|
||||
else:
|
||||
result = overloadedCallOpr(c, n) # this uses efNoUndeclared
|
||||
@@ -1364,6 +1460,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
|
||||
markUsed(c, n.info, s)
|
||||
onUse(n.info, s)
|
||||
if s.typ == nil:
|
||||
return localErrorNode(c, n, "symbol '$1' has no type" % [s.name.s])
|
||||
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
|
||||
return s.typ.n
|
||||
result = newSymNode(s, n.info)
|
||||
@@ -1457,7 +1555,8 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
|
||||
onUse(n[1].info, s)
|
||||
return
|
||||
|
||||
n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable})
|
||||
# extra flags since LHS may become a call operand:
|
||||
n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable, efAllowSymChoice})
|
||||
#restoreOldStyleType(n[0])
|
||||
var i = considerQuotedIdent(c, n[1], n)
|
||||
var ty = n[0].typ
|
||||
@@ -1611,7 +1710,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
return
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
# signal that generic parameters may be applied after
|
||||
n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric})
|
||||
n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric, efAllowSymChoice})
|
||||
var arr = skipTypes(n[0].typ, {tyGenericInst, tyUserTypeClassInst, tyOwned,
|
||||
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
|
||||
if arr.kind == tyStatic:
|
||||
@@ -1670,7 +1769,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result = nil
|
||||
else:
|
||||
let s = if n[0].kind == nkSym: n[0].sym
|
||||
elif n[0].kind in nkSymChoices: n[0][0].sym
|
||||
elif n[0].kind in nkSymChoices + {nkOpenSym}: n[0][0].sym
|
||||
else: nil
|
||||
if s != nil:
|
||||
case s.kind
|
||||
@@ -1710,7 +1809,7 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
||||
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
|
||||
# nodes?
|
||||
let aOrig = nOrig[0]
|
||||
result = newTreeI(nkCall, n.info, setterId, a[0], semExprWithType(c, n[1]))
|
||||
result = newTreeI(nkCall, n.info, setterId, a[0], n[1])
|
||||
result.flags.incl nfDotSetter
|
||||
let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1])
|
||||
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
|
||||
@@ -1817,6 +1916,39 @@ proc goodLineInfo(arg: PNode): TLineInfo =
|
||||
else:
|
||||
arg.info
|
||||
|
||||
proc makeTupleAssignments(c: PContext; n: PNode): PNode =
|
||||
## expand tuple unpacking assignment into series of assignments
|
||||
##
|
||||
## mirrored with semstmts.makeVarTupleSection
|
||||
let lhs = n[0]
|
||||
let value = semExprWithType(c, n[1], {efTypeAllowed})
|
||||
if value.typ.kind != tyTuple:
|
||||
localError(c.config, n[1].info, errTupleUnpackingTupleExpected %
|
||||
[typeToString(value.typ, preferDesc)])
|
||||
elif lhs.len != value.typ.len:
|
||||
localError(c.config, n.info, errTupleUnpackingDifferentLengths %
|
||||
[$lhs.len, typeToString(value.typ, preferDesc), $value.typ.len])
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
|
||||
temp.typ = value.typ
|
||||
temp.flags.incl(sfGenSym)
|
||||
var v = newNodeI(nkLetSection, value.info)
|
||||
let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
||||
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
||||
vpart[0] = tempNode
|
||||
vpart[1] = c.graph.emptyNode
|
||||
vpart[2] = value
|
||||
v.add vpart
|
||||
result.add(v)
|
||||
|
||||
for i in 0..<lhs.len:
|
||||
if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
|
||||
# skip _ assignments if we are using a temp as they are already evaluated
|
||||
discard
|
||||
else:
|
||||
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
|
||||
|
||||
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
checkSonsLen(n, 2, c.config)
|
||||
var a = n[0]
|
||||
@@ -1845,7 +1977,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
result.add(n[1])
|
||||
if mode == noOverloadedSubscript:
|
||||
bracketNotFoundError(c, result)
|
||||
return n
|
||||
return errorNode(c, n)
|
||||
else:
|
||||
result = semExprNoType(c, result)
|
||||
return result
|
||||
@@ -1859,7 +1991,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
# unfortunately we need to rewrite ``(x, y) = foo()`` already here so
|
||||
# that overloading of the assignment operator still works. Usually we
|
||||
# prefer to do these rewritings in transf.nim:
|
||||
return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.idgen, c.p.owner), {})
|
||||
return semStmt(c, makeTupleAssignments(c, n), {})
|
||||
else:
|
||||
a = semExprWithType(c, a, {efLValue})
|
||||
else:
|
||||
@@ -2009,12 +2141,10 @@ proc semYield(c: PContext, n: PNode): PNode =
|
||||
if c.p.owner == nil or c.p.owner.kind != skIterator:
|
||||
localError(c.config, n.info, errYieldNotAllowedHere)
|
||||
elif n[0].kind != nkEmpty:
|
||||
n[0] = semExprWithType(c, n[0]) # check for type compatibility:
|
||||
var iterType = c.p.owner.typ
|
||||
let restype = iterType[0]
|
||||
n[0] = semExprWithType(c, n[0], {}, restype) # check for type compatibility:
|
||||
if restype != nil:
|
||||
if restype.kind != tyUntyped:
|
||||
n[0] = fitNode(c, restype, n[0], n.info)
|
||||
if n[0].typ == nil: internalError(c.config, n.info, "semYield")
|
||||
|
||||
if resultTypeIsInferrable(restype):
|
||||
@@ -2022,6 +2152,8 @@ proc semYield(c: PContext, n: PNode): PNode =
|
||||
iterType[0] = inferred
|
||||
if c.p.resultSym != nil:
|
||||
c.p.resultSym.typ = inferred
|
||||
else:
|
||||
n[0] = fitNode(c, restype, n[0], n.info)
|
||||
|
||||
semYieldVarResult(c, n, restype)
|
||||
else:
|
||||
@@ -2073,6 +2205,8 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
|
||||
result = n.sym
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
result = n[0].sym
|
||||
of nkOpenSym:
|
||||
result = lookUpForDeclared(c, n[0], onlyCurrentScope)
|
||||
else:
|
||||
localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
|
||||
result = nil
|
||||
@@ -2108,6 +2242,7 @@ proc expectString(c: PContext, n: PNode): string =
|
||||
if n.kind in nkStrKinds:
|
||||
return n.strVal
|
||||
else:
|
||||
result = ""
|
||||
localError(c.config, n.info, errStringLiteralExpected)
|
||||
|
||||
proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
|
||||
@@ -2132,7 +2267,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
|
||||
let headSymbol = macroCall[0]
|
||||
var cands = 0
|
||||
var cand: PSym = nil
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var symx = initOverloadIter(o, c, headSymbol)
|
||||
while symx != nil:
|
||||
if symx.kind in {skTemplate, skMacro} and symx.typ.len == macroCall.len:
|
||||
@@ -2244,7 +2379,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
|
||||
for i in 1..<ids.len:
|
||||
let exp = semExprWithType(c, quotes[i+1], {})
|
||||
let typ = exp.typ
|
||||
if tfTriggersCompileTime notin typ.flags and exp.kind == nkSym and exp.sym.kind notin routineKinds + {skType}:
|
||||
if tfTriggersCompileTime notin typ.flags and typ.kind != tyStatic and exp.kind == nkSym and exp.sym.kind notin routineKinds + {skType}:
|
||||
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], newNodeIT(nkType, n.info, typ), c.graph.emptyNode)
|
||||
else:
|
||||
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
|
||||
@@ -2383,9 +2518,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P
|
||||
of mAddr:
|
||||
markUsed(c, n.info, s)
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result[0] = newSymNode(s, n[0].info)
|
||||
result[1] = semAddrArg(c, n[1])
|
||||
result.typ = makePtrType(c, result[1].typ)
|
||||
result = semAddr(c, n[1])
|
||||
of mTypeOf:
|
||||
markUsed(c, n.info, s)
|
||||
result = semTypeOf(c, n)
|
||||
@@ -2512,9 +2645,10 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
# If semCheck is set to false, ``when`` will return the verbatim AST of
|
||||
# the correct branch. Otherwise the AST will be passed through semStmt.
|
||||
result = nil
|
||||
let flags = if semCheck: {efWantStmt} else: {}
|
||||
|
||||
template setResult(e: untyped) =
|
||||
if semCheck: result = semExpr(c, e) # do not open a new scope!
|
||||
if semCheck: result = semExpr(c, e, flags) # do not open a new scope!
|
||||
else: result = e
|
||||
|
||||
# Check if the node is "when nimvm"
|
||||
@@ -2526,7 +2660,9 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
var typ = commonTypeBegin
|
||||
if n.len in 1..2 and n[0].kind == nkElifBranch and (
|
||||
n.len == 1 or n[1].kind == nkElse):
|
||||
let exprNode = n[0][0]
|
||||
var exprNode = n[0][0]
|
||||
if exprNode.kind == nkOpenSym:
|
||||
exprNode = exprNode[0]
|
||||
if exprNode.kind == nkIdent:
|
||||
whenNimvm = lookUp(c, exprNode).magic == mNimvm
|
||||
elif exprNode.kind == nkSym:
|
||||
@@ -2540,7 +2676,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
checkSonsLen(it, 2, c.config)
|
||||
if whenNimvm:
|
||||
if semCheck:
|
||||
it[1] = semExpr(c, it[1])
|
||||
it[1] = semExpr(c, it[1], flags)
|
||||
typ = commonType(c, typ, it[1].typ)
|
||||
result = n # when nimvm is not elimited until codegen
|
||||
else:
|
||||
@@ -2556,8 +2692,10 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
checkSonsLen(it, 1, c.config)
|
||||
if result == nil or whenNimvm:
|
||||
if semCheck:
|
||||
it[0] = semExpr(c, it[0])
|
||||
it[0] = semExpr(c, it[0], flags)
|
||||
typ = commonType(c, typ, it[0].typ)
|
||||
if typ != nil and typ.kind != tyUntyped:
|
||||
it[0] = fitNode(c, typ, it[0], it[0].info)
|
||||
if result == nil:
|
||||
result = it[0]
|
||||
else: illFormedAst(n, c.config)
|
||||
@@ -2702,6 +2840,12 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
|
||||
# can check if field name matches expected type here
|
||||
let expectedElemType = if expected != nil: expected[i] else: nil
|
||||
n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType)
|
||||
if expectedElemType != nil and
|
||||
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
|
||||
# hasEmpty/nil check is to not break existing code like
|
||||
# `const foo = [(1, {}), (2, {false})]`,
|
||||
# `const foo = if true: (0, nil) else: (1, new(int))`
|
||||
n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info)
|
||||
|
||||
if n[i][1].typ.kind == tyTypeDesc:
|
||||
localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.")
|
||||
@@ -2728,6 +2872,12 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT
|
||||
for i in 0..<n.len:
|
||||
let expectedElemType = if expected != nil: expected[i] else: nil
|
||||
n[i] = semExprWithType(c, n[i], {}, expectedElemType)
|
||||
if expectedElemType != nil and
|
||||
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
|
||||
# hasEmpty/nil check is to not break existing code like
|
||||
# `const foo = [(1, {}), (2, {false})]`,
|
||||
# `const foo = if true: (0, nil) else: (1, new(int))`
|
||||
n[i] = fitNode(c, expectedElemType, n[i], n[i].info)
|
||||
addSonSkipIntLit(typ, n[i].typ, c.idgen)
|
||||
result.typ = typ
|
||||
|
||||
@@ -2783,7 +2933,7 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkExportStmt, n.info)
|
||||
for i in 0..<n.len:
|
||||
let a = n[i]
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var s = initOverloadIter(o, c, a)
|
||||
if s == nil:
|
||||
localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a))
|
||||
@@ -2863,6 +3013,18 @@ proc asBracketExpr(c: PContext; n: PNode): PNode =
|
||||
return result
|
||||
return nil
|
||||
|
||||
proc isOpenArraySym(x: PNode): bool =
|
||||
var x = x
|
||||
while true:
|
||||
case x.kind
|
||||
of {nkAddr, nkHiddenAddr}:
|
||||
x = x[0]
|
||||
of {nkHiddenStdConv, nkHiddenDeref}:
|
||||
x = x[1]
|
||||
else:
|
||||
break
|
||||
result = x.kind == nkSym
|
||||
|
||||
proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) =
|
||||
# This takes care of complicated signatures such as:
|
||||
# proc foo(a: int, b = a)
|
||||
@@ -2883,7 +3045,10 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode)
|
||||
if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
|
||||
let paramPos = defExpr.sym.position + 1
|
||||
|
||||
if call[paramPos].kind != nkSym:
|
||||
if call[paramPos].skipAddr.kind != nkSym and not (
|
||||
skipTypes(call[paramPos].typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
|
||||
isOpenArraySym(call[paramPos])
|
||||
):
|
||||
let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen,
|
||||
c.p.owner, letSection.info, c.p.owner.options)
|
||||
hoistedVarSym.typ = call[paramPos].typ
|
||||
@@ -2909,8 +3074,8 @@ proc getNilType(c: PContext): PType =
|
||||
result.align = c.config.target.ptrSize.int16
|
||||
c.nilTypeCache = result
|
||||
|
||||
proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
|
||||
var o: TOverloadIter
|
||||
proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode =
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var i = 0
|
||||
var a = initOverloadIter(o, c, n)
|
||||
while a != nil:
|
||||
@@ -2922,7 +3087,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
|
||||
if i <= 1:
|
||||
if sfGenSym notin s.flags:
|
||||
result = newSymNode(s, info)
|
||||
markUsed(c, info, s)
|
||||
markUsed(c, info, s, efInCall notin flags)
|
||||
onUse(info, s)
|
||||
else:
|
||||
result = n
|
||||
@@ -2943,6 +3108,48 @@ proc semPragmaStmt(c: PContext; n: PNode) =
|
||||
else:
|
||||
pragma(c, c.p.owner, n, stmtPragmas, true)
|
||||
|
||||
proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode,
|
||||
flags: TExprFlags, expectedType: PType): PSym =
|
||||
# result is nil on error or if a node that can't produce a sym is resolved
|
||||
let ident = considerQuotedIdent(c, n)
|
||||
var filter = {low(TSymKind)..high(TSymKind)}
|
||||
if efNoEvaluateGeneric in flags:
|
||||
# `a[...]` where `a` is a module or package is not possible
|
||||
filter.excl {skModule, skPackage}
|
||||
let candidates = lookUpCandidates(c, ident, filter)
|
||||
if candidates.len == 0:
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n.info)
|
||||
elif candidates.len == 1 or {efNoEvaluateGeneric, efInCall} * flags != {}:
|
||||
# unambiguous, or we don't care about ambiguity
|
||||
result = candidates[0]
|
||||
else:
|
||||
# ambiguous symbols have 1 last chance as a symchoice,
|
||||
# but type symbols cannot participate in symchoices
|
||||
var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c))
|
||||
for c in candidates:
|
||||
if c.kind notin {skType, skModule, skPackage}:
|
||||
choice.add newSymNode(c, n.info)
|
||||
if choice.len == 0:
|
||||
# we know candidates.len > 1, we just couldn't put any in a symchoice
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
return nil
|
||||
resolveSymChoice(c, choice, flags, expectedType)
|
||||
# choice.len == 1 can be true here but as long as it's a symchoice
|
||||
# it's still not resolved
|
||||
if isSymChoice(choice):
|
||||
result = nil
|
||||
if efAllowSymChoice in flags:
|
||||
resultNode = choice
|
||||
else:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
else:
|
||||
if choice.kind == nkSym:
|
||||
result = choice.sym
|
||||
else:
|
||||
# resolution could have generated nkHiddenStdConv etc
|
||||
resultNode = semExpr(c, choice, flags, expectedType)
|
||||
result = nil
|
||||
|
||||
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
when defined(nimCompilerStacktraceHints):
|
||||
setFrameMsg c.config$n.info & " " & $n.kind
|
||||
@@ -2980,25 +3187,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
if nfSem in n.flags: return
|
||||
case n.kind
|
||||
of nkIdent, nkAccQuoted:
|
||||
var s: PSym
|
||||
if expectedType != nil and (
|
||||
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
|
||||
expected.kind == tyEnum):
|
||||
let nameId = considerQuotedIdent(c, n).id
|
||||
for f in expected.n:
|
||||
if f.kind == nkSym and f.sym.name.id == nameId:
|
||||
s = f.sym
|
||||
break
|
||||
let s = resolveIdentToSym(c, n, result, flags, expectedType)
|
||||
if s == nil:
|
||||
let checks = if efNoEvaluateGeneric in flags:
|
||||
{checkUndeclared, checkPureEnumFields}
|
||||
elif efInCall in flags:
|
||||
{checkUndeclared, checkModule, checkPureEnumFields}
|
||||
else:
|
||||
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
|
||||
s = qualifiedLookUp(c, n, checks)
|
||||
if s == nil:
|
||||
return
|
||||
# resolveIdentToSym either errored or gave a result node
|
||||
return
|
||||
if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
|
||||
case s.kind
|
||||
of skProc, skFunc, skMethod, skConverter, skIterator:
|
||||
@@ -3012,23 +3204,35 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
of skEnumField:
|
||||
result = enumFieldSymChoice(c, n, s)
|
||||
result = enumFieldSymChoice(c, n, s, flags)
|
||||
else:
|
||||
result = semSym(c, n, s, flags)
|
||||
if expectedType != nil and isSymChoice(result):
|
||||
result = fitNode(c, expectedType, result, n.info)
|
||||
if result.kind == nkSym:
|
||||
result = semSym(c, result, result.sym, flags)
|
||||
if isSymChoice(result):
|
||||
result = semSymChoice(c, result, flags, expectedType)
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
result = semSymChoice(c, n, flags, expectedType)
|
||||
of nkSym:
|
||||
let s = n.sym
|
||||
if nfDisabledOpenSym in n.flags:
|
||||
let override = genericsOpenSym in c.features
|
||||
let res = semOpenSym(c, n, flags, expectedType,
|
||||
warnDisabled = not override)
|
||||
if res != nil:
|
||||
assert override
|
||||
return res
|
||||
# because of the changed symbol binding, this does not mean that we
|
||||
# don't have to check the symbol for semantics here again!
|
||||
result = semSym(c, n, n.sym, flags)
|
||||
result = semSym(c, n, s, flags)
|
||||
of nkOpenSym:
|
||||
assert n.len == 1
|
||||
let inner = n[0]
|
||||
result = semOpenSym(c, inner, flags, expectedType)
|
||||
of nkEmpty, nkNone, nkCommentStmt, nkType:
|
||||
discard
|
||||
of nkNilLit:
|
||||
if result.typ == nil:
|
||||
result.typ = getNilType(c)
|
||||
if expectedType != nil:
|
||||
if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}:
|
||||
var m = newCandidate(c, result.typ)
|
||||
if typeRel(m, expectedType, result.typ) >= isSubtype:
|
||||
result.typ = expectedType
|
||||
@@ -3040,7 +3244,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
expected.kind in {tyInt..tyInt64,
|
||||
tyUInt..tyUInt64,
|
||||
tyFloat..tyFloat128}):
|
||||
result.typ = expected
|
||||
if expected.kind in {tyFloat..tyFloat128}:
|
||||
n.transitionIntToFloatKind(nkFloatLit)
|
||||
changeType(c, result, expectedType, check=true)
|
||||
@@ -3112,7 +3315,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
# XXX think about this more (``set`` procs)
|
||||
let ambig = c.isAmbiguous
|
||||
if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2:
|
||||
result = semConv(c, n, expectedType)
|
||||
result = semConv(c, n, flags, expectedType)
|
||||
elif ambig and n.len == 1:
|
||||
errorUseQualifier(c, n.info, s)
|
||||
elif n.len == 1:
|
||||
@@ -3198,8 +3401,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
of nkAddr:
|
||||
result = n
|
||||
checkSonsLen(n, 1, c.config)
|
||||
result[0] = semAddrArg(c, n[0])
|
||||
result.typ = makePtrType(c, result[0].typ)
|
||||
result = semAddr(c, n[0])
|
||||
of nkHiddenAddr, nkHiddenDeref:
|
||||
checkSonsLen(n, 1, c.config)
|
||||
n[0] = semExpr(c, n[0], flags, expectedType)
|
||||
@@ -3219,10 +3421,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
considerGenSyms(c, n)
|
||||
of nkTableConstr:
|
||||
result = semTableConstr(c, n, expectedType)
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
# handling of sym choices is context dependent
|
||||
# the node is left intact for now
|
||||
discard
|
||||
of nkStaticExpr: result = semStaticExpr(c, n[0], expectedType)
|
||||
of nkAsgn, nkFastAsgn: result = semAsgn(c, n)
|
||||
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags, expectedType)
|
||||
|
||||
@@ -18,6 +18,15 @@ type
|
||||
replaceByFieldName: bool
|
||||
c: PContext
|
||||
|
||||
proc wrapNewScope(c: PContext, n: PNode): PNode {.inline.} =
|
||||
# use `if true` to not interfere with `break`
|
||||
# just opening scope via `openScope(c)` isn't enough,
|
||||
# a scope has to be opened in the codegen as well for reused
|
||||
# template instantiations
|
||||
let trueLit = newIntLit(c.graph, n.info, 1)
|
||||
trueLit.typ = getSysType(c.graph, n.info, tyBool)
|
||||
result = newTreeI(nkIfStmt, n.info, newTreeI(nkElifBranch, n.info, trueLit, n))
|
||||
|
||||
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
if c.field != nil and isEmptyType(c.field.typ):
|
||||
result = newNode(nkEmpty)
|
||||
@@ -70,7 +79,9 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
|
||||
fc.replaceByFieldName = c.m == mFieldPairs
|
||||
openScope(c.c)
|
||||
inc c.c.inUnrolledContext
|
||||
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
var body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
# new scope for each field that codegen should know about:
|
||||
body = wrapNewScope(c.c, body)
|
||||
father.add(semStmt(c.c, body, {}))
|
||||
dec c.c.inUnrolledContext
|
||||
closeScope(c.c)
|
||||
@@ -145,6 +156,8 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
fc.c = c
|
||||
fc.replaceByFieldName = m == mFieldPairs
|
||||
var body = instFieldLoopBody(fc, loopBody, n)
|
||||
# new scope for each field that codegen should know about:
|
||||
body = wrapNewScope(c, body)
|
||||
inc c.inUnrolledContext
|
||||
stmts.add(semStmt(c, body, {}))
|
||||
dec c.inUnrolledContext
|
||||
|
||||
@@ -230,7 +230,13 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g)
|
||||
of mDivF64:
|
||||
result = newFloatNodeT(getFloat(a) / getFloat(b), n, g)
|
||||
of mIsNil: result = newIntNodeT(toInt128(ord(a.kind == nkNilLit)), n, idgen, g)
|
||||
of mIsNil:
|
||||
let val = a.kind == nkNilLit or
|
||||
# nil closures have the value (nil, nil)
|
||||
(a.typ != nil and skipTypes(a.typ, abstractRange).kind == tyProc and
|
||||
a.kind == nkTupleConstr and a.len == 2 and
|
||||
a[0].kind == nkNilLit and a[1].kind == nkNilLit)
|
||||
result = newIntNodeT(toInt128(ord(val)), n, idgen, g)
|
||||
of mLtI, mLtB, mLtEnum, mLtCh:
|
||||
result = newIntNodeT(toInt128(ord(getOrdValue(a) < getOrdValue(b))), n, idgen, g)
|
||||
of mLeI, mLeB, mLeEnum, mLeCh:
|
||||
@@ -403,14 +409,17 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
|
||||
result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g)
|
||||
of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
|
||||
var val = a.getOrdValue
|
||||
if check: rangeCheck(n, val, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
if dstTyp.kind in {tyUInt..tyUInt64}:
|
||||
result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g)
|
||||
result.transitionIntKind(nkUIntLit)
|
||||
else:
|
||||
if check: rangeCheck(n, val, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
else:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
if check and result.kind in {nkCharLit..nkUInt64Lit}:
|
||||
if check and result.kind in {nkCharLit..nkUInt64Lit} and
|
||||
dstTyp.kind notin {tyUInt..tyUInt64}:
|
||||
rangeCheck(n, getInt(result), g)
|
||||
of tyFloat..tyFloat64:
|
||||
case srcTyp.kind
|
||||
@@ -679,10 +688,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
except DivByZeroDefect:
|
||||
localError(g.config, n.info, "division by zero")
|
||||
of nkAddr:
|
||||
var a = getConstExpr(m, n[0], idgen, g)
|
||||
if a != nil:
|
||||
result = n
|
||||
n[0] = a
|
||||
result = nil # don't fold paths containing nkAddr
|
||||
of nkBracket, nkCurly:
|
||||
result = copyNode(n)
|
||||
for son in n.items:
|
||||
@@ -728,6 +734,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
if leValueConv(n[1], a) and leValueConv(a, n[2]):
|
||||
result = a # a <= x and x <= b
|
||||
result.typ = n.typ
|
||||
elif n.typ.kind in {tyUInt..tyUInt64}:
|
||||
discard "don't check uints"
|
||||
else:
|
||||
localError(g.config, n.info,
|
||||
"conversion from $1 to $2 is invalid" %
|
||||
|
||||
@@ -56,6 +56,9 @@ template isMixedIn(sym): bool =
|
||||
s.magic == mNone and
|
||||
s.kind in OverloadableSyms)
|
||||
|
||||
template canOpenSym(s): bool =
|
||||
{withinMixin, withinConcept} * flags == {withinMixin} and s.id notin ctx.toBind
|
||||
|
||||
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
ctx: var GenericCtx; flags: TSemGenericFlags,
|
||||
fromDotExpr=false): PNode =
|
||||
@@ -68,6 +71,15 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result.transitionSonsKind(nkClosedSymChoice)
|
||||
else:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
if canOpenSym(s):
|
||||
if openSym in c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
@@ -95,6 +107,12 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result = n
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
of skParam:
|
||||
result = n
|
||||
@@ -103,11 +121,23 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
if (s.typ != nil) and
|
||||
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
else:
|
||||
result = n
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
|
||||
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
@@ -147,6 +177,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
|
||||
var s = qualifiedLookUp(c, n, luf)
|
||||
if s != nil:
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
else:
|
||||
n[0] = semGenericStmt(c, n[0], flags, ctx)
|
||||
@@ -167,6 +198,17 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
elif s.isMixedIn:
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
else:
|
||||
if s.kind == skType and candidates.len > 1:
|
||||
var ambig = false
|
||||
let s2 = searchInScopes(c, ident, ambig)
|
||||
if ambig:
|
||||
# this is a type conversion like a.T where T is ambiguous with
|
||||
# other types or routines
|
||||
# in regular code, this never considers a type conversion and
|
||||
# skips to routine overloading
|
||||
# so symchoices are used which behave similarly with type symbols
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
return
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
|
||||
result = newDot(result, syms)
|
||||
|
||||
@@ -176,6 +218,18 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
|
||||
styleCheckDef(c, n.info, s, kind)
|
||||
onDef(n.info, s)
|
||||
|
||||
proc addTempDeclToIdents(c: PContext; n: PNode; kind: TSymKind; inCall: bool) =
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
if inCall:
|
||||
addTempDecl(c, n, kind)
|
||||
of nkCallKinds:
|
||||
for s in n:
|
||||
addTempDeclToIdents(c, s, kind, true)
|
||||
else:
|
||||
for s in n:
|
||||
addTempDeclToIdents(c, s, kind, inCall)
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode,
|
||||
flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
|
||||
result = n
|
||||
@@ -197,7 +251,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
#var s = qualifiedLookUp(c, n, luf)
|
||||
#if s != nil: result = semGenericStmtSymbol(c, n, s)
|
||||
# XXX for example: ``result.add`` -- ``add`` needs to be looked up here...
|
||||
var dummy: bool
|
||||
var dummy: bool = false
|
||||
result = fuzzyLookup(c, n, flags, ctx, dummy)
|
||||
of nkSym:
|
||||
let a = n.sym
|
||||
@@ -348,7 +402,9 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
var a = n[i]
|
||||
checkMinSonsLen(a, 1, c.config)
|
||||
for j in 0..<a.len-1:
|
||||
a[j] = semGenericStmt(c, a[j], flags, ctx)
|
||||
a[j] = semGenericStmt(c, a[j], flags+{withinMixin}, ctx)
|
||||
addTempDeclToIdents(c, a[j], skVar, false)
|
||||
|
||||
a[^1] = semGenericStmtScope(c, a[^1], flags, ctx)
|
||||
closeScope(c)
|
||||
of nkForStmt, nkParForStmt:
|
||||
@@ -445,8 +501,47 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
of nkIdent: a = n[i]
|
||||
else: illFormedAst(n, c.config)
|
||||
addDecl(c, newSymS(skUnknown, getIdentNode(c, a), c))
|
||||
of nkObjectTy, nkTupleTy, nkTupleClassTy:
|
||||
discard
|
||||
of nkTupleTy:
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
case a.kind:
|
||||
of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
|
||||
of nkIdentDefs:
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
|
||||
a[^1] = semGenericStmt(c, a[^1], flags, ctx)
|
||||
for j in 0..<a.len-2:
|
||||
addTempDecl(c, getIdentNode(c, a[j]), skField)
|
||||
else:
|
||||
illFormedAst(a, c.config)
|
||||
of nkObjectTy:
|
||||
if n.len > 0:
|
||||
openScope(c)
|
||||
for i in 0..<n.len:
|
||||
result[i] = semGenericStmt(c, n[i], flags, ctx)
|
||||
closeScope(c)
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
case a.kind:
|
||||
of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
|
||||
of nkIdentDefs:
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
|
||||
a[^1] = semGenericStmt(c, a[^1], flags, ctx)
|
||||
for j in 0..<a.len-2:
|
||||
addTempDecl(c, getIdentNode(c, a[j]), skField)
|
||||
of nkRecCase, nkRecWhen:
|
||||
n[i] = semGenericStmt(c, a, flags, ctx)
|
||||
else:
|
||||
illFormedAst(a, c.config)
|
||||
of nkRecCase:
|
||||
checkSonsLen(n[0], 3, c.config)
|
||||
n[0][^2] = semGenericStmt(c, n[0][^2], flags+{withinTypeDesc}, ctx)
|
||||
n[0][^1] = semGenericStmt(c, n[0][^1], flags, ctx)
|
||||
addTempDecl(c, getIdentNode(c, n[0][0]), skField)
|
||||
for i in 1..<n.len:
|
||||
n[i] = semGenericStmt(c, n[i], flags, ctx)
|
||||
of nkFormalParams:
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
for i in 1..<n.len:
|
||||
|
||||
@@ -60,6 +60,15 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
|
||||
elif t.kind in {tyGenericParam, tyConcept}:
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or
|
||||
(q.typ.kind == tyGenericParam and
|
||||
q.typ.sons.len > 0 and
|
||||
q.typ.sons[0].kind == tyStatic)) and
|
||||
c.inGenericContext == 0 and c.matchedConcept == nil:
|
||||
# generic/concept type bodies will try to instantiate static values but
|
||||
# won't actually use them
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind == tyGenericInvocation:
|
||||
#t = instGenericContainer(c, a, t)
|
||||
t = generateTypeInstance(c, pt, a, t)
|
||||
@@ -272,11 +281,13 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
# call head symbol, because this leads to infinite recursion.
|
||||
if oldParam.ast != nil:
|
||||
var def = oldParam.ast.copyTree
|
||||
if def.kind == nkCall:
|
||||
if def.kind in nkCallKinds:
|
||||
for i in 1..<def.len:
|
||||
def[i] = replaceTypeVarsN(cl, def[i])
|
||||
def[i] = replaceTypeVarsN(cl, def[i], 1)
|
||||
|
||||
def = semExprWithType(c, def)
|
||||
# allow symchoice since node will be fit later
|
||||
# although expectedType should cover it
|
||||
def = semExprWithType(c, def, {efAllowSymChoice}, typeToFit)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
|
||||
@@ -330,7 +341,12 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
# generates an instantiated proc
|
||||
if c.instCounter > 50:
|
||||
globalError(c.config, info, "generic instantiation too nested")
|
||||
inc(c.instCounter)
|
||||
inc c.instCounter
|
||||
let currentTypeofContext = c.inTypeofContext
|
||||
c.inTypeofContext = 0
|
||||
defer:
|
||||
dec c.instCounter
|
||||
c.inTypeofContext = currentTypeofContext
|
||||
# careful! we copy the whole AST including the possibly nil body!
|
||||
var n = copyTree(fn.ast)
|
||||
# NOTE: for access of private fields within generics from a different module
|
||||
@@ -364,15 +380,19 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
# see ttypeor.nim test.
|
||||
var i = 0
|
||||
newSeq(entry.concreteTypes, fn.typ.len+gp.len-1)
|
||||
# let param instantiation know we are in a concept for unresolved statics:
|
||||
c.matchedConcept = oldMatchedConcept
|
||||
for s in instantiateGenericParamList(c, gp, pt):
|
||||
addDecl(c, s)
|
||||
entry.concreteTypes[i] = s.typ
|
||||
inc i
|
||||
c.matchedConcept = nil
|
||||
pushProcCon(c, result)
|
||||
instantiateProcType(c, pt, result, info)
|
||||
for j in 1..<result.typ.len:
|
||||
entry.concreteTypes[i] = result.typ[j]
|
||||
inc i
|
||||
#echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len
|
||||
if tfTriggersCompileTime in result.typ.flags:
|
||||
incl(result.flags, sfCompileTime)
|
||||
n[genericParamsPos] = c.graph.emptyNode
|
||||
@@ -395,7 +415,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
if result.magic notin {mSlice, mTypeOf}:
|
||||
# 'toOpenArray' is special and it is allowed to return 'openArray':
|
||||
paramsTypeCheck(c, result.typ)
|
||||
#echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len
|
||||
else:
|
||||
#echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len
|
||||
result = oldPrc
|
||||
popProcCon(c)
|
||||
popInfoContext(c.config)
|
||||
@@ -404,7 +426,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
popOwner(c)
|
||||
c.currentScope = oldScope
|
||||
discard c.friendModules.pop()
|
||||
dec(c.instCounter)
|
||||
c.matchedConcept = oldMatchedConcept
|
||||
if result.kind == skMethod: finishMethod(c, result)
|
||||
|
||||
|
||||
@@ -30,13 +30,15 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
|
||||
if asgnExpr.sons.len > 1:
|
||||
result = newTree(nkAsgn, result[1], asgnExpr)
|
||||
|
||||
proc semAddrArg(c: PContext; n: PNode): PNode =
|
||||
proc semAddr(c: PContext; n: PNode): PNode =
|
||||
result = newNodeI(nkAddr, n.info)
|
||||
let x = semExprWithType(c, n)
|
||||
if x.kind == nkSym:
|
||||
x.sym.flags.incl(sfAddrTaken)
|
||||
if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}:
|
||||
localError(c.config, n.info, errExprHasNoAddress)
|
||||
result = x
|
||||
result.add x
|
||||
result.typ = makePtrType(c, x.typ)
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
var m = BiggestInt 1 # typeOfIter
|
||||
@@ -47,6 +49,8 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
else:
|
||||
m = mode.intVal
|
||||
result = newNodeI(nkTypeOfExpr, n.info)
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
result.add typExpr
|
||||
result.typ = makeTypeDesc(c, typExpr.typ)
|
||||
@@ -66,7 +70,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
|
||||
bracketNotFoundError(c, x)
|
||||
#localError(c.config, n.info, "could not resolve: " & $n)
|
||||
result = n
|
||||
result = errorNode(c, n)
|
||||
|
||||
proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
# rewrite `[]=`(a, i, x) back to ``a[i] = x``.
|
||||
@@ -519,7 +523,9 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
|
||||
selfPtr.add transFormedSym.ast[bodyPos][1]
|
||||
selfPtr.typ = selfSymbolType
|
||||
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor)
|
||||
# TODO: suppress var destructor warnings; if newFinalizer is not
|
||||
# TODO: deprecated, try to implement plain T destructor
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor, suppressVarDestructorWarning = true)
|
||||
result = addDefaultFieldForNew(c, n)
|
||||
|
||||
proc semPrivateAccess(c: PContext, n: PNode): PNode =
|
||||
@@ -547,9 +553,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
case n[0].sym.magic
|
||||
of mAddr:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = n
|
||||
result[1] = semAddrArg(c, n[1])
|
||||
result.typ = makePtrType(c, result[1].typ)
|
||||
result = semAddr(c, n[1])
|
||||
of mTypeOf:
|
||||
result = semTypeOf(c, n)
|
||||
of mSizeOf:
|
||||
@@ -668,7 +672,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
result.typ = expectedType # type inference for empty sequence # bug #21377
|
||||
of mEnsureMove:
|
||||
result = n
|
||||
if isAssignable(c, n[1]) notin {arLValue, arLocalLValue}:
|
||||
localError(c.config, n.info, "'" & $n[1] & "'" & " is not a mutable location; it cannot be moved")
|
||||
if n[1].kind in {nkStmtListExpr, nkBlockExpr,
|
||||
nkIfExpr, nkCaseStmt, nkTryStmt}:
|
||||
localError(c.config, n.info, "Nested expressions cannot be moved: '" & $n[1] & "'")
|
||||
else:
|
||||
result = n
|
||||
|
||||
@@ -91,7 +91,9 @@ proc semConstrField(c: PContext, flags: TExprFlags,
|
||||
assignment[0] = newSymNode(field)
|
||||
assignment[1] = initValue
|
||||
assignment.flags.incl nfSem
|
||||
return initValue
|
||||
result = initValue
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc branchVals(c: PContext, caseNode: PNode, caseIdx: int,
|
||||
isStmtBranch: bool): IntSet =
|
||||
@@ -184,6 +186,23 @@ proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode,
|
||||
asgnExpr.typ = recTyp
|
||||
defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr)
|
||||
|
||||
proc collectBranchFields(c: PContext, n: PNode, discriminatorVal: PNode,
|
||||
constrCtx: var ObjConstrContext, flags: TExprFlags) =
|
||||
# All bets are off. If any of the branches has a mandatory
|
||||
# fields we must produce an error:
|
||||
for i in 1..<n.len:
|
||||
let branchNode = n[i]
|
||||
if branchNode != nil:
|
||||
let oldCheckDefault = constrCtx.checkDefault
|
||||
constrCtx.checkDefault = true
|
||||
let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
|
||||
constrCtx.checkDefault = oldCheckDefault
|
||||
if len(defaults) > 0:
|
||||
localError(c.config, discriminatorVal.info, "branch initialization " &
|
||||
"with a runtime discriminator is not supported " &
|
||||
"for a branch whose fields have default values.")
|
||||
discard collectMissingCaseFields(c, n[i], constrCtx, @[])
|
||||
|
||||
proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
|
||||
case n.kind
|
||||
@@ -322,13 +341,27 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
discriminator.sym,
|
||||
constrCtx.initExpr)
|
||||
if discriminatorVal == nil:
|
||||
# None of the branches were explicitly selected by the user and no
|
||||
# value was given to the discrimator. We can assume that it will be
|
||||
# initialized to zero and this will select a particular branch as
|
||||
# a result:
|
||||
let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0)
|
||||
let matchedBranch = n.pickCaseBranch defaultValue
|
||||
discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[])
|
||||
if discriminator.sym.ast != nil:
|
||||
# branch is selected by the default field value of discriminator
|
||||
let discriminatorDefaultVal = discriminator.sym.ast
|
||||
result.status = initUnknown
|
||||
result.defaults.add newTree(nkExprColonExpr, n[0], discriminatorDefaultVal)
|
||||
if discriminatorDefaultVal.kind == nkIntLit:
|
||||
let matchedBranch = n.pickCaseBranch discriminatorDefaultVal
|
||||
if matchedBranch != nil:
|
||||
let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
|
||||
result.defaults.add defaults
|
||||
collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults)
|
||||
else:
|
||||
collectBranchFields(c, n, discriminatorDefaultVal, constrCtx, flags)
|
||||
else:
|
||||
# None of the branches were explicitly selected by the user and no
|
||||
# value was given to the discrimator. We can assume that it will be
|
||||
# initialized to zero and this will select a particular branch as
|
||||
# a result:
|
||||
let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0)
|
||||
let matchedBranch = n.pickCaseBranch defaultValue
|
||||
discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[])
|
||||
else:
|
||||
result.status = initPartial
|
||||
if discriminatorVal.kind == nkIntLit:
|
||||
@@ -340,20 +373,8 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
result.defaults.add defaults
|
||||
collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults)
|
||||
else:
|
||||
# All bets are off. If any of the branches has a mandatory
|
||||
# fields we must produce an error:
|
||||
for i in 1..<n.len:
|
||||
let branchNode = n[i]
|
||||
if branchNode != nil:
|
||||
let oldCheckDefault = constrCtx.checkDefault
|
||||
constrCtx.checkDefault = true
|
||||
let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
|
||||
constrCtx.checkDefault = oldCheckDefault
|
||||
if len(defaults) > 0:
|
||||
localError(c.config, discriminatorVal.info, "branch initialization " &
|
||||
"with a runtime discriminator is not supported " &
|
||||
"for a branch whose fields have default values.")
|
||||
discard collectMissingCaseFields(c, n[i], constrCtx, @[])
|
||||
collectBranchFields(c, n, discriminatorVal, constrCtx, flags)
|
||||
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
let e = semConstrField(c, flags, field, constrCtx.initExpr)
|
||||
@@ -451,7 +472,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
result.typ.flags.incl tfHasOwned
|
||||
if t.kind != tyObject:
|
||||
return localErrorNode(c, result, if t.kind != tyGenericBody:
|
||||
"object constructor needs an object type".dup(addDeclaredLoc(c.config, t))
|
||||
"object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t))
|
||||
else: "cannot instantiate: '" &
|
||||
typeToString(t, preferDesc) &
|
||||
"'; the object's generic parameters cannot be inferred and must be explicitly given"
|
||||
|
||||
@@ -356,12 +356,15 @@ proc useVar(a: PEffects, n: PNode) =
|
||||
type
|
||||
TIntersection = seq[tuple[id, count: int]] # a simple count table
|
||||
|
||||
proc addToIntersection(inter: var TIntersection, s: int) =
|
||||
proc addToIntersection(inter: var TIntersection, s: int, zeroInit: bool) =
|
||||
for j in 0..<inter.len:
|
||||
if s == inter[j].id:
|
||||
inc inter[j].count
|
||||
return
|
||||
inter.add((id: s, count: 1))
|
||||
if zeroInit:
|
||||
inter.add((id: s, count: 0))
|
||||
else:
|
||||
inter.add((id: s, count: 1))
|
||||
|
||||
proc throws(tracked, n, orig: PNode) =
|
||||
if n.typ == nil or n.typ.kind != tyError:
|
||||
@@ -465,7 +468,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
|
||||
track(tracked, n[0])
|
||||
dec tracked.inTryStmt
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], false)
|
||||
|
||||
var branches = 1
|
||||
var hasFinally = false
|
||||
@@ -500,7 +503,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
|
||||
tracked.init.add b[j][2].sym.id
|
||||
track(tracked, b[^1])
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], false)
|
||||
else:
|
||||
setLen(tracked.init, oldState)
|
||||
track(tracked, b[^1])
|
||||
@@ -578,7 +581,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||
if paramType != nil and tfNotNil in paramType.flags and n.typ != nil:
|
||||
let ntyp = n.typ.skipTypesOrNil({tyVar, tyLent, tySink})
|
||||
if ntyp != nil and tfNotNil notin ntyp.flags:
|
||||
if isAddrNode(n):
|
||||
if n.kind in {nkAddr, nkHiddenAddr}:
|
||||
# addr(x[]) can't be proven, but addr(x) can:
|
||||
if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
|
||||
elif (n.kind == nkSym and n.sym.kind in routineKinds) or
|
||||
@@ -696,9 +699,11 @@ proc trackCase(tracked: PEffects, n: PNode) =
|
||||
addCaseBranchFacts(tracked.guards, n, i)
|
||||
for i in 0..<branch.len:
|
||||
track(tracked, branch[i])
|
||||
if not breaksBlock(branch.lastSon): inc toCover
|
||||
let hasBreaksBlock = breaksBlock(branch.lastSon)
|
||||
if not hasBreaksBlock:
|
||||
inc toCover
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
|
||||
|
||||
setLen(tracked.init, oldState)
|
||||
if not stringCase or lastSon(n).kind == nkElse:
|
||||
@@ -718,9 +723,11 @@ proc trackIf(tracked: PEffects, n: PNode) =
|
||||
var inter: TIntersection = @[]
|
||||
var toCover = 0
|
||||
track(tracked, n[0][1])
|
||||
if not breaksBlock(n[0][1]): inc toCover
|
||||
let hasBreaksBlock = breaksBlock(n[0][1])
|
||||
if not hasBreaksBlock:
|
||||
inc toCover
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
|
||||
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
@@ -732,9 +739,12 @@ proc trackIf(tracked: PEffects, n: PNode) =
|
||||
setLen(tracked.init, oldState)
|
||||
for i in 0..<branch.len:
|
||||
track(tracked, branch[i])
|
||||
if not breaksBlock(branch.lastSon): inc toCover
|
||||
let hasBreaksBlock = breaksBlock(branch.lastSon)
|
||||
if not hasBreaksBlock:
|
||||
inc toCover
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
|
||||
|
||||
setLen(tracked.init, oldState)
|
||||
if lastSon(n).len == 1:
|
||||
for id, count in items(inter):
|
||||
@@ -878,7 +888,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
mergeRaises(tracked, effectList[exceptionEffects], n)
|
||||
mergeTags(tracked, effectList[tagEffects], n)
|
||||
gcsafeAndSideeffectCheck()
|
||||
if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished, mExpandToAst, mQuoteAst}:
|
||||
if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished, mExpandToAst, mQuoteAst, mType}:
|
||||
for i in 1..<n.len:
|
||||
trackOperandForIndirectCall(tracked, n[i], op, i, a)
|
||||
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||
@@ -1047,7 +1057,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
|
||||
tracked.owner.flags.incl sfInjectDestructors
|
||||
# bug #15038: ensure consistency
|
||||
if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ
|
||||
if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ = n.sym.typ
|
||||
of nkHiddenAddr, nkAddr:
|
||||
if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym):
|
||||
useVarNoInitCheck(tracked, n[0], n[0].sym)
|
||||
@@ -1319,6 +1329,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
|
||||
proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
|
||||
if spec.typ.kind == tyOr:
|
||||
result = false
|
||||
for t in spec.typ.sons:
|
||||
if safeInheritanceDiff(g.excType(real), t) <= 0:
|
||||
return true
|
||||
|
||||
@@ -129,17 +129,141 @@ proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode
|
||||
closeScope(c)
|
||||
|
||||
const
|
||||
skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
|
||||
nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch,
|
||||
skipForDiscardable = {nkStmtList, nkStmtListExpr,
|
||||
nkOfBranch, nkElse, nkFinally, nkExceptBranch,
|
||||
nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr,
|
||||
nkHiddenStdConv, nkHiddenDeref}
|
||||
nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref}
|
||||
|
||||
proc implicitlyDiscardable(n: PNode): bool =
|
||||
var n = n
|
||||
while n.kind in skipForDiscardable: n = n.lastSon
|
||||
result = n.kind in nkLastBlockStmts or
|
||||
(isCallExpr(n) and n[0].kind == nkSym and
|
||||
sfDiscardable in n[0].sym.flags)
|
||||
# same traversal as endsInNoReturn
|
||||
template checkBranch(branch) =
|
||||
if not implicitlyDiscardable(branch):
|
||||
return false
|
||||
|
||||
var it = n
|
||||
# skip these beforehand, no special handling needed
|
||||
while it.kind in skipForDiscardable and it.len > 0:
|
||||
it = it.lastSon
|
||||
|
||||
case it.kind
|
||||
of nkIfExpr, nkIfStmt:
|
||||
for branch in it:
|
||||
checkBranch:
|
||||
if branch.len == 2:
|
||||
branch[1]
|
||||
elif branch.len == 1:
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `if` statement during implicitlyDiscardable"
|
||||
# all branches are discardable
|
||||
result = true
|
||||
of nkCaseStmt:
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
checkBranch:
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
branch[^1]
|
||||
of nkElifBranch:
|
||||
branch[1]
|
||||
of nkElse:
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `case` statement in endsInNoReturn"
|
||||
# all branches are discardable
|
||||
result = true
|
||||
of nkTryStmt:
|
||||
checkBranch(it[0])
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
if branch.kind != nkFinally:
|
||||
checkBranch(branch[^1])
|
||||
# all branches are discardable
|
||||
result = true
|
||||
of nkCallKinds:
|
||||
result = it[0].kind == nkSym and {sfDiscardable, sfNoReturn} * it[0].sym.flags != {}
|
||||
of nkLastBlockStmts:
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc endsInNoReturn(n: PNode, returningNode: var PNode; discardableCheck = false): bool =
|
||||
## check if expr ends the block like raising or call of noreturn procs do
|
||||
result = false # assume it does return
|
||||
|
||||
template checkBranch(branch) =
|
||||
if not endsInNoReturn(branch, returningNode, discardableCheck):
|
||||
# proved a branch returns
|
||||
return false
|
||||
|
||||
var it = n
|
||||
# skip these beforehand, no special handling needed
|
||||
let skips = if discardableCheck: skipForDiscardable else: skipForDiscardable-{nkBlockExpr, nkBlockStmt}
|
||||
while it.kind in skips and it.len > 0:
|
||||
it = it.lastSon
|
||||
|
||||
case it.kind
|
||||
of nkIfExpr, nkIfStmt:
|
||||
var hasElse = false
|
||||
for branch in it:
|
||||
checkBranch:
|
||||
if branch.len == 2:
|
||||
branch[1]
|
||||
elif branch.len == 1:
|
||||
hasElse = true
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `if` statement during endsInNoReturn"
|
||||
# none of the branches returned
|
||||
result = hasElse # Only truly a no-return when it's exhaustive
|
||||
of nkCaseStmt:
|
||||
let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc})
|
||||
# semCase should already have checked for exhaustiveness in this case
|
||||
# effectively the same as having an else
|
||||
var hasElse = caseTyp.shouldCheckCaseCovered()
|
||||
|
||||
# actual noreturn checks
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
checkBranch:
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
branch[^1]
|
||||
of nkElifBranch:
|
||||
branch[1]
|
||||
of nkElse:
|
||||
hasElse = true
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `case` statement in endsInNoReturn"
|
||||
# Can only guarantee a noreturn if there is an else or it's exhaustive
|
||||
result = hasElse
|
||||
of nkTryStmt:
|
||||
checkBranch(it[0])
|
||||
var lastIndex = it.len - 1
|
||||
if it[lastIndex].kind == nkFinally:
|
||||
# if finally is noreturn, then the entire statement is noreturn
|
||||
if endsInNoReturn(it[lastIndex][^1], returningNode, discardableCheck):
|
||||
return true
|
||||
dec lastIndex
|
||||
for i in 1 .. lastIndex:
|
||||
let branch = it[i]
|
||||
checkBranch(branch[^1])
|
||||
# none of the branches returned
|
||||
result = true
|
||||
of nkLastBlockStmts:
|
||||
result = true
|
||||
of nkCallKinds:
|
||||
result = it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
|
||||
if not result:
|
||||
returningNode = it
|
||||
else:
|
||||
result = false
|
||||
returningNode = it
|
||||
|
||||
proc endsInNoReturn(n: PNode): bool =
|
||||
var dummy: PNode = nil
|
||||
result = endsInNoReturn(n, dummy)
|
||||
|
||||
proc fixNilType(c: PContext; n: PNode) =
|
||||
if isAtom(n):
|
||||
@@ -162,10 +286,11 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
|
||||
localError(c.config, result.info, "expression has no type: " &
|
||||
renderTree(result, {renderNoComments}))
|
||||
else:
|
||||
# Ignore noreturn procs since they don't have a type
|
||||
var n = result
|
||||
while n.kind in skipForDiscardable:
|
||||
if n.kind == nkTryStmt: n = n[0]
|
||||
else: n = n.lastSon
|
||||
if result.endsInNoReturn(n, discardableCheck = true):
|
||||
return
|
||||
|
||||
var s = "expression '" & $n & "' is of type '" &
|
||||
result.typ.typeToString & "' and has to be used (or discarded)"
|
||||
if result.info.line != n.info.line or
|
||||
@@ -331,6 +456,8 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
|
||||
result.typ = typ
|
||||
if not floatRangeCheck(result.floatVal, typ):
|
||||
localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)])
|
||||
elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring:
|
||||
discard "keep nkHiddenStdConv for cstring conversions"
|
||||
else:
|
||||
changeType(c, r1, typ, check=true)
|
||||
result = r1
|
||||
@@ -350,7 +477,7 @@ proc identWithin(n: PNode, s: PIdent): bool =
|
||||
|
||||
proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym =
|
||||
if isTopLevel(c):
|
||||
result = semIdentWithPragma(c, kind, n, {sfExported})
|
||||
result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true)
|
||||
incl(result.flags, sfGlobal)
|
||||
#if kind in {skVar, skLet}:
|
||||
# echo "global variable here ", n.info, " ", result.name.s
|
||||
@@ -590,14 +717,22 @@ proc globalVarInitCheck(c: PContext, n: PNode) =
|
||||
if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n):
|
||||
localError(c.config, n.info, errCannotAssignToGlobal)
|
||||
|
||||
const
|
||||
errTupleUnpackingTupleExpected = "tuple expected for tuple unpacking, but got '$1'"
|
||||
errTupleUnpackingDifferentLengths = "tuple with $1 elements expected, but got '$2' with $3 elements"
|
||||
|
||||
proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode =
|
||||
## expand tuple unpacking assignments into new var/let/const section
|
||||
##
|
||||
## mirrored with semexprs.makeTupleAssignments
|
||||
if typ.kind != tyTuple:
|
||||
localError(c.config, a.info, errXExpected, "tuple")
|
||||
localError(c.config, a.info, errTupleUnpackingTupleExpected %
|
||||
[typeToString(typ, preferDesc)])
|
||||
elif a.len-2 != typ.len:
|
||||
localError(c.config, a.info, errWrongNumberOfVariables)
|
||||
localError(c.config, a.info, errTupleUnpackingDifferentLengths %
|
||||
[$(a.len-2), typeToString(typ, preferDesc), $typ.len])
|
||||
var
|
||||
tmpTuple: PSym
|
||||
tempNode: PNode = nil
|
||||
lastDef: PNode
|
||||
let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs
|
||||
# temporary not needed if not const and RHS is tuple literal
|
||||
@@ -605,17 +740,18 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
|
||||
let useTemp = def.kind notin {nkPar, nkTupleConstr} or symkind == skConst
|
||||
if useTemp:
|
||||
# use same symkind for compatibility with original section
|
||||
tmpTuple = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
|
||||
tmpTuple.typ = typ
|
||||
tmpTuple.flags.incl(sfGenSym)
|
||||
let temp = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
|
||||
temp.typ = typ
|
||||
temp.flags.incl(sfGenSym)
|
||||
lastDef = newNodeI(defkind, a.info)
|
||||
newSons(lastDef, 3)
|
||||
lastDef[0] = newSymNode(tmpTuple)
|
||||
lastDef[0] = newSymNode(temp)
|
||||
# NOTE: at the moment this is always ast.emptyNode, see parser.nim
|
||||
lastDef[1] = a[^2]
|
||||
lastDef[2] = def
|
||||
tmpTuple.ast = lastDef
|
||||
temp.ast = lastDef
|
||||
addToVarSection(c, origResult, n, lastDef)
|
||||
tempNode = newSymNode(temp)
|
||||
result = newNodeI(n.kind, a.info)
|
||||
for j in 0..<a.len-2:
|
||||
let name = a[j]
|
||||
@@ -634,7 +770,7 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
|
||||
lastDef[0] = name
|
||||
lastDef[^2] = c.graph.emptyNode
|
||||
if useTemp:
|
||||
lastDef[^1] = newTreeIT(nkBracketExpr, name.info, typ[j], newSymNode(tmpTuple), newIntNode(nkIntLit, j))
|
||||
lastDef[^1] = newTupleAccessRaw(tempNode, j)
|
||||
else:
|
||||
var val = def[j]
|
||||
if val.kind == nkExprColonExpr: val = val[1]
|
||||
@@ -656,9 +792,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
addToVarSection(c, result, b)
|
||||
continue
|
||||
|
||||
var hasUserSpecifiedType = false
|
||||
var typ: PType = nil
|
||||
if a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
hasUserSpecifiedType = true
|
||||
|
||||
var typFlags: TTypeAllowedFlags
|
||||
|
||||
@@ -730,6 +868,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
addToVarSection(c, result, n, a)
|
||||
continue
|
||||
var v = semIdentDef(c, a[j], symkind, false)
|
||||
when defined(nimsuggest):
|
||||
v.hasUserSpecifiedType = hasUserSpecifiedType
|
||||
styleCheckDef(c, v)
|
||||
onDef(a[j].info, v)
|
||||
if sfGenSym notin v.flags:
|
||||
@@ -803,9 +943,11 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
addToVarSection(c, result, b)
|
||||
continue
|
||||
|
||||
var hasUserSpecifiedType = false
|
||||
var typ: PType = nil
|
||||
if a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
hasUserSpecifiedType = true
|
||||
|
||||
var typFlags: TTypeAllowedFlags
|
||||
|
||||
@@ -846,14 +988,20 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
else:
|
||||
for j in 0..<a.len-2:
|
||||
var v = semIdentDef(c, a[j], skConst)
|
||||
when defined(nimsuggest):
|
||||
v.hasUserSpecifiedType = hasUserSpecifiedType
|
||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
styleCheckDef(c, v)
|
||||
onDef(a[j].info, v)
|
||||
|
||||
setVarType(c, v, typ)
|
||||
when false:
|
||||
v.ast = def # no need to copy
|
||||
var fillSymbol = true
|
||||
if v.typ != nil:
|
||||
# symbol already has type and probably value
|
||||
# don't mutate
|
||||
fillSymbol = false
|
||||
else:
|
||||
setVarType(c, v, typ)
|
||||
b = newNodeI(nkConstDef, a.info)
|
||||
if importantComments(c.config): b.comment = a.comment
|
||||
# postfix not generated here (to generate, get rid of it in transf)
|
||||
@@ -866,8 +1014,9 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
b.add newSymNode(v)
|
||||
b.add a[1]
|
||||
b.add copyTree(def)
|
||||
v.ast = b
|
||||
addToVarSection(c, result, n, b)
|
||||
if fillSymbol:
|
||||
v.ast = b
|
||||
addToVarSection(c, result, n, b)
|
||||
dec c.inStaticContext
|
||||
|
||||
include semfields
|
||||
@@ -894,7 +1043,10 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
if iterAfterVarLent.kind != tyTuple or n.len == 3:
|
||||
if n.len == 3:
|
||||
if n[0].kind == nkVarTuple:
|
||||
if n[0].len-1 != iterAfterVarLent.len:
|
||||
if iterAfterVarLent.kind != tyTuple:
|
||||
return localErrorNode(c, n, n[0].info, errTupleUnpackingTupleExpected %
|
||||
[typeToString(n[1].typ, preferDesc)])
|
||||
elif n[0].len-1 != iterAfterVarLent.len:
|
||||
return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables)
|
||||
|
||||
for i in 0..<n[0].len-1:
|
||||
@@ -1007,7 +1159,7 @@ proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string;
|
||||
if maType == nil: return
|
||||
|
||||
let headSymbol = selector[0]
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var match: PSym = nil
|
||||
var symx = initOverloadIter(o, c, headSymbol)
|
||||
while symx != nil:
|
||||
@@ -1041,7 +1193,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
toResolve.add newIdentNode(getIdent(c.cache, "case"), n.info)
|
||||
toResolve.add n[0]
|
||||
|
||||
var errors: CandidateErrors
|
||||
var errors: CandidateErrors = @[]
|
||||
var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoDiagnostics},
|
||||
errors, false)
|
||||
if r.state == csMatch:
|
||||
@@ -1113,20 +1265,14 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil
|
||||
openScope(c)
|
||||
pushCaseContext(c, n)
|
||||
n[0] = semExprWithType(c, n[0])
|
||||
var chckCovered = false
|
||||
var covered: Int128 = toInt128(0)
|
||||
var typ = commonTypeBegin
|
||||
var expectedType = expectedType
|
||||
var hasElse = false
|
||||
let caseTyp = skipTypes(n[0].typ, abstractVar-{tyTypeDesc})
|
||||
const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
|
||||
var chckCovered = caseTyp.shouldCheckCaseCovered()
|
||||
case caseTyp.kind
|
||||
of shouldChckCovered:
|
||||
chckCovered = true
|
||||
of tyRange:
|
||||
if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
|
||||
chckCovered = true
|
||||
of tyFloat..tyFloat128, tyString, tyCstring, tyError:
|
||||
of tyFloat..tyFloat128, tyString, tyCstring, tyError, shouldChckCovered, tyRange:
|
||||
discard
|
||||
else:
|
||||
popCaseContext(c)
|
||||
@@ -1461,9 +1607,12 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
# final pass
|
||||
if a[2].kind in nkCallKinds:
|
||||
incl a[2].flags, nfSem # bug #10548
|
||||
if sfExportc in s.flags and s.typ.kind == tyAlias:
|
||||
localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
|
||||
|
||||
if sfExportc in s.flags:
|
||||
if s.typ.kind == tyAlias:
|
||||
localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
|
||||
elif s.typ.kind == tyGenericBody:
|
||||
localError(c.config, name.info, "{.exportc.} not allowed for generic types")
|
||||
|
||||
if tfBorrowDot in s.typ.flags:
|
||||
let body = s.typ.skipTypes({tyGenericBody})
|
||||
if body.kind != tyDistinct:
|
||||
@@ -1509,21 +1658,27 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
for sk in c.skipTypes:
|
||||
discard semTypeNode(c, sk, nil)
|
||||
c.skipTypes = @[]
|
||||
proc checkForMetaFields(c: PContext; n: PNode) =
|
||||
proc checkMeta(c: PContext; n: PNode; t: PType) =
|
||||
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
|
||||
|
||||
proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
|
||||
proc checkMeta(c: PContext; n: PNode; t: PType; hasError: var bool; parent: PType) =
|
||||
if t != nil and (t.isMetaType or t.kind == tyNone) and tfGenericTypeParam notin t.flags:
|
||||
if t.kind == tyBuiltInTypeClass and t.len == 1 and t[0].kind == tyProc:
|
||||
localError(c.config, n.info, ("'$1' is not a concrete type; " &
|
||||
"for a callback without parameters use 'proc()'") % t.typeToString)
|
||||
elif t.kind == tyNone and parent != nil:
|
||||
# TODO: openarray has the `tfGenericTypeParam` flag & generics
|
||||
# TODO: handle special cases (sink etc.) and views
|
||||
localError(c.config, n.info, errTIsNotAConcreteType % parent.typeToString)
|
||||
else:
|
||||
localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
|
||||
hasError = true
|
||||
|
||||
if n.isNil: return
|
||||
case n.kind
|
||||
of nkRecList, nkRecCase:
|
||||
for s in n: checkForMetaFields(c, s)
|
||||
for s in n: checkForMetaFields(c, s, hasError)
|
||||
of nkOfBranch, nkElse:
|
||||
checkForMetaFields(c, n.lastSon)
|
||||
checkForMetaFields(c, n.lastSon, hasError)
|
||||
of nkSym:
|
||||
let t = n.sym.typ
|
||||
case t.kind
|
||||
@@ -1531,9 +1686,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
|
||||
tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink, tyOwned:
|
||||
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
|
||||
for i in start..<t.len:
|
||||
checkMeta(c, n, t[i])
|
||||
checkMeta(c, n, t[i], hasError, t)
|
||||
else:
|
||||
checkMeta(c, n, t)
|
||||
checkMeta(c, n, t, hasError, nil)
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
|
||||
@@ -1568,9 +1723,11 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
assert s.typ != nil
|
||||
assignType(s.typ, t)
|
||||
s.typ.itemId = t.itemId # same id
|
||||
checkConstructedType(c.config, s.info, s.typ)
|
||||
var hasError = false
|
||||
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
|
||||
checkForMetaFields(c, s.typ.n)
|
||||
checkForMetaFields(c, s.typ.n, hasError)
|
||||
if not hasError:
|
||||
checkConstructedType(c.config, s.info, s.typ)
|
||||
|
||||
# fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name:
|
||||
if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c):
|
||||
@@ -1648,18 +1805,27 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind) =
|
||||
|
||||
proc semBorrow(c: PContext, n: PNode, s: PSym) =
|
||||
# search for the correct alias:
|
||||
var b = searchForBorrowProc(c, c.currentScope.parent, s)
|
||||
if b != nil:
|
||||
# store the alias:
|
||||
n[bodyPos] = newSymNode(b)
|
||||
# Carry over the original symbol magic, this is necessary in order to ensure
|
||||
# the semantic pass is correct
|
||||
s.magic = b.magic
|
||||
if b.typ != nil and b.typ.len > 0:
|
||||
s.typ.n[0] = b.typ.n[0]
|
||||
s.typ.flags = b.typ.flags
|
||||
else:
|
||||
localError(c.config, n.info, errNoSymbolToBorrowFromFound)
|
||||
var (b, state) = searchForBorrowProc(c, c.currentScope.parent, s)
|
||||
case state
|
||||
of bsMatch:
|
||||
# store the alias:
|
||||
n[bodyPos] = newSymNode(b)
|
||||
# Carry over the original symbol magic, this is necessary in order to ensure
|
||||
# the semantic pass is correct
|
||||
s.magic = b.magic
|
||||
if b.typ != nil and b.typ.len > 0:
|
||||
s.typ.n[0] = b.typ.n[0]
|
||||
s.typ.flags = b.typ.flags
|
||||
of bsNoDistinct:
|
||||
localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless")
|
||||
of bsReturnNotMatch:
|
||||
localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ[0]))
|
||||
of bsGeneric:
|
||||
localError(c.config, n.info, "borrow with generic parameter is not supported")
|
||||
of bsNotSupported:
|
||||
localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s)
|
||||
else:
|
||||
localError(c.config, n.info, errNoSymbolToBorrowFromFound)
|
||||
|
||||
proc swapResult(n: PNode, sRes: PSym, dNode: PNode) =
|
||||
## Swap nodes that are (skResult) symbols to d(estination)Node.
|
||||
@@ -1767,7 +1933,6 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||
n[genericParamsPos] = c.graph.emptyNode
|
||||
# for LL we need to avoid wrong aliasing
|
||||
let params = copyTree n.typ.n
|
||||
n[paramsPos] = params
|
||||
s.typ = n.typ
|
||||
for i in 1..<params.len:
|
||||
if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
|
||||
@@ -1876,7 +2041,7 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverridden)
|
||||
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) =
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
let cond = case op
|
||||
@@ -1884,6 +2049,11 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
t.len == 2 and t[0] == nil and t[1].kind == tyVar
|
||||
of attachedTrace:
|
||||
t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer
|
||||
of attachedDestructor:
|
||||
if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
t.len == 2 and t[0] == nil
|
||||
else:
|
||||
t.len == 2 and t[0] == nil and t[1].kind == tyVar
|
||||
else:
|
||||
t.len >= 2 and t[0] == nil
|
||||
|
||||
@@ -1895,7 +2065,8 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
elif obj.kind == tyGenericInvocation: obj = obj[0]
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||
if op == attachedDestructor and t[1].kind == tyVar:
|
||||
if (not suppressVarDestructorWarning) and op == attachedDestructor and t[1].kind == tyVar and
|
||||
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter")
|
||||
obj = canonType(c, obj)
|
||||
let ao = getAttachedOp(c.graph, obj, op)
|
||||
@@ -1915,8 +2086,12 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=trace' must be proc[T: object](x: var T; env: pointer)")
|
||||
of attachedDestructor:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)")
|
||||
if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)")
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=destroy' must be proc[T: object](x: var T)")
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
|
||||
@@ -2613,9 +2788,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
|
||||
var m = n[i]
|
||||
while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates
|
||||
m = m.lastSon
|
||||
if m.kind in nkLastBlockStmts or
|
||||
m.kind in nkCallKinds and m[0].kind == nkSym and
|
||||
sfNoReturn in m[0].sym.flags:
|
||||
if endsInNoReturn(m):
|
||||
for j in i + 1..<n.len:
|
||||
case n[j].kind
|
||||
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkState: discard
|
||||
|
||||
@@ -51,7 +51,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
|
||||
isField = false): PNode =
|
||||
var
|
||||
a: PSym
|
||||
o: TOverloadIter
|
||||
o: TOverloadIter = default(TOverloadIter)
|
||||
var i = 0
|
||||
a = initOverloadIter(o, c, n)
|
||||
while a != nil:
|
||||
@@ -135,24 +135,32 @@ type
|
||||
noGenSym: int
|
||||
inTemplateHeader: int
|
||||
|
||||
proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
|
||||
proc isTemplParam(c: TemplCtx, s: PSym): bool {.inline.} =
|
||||
result = s.kind == skParam and
|
||||
s.owner == c.owner and sfTemplateParam in s.flags
|
||||
|
||||
proc getIdentReplaceParams(c: var TemplCtx, n: var PNode): tuple[node: PNode, hasParam: bool] =
|
||||
case n.kind
|
||||
of nkPostfix: result = getIdentNode(c, n[1])
|
||||
of nkPragmaExpr: result = getIdentNode(c, n[0])
|
||||
of nkPostfix: result = getIdentReplaceParams(c, n[1])
|
||||
of nkPragmaExpr: result = getIdentReplaceParams(c, n[0])
|
||||
of nkIdent:
|
||||
result = n
|
||||
result = (n, false)
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and s.kind == skParam:
|
||||
result = newSymNode(s, n.info)
|
||||
of nkAccQuoted, nkSym: result = n
|
||||
if s != nil and isTemplParam(c, s):
|
||||
n = newSymNode(s, n.info)
|
||||
result = (n, true)
|
||||
of nkSym:
|
||||
result = (n, isTemplParam(c, n.sym))
|
||||
of nkAccQuoted:
|
||||
result = (n, false)
|
||||
for i in 0..<n.safeLen:
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n[i])
|
||||
if hasParam:
|
||||
result.node[i] = ident
|
||||
result.hasParam = true
|
||||
else:
|
||||
illFormedAst(n, c.c.config)
|
||||
result = n
|
||||
|
||||
proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
|
||||
result = n.kind == nkSym and n.sym.kind == skParam and
|
||||
n.sym.owner == c.owner and sfTemplateParam in n.sym.flags
|
||||
result = (n, false)
|
||||
|
||||
proc semTemplBody(c: var TemplCtx, n: PNode): PNode
|
||||
|
||||
@@ -167,19 +175,6 @@ proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
|
||||
result = semTemplBody(c, n)
|
||||
closeScope(c)
|
||||
|
||||
proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
|
||||
result = n
|
||||
if n.kind == nkIdent:
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and s.kind == skParam:
|
||||
incl(s.flags, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
result[i] = onlyReplaceParams(c, n[i])
|
||||
|
||||
proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
|
||||
result = newSym(kind, considerQuotedIdent(c.c, n), c.c.idgen, c.owner, n.info)
|
||||
incl(result.flags, sfGenSym)
|
||||
@@ -191,24 +186,10 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
k == skField:
|
||||
# even if injected, don't produce a sym choice here:
|
||||
#n = semTemplBody(c, n)
|
||||
var x = n
|
||||
while true:
|
||||
case x.kind
|
||||
of nkPostfix: x = x[1]
|
||||
of nkPragmaExpr: x = x[0]
|
||||
of nkIdent: break
|
||||
of nkAccQuoted:
|
||||
# consider: type `T TemplParam` {.inject.}
|
||||
# it suffices to return to treat it like 'inject':
|
||||
n = onlyReplaceParams(c, n)
|
||||
return
|
||||
else:
|
||||
illFormedAst(x, c.c.config)
|
||||
let ident = getIdentNode(c, x)
|
||||
if not isTemplParam(c, ident):
|
||||
if k != skField: c.toInject.incl(x.ident.id)
|
||||
else:
|
||||
replaceIdentBySym(c.c, n, ident)
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n)
|
||||
if not hasParam:
|
||||
if k != skField:
|
||||
c.toInject.incl(considerQuotedIdent(c.c, ident).id)
|
||||
else:
|
||||
if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma):
|
||||
let pragmaNode = n[1]
|
||||
@@ -218,15 +199,15 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
var found = false
|
||||
if ni.kind == nkIdent:
|
||||
for a in templatePragmas:
|
||||
if ni.ident == getIdent(c.c.cache, $a):
|
||||
if ni.ident.id == ord(a):
|
||||
found = true
|
||||
break
|
||||
if not found:
|
||||
openScope(c)
|
||||
pragmaNode[i] = semTemplBody(c, pragmaNode[i])
|
||||
closeScope(c)
|
||||
let ident = getIdentNode(c, n)
|
||||
if not isTemplParam(c, ident):
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n)
|
||||
if not hasParam:
|
||||
if n.kind != nkSym and not (n.kind == nkIdent and n.ident.id == ord(wUnderscore)):
|
||||
let local = newGenSym(k, ident, c)
|
||||
addPrelimDecl(c.c, local)
|
||||
@@ -235,77 +216,95 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
replaceIdentBySym(c.c, n, newSymNode(local, n.info))
|
||||
if k == skParam and c.inTemplateHeader > 0:
|
||||
local.flags.incl sfTemplateParam
|
||||
else:
|
||||
replaceIdentBySym(c.c, n, ident)
|
||||
|
||||
proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
|
||||
proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField: bool): PNode =
|
||||
incl(s.flags, sfUsed)
|
||||
# bug #12885; ideally sem'checking is performed again afterwards marking
|
||||
# the symbol as used properly, but the nfSem mechanism currently prevents
|
||||
# that from happening, so we mark the module as used here already:
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
markOwnerModuleAsUsed(c.c, s)
|
||||
# we do not call onUse here, as the identifier is not really
|
||||
# resolved here. We will fixup the used identifiers later.
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
result = n
|
||||
of OverloadableSyms-{skTemplate,skMacro}:
|
||||
result = symChoice(c, n, s, scOpen, isField)
|
||||
of skTemplate, skMacro:
|
||||
result = symChoice(c, n, s, scOpen, isField)
|
||||
if result.kind == nkSym:
|
||||
# template/macro symbols might need to be semchecked again
|
||||
# prepareOperand etc don't do this without setting the type to nil
|
||||
result.typ = nil
|
||||
of OverloadableSyms:
|
||||
result = symChoice(c.c, n, s, scOpen, isField)
|
||||
if not isField and result.kind in {nkSym, nkOpenSymChoice}:
|
||||
if openSym in c.c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
of skGenericParam:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
|
||||
if not isField and s.owner != c.owner:
|
||||
if openSym in c.c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
of skParam:
|
||||
result = n
|
||||
of skType:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
|
||||
else:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else: result = newSymNode(s, n.info)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if not isField:
|
||||
if openSym in c.c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
# Issue #12832
|
||||
when defined(nimsuggest):
|
||||
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
|
||||
suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false)
|
||||
# field access (dot expr) will be handled by builtinFieldAccess
|
||||
if not isField:
|
||||
styleCheckUse(c, n.info, s)
|
||||
styleCheckUse(c.c, n.info, s)
|
||||
|
||||
proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
|
||||
proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode =
|
||||
result = n
|
||||
if n.kind == nkIdent:
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and (s.kind == skParam or sfGenSym in s.flags):
|
||||
if s.owner == c.owner and (s.kind == skParam or
|
||||
(sfGenSym in s.flags and not explicitInject)):
|
||||
incl(s.flags, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
result[i] = semRoutineInTemplName(c, n[i])
|
||||
result[i] = semRoutineInTemplName(c, n[i], explicitInject)
|
||||
|
||||
proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
|
||||
result = n
|
||||
checkSonsLen(n, bodyPos + 1, c.c.config)
|
||||
# routines default to 'inject':
|
||||
if n.kind notin nkLambdaKinds and symBinding(n[pragmasPos]) == spGenSym:
|
||||
let ident = getIdentNode(c, n[namePos])
|
||||
if not isTemplParam(c, ident):
|
||||
var s = newGenSym(k, ident, c)
|
||||
s.ast = n
|
||||
addPrelimDecl(c.c, s)
|
||||
styleCheckDef(c.c, n.info, s)
|
||||
onDef(n.info, s)
|
||||
n[namePos] = newSymNode(s, n[namePos].info)
|
||||
if n.kind notin nkLambdaKinds:
|
||||
# routines default to 'inject':
|
||||
let binding = symBinding(n[pragmasPos])
|
||||
if binding == spGenSym:
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
|
||||
if not hasParam:
|
||||
var s = newGenSym(k, ident, c)
|
||||
s.ast = n
|
||||
addPrelimDecl(c.c, s)
|
||||
styleCheckDef(c.c, n.info, s)
|
||||
onDef(n.info, s)
|
||||
n[namePos] = newSymNode(s, n[namePos].info)
|
||||
else:
|
||||
n[namePos] = ident
|
||||
else:
|
||||
n[namePos] = ident
|
||||
else:
|
||||
n[namePos] = semRoutineInTemplName(c, n[namePos])
|
||||
n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject)
|
||||
# open scope for parameters
|
||||
openScope(c)
|
||||
for i in patternPos..paramsPos-1:
|
||||
@@ -381,7 +380,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
else:
|
||||
if s.kind in {skType, skVar, skLet, skConst}:
|
||||
discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
|
||||
result = semTemplSymbol(c.c, n, s, c.noGenSym > 0)
|
||||
result = semTemplSymbol(c, n, s, c.noGenSym > 0)
|
||||
of nkBind:
|
||||
result = semTemplBody(c, n[0])
|
||||
of nkBindStmt:
|
||||
@@ -526,14 +525,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
if x.kind == nkExprColonExpr:
|
||||
x[1] = semTemplBody(c, x[1])
|
||||
of nkBracketExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
if n.typ == nil:
|
||||
# if a[b] is nested inside a typed expression, don't convert it
|
||||
# back to `[]`(a, b), prepareOperand will not typecheck it again
|
||||
# and so `[]` will not be resolved
|
||||
# checking if a[b] is typed should be enough to cover this case
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkCurlyExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
if n.typ == nil:
|
||||
# see nkBracketExpr case for explanation
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
checkSonsLen(n, 2, c.c.config)
|
||||
@@ -543,17 +549,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
let k = a.kind
|
||||
case k
|
||||
of nkBracketExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
if a.typ == nil:
|
||||
# see nkBracketExpr case above for explanation
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
let a0 = semTemplBody(c, a[0])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkCurlyExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
if a.typ == nil:
|
||||
# see nkBracketExpr case above for explanation
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
result = semTemplBodySons(c, result)
|
||||
else:
|
||||
result = semTemplBodySons(c, n)
|
||||
@@ -566,6 +576,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
# so we use the generic code for nkDotExpr too
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
# mirror the nkIdent case
|
||||
# do not symchoice a quoted template parameter (bug #2390):
|
||||
if s.owner == c.owner and s.kind == skParam and
|
||||
n.kind == nkAccQuoted and n.len == 1:
|
||||
@@ -577,7 +588,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
elif contains(c.toMixin, s.name.id):
|
||||
return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
|
||||
else:
|
||||
return symChoice(c.c, n, s, scOpen, c.noGenSym > 0)
|
||||
if s.kind in {skType, skVar, skLet, skConst}:
|
||||
discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
|
||||
return semTemplSymbol(c, n, s, c.noGenSym > 0)
|
||||
if n.kind == nkDotExpr:
|
||||
result = n
|
||||
result[0] = semTemplBody(c, n[0])
|
||||
@@ -673,6 +686,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
# a template's parameters are not gensym'ed even if that was originally the
|
||||
# case as we determine whether it's a template parameter in the template
|
||||
# body by the absence of the sfGenSym flag:
|
||||
let retType = s.typ[0]
|
||||
if retType != nil and retType.kind != tyUntyped:
|
||||
allUntyped = false
|
||||
for i in 1..<s.typ.n.len:
|
||||
let param = s.typ.n[i].sym
|
||||
if param.name.id != ord(wUnderscore):
|
||||
|
||||
@@ -141,6 +141,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
result.n.add symNode
|
||||
styleCheckDef(c, e)
|
||||
onDef(e.info, e)
|
||||
suggestSym(c.graph, e.info, e, c.graph.usageSym)
|
||||
if sfGenSym notin e.flags:
|
||||
if not isPure:
|
||||
addInterfaceOverloadableSymAt(c, c.currentScope, e)
|
||||
@@ -228,6 +229,7 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
|
||||
return false
|
||||
|
||||
proc fitDefaultNode(c: PContext, n: PNode): PType =
|
||||
inc c.inStaticContext
|
||||
let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
|
||||
n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
|
||||
let oldType = n[^1].typ
|
||||
@@ -235,9 +237,19 @@ proc fitDefaultNode(c: PContext, n: PNode): PType =
|
||||
if n[^2].kind != nkEmpty:
|
||||
if expectedType != nil and oldType != expectedType:
|
||||
n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info)
|
||||
changeType(c, n[^1], expectedType, true) # infer types for default fields value
|
||||
# bug #22926; be cautious that it uses `semConstExpr` to
|
||||
# evaulate the default fields; it's only natural to use
|
||||
# `changeType` to infer types for constant values
|
||||
# that's also the reason why we don't use `semExpr` to check
|
||||
# the type since two overlapping error messages might be produced
|
||||
result = n[^1].typ
|
||||
else:
|
||||
result = n[^1].typ
|
||||
# xxx any troubles related to defaults fields, consult `semConst` for a potential answer
|
||||
if n[^1].kind != nkNilLit:
|
||||
typeAllowedCheck(c, n.info, result, skConst, {taProcContextIsNotMacro, taIsDefaultField})
|
||||
dec c.inStaticContext
|
||||
|
||||
proc isRecursiveType*(t: PType): bool =
|
||||
# handle simple recusive types before typeFinalPass
|
||||
@@ -335,6 +347,14 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||
localError(c.config, n.info, errXExpectsOneTypeParam % "range")
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
|
||||
proc semArrayIndexConst(c: PContext, e: PNode, info: TLineInfo): PType =
|
||||
let x = semConstExpr(c, e)
|
||||
if x.kind in {nkIntLit..nkUInt64Lit}:
|
||||
result = makeRangeType(c, 0, x.intVal-1, info,
|
||||
x.typ.skipTypes({tyTypeDesc}))
|
||||
else:
|
||||
result = x.typ.skipTypes({tyTypeDesc})
|
||||
|
||||
proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
if isRange(n):
|
||||
result = semRangeAux(c, n, nil)
|
||||
@@ -351,14 +371,18 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
localError(c.config, n.info,
|
||||
"Array length can't be negative, but was " & $e.intVal)
|
||||
result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
||||
elif e.kind == nkSym and e.typ.kind == tyStatic:
|
||||
if e.sym.ast != nil:
|
||||
return semArrayIndex(c, e.sym.ast)
|
||||
if e.typ.lastSon.kind != tyGenericParam and not isOrdinalType(e.typ.lastSon):
|
||||
let info = if n.safeLen > 1: n[1].info else: n.info
|
||||
localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
|
||||
result = makeRangeWithStaticExpr(c, e)
|
||||
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
||||
elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc) :
|
||||
if e.typ.kind == tyStatic:
|
||||
if e.sym.ast != nil:
|
||||
return semArrayIndex(c, e.sym.ast)
|
||||
if e.typ.lastSon.kind != tyGenericParam and not isOrdinalType(e.typ.lastSon):
|
||||
let info = if n.safeLen > 1: n[1].info else: n.info
|
||||
localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
|
||||
result = makeRangeWithStaticExpr(c, e)
|
||||
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
||||
else:
|
||||
result = e.typ.skipTypes({tyTypeDesc})
|
||||
result.flags.incl tfImplicitStatic
|
||||
elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
|
||||
if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})):
|
||||
localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
|
||||
@@ -371,12 +395,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
elif e.kind == nkIdent:
|
||||
result = e.typ.skipTypes({tyTypeDesc})
|
||||
else:
|
||||
let x = semConstExpr(c, e)
|
||||
if x.kind in {nkIntLit..nkUInt64Lit}:
|
||||
result = makeRangeType(c, 0, x.intVal-1, n.info,
|
||||
x.typ.skipTypes({tyTypeDesc}))
|
||||
else:
|
||||
result = x.typ.skipTypes({tyTypeDesc})
|
||||
result = semArrayIndexConst(c, e, n.info)
|
||||
#localError(c.config, n[1].info, errConstExprExpected)
|
||||
|
||||
proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||
@@ -388,13 +407,16 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||
if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB)
|
||||
if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr} and
|
||||
tfUnresolved notin indxB.flags:
|
||||
if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}:
|
||||
discard
|
||||
elif not isOrdinalType(indxB):
|
||||
if not isOrdinalType(indxB):
|
||||
localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(indxB, preferDesc))
|
||||
elif enumHasHoles(indxB):
|
||||
localError(c.config, n[1].info, "enum '$1' has holes" %
|
||||
typeToString(indxB.skipTypes({tyRange})))
|
||||
elif indxB.kind != tyRange and
|
||||
lengthOrd(c.config, indxB) > high(uint16).int:
|
||||
# assume range type is intentional
|
||||
localError(c.config, n[1].info,
|
||||
"index type '$1' for array is too large" % typeToString(indxB))
|
||||
base = semTypeNode(c, n[2], nil)
|
||||
# ensure we only construct a tyArray when there was no error (bug #3048):
|
||||
result = newOrPrevType(tyArray, prev, c)
|
||||
@@ -499,7 +521,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||
result = newSymG(kind, n, c)
|
||||
|
||||
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
||||
allowed: TSymFlags): PSym =
|
||||
allowed: TSymFlags, fromTopLevel = false): PSym =
|
||||
if n.kind == nkPragmaExpr:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = semIdentVis(c, kind, n[0], allowed)
|
||||
@@ -514,6 +536,16 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
||||
else: discard
|
||||
else:
|
||||
result = semIdentVis(c, kind, n, allowed)
|
||||
let invalidPragmasForPush = if fromTopLevel and sfWasGenSym notin result.flags:
|
||||
{}
|
||||
else:
|
||||
{wExportc, wExportCpp, wDynlib}
|
||||
case kind
|
||||
of skField: implicitPragmas(c, result, n.info, fieldPragmas)
|
||||
of skVar: implicitPragmas(c, result, n.info, varPragmas-invalidPragmasForPush)
|
||||
of skLet: implicitPragmas(c, result, n.info, letPragmas-invalidPragmasForPush)
|
||||
of skConst: implicitPragmas(c, result, n.info, constPragmas-invalidPragmasForPush)
|
||||
else: discard
|
||||
|
||||
proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
|
||||
let ex = t[branchIndex][currentEx].skipConv
|
||||
@@ -1219,7 +1251,7 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
|
||||
result = semTypeNode(c, n[0], nil)
|
||||
constraint = semNodeKindConstraints(n, c.config, 1)
|
||||
elif n.kind == nkCall and
|
||||
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and
|
||||
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and
|
||||
considerQuotedIdent(c, n[0]).s == "{}":
|
||||
result = semTypeNode(c, n[1], nil)
|
||||
constraint = semNodeKindConstraints(n, c.config, 2)
|
||||
@@ -1297,7 +1329,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
def.typ = makeTypeFromExpr(c, def.copyTree)
|
||||
break determineType
|
||||
|
||||
def = semExprWithType(c, def, {efDetermineType}, defTyp)
|
||||
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, defTyp)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
|
||||
@@ -1394,7 +1426,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
"' is only valid for macros and templates")
|
||||
# 'auto' as a return type does not imply a generic:
|
||||
elif r.kind == tyAnything:
|
||||
discard
|
||||
r = copyType(r, nextTypeId c.idgen, r.owner)
|
||||
r.flags.incl tfRetType
|
||||
elif r.kind == tyStatic:
|
||||
# type allowed should forbid this type
|
||||
discard
|
||||
@@ -1483,6 +1516,14 @@ proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): b
|
||||
var newf = newNodeI(nkRecList, n.info)
|
||||
semRecordNodeAux(c, t.n, check, pos, newf, t)
|
||||
|
||||
proc containsGenericInvocationWithForward(n: PNode): bool =
|
||||
if n.kind == nkSym and n.sym.ast != nil and n.sym.ast.len > 1 and n.sym.ast[2].kind == nkObjectTy:
|
||||
for p in n.sym.ast[2][^1]:
|
||||
if p.kind == nkIdentDefs and p[1].typ != nil and p[1].typ.kind == tyGenericInvocation and
|
||||
p[1][0].kind == nkSym and p[1][0].typ.kind == tyForward:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
if s.typ == nil:
|
||||
localError(c.config, n.info, "cannot instantiate the '$1' $2" %
|
||||
@@ -1492,20 +1533,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
var t = s.typ.skipTypes({tyAlias})
|
||||
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
|
||||
t = t.base
|
||||
|
||||
result = newOrPrevType(tyGenericInvocation, prev, c)
|
||||
addSonSkipIntLit(result, t, c.idgen)
|
||||
|
||||
template addToResult(typ) =
|
||||
template addToResult(typ, skip) =
|
||||
|
||||
if typ.isNil:
|
||||
internalAssert c.config, false
|
||||
rawAddSon(result, typ)
|
||||
else: addSonSkipIntLit(result, typ, c.idgen)
|
||||
else:
|
||||
if skip:
|
||||
addSonSkipIntLit(result, typ, c.idgen)
|
||||
else:
|
||||
rawAddSon(result, makeRangeWithStaticExpr(c, typ.n))
|
||||
|
||||
if t.kind == tyForward:
|
||||
for i in 1..<n.len:
|
||||
var elem = semGenericParamInInvocation(c, n[i])
|
||||
addToResult(elem)
|
||||
addToResult(elem, true)
|
||||
return
|
||||
elif t.kind != tyGenericBody:
|
||||
# we likely got code of the form TypeA[TypeB] where TypeA is
|
||||
@@ -1525,7 +1570,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
|
||||
var isConcrete = true
|
||||
|
||||
let rType = m.call[0].typ
|
||||
let mIndex = if rType != nil: rType.len - 1 else: -1
|
||||
for i in 1..<m.call.len:
|
||||
var typ = m.call[i].typ
|
||||
# is this a 'typedesc' *parameter*? If so, use the typedesc type,
|
||||
@@ -1533,17 +1579,22 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
if m.call[i].kind == nkSym and m.call[i].sym.kind == skParam and
|
||||
typ.kind == tyTypeDesc and containsGenericType(typ):
|
||||
isConcrete = false
|
||||
addToResult(typ)
|
||||
addToResult(typ, true)
|
||||
else:
|
||||
typ = typ.skipTypes({tyTypeDesc})
|
||||
if containsGenericType(typ): isConcrete = false
|
||||
addToResult(typ)
|
||||
var skip = true
|
||||
if mIndex >= i - 1 and tfImplicitStatic in rType[i - 1].flags and isIntLit(typ):
|
||||
skip = false
|
||||
addToResult(typ, skip)
|
||||
|
||||
if isConcrete:
|
||||
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
|
||||
# XXX: What kind of error is this? is it still relevant?
|
||||
localError(c.config, n.info, errCannotInstantiateX % s.name.s)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
elif containsGenericInvocationWithForward(n[0]):
|
||||
c.skipTypes.add n #fixes 1500
|
||||
else:
|
||||
result = instGenericContainer(c, n.info, result,
|
||||
allowMetaTypes = false)
|
||||
@@ -1562,7 +1613,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
recomputeFieldPositions(tx, tx.n, position)
|
||||
|
||||
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
||||
if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody} and prev != nil:
|
||||
if prev != nil and (prev.kind == tyGenericBody or
|
||||
typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}):
|
||||
result = newTypeS(tyAlias, c)
|
||||
result.rawAddSon typeExpr
|
||||
result.sym = prev.sym
|
||||
@@ -1755,6 +1807,8 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
|
||||
openScope(c)
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let t = semExprWithType(c, n, {efInTypeof})
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
@@ -1769,6 +1823,8 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
|
||||
else:
|
||||
m = mode.intVal
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
@@ -1817,7 +1873,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
return errorSym(c, n)
|
||||
if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}:
|
||||
# this implements the wanted ``var v: V, x: V`` feature ...
|
||||
var ov: TOverloadIter
|
||||
var ov: TOverloadIter = default(TOverloadIter)
|
||||
var amb = initOverloadIter(ov, c, n)
|
||||
while amb != nil and amb.kind != skType:
|
||||
amb = nextOverloadIter(ov, c, n)
|
||||
@@ -1860,11 +1916,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of nkTupleConstr: result = semAnonTuple(c, n, prev)
|
||||
of nkCallKinds:
|
||||
let x = n[0]
|
||||
let ident = case x.kind
|
||||
of nkIdent: x.ident
|
||||
of nkSym: x.sym.name
|
||||
of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name
|
||||
else: nil
|
||||
let ident = x.getPIdent
|
||||
if ident != nil and ident.s == "[]":
|
||||
let b = newNodeI(nkBracketExpr, n.info)
|
||||
for i in 1..<n.len: b.add(n[i])
|
||||
@@ -1954,12 +2006,15 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
elif op.id == ord(wType):
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = semTypeOf(c, n[1], prev)
|
||||
elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf:
|
||||
elif op.s == "typeof" and (
|
||||
(n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or
|
||||
(n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)):
|
||||
result = semTypeOf2(c, n, prev)
|
||||
elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
|
||||
result = semTypeExpr(c, n[1], prev)
|
||||
else:
|
||||
if c.inGenericContext > 0 and n.kind == nkCall:
|
||||
let n = semGenericStmt(c, n)
|
||||
result = makeTypeFromExpr(c, n.copyTree)
|
||||
else:
|
||||
result = semTypeExpr(c, n, prev)
|
||||
@@ -2040,7 +2095,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if s.kind != skError: localError(c.config, n.info, errTypeExpected)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
|
||||
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
|
||||
internalAssert c.config, s.typ.base.kind != tyNone
|
||||
result = s.typ.base
|
||||
elif prev == nil:
|
||||
result = s.typ
|
||||
@@ -2064,7 +2119,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if s.kind == skType:
|
||||
s.typ
|
||||
else:
|
||||
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
|
||||
internalAssert c.config, s.typ.base.kind != tyNone
|
||||
s.typ.base
|
||||
let alias = maybeAliasType(c, t, prev)
|
||||
if alias != nil:
|
||||
@@ -2113,12 +2168,18 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
else:
|
||||
let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
|
||||
result = semProcTypeWithScope(c, n, prev, symKind)
|
||||
if result == nil:
|
||||
localError(c.config, n.info, "type expected, but got: " & renderTree(n))
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
if n.kind == nkIteratorTy and result.kind == tyProc:
|
||||
result.flags.incl(tfIterator)
|
||||
if result.callConv == ccClosure and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
result.flags.incl tfHasAsgn
|
||||
of nkEnumTy: result = semEnum(c, n, prev)
|
||||
of nkType: result = n.typ
|
||||
of nkStmtListType: result = semStmtListType(c, n, prev)
|
||||
of nkBlockType: result = semBlockType(c, n, prev)
|
||||
of nkOpenSym: result = semTypeNode(c, n[0], prev)
|
||||
else:
|
||||
result = semTypeExpr(c, n, prev)
|
||||
when false:
|
||||
|
||||
@@ -46,7 +46,8 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
|
||||
# XXX: This happens for prematurely cached
|
||||
# types such as Channel[empty]. Why?
|
||||
# See the notes for PActor in handleGenericInvocation
|
||||
return
|
||||
# if this is return the same type gets cached more than it needs to
|
||||
continue
|
||||
if not sameFlags(inst, key):
|
||||
continue
|
||||
|
||||
@@ -87,8 +88,8 @@ type
|
||||
recursionLimit: int
|
||||
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
|
||||
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode
|
||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym
|
||||
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode
|
||||
|
||||
proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable =
|
||||
result = LayeredIdTable()
|
||||
@@ -128,11 +129,20 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
else: t.n
|
||||
result = copyNode(n)
|
||||
result.typ = t
|
||||
if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym)
|
||||
if result.kind == nkSym:
|
||||
result.sym =
|
||||
if n.typ != nil and n.typ == n.sym.typ:
|
||||
replaceTypeVarsS(cl, n.sym, result.typ)
|
||||
else:
|
||||
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
|
||||
let isCall = result.kind in nkCallKinds
|
||||
# don't try to instantiate symchoice symbols, they can be
|
||||
# generic procs which the compiler will think are uninstantiated
|
||||
# because their type will contain uninstantiated params
|
||||
let isSymChoice = result.kind in nkSymChoices
|
||||
for i in 0..<n.safeLen:
|
||||
# XXX HACK: ``f(a, b)``, avoid to instantiate `f`
|
||||
if isCall and i == 0: result.add(n[i])
|
||||
if isSymChoice or (isCall and i == 0): result.add(n[i])
|
||||
else: result.add(prepareNode(cl, n[i]))
|
||||
|
||||
proc isTypeParam(n: PNode): bool =
|
||||
@@ -204,7 +214,7 @@ proc hasValuelessStatics(n: PNode): bool =
|
||||
a
|
||||
proc doThing(_: MyThing)
|
||||
]#
|
||||
if n.safeLen == 0:
|
||||
if n.safeLen == 0 and n.kind != nkEmpty: # Some empty nodes can get in here
|
||||
n.typ == nil or n.typ.kind == tyStatic
|
||||
else:
|
||||
for x in n:
|
||||
@@ -212,7 +222,7 @@ proc hasValuelessStatics(n: PNode): bool =
|
||||
return true
|
||||
false
|
||||
|
||||
proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
|
||||
proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode =
|
||||
if n == nil: return
|
||||
result = copyNode(n)
|
||||
if n.typ != nil:
|
||||
@@ -223,7 +233,11 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
|
||||
discard
|
||||
of nkOpenSymChoice, nkClosedSymChoice: result = n
|
||||
of nkSym:
|
||||
result.sym = replaceTypeVarsS(cl, n.sym)
|
||||
result.sym =
|
||||
if n.typ != nil and n.typ == n.sym.typ:
|
||||
replaceTypeVarsS(cl, n.sym, result.typ)
|
||||
else:
|
||||
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
|
||||
if result.sym.typ.kind == tyVoid:
|
||||
# don't add the 'void' field
|
||||
result = newNodeI(nkRecList, n.info)
|
||||
@@ -254,8 +268,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
|
||||
when false:
|
||||
n = reResolveCallsWithTypedescParams(cl, n)
|
||||
result = if cl.allowMetaTypes: n
|
||||
else: cl.c.semExpr(cl.c, n)
|
||||
if not cl.allowMetaTypes:
|
||||
else: cl.c.semExpr(cl.c, n, {}, expectedType)
|
||||
if not cl.allowMetaTypes and expectedType != nil:
|
||||
assert result.kind notin nkCallKinds
|
||||
else:
|
||||
if n.len > 0:
|
||||
@@ -265,7 +279,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
|
||||
for i in start..<n.len:
|
||||
result[i] = replaceTypeVarsN(cl, n[i])
|
||||
|
||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
|
||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
|
||||
if s == nil: return nil
|
||||
# symbol is not our business:
|
||||
if cl.owner != nil and s.owner != cl.owner:
|
||||
@@ -306,11 +320,14 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
|
||||
incl(result.flags, sfFromGeneric)
|
||||
#idTablePut(cl.symMap, s, result)
|
||||
result.owner = s.owner
|
||||
result.typ = replaceTypeVarsT(cl, s.typ)
|
||||
result.typ = t
|
||||
if result.kind != skType:
|
||||
result.ast = replaceTypeVarsN(cl, s.ast)
|
||||
|
||||
proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
|
||||
if tfRetType in t.flags and t.kind == tyAnything:
|
||||
# don't bind `auto` return type to a previous binding of `auto`
|
||||
return nil
|
||||
result = cl.typeMap.lookup(t)
|
||||
if result == nil:
|
||||
if cl.allowMetaTypes or tfRetType in t.flags: return
|
||||
@@ -548,7 +565,9 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
result = t
|
||||
if t == nil: return
|
||||
|
||||
if t.kind in {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses:
|
||||
const lookupMetas = {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses - {tyAnything}
|
||||
if t.kind in lookupMetas or
|
||||
(t.kind == tyAnything and tfRetType notin t.flags):
|
||||
let lookup = cl.typeMap.lookup(t)
|
||||
if lookup != nil: return lookup
|
||||
|
||||
@@ -691,12 +710,13 @@ proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo;
|
||||
result.owner = owner
|
||||
|
||||
proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
|
||||
owner: PSym, allowMetaTypes = false): PNode =
|
||||
owner: PSym, allowMetaTypes = false,
|
||||
fromStaticExpr = false, expectedType: PType = nil): PNode =
|
||||
var typeMap = initLayeredTypeMap(pt)
|
||||
var cl = initTypeVars(p, typeMap, n.info, owner)
|
||||
cl.allowMetaTypes = allowMetaTypes
|
||||
pushInfoContext(p.config, n.info)
|
||||
result = replaceTypeVarsN(cl, n)
|
||||
result = replaceTypeVarsN(cl, n, expectedType = expectedType)
|
||||
popInfoContext(p.config)
|
||||
|
||||
when false:
|
||||
|
||||
@@ -260,7 +260,8 @@ when defined(debugSigHashes):
|
||||
# (select hash from sighashes group by hash having count(*) > 1) order by hash;
|
||||
|
||||
proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): SigHash =
|
||||
var c: MD5Context
|
||||
result = default(SigHash)
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init c
|
||||
hashType c, t, flags+{CoOwnerSig}, conf
|
||||
md5Final c, result.MD5Digest
|
||||
@@ -269,7 +270,8 @@ proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}):
|
||||
typeToString(t), $result)
|
||||
|
||||
proc hashProc(s: PSym; conf: ConfigRef): SigHash =
|
||||
var c: MD5Context
|
||||
result = default(SigHash)
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init c
|
||||
hashType c, s.typ, {CoProc}, conf
|
||||
|
||||
@@ -289,7 +291,8 @@ proc hashProc(s: PSym; conf: ConfigRef): SigHash =
|
||||
md5Final c, result.MD5Digest
|
||||
|
||||
proc hashNonProc*(s: PSym): SigHash =
|
||||
var c: MD5Context
|
||||
result = default(SigHash)
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init c
|
||||
hashSym(c, s)
|
||||
var it = s
|
||||
@@ -305,7 +308,8 @@ proc hashNonProc*(s: PSym): SigHash =
|
||||
md5Final c, result.MD5Digest
|
||||
|
||||
proc hashOwner*(s: PSym): SigHash =
|
||||
var c: MD5Context
|
||||
result = default(SigHash)
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init c
|
||||
var m = s
|
||||
while m.kind != skModule: m = m.owner
|
||||
@@ -378,7 +382,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
|
||||
graph.symBodyHashes.withValue(sym.id, value):
|
||||
return value[]
|
||||
|
||||
var c: MD5Context
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init(c)
|
||||
c.hashType(sym.typ, {CoProc}, graph.config)
|
||||
c &= char(sym.kind)
|
||||
|
||||
@@ -78,7 +78,8 @@ type
|
||||
# or when the explain pragma is used. may be
|
||||
# triggered with an idetools command in the
|
||||
# future.
|
||||
inheritancePenalty: int # to prefer closest father object type
|
||||
# to prefer closest father object type
|
||||
inheritancePenalty: int
|
||||
firstMismatch*: MismatchInfo # mismatch info for better error messages
|
||||
diagnosticsEnabled*: bool
|
||||
|
||||
@@ -93,8 +94,9 @@ type
|
||||
|
||||
const
|
||||
isNilConversion = isConvertible # maybe 'isIntConv' fits better?
|
||||
maxInheritancePenalty = high(int) div 2
|
||||
|
||||
proc markUsed*(c: PContext; info: TLineInfo, s: PSym)
|
||||
proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true)
|
||||
proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
|
||||
|
||||
template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
|
||||
@@ -113,7 +115,7 @@ proc initCandidateAux(ctx: PContext,
|
||||
c.call = nil
|
||||
c.baseTypeMatch = false
|
||||
c.genericConverter = false
|
||||
c.inheritancePenalty = 0
|
||||
c.inheritancePenalty = -1
|
||||
|
||||
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
|
||||
initCandidateAux(ctx, c, callee)
|
||||
@@ -140,15 +142,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
|
||||
initCandidateAux(ctx, c, callee.typ)
|
||||
c.calleeSym = callee
|
||||
if callee.kind in skProcKinds and calleeScope == -1:
|
||||
if callee.originatingModule == ctx.module:
|
||||
c.calleeScope = 2
|
||||
var owner = callee
|
||||
while true:
|
||||
owner = owner.skipGenericOwner
|
||||
if owner.kind == skModule: break
|
||||
inc c.calleeScope
|
||||
else:
|
||||
c.calleeScope = 1
|
||||
c.calleeScope = cmpScopes(ctx, callee)
|
||||
else:
|
||||
c.calleeScope = calleeScope
|
||||
c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
|
||||
@@ -175,19 +169,19 @@ proc newCandidate*(ctx: PContext, callee: PSym,
|
||||
proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
|
||||
initCandidate(ctx, result, callee)
|
||||
|
||||
proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
||||
a.c = b.c
|
||||
a.exactMatches = b.exactMatches
|
||||
a.subtypeMatches = b.subtypeMatches
|
||||
a.convMatches = b.convMatches
|
||||
a.intConvMatches = b.intConvMatches
|
||||
a.genericMatches = b.genericMatches
|
||||
a.state = b.state
|
||||
a.callee = b.callee
|
||||
a.calleeSym = b.calleeSym
|
||||
a.call = copyTree(b.call)
|
||||
a.baseTypeMatch = b.baseTypeMatch
|
||||
copyIdTable(a.bindings, b.bindings)
|
||||
proc copyCandidate(dest: var TCandidate, src: TCandidate) =
|
||||
dest.c = src.c
|
||||
dest.exactMatches = src.exactMatches
|
||||
dest.subtypeMatches = src.subtypeMatches
|
||||
dest.convMatches = src.convMatches
|
||||
dest.intConvMatches = src.intConvMatches
|
||||
dest.genericMatches = src.genericMatches
|
||||
dest.state = src.state
|
||||
dest.callee = src.callee
|
||||
dest.calleeSym = src.calleeSym
|
||||
dest.call = copyTree(src.call)
|
||||
dest.baseTypeMatch = src.baseTypeMatch
|
||||
copyIdTable(dest.bindings, src.bindings)
|
||||
|
||||
proc typeRel*(c: var TCandidate, f, aOrig: PType,
|
||||
flags: TTypeRelFlags = {}): TTypeRelation
|
||||
@@ -202,10 +196,10 @@ proc checkGeneric(a, b: TCandidate): int =
|
||||
let tra = typeRel(ma, bb[i], aa[i], {trDontBind})
|
||||
var mb = newCandidate(c, aa[i])
|
||||
let trb = typeRel(mb, aa[i], bb[i], {trDontBind})
|
||||
if tra == isGeneric and trb == isNone:
|
||||
if tra == isGeneric and trb in {isNone, isInferred, isInferredConvertible}:
|
||||
if winner == -1: return 0
|
||||
winner = 1
|
||||
if trb == isGeneric and tra == isNone:
|
||||
if trb == isGeneric and tra in {isNone, isInferred, isInferredConvertible}:
|
||||
if winner == 1: return 0
|
||||
winner = -1
|
||||
result = winner
|
||||
@@ -215,14 +209,24 @@ proc sumGeneric(t: PType): int =
|
||||
# and Foo[T] has the value 2 so that we know Foo[Foo[T]] is more
|
||||
# specific than Foo[T].
|
||||
var t = t
|
||||
var isvar = 0
|
||||
while true:
|
||||
case t.kind
|
||||
of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray,
|
||||
tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody,
|
||||
tyLent, tyOwned:
|
||||
of tyAlias, tySink, tyNot: t = t.lastSon
|
||||
of tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray,
|
||||
tyOpenArray, tyVarargs, tySet, tyRange, tySequence,
|
||||
tyLent, tyOwned, tyVar:
|
||||
t = t.lastSon
|
||||
inc result
|
||||
of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyVoid,
|
||||
tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||
tyUInt..tyUInt64, tyCompositeTypeClass, tyBuiltInTypeClass:
|
||||
inc result
|
||||
break
|
||||
of tyGenericBody:
|
||||
t = t.typeBodyImpl
|
||||
of tyGenericInst, tyStatic:
|
||||
t = t[0]
|
||||
inc result
|
||||
of tyOr:
|
||||
var maxBranch = 0
|
||||
for branch in t.sons:
|
||||
@@ -230,32 +234,25 @@ proc sumGeneric(t: PType): int =
|
||||
if branchSum > maxBranch: maxBranch = branchSum
|
||||
inc result, maxBranch
|
||||
break
|
||||
of tyVar:
|
||||
t = t[0]
|
||||
inc result
|
||||
inc isvar
|
||||
of tyTypeDesc:
|
||||
t = t.lastSon
|
||||
if t.kind == tyEmpty: break
|
||||
inc result
|
||||
of tyGenericParam:
|
||||
if t.len > 0:
|
||||
t = t.skipModifier
|
||||
else:
|
||||
inc result
|
||||
break
|
||||
of tyUntyped, tyTyped: break
|
||||
of tyGenericInvocation, tyTuple, tyProc, tyAnd:
|
||||
result += ord(t.kind in {tyGenericInvocation, tyAnd})
|
||||
result += ord(t.kind == tyAnd)
|
||||
for i in 0..<t.len:
|
||||
if t[i] != nil:
|
||||
result += sumGeneric(t[i])
|
||||
break
|
||||
of tyStatic:
|
||||
return sumGeneric(t[0]) + 1
|
||||
of tyGenericParam, tyUntyped, tyTyped: break
|
||||
of tyAlias, tySink: t = t.lastSon
|
||||
of tyBool, tyChar, tyEnum, tyObject, tyPointer,
|
||||
tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||
tyUInt..tyUInt64, tyCompositeTypeClass:
|
||||
return isvar + 1
|
||||
of tyBuiltInTypeClass:
|
||||
return isvar
|
||||
else:
|
||||
return 0
|
||||
break
|
||||
|
||||
proc complexDisambiguation(a, b: PType): int =
|
||||
# 'a' matches better if *every* argument matches better or equal than 'b'.
|
||||
@@ -290,7 +287,16 @@ proc writeMatches*(c: TCandidate) =
|
||||
echo " conv matches: ", c.convMatches
|
||||
echo " inheritance: ", c.inheritancePenalty
|
||||
|
||||
proc cmpCandidates*(a, b: TCandidate): int =
|
||||
proc cmpInheritancePenalty(a, b: int): int =
|
||||
var eb = b
|
||||
var ea = a
|
||||
if b < 0:
|
||||
eb = maxInheritancePenalty # defacto max penalty
|
||||
if a < 0:
|
||||
ea = maxInheritancePenalty
|
||||
eb - ea
|
||||
|
||||
proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
|
||||
result = a.exactMatches - b.exactMatches
|
||||
if result != 0: return
|
||||
result = a.genericMatches - b.genericMatches
|
||||
@@ -301,16 +307,16 @@ proc cmpCandidates*(a, b: TCandidate): int =
|
||||
if result != 0: return
|
||||
result = a.convMatches - b.convMatches
|
||||
if result != 0: return
|
||||
# the other way round because of other semantics:
|
||||
result = b.inheritancePenalty - a.inheritancePenalty
|
||||
result = cmpInheritancePenalty(a.inheritancePenalty, b.inheritancePenalty)
|
||||
if result != 0: return
|
||||
# check for generic subclass relation
|
||||
result = checkGeneric(a, b)
|
||||
if isFormal:
|
||||
# check for generic subclass relation
|
||||
result = checkGeneric(a, b)
|
||||
if result != 0: return
|
||||
# prefer more specialized generic over more general generic:
|
||||
result = complexDisambiguation(a.callee, b.callee)
|
||||
if result != 0: return
|
||||
# prefer more specialized generic over more general generic:
|
||||
result = complexDisambiguation(a.callee, b.callee)
|
||||
# only as a last resort, consider scoping:
|
||||
if result != 0: return
|
||||
result = a.calleeScope - b.calleeScope
|
||||
|
||||
proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
|
||||
@@ -330,16 +336,27 @@ template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = p
|
||||
result.add renderTree(n[i][0])
|
||||
result.add ": "
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
|
||||
# XXX we really need to 'tryExpr' here!
|
||||
arg = c.semOperand(c, n[i][1])
|
||||
n[i].typ = arg.typ
|
||||
n[i][1] = arg
|
||||
arg = c.semTryExpr(c, n[i][1])
|
||||
if arg == nil:
|
||||
arg = n[i][1]
|
||||
arg.typ = newTypeS(tyUntyped, c)
|
||||
else:
|
||||
if arg.typ == nil:
|
||||
arg.typ = newTypeS(tyVoid, c)
|
||||
n[i].typ = arg.typ
|
||||
n[i][1] = arg
|
||||
else:
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
|
||||
nkOfBranch, nkElifBranch,
|
||||
nkExceptBranch}:
|
||||
arg = c.semOperand(c, n[i])
|
||||
n[i] = arg
|
||||
arg = c.semTryExpr(c, n[i])
|
||||
if arg == nil:
|
||||
arg = n[i]
|
||||
arg.typ = newTypeS(tyUntyped, c)
|
||||
else:
|
||||
if arg.typ == nil:
|
||||
arg.typ = newTypeS(tyVoid, c)
|
||||
n[i] = arg
|
||||
if arg.typ != nil and arg.typ.kind == tyError: return
|
||||
result.add argTypeToString(arg, prefer)
|
||||
|
||||
@@ -456,6 +473,45 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
|
||||
else: result = isIntConv
|
||||
else: result = isNone
|
||||
|
||||
proc reduceToBase(f: PType): PType =
|
||||
#[
|
||||
Returns the lowest order (most general) type that that is compatible with the input.
|
||||
E.g.
|
||||
A[T] = ptr object ... A -> ptr object
|
||||
A[N: static[int]] = array[N, int] ... A -> array
|
||||
]#
|
||||
case f.kind:
|
||||
of tyGenericParam:
|
||||
if f.len <= 0 or f.skipModifier == nil:
|
||||
result = f
|
||||
else:
|
||||
result = reduceToBase(f.skipModifier)
|
||||
of tyGenericInvocation:
|
||||
result = reduceToBase(f.baseClass)
|
||||
of tyCompositeTypeClass, tyAlias:
|
||||
if f.len <= 0 or f[0] == nil:
|
||||
result = f
|
||||
else:
|
||||
result = reduceToBase(f.elementType)
|
||||
of tyGenericInst:
|
||||
result = reduceToBase(f.skipModifier)
|
||||
of tyGenericBody:
|
||||
result = reduceToBase(f.typeBodyImpl)
|
||||
of tyUserTypeClass:
|
||||
if f.isResolvedUserTypeClass:
|
||||
result = f.base # ?? idk if this is right
|
||||
else:
|
||||
result = f.lastSon
|
||||
of tyStatic, tyOwned, tyVar, tyLent, tySink:
|
||||
result = reduceToBase(f.base)
|
||||
of tyInferred:
|
||||
# This is not true "After a candidate type is selected"
|
||||
result = reduceToBase(f.base)
|
||||
of tyRange:
|
||||
result = f.lastSon
|
||||
else:
|
||||
result = f
|
||||
|
||||
proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
|
||||
if fGenericOrigin != nil and last.kind == tyGenericInst and
|
||||
last.len-1 == fGenericOrigin.len:
|
||||
@@ -470,7 +526,8 @@ proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
|
||||
var depth = 0
|
||||
var last = a
|
||||
while t != nil and not sameObjectTypes(f, t):
|
||||
assert t.kind == tyObject
|
||||
if t.kind != tyObject: # avoid entering generic params etc
|
||||
return -1
|
||||
t = t[0]
|
||||
if t == nil: break
|
||||
last = t
|
||||
@@ -533,7 +590,7 @@ proc minRel(a, b: TTypeRelation): TTypeRelation =
|
||||
if a <= b: result = a
|
||||
else: result = b
|
||||
|
||||
proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
proc recordRel(c: var TCandidate, f, a: PType, flags: TTypeRelFlags): TTypeRelation =
|
||||
result = isNone
|
||||
if sameType(f, a):
|
||||
result = isEqual
|
||||
@@ -542,8 +599,13 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
let firstField = if f.kind == tyTuple: 0
|
||||
else: 1
|
||||
for i in firstField..<f.len:
|
||||
var m = typeRel(c, f[i], a[i])
|
||||
var m = typeRel(c, f[i], a[i], flags)
|
||||
if m < isSubtype: return isNone
|
||||
if m == isSubtype and a[i].kind != tyNil and c.inheritancePenalty > -1:
|
||||
# we can't process individual element type conversions from a
|
||||
# type conversion for the whole tuple
|
||||
# subtype relations need type conversions when inheritance is used
|
||||
return isNone
|
||||
result = minRel(result, m)
|
||||
if f.n != nil and a.n != nil:
|
||||
for i in 0..<f.n.len:
|
||||
@@ -553,7 +615,7 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
else:
|
||||
var x = f.n[i].sym
|
||||
var y = a.n[i].sym
|
||||
if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype:
|
||||
if f.kind == tyObject and typeRel(c, x.typ, y.typ, flags) < isSubtype:
|
||||
return isNone
|
||||
if x.name.id != y.name.id: return isNone
|
||||
|
||||
@@ -810,7 +872,8 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
|
||||
result = t
|
||||
|
||||
proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
|
||||
allowUnresolved = false): PNode =
|
||||
allowUnresolved = false,
|
||||
expectedType: PType = nil): PNode =
|
||||
# Consider this example:
|
||||
# type Value[N: static[int]] = object
|
||||
# proc foo[N](a: Value[N], r: range[0..(N-1)])
|
||||
@@ -819,6 +882,8 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
|
||||
# This proc is used to evaluate such static expressions.
|
||||
let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil,
|
||||
allowMetaTypes = allowUnresolved)
|
||||
if instantiated.kind in nkCallKinds:
|
||||
return nil
|
||||
result = c.c.semExpr(c.c, instantiated)
|
||||
|
||||
proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
|
||||
@@ -932,9 +997,21 @@ proc inferStaticsInRange(c: var TCandidate,
|
||||
doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete))
|
||||
|
||||
template subtypeCheck() =
|
||||
if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {
|
||||
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
|
||||
case result
|
||||
of isIntConv:
|
||||
result = isNone
|
||||
of isSubrange:
|
||||
discard # XXX should be isNone with preview define, warnings
|
||||
of isConvertible:
|
||||
if f.lastSon.skipTypes(abstractInst).kind != tyOpenArray:
|
||||
# exclude var openarray which compiler supports
|
||||
result = isNone
|
||||
of isSubtype:
|
||||
if f.lastSon.skipTypes(abstractInst).kind in {
|
||||
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
|
||||
# compiler can't handle subtype conversions with pointer indirection
|
||||
result = isNone
|
||||
else: discard
|
||||
|
||||
proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
|
||||
# this proc is always called for a pair of matching types
|
||||
@@ -991,8 +1068,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
# of the designated type class.
|
||||
#
|
||||
# 3) When used with two type classes, it will check whether the types
|
||||
# matching the first type class are a strict subset of the types matching
|
||||
# the other. This allows us to compare the signatures of generic procs in
|
||||
# matching the first type class (aOrig) are a strict subset of the types matching
|
||||
# the other (f). This allows us to compare the signatures of generic procs in
|
||||
# order to give preferrence to the most specific one:
|
||||
#
|
||||
# seq[seq[any]] is a strict subset of seq[any] and hence more specific.
|
||||
@@ -1148,7 +1225,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
# being passed as parameters
|
||||
return isNone
|
||||
else: discard
|
||||
|
||||
case f.kind
|
||||
of tyEnum:
|
||||
if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual
|
||||
@@ -1165,9 +1241,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if result notin {isNone, isGeneric}:
|
||||
# resolve any late-bound static expressions
|
||||
# that may appear in the range:
|
||||
let expectedType = base(f)
|
||||
for i in 0..1:
|
||||
if f.n[i].kind == nkStaticExpr:
|
||||
f.n[i] = tryResolvingStaticExpr(c, f.n[i])
|
||||
let r = tryResolvingStaticExpr(c, f.n[i], expectedType = expectedType)
|
||||
if r != nil:
|
||||
f.n[i] = r
|
||||
result = typeRangeRel(f, a)
|
||||
else:
|
||||
let f = skipTypes(f, {tyRange})
|
||||
@@ -1203,13 +1282,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
|
||||
subtypeCheck()
|
||||
of tyArray:
|
||||
case a.kind
|
||||
of tyArray:
|
||||
a = reduceToBase(a)
|
||||
if a.kind == tyArray:
|
||||
var fRange = f[0]
|
||||
var aRange = a[0]
|
||||
if fRange.kind == tyGenericParam:
|
||||
if fRange.kind in {tyGenericParam, tyAnything}:
|
||||
var prev = PType(idTableGet(c.bindings, fRange))
|
||||
if prev == nil:
|
||||
if typeRel(c, fRange, aRange) == isNone:
|
||||
return isNone
|
||||
put(c, fRange, a[0])
|
||||
fRange = a
|
||||
else:
|
||||
@@ -1222,7 +1303,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, ff, aa, flags)
|
||||
|
||||
if result < isGeneric:
|
||||
if nimEnableCovariance and
|
||||
trNoCovariance notin flags and
|
||||
@@ -1233,13 +1313,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
return isNone
|
||||
|
||||
if fRange.rangeHasUnresolvedStatic:
|
||||
if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange:
|
||||
return
|
||||
return inferStaticsInRange(c, fRange, a)
|
||||
elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
|
||||
return inferStaticsInRange(c, aRange, f)
|
||||
elif result == isGeneric and concreteType(c, aa, ff) == nil:
|
||||
return isNone
|
||||
else:
|
||||
if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange):
|
||||
result = isNone
|
||||
else: discard
|
||||
of tyUncheckedArray:
|
||||
if a.kind == tyUncheckedArray:
|
||||
result = typeRel(c, base(f), base(a), flags)
|
||||
@@ -1321,16 +1404,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
skipOwned(a)
|
||||
if a.kind == f.kind: result = isEqual
|
||||
of tyTuple:
|
||||
if a.kind == tyTuple: result = recordRel(c, f, a)
|
||||
if a.kind == tyTuple: result = recordRel(c, f, a, flags)
|
||||
of tyObject:
|
||||
if a.kind == tyObject:
|
||||
if sameObjectTypes(f, a):
|
||||
let effectiveArgType = if useTypeLoweringRuleInTypeClass:
|
||||
a
|
||||
else:
|
||||
reduceToBase(a)
|
||||
if effectiveArgType.kind == tyObject:
|
||||
if sameObjectTypes(f, effectiveArgType):
|
||||
c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
|
||||
result = isEqual
|
||||
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
|
||||
elif trIsOutParam notin flags:
|
||||
var depth = isObjectSubtype(c, a, f, nil)
|
||||
if depth > 0:
|
||||
inc(c.inheritancePenalty, depth)
|
||||
c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil)
|
||||
if c.inheritancePenalty > 0:
|
||||
result = isSubtype
|
||||
of tyDistinct:
|
||||
a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
|
||||
@@ -1353,7 +1440,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
result = isNone
|
||||
|
||||
of tyPtr, tyRef:
|
||||
skipOwned(a)
|
||||
a = reduceToBase(a)
|
||||
if a.kind == f.kind:
|
||||
# ptr[R, T] can be passed to ptr[T], but not the other way round:
|
||||
if a.len < f.len: return isNone
|
||||
@@ -1447,8 +1534,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let origF = f
|
||||
var f = if prev == nil: f else: prev
|
||||
|
||||
let roota = a.skipGenericAlias
|
||||
let rootf = f.skipGenericAlias
|
||||
let deptha = a.genericAliasDepth()
|
||||
let depthf = f.genericAliasDepth()
|
||||
let skipBoth = deptha == depthf and (a.len > 0 and f.len > 0 and a.base != f.base)
|
||||
|
||||
let roota = if skipBoth or deptha > depthf: a.skipGenericAlias else: a
|
||||
let rootf = if skipBoth or depthf > deptha: f.skipGenericAlias else: f
|
||||
|
||||
if a.kind == tyGenericInst:
|
||||
if roota.base == rootf.base:
|
||||
@@ -1499,7 +1590,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if aAsObject.kind == tyObject and trIsOutParam notin flags:
|
||||
let baseType = aAsObject.base
|
||||
if baseType != nil:
|
||||
c.inheritancePenalty += 1
|
||||
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
|
||||
let ret = typeRel(c, f, baseType, flags)
|
||||
return if ret in {isEqual,isGeneric}: isSubtype else: ret
|
||||
|
||||
@@ -1520,6 +1611,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
|
||||
of tyGenericInvocation:
|
||||
var x = a.skipGenericAlias
|
||||
if x.kind == tyGenericParam and x.len > 0:
|
||||
x = x.lastSon
|
||||
let concpt = f[0].skipTypes({tyGenericBody})
|
||||
var preventHack = concpt.kind == tyConcept
|
||||
if x.kind == tyOwned and f[0].kind != tyOwned:
|
||||
@@ -1530,12 +1623,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
c.calleeSym != nil and
|
||||
c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack:
|
||||
let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
|
||||
#echo "inferred ", typeToString(inst), " for ", f
|
||||
return typeRel(c, inst, a, flags)
|
||||
|
||||
if x.kind == tyGenericInvocation:
|
||||
if f[0] == x[0]:
|
||||
for i in 1..<f.len:
|
||||
# Handle when checking against a generic that isn't fully instantiated
|
||||
if i >= x.len: return
|
||||
let tr = typeRel(c, f[i], x[i], flags)
|
||||
if tr <= isSubtype: return
|
||||
result = isGeneric
|
||||
@@ -1557,9 +1651,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
var fskip = skippedNone
|
||||
let aobj = x.skipToObject(askip)
|
||||
let fobj = genericBody.lastSon.skipToObject(fskip)
|
||||
var depth = -1
|
||||
if fobj != nil and aobj != nil and askip == fskip:
|
||||
depth = isObjectSubtype(c, aobj, fobj, f)
|
||||
result = typeRel(c, genericBody, x, flags)
|
||||
if result != isNone:
|
||||
# see tests/generics/tgeneric3.nim for an example that triggers this
|
||||
@@ -1585,7 +1676,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
put(c, key, x)
|
||||
elif typeRel(c, old, x, flags + {trDontBind}) == isNone:
|
||||
return isNone
|
||||
|
||||
var depth = -1
|
||||
if fobj != nil and aobj != nil and askip == fskip:
|
||||
depth = isObjectSubtype(c, aobj, fobj, f)
|
||||
|
||||
if result == isNone:
|
||||
# Here object inheriting from generic/specialized generic object
|
||||
# crossing path with metatypes/aliases, so we need to separate them
|
||||
@@ -1595,7 +1689,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
depth = -1
|
||||
|
||||
if depth >= 0:
|
||||
c.inheritancePenalty += depth
|
||||
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
|
||||
# bug #4863: We still need to bind generic alias crap, so
|
||||
# we cannot return immediately:
|
||||
result = if depth == 0: isGeneric else: isSubtype
|
||||
@@ -1614,20 +1708,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
considerPreviousT:
|
||||
result = isNone
|
||||
let oldInheritancePenalty = c.inheritancePenalty
|
||||
var maxInheritance = 0
|
||||
var minInheritance = maxInheritancePenalty
|
||||
for branch in f.sons:
|
||||
c.inheritancePenalty = 0
|
||||
c.inheritancePenalty = -1
|
||||
let x = typeRel(c, branch, aOrig, flags)
|
||||
maxInheritance = max(maxInheritance, c.inheritancePenalty)
|
||||
# 'or' implies maximum matching result:
|
||||
if x > result: result = x
|
||||
if x >= result:
|
||||
if c.inheritancePenalty > -1:
|
||||
minInheritance = min(minInheritance, c.inheritancePenalty)
|
||||
result = x
|
||||
if result >= isIntConv:
|
||||
if minInheritance < maxInheritancePenalty:
|
||||
c.inheritancePenalty = oldInheritancePenalty + minInheritance
|
||||
if result > isGeneric: result = isGeneric
|
||||
bindingRet result
|
||||
else:
|
||||
result = isNone
|
||||
c.inheritancePenalty = oldInheritancePenalty + maxInheritance
|
||||
|
||||
of tyNot:
|
||||
considerPreviousT:
|
||||
for branch in f.sons:
|
||||
@@ -1647,8 +1742,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
considerPreviousT:
|
||||
let target = f[0]
|
||||
let targetKind = target.kind
|
||||
let effectiveArgType = a.skipTypes({tyRange, tyGenericInst,
|
||||
tyBuiltInTypeClass, tyAlias, tySink, tyOwned})
|
||||
var effectiveArgType = reduceToBase(a)
|
||||
effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
|
||||
if targetKind == effectiveArgType.kind:
|
||||
if effectiveArgType.isEmptyContainer:
|
||||
return isNone
|
||||
@@ -1658,7 +1753,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if tfExplicitCallConv in target.flags and
|
||||
target.callConv != effectiveArgType.callConv:
|
||||
return isNone
|
||||
put(c, f, a)
|
||||
if doBind: put(c, f, a)
|
||||
return isGeneric
|
||||
else:
|
||||
return isNone
|
||||
@@ -1744,18 +1839,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
else:
|
||||
# check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)'
|
||||
if f.len > 0 and f[0].kind != tyNone:
|
||||
let oldInheritancePenalty = c.inheritancePenalty
|
||||
result = typeRel(c, f[0], a, flags + {trDontBind,trBindGenericParam})
|
||||
if doBindGP and result notin {isNone, isGeneric}:
|
||||
let concrete = concreteType(c, a, f)
|
||||
if concrete == nil: return isNone
|
||||
put(c, f, concrete)
|
||||
# bug #6526
|
||||
if result in {isEqual, isSubtype}:
|
||||
# 'T: Class' is a *better* match than just 'T'
|
||||
# but 'T: Subclass' is even better:
|
||||
c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty -
|
||||
100 * ord(result == isEqual)
|
||||
result = isGeneric
|
||||
elif a.kind == tyTypeDesc:
|
||||
# somewhat special typing rule, the following is illegal:
|
||||
@@ -1770,7 +1859,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if tfWildcard in a.flags:
|
||||
a.sym.transitionGenericParamToType()
|
||||
a.flags.excl tfWildcard
|
||||
else:
|
||||
elif doBind:
|
||||
# careful: `trDontDont` (set by `checkGeneric`) is not always respected in this call graph.
|
||||
# typRel having two different modes (binding and non-binding) can make things harder to
|
||||
# reason about and maintain. Refactoring typeRel to not be responsible for setting, or
|
||||
# at least validating, bindings can have multiple benefits. This is debatable. I'm not 100% sure.
|
||||
# A design that allows a proper complexity analysis of types like `tyOr` would be ideal.
|
||||
concrete = concreteType(c, a, f)
|
||||
if concrete == nil:
|
||||
return isNone
|
||||
@@ -1783,7 +1877,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
elif x.kind == tyGenericParam:
|
||||
result = isGeneric
|
||||
else:
|
||||
# This is the bound type - can't benifit from these tallies
|
||||
let
|
||||
inheritancePenaltyOld = c.inheritancePenalty
|
||||
result = typeRel(c, x, a, flags) # check if it fits
|
||||
c.inheritancePenalty = inheritancePenaltyOld
|
||||
if result > isGeneric: result = isGeneric
|
||||
of tyStatic:
|
||||
let prev = PType(idTableGet(c.bindings, f))
|
||||
@@ -1885,6 +1983,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
# fix the expression, so it contains the already instantiated types
|
||||
if f.n == nil or f.n.kind == nkEmpty: return isGeneric
|
||||
let reevaluated = tryResolvingStaticExpr(c, f.n)
|
||||
if reevaluated == nil:
|
||||
result = isNone
|
||||
return
|
||||
case reevaluated.typ.kind
|
||||
of tyTypeDesc:
|
||||
result = typeRel(c, a, reevaluated.typ.base, flags)
|
||||
@@ -1994,7 +2095,9 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
dest = generateTypeInstance(c, m.bindings, arg, dest)
|
||||
let fdest = typeRel(m, f, dest)
|
||||
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
|
||||
markUsed(c, arg.info, c.converters[i])
|
||||
# can't fully mark used yet, may not be used in final call
|
||||
incl(c.converters[i].flags, sfUsed)
|
||||
markOwnerModuleAsUsed(c, c.converters[i])
|
||||
var s = newSymNode(c.converters[i])
|
||||
s.typ = c.converters[i].typ
|
||||
s.info = arg.info
|
||||
@@ -2042,7 +2145,7 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
if result != nil:
|
||||
if result.typ == nil: return nil
|
||||
# bug #13378, ensure we produce a real generic instantiation:
|
||||
result = c.semExpr(c, call)
|
||||
result = c.semExpr(c, call, {efNoSem2Check})
|
||||
# resulting type must be consistent with the other arguments:
|
||||
var r = typeRel(m, f[0], result.typ)
|
||||
if r < isGeneric: return nil
|
||||
@@ -2069,6 +2172,7 @@ template matchesVoidProc(t: PType): bool =
|
||||
|
||||
proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
argSemantized, argOrig: PNode): PNode =
|
||||
result = nil
|
||||
var
|
||||
fMaybeStatic = f.skipTypes({tyDistinct})
|
||||
arg = argSemantized
|
||||
@@ -2132,28 +2236,33 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
else:
|
||||
return argSemantized # argOrig
|
||||
|
||||
# If r == isBothMetaConvertible then we rerun typeRel.
|
||||
# bothMetaCounter is for safety to avoid any infinite loop,
|
||||
# I don't have any example when it is needed.
|
||||
# lastBindingsLenth is used to check whether m.bindings remains the same,
|
||||
# because in that case there is no point in continuing.
|
||||
var bothMetaCounter = 0
|
||||
var lastBindingsLength = -1
|
||||
while r == isBothMetaConvertible and
|
||||
lastBindingsLength != m.bindings.counter and
|
||||
bothMetaCounter < 100:
|
||||
lastBindingsLength = m.bindings.counter
|
||||
inc(bothMetaCounter)
|
||||
if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
|
||||
result = c.semInferredLambda(c, m.bindings, arg)
|
||||
elif arg.kind != nkSym:
|
||||
return nil
|
||||
else:
|
||||
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
|
||||
result = newSymNode(inferred, arg.info)
|
||||
inc(m.convMatches)
|
||||
arg = result
|
||||
r = typeRel(m, f, arg.typ)
|
||||
block instantiateGenericRoutine:
|
||||
# In the case where the matched value is a generic proc, we need to
|
||||
# fully instantiate it and then rerun typeRel to make sure it matches.
|
||||
# instantiationCounter is for safety to avoid any infinite loop,
|
||||
# I don't have any example when it is needed.
|
||||
# lastBindingCount is used to check whether m.bindings remains the same,
|
||||
# because in that case there is no point in continuing.
|
||||
var instantiationCounter = 0
|
||||
var lastBindingCount = -1
|
||||
while r in {isBothMetaConvertible, isInferred, isInferredConvertible} and
|
||||
lastBindingCount != m.bindings.counter and
|
||||
instantiationCounter < 100:
|
||||
lastBindingCount = m.bindings.counter
|
||||
inc(instantiationCounter)
|
||||
if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
|
||||
result = c.semInferredLambda(c, m.bindings, arg)
|
||||
elif arg.kind != nkSym:
|
||||
return nil
|
||||
elif arg.sym.kind in {skMacro, skTemplate}:
|
||||
return nil
|
||||
else:
|
||||
if arg.sym.ast == nil:
|
||||
return nil
|
||||
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
|
||||
result = newSymNode(inferred, arg.info)
|
||||
arg = result
|
||||
r = typeRel(m, f, arg.typ)
|
||||
|
||||
case r
|
||||
of isConvertible:
|
||||
@@ -2180,29 +2289,20 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
result = arg
|
||||
else:
|
||||
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
|
||||
of isInferred, isInferredConvertible:
|
||||
if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
|
||||
result = c.semInferredLambda(c, m.bindings, arg)
|
||||
elif arg.kind != nkSym:
|
||||
return nil
|
||||
elif arg.sym.kind in {skMacro, skTemplate}:
|
||||
return nil
|
||||
else:
|
||||
if arg.sym.ast == nil:
|
||||
return nil
|
||||
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
|
||||
result = newSymNode(inferred, arg.info)
|
||||
if r == isInferredConvertible:
|
||||
inc(m.convMatches)
|
||||
result = implicitConv(nkHiddenStdConv, f, result, m, c)
|
||||
else:
|
||||
inc(m.genericMatches)
|
||||
of isInferred:
|
||||
# result should be set in above while loop:
|
||||
assert result != nil
|
||||
inc(m.genericMatches)
|
||||
of isInferredConvertible:
|
||||
# result should be set in above while loop:
|
||||
assert result != nil
|
||||
inc(m.convMatches)
|
||||
result = implicitConv(nkHiddenStdConv, f, result, m, c)
|
||||
of isGeneric:
|
||||
inc(m.genericMatches)
|
||||
if arg.typ == nil:
|
||||
result = arg
|
||||
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or
|
||||
m.inheritancePenalty > oldInheritancePenalty:
|
||||
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or cmpInheritancePenalty(oldInheritancePenalty, m.inheritancePenalty) > 0:
|
||||
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
||||
elif arg.typ.isEmptyContainer:
|
||||
result = arg.copyTree
|
||||
@@ -2210,8 +2310,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
else:
|
||||
result = arg
|
||||
of isBothMetaConvertible:
|
||||
# This is the result for the 101th time.
|
||||
result = nil
|
||||
# result should be set in above while loop:
|
||||
assert result != nil
|
||||
inc(m.convMatches)
|
||||
result = arg
|
||||
of isFromIntLit:
|
||||
# too lazy to introduce another ``*matches`` field, so we conflate
|
||||
# ``isIntConv`` and ``isIntLit`` here:
|
||||
@@ -2286,56 +2388,76 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||
if arg == nil or arg.kind notin nkSymChoices:
|
||||
result = paramTypesMatchAux(m, f, a, arg, argOrig)
|
||||
else:
|
||||
# CAUTION: The order depends on the used hashing scheme. Thus it is
|
||||
# incorrect to simply use the first fitting match. However, to implement
|
||||
# this correctly is inefficient. We have to copy `m` here to be able to
|
||||
# roll back the side effects of the unification algorithm.
|
||||
let c = m.c
|
||||
var
|
||||
x = newCandidate(c, m.callee)
|
||||
y = newCandidate(c, m.callee)
|
||||
z = newCandidate(c, m.callee)
|
||||
x.calleeSym = m.calleeSym
|
||||
y.calleeSym = m.calleeSym
|
||||
z.calleeSym = m.calleeSym
|
||||
# symbol kinds that don't participate in symchoice type disambiguation:
|
||||
let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage, skType}
|
||||
|
||||
var best = -1
|
||||
for i in 0..<arg.len:
|
||||
if arg[i].sym.kind in {skProc, skFunc, skMethod, skConverter,
|
||||
skIterator, skMacro, skTemplate, skEnumField}:
|
||||
copyCandidate(z, m)
|
||||
z.callee = arg[i].typ
|
||||
if tfUnresolved in z.callee.flags: continue
|
||||
z.calleeSym = arg[i].sym
|
||||
# XXX this is still all wrong: (T, T) should be 2 generic matches
|
||||
# and (int, int) 2 exact matches, etc. Essentially you cannot call
|
||||
# typeRel here and expect things to work!
|
||||
let r = typeRel(z, f, arg[i].typ)
|
||||
incMatches(z, r, 2)
|
||||
if r != isNone:
|
||||
z.state = csMatch
|
||||
case x.state
|
||||
of csEmpty, csNoMatch:
|
||||
x = z
|
||||
best = i
|
||||
of csMatch:
|
||||
let cmp = cmpCandidates(x, z)
|
||||
if cmp < 0:
|
||||
best = i
|
||||
x = z
|
||||
elif cmp == 0:
|
||||
y = z # z is as good as x
|
||||
result = arg
|
||||
|
||||
if x.state == csEmpty:
|
||||
result = nil
|
||||
elif y.state == csMatch and cmpCandidates(x, y) == 0:
|
||||
if x.state != csMatch:
|
||||
internalError(m.c.graph.config, arg.info, "x.state is not csMatch")
|
||||
# ambiguous: more than one symbol fits!
|
||||
# See tsymchoice_for_expr as an example. 'f.kind == tyUntyped' should match
|
||||
# anyway:
|
||||
if f.kind in {tyUntyped, tyTyped}: result = arg
|
||||
else: result = nil
|
||||
if f.kind in {tyTyped, tyUntyped}:
|
||||
var
|
||||
bestScope = -1
|
||||
counts = 0
|
||||
for i in 0..<arg.len:
|
||||
if arg[i].sym.kind in matchSet:
|
||||
let thisScope = cmpScopes(m.c, arg[i].sym)
|
||||
if thisScope > bestScope:
|
||||
best = i
|
||||
bestScope = thisScope
|
||||
counts = 0
|
||||
elif thisScope == bestScope:
|
||||
inc counts
|
||||
if best == -1:
|
||||
result = nil
|
||||
elif counts > 0:
|
||||
best = -1
|
||||
else:
|
||||
# CAUTION: The order depends on the used hashing scheme. Thus it is
|
||||
# incorrect to simply use the first fitting match. However, to implement
|
||||
# this correctly is inefficient. We have to copy `m` here to be able to
|
||||
# roll back the side effects of the unification algorithm.
|
||||
let c = m.c
|
||||
var
|
||||
x = newCandidate(c, m.callee) # potential "best"
|
||||
y = newCandidate(c, m.callee) # potential competitor with x
|
||||
z = newCandidate(c, m.callee) # buffer for copies of m
|
||||
x.calleeSym = m.calleeSym
|
||||
y.calleeSym = m.calleeSym
|
||||
z.calleeSym = m.calleeSym
|
||||
|
||||
for i in 0..<arg.len:
|
||||
if arg[i].sym.kind in matchSet:
|
||||
copyCandidate(z, m)
|
||||
z.callee = arg[i].typ
|
||||
if tfUnresolved in z.callee.flags: continue
|
||||
z.calleeSym = arg[i].sym
|
||||
z.calleeScope = cmpScopes(m.c, arg[i].sym)
|
||||
# XXX this is still all wrong: (T, T) should be 2 generic matches
|
||||
# and (int, int) 2 exact matches, etc. Essentially you cannot call
|
||||
# typeRel here and expect things to work!
|
||||
let r = typeRel(z, f, arg[i].typ)
|
||||
incMatches(z, r, 2)
|
||||
if r != isNone:
|
||||
z.state = csMatch
|
||||
case x.state
|
||||
of csEmpty, csNoMatch:
|
||||
x = z
|
||||
best = i
|
||||
of csMatch:
|
||||
let cmp = cmpCandidates(x, z, isFormal=false)
|
||||
if cmp < 0:
|
||||
best = i
|
||||
x = z
|
||||
elif cmp == 0:
|
||||
y = z # z is as good as x
|
||||
|
||||
if x.state == csEmpty:
|
||||
result = nil
|
||||
elif y.state == csMatch and cmpCandidates(x, y, isFormal=false) == 0:
|
||||
if x.state != csMatch:
|
||||
internalError(m.c.graph.config, arg.info, "x.state is not csMatch")
|
||||
result = nil
|
||||
if best > -1 and result != nil:
|
||||
# only one valid interpretation found:
|
||||
markUsed(m.c, arg.info, arg[best].sym)
|
||||
onUse(arg.info, arg[best].sym)
|
||||
@@ -2395,7 +2517,7 @@ proc arrayConstr(c: PContext, n: PNode): PType =
|
||||
result = newTypeS(tyArray, c)
|
||||
rawAddSon(result, makeRangeType(c, 0, 0, n.info))
|
||||
addSonSkipIntLit(result, skipTypes(n.typ,
|
||||
{tyGenericInst, tyVar, tyLent, tyOrdinal}), c.idgen)
|
||||
{tyVar, tyLent, tyOrdinal}), c.idgen)
|
||||
|
||||
proc arrayConstr(c: PContext, info: TLineInfo): PType =
|
||||
result = newTypeS(tyArray, c)
|
||||
@@ -2623,12 +2745,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
m.firstMismatch.arg = a
|
||||
m.firstMismatch.formal = formal
|
||||
|
||||
proc semFinishOperands*(c: PContext, n: PNode) =
|
||||
# this needs to be called to ensure that after overloading resolution every
|
||||
# argument has been sem'checked:
|
||||
for i in 1..<n.len:
|
||||
n[i] = prepareOperand(c, n[i])
|
||||
|
||||
proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||
# for 'suggest' support:
|
||||
var marker = initIntSet()
|
||||
|
||||
@@ -433,10 +433,9 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
typ.paddingAtEnd = typ.base.paddingAtEnd
|
||||
|
||||
of tyForward:
|
||||
# is this really illegal recursion, or maybe just unknown?
|
||||
typ.size = szIllegalRecursion
|
||||
typ.align = szIllegalRecursion
|
||||
typ.paddingAtEnd = szIllegalRecursion
|
||||
typ.size = szUnknownSize
|
||||
typ.align = szUnknownSize
|
||||
typ.paddingAtEnd = szUnknownSize
|
||||
|
||||
of tyStatic:
|
||||
if typ.n != nil:
|
||||
|
||||
@@ -11,7 +11,7 @@ type
|
||||
Mapping = object
|
||||
## Mapping refers to a line in the JS output.
|
||||
## It is made up of segments which refer to the tokens in the line
|
||||
case inSource: bool # Whether the line in JS has Nim equivilant
|
||||
case inSource: bool # Whether the line in JS has Nim equivalent
|
||||
of true:
|
||||
file: int # Index into files list
|
||||
line: int # 0 indexed line of code in the Nim source
|
||||
|
||||
@@ -109,6 +109,16 @@ stmtList:
|
||||
|
||||
"""
|
||||
|
||||
proc castToVoidPointer(g: ModuleGraph, n: PNode, fvField: PNode): PNode =
|
||||
if g.config.backend == backendCpp:
|
||||
result = fvField
|
||||
else:
|
||||
let ptrType = getSysType(g, n.info, tyPointer)
|
||||
result = newNodeI(nkCast, fvField.info)
|
||||
result.add newNodeI(nkEmpty, fvField.info)
|
||||
result.add fvField
|
||||
result.typ = ptrType
|
||||
|
||||
proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
|
||||
varSection, varInit, call, barrier, fv: PNode;
|
||||
idgen: IdGenerator;
|
||||
@@ -156,8 +166,9 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
|
||||
if barrier == nil:
|
||||
# by now 'fv' is shared and thus might have beeen overwritten! we need
|
||||
# to use the thread-local view instead:
|
||||
let castExpr = castToVoidPointer(g, f, threadLocalProm.newSymNode)
|
||||
body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,
|
||||
threadLocalProm.newSymNode)
|
||||
castExpr)
|
||||
else:
|
||||
body.add call
|
||||
if barrier != nil:
|
||||
@@ -413,7 +424,8 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
|
||||
# create flowVar:
|
||||
result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
|
||||
if barrier == nil:
|
||||
result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, fvField)
|
||||
let castExpr = castToVoidPointer(g, n, fvField)
|
||||
result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, castExpr)
|
||||
|
||||
elif spawnKind == srByVar:
|
||||
var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options)
|
||||
|
||||
@@ -81,7 +81,16 @@ proc cmpSuggestions(a, b: Suggest): int =
|
||||
# independent of hashing order:
|
||||
result = cmp(a.name[], b.name[])
|
||||
|
||||
proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int =
|
||||
proc scanForTrailingAsterisk(line: string, start: int): int =
|
||||
result = 0
|
||||
while start+result < line.len and line[start+result] in {' ', '\t'}:
|
||||
inc result
|
||||
if start+result < line.len and line[start+result] == '*':
|
||||
inc result
|
||||
else:
|
||||
result = 0
|
||||
|
||||
proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo; skipTrailingAsterisk: bool = false): int =
|
||||
let
|
||||
line = sourceLine(conf, info)
|
||||
column = toColumn(info)
|
||||
@@ -103,8 +112,10 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int
|
||||
result = 0
|
||||
elif ident[0] in linter.Letters and ident[^1] != '=':
|
||||
result = identLen(line, column)
|
||||
if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0:
|
||||
if cmpIgnoreStyle(line[column..column + result - 1], ident[0..min(result-1,len(ident)-1)]) != 0:
|
||||
result = 0
|
||||
if skipTrailingAsterisk and result > 0:
|
||||
result += scanForTrailingAsterisk(line, column + result)
|
||||
else:
|
||||
var sourceIdent: string
|
||||
result = parseWhile(line, sourceIdent,
|
||||
@@ -154,7 +165,10 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info
|
||||
result.qualifiedPath.add(s.name.s)
|
||||
|
||||
if s.typ != nil:
|
||||
result.forth = typeToString(s.typ)
|
||||
if section == ideInlayHints:
|
||||
result.forth = typeToString(s.typ, preferInlayHint)
|
||||
else:
|
||||
result.forth = typeToString(s.typ, preferInferredEffects)
|
||||
else:
|
||||
result.forth = ""
|
||||
when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
|
||||
@@ -173,58 +187,90 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info
|
||||
result.filePath = toFullPath(g.config, infox)
|
||||
result.line = toLinenumber(infox)
|
||||
result.column = toColumn(infox)
|
||||
result.tokenLen = if section != ideHighlight:
|
||||
result.tokenLen = if section notin {ideHighlight, ideInlayHints}:
|
||||
s.name.s.len
|
||||
else:
|
||||
getTokenLenFromSource(g.config, s.name.s, infox)
|
||||
getTokenLenFromSource(g.config, s.name.s, infox, section == ideInlayHints)
|
||||
result.version = g.config.suggestVersion
|
||||
result.endLine = endLine
|
||||
result.endCol = endCol
|
||||
|
||||
proc `$`*(suggest: Suggest): string =
|
||||
result = $suggest.section
|
||||
proc `$`*(suggest: SuggestInlayHint): string =
|
||||
result = $suggest.kind
|
||||
result.add(sep)
|
||||
if suggest.section == ideHighlight:
|
||||
if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
|
||||
result.add("skGlobalVar")
|
||||
elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
|
||||
result.add("skGlobalLet")
|
||||
result.add($suggest.line)
|
||||
result.add(sep)
|
||||
result.add($suggest.column)
|
||||
result.add(sep)
|
||||
result.add(suggest.label)
|
||||
result.add(sep)
|
||||
result.add($suggest.paddingLeft)
|
||||
result.add(sep)
|
||||
result.add($suggest.paddingRight)
|
||||
result.add(sep)
|
||||
result.add($suggest.allowInsert)
|
||||
result.add(sep)
|
||||
result.add(suggest.tooltip)
|
||||
|
||||
proc `$`*(suggest: Suggest): string =
|
||||
if suggest.section == ideInlayHints:
|
||||
result = $suggest.inlayHintInfo
|
||||
else:
|
||||
result = $suggest.section
|
||||
result.add(sep)
|
||||
if suggest.section == ideHighlight:
|
||||
if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
|
||||
result.add("skGlobalVar")
|
||||
elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
|
||||
result.add("skGlobalLet")
|
||||
else:
|
||||
result.add($suggest.symkind.TSymKind)
|
||||
result.add(sep)
|
||||
result.add($suggest.line)
|
||||
result.add(sep)
|
||||
result.add($suggest.column)
|
||||
result.add(sep)
|
||||
result.add($suggest.tokenLen)
|
||||
else:
|
||||
result.add($suggest.symkind.TSymKind)
|
||||
result.add(sep)
|
||||
result.add($suggest.line)
|
||||
result.add(sep)
|
||||
result.add($suggest.column)
|
||||
result.add(sep)
|
||||
result.add($suggest.tokenLen)
|
||||
else:
|
||||
result.add($suggest.symkind.TSymKind)
|
||||
result.add(sep)
|
||||
if suggest.qualifiedPath.len != 0:
|
||||
result.add(suggest.qualifiedPath.join("."))
|
||||
result.add(sep)
|
||||
result.add(suggest.forth)
|
||||
result.add(sep)
|
||||
result.add(suggest.filePath)
|
||||
result.add(sep)
|
||||
result.add($suggest.line)
|
||||
result.add(sep)
|
||||
result.add($suggest.column)
|
||||
result.add(sep)
|
||||
when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
|
||||
result.add(suggest.doc.escape)
|
||||
if suggest.version == 0 or suggest.version == 3:
|
||||
result.add(sep)
|
||||
result.add($suggest.quality)
|
||||
if suggest.section == ideSug:
|
||||
if suggest.qualifiedPath.len != 0:
|
||||
result.add(suggest.qualifiedPath.join("."))
|
||||
result.add(sep)
|
||||
result.add(suggest.forth)
|
||||
result.add(sep)
|
||||
result.add(suggest.filePath)
|
||||
result.add(sep)
|
||||
result.add($suggest.line)
|
||||
result.add(sep)
|
||||
result.add($suggest.column)
|
||||
result.add(sep)
|
||||
when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
|
||||
result.add(suggest.doc.escape)
|
||||
if suggest.version == 0 or suggest.version == 3:
|
||||
result.add(sep)
|
||||
result.add($suggest.prefix)
|
||||
result.add($suggest.quality)
|
||||
if suggest.section == ideSug:
|
||||
result.add(sep)
|
||||
result.add($suggest.prefix)
|
||||
|
||||
if (suggest.version == 3 and suggest.section in {ideOutline, ideExpand}):
|
||||
result.add(sep)
|
||||
result.add($suggest.endLine)
|
||||
result.add(sep)
|
||||
result.add($suggest.endCol)
|
||||
if (suggest.version == 3 and suggest.section in {ideOutline, ideExpand}):
|
||||
result.add(sep)
|
||||
result.add($suggest.endLine)
|
||||
result.add(sep)
|
||||
result.add($suggest.endCol)
|
||||
|
||||
proc suggestToSuggestInlayHint*(sug: Suggest): SuggestInlayHint =
|
||||
SuggestInlayHint(
|
||||
kind: sihkType,
|
||||
line: sug.line,
|
||||
column: sug.column + sug.tokenLen,
|
||||
label: ": " & sug.forth,
|
||||
paddingLeft: false,
|
||||
paddingRight: false,
|
||||
allowInsert: true,
|
||||
tooltip: ""
|
||||
)
|
||||
|
||||
proc suggestResult*(conf: ConfigRef; s: Suggest) =
|
||||
if not isNil(conf.suggestionResultHook):
|
||||
@@ -507,7 +553,7 @@ proc findDefinition(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym
|
||||
if s.isNil: return
|
||||
if isTracked(info, g.config.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags):
|
||||
suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym))
|
||||
if sfForward notin s.flags and g.config.suggestVersion != 3:
|
||||
if sfForward notin s.flags and g.config.suggestVersion < 3:
|
||||
suggestQuit()
|
||||
else:
|
||||
usageSym = s
|
||||
@@ -522,7 +568,7 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i
|
||||
## misnamed: should be 'symDeclared'
|
||||
let conf = g.config
|
||||
when defined(nimsuggest):
|
||||
g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add SymInfoPair(sym: s, info: info)
|
||||
g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add SymInfoPair(sym: s, info: info, isDecl: isDecl)
|
||||
|
||||
if conf.suggestVersion == 0:
|
||||
if s.allUsages.len == 0:
|
||||
@@ -596,7 +642,7 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) =
|
||||
else:
|
||||
inc i
|
||||
|
||||
proc markUsed(c: PContext; info: TLineInfo; s: PSym) =
|
||||
proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true) =
|
||||
let conf = c.config
|
||||
incl(s.flags, sfUsed)
|
||||
if s.kind == skEnumField and s.owner != nil:
|
||||
@@ -613,7 +659,8 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) =
|
||||
if sfError in s.flags: userError(conf, info, s)
|
||||
when defined(nimsuggest):
|
||||
suggestSym(c.graph, info, s, c.graph.usageSym, false)
|
||||
styleCheckUse(c, info, s)
|
||||
if checkStyle:
|
||||
styleCheckUse(c, info, s)
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
|
||||
proc safeSemExpr*(c: PContext, n: PNode): PNode =
|
||||
@@ -711,7 +758,7 @@ proc suggestSentinel*(c: PContext) =
|
||||
|
||||
when defined(nimsuggest):
|
||||
proc onDef(graph: ModuleGraph, s: PSym, info: TLineInfo) =
|
||||
if graph.config.suggestVersion == 3 and info.exactEquals(s.info):
|
||||
if graph.config.suggestVersion >= 3 and info.exactEquals(s.info):
|
||||
suggestSym(graph, info, s, graph.usageSym)
|
||||
|
||||
template getPContext(): untyped =
|
||||
|
||||
@@ -57,7 +57,7 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache
|
||||
else:
|
||||
inc(i, 2)
|
||||
while i < line.len and line[i] in Whitespace: inc(i)
|
||||
var p: Parser
|
||||
var p: Parser = default(Parser)
|
||||
openParser(p, filename, llStreamOpen(substr(line, i)), cache, config)
|
||||
result = parseAll(p)
|
||||
closeParser(p)
|
||||
|
||||
@@ -100,12 +100,12 @@ proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
|
||||
else:
|
||||
result = newSymNode(r)
|
||||
|
||||
proc transform(c: PTransf, n: PNode): PNode
|
||||
proc transform(c: PTransf, n: PNode, noConstFold = false): PNode
|
||||
|
||||
proc transformSons(c: PTransf, n: PNode): PNode =
|
||||
proc transformSons(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
result = newTransNode(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = transform(c, n[i])
|
||||
result[i] = transform(c, n[i], noConstFold)
|
||||
|
||||
proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode =
|
||||
result = newTransNode(kind, ri.info, 2)
|
||||
@@ -452,6 +452,14 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
let rhs = transform(c, e)
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
|
||||
|
||||
# bug #23536; note that the info of forLoopBody should't change
|
||||
for idx in 0 ..< result.len:
|
||||
var changeNode = result[idx]
|
||||
changeNode.info = c.transCon.forStmt.info
|
||||
for i, child in changeNode:
|
||||
child.info = changeNode.info
|
||||
|
||||
inc(c.transCon.yieldStmts)
|
||||
if c.transCon.yieldStmts <= 1:
|
||||
# common case
|
||||
@@ -462,15 +470,10 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
result.add(introduceNewLocalVars(c, c.transCon.forLoopBody))
|
||||
c.isIntroducingNewLocalVars = false
|
||||
|
||||
for idx in 0 ..< result.len:
|
||||
var changeNode = result[idx]
|
||||
changeNode.info = c.transCon.forStmt.info
|
||||
for i, child in changeNode:
|
||||
child.info = changeNode.info
|
||||
|
||||
proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
|
||||
result = transformSons(c, n)
|
||||
if c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags: return
|
||||
proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds, isAddr = false): PNode =
|
||||
result = transformSons(c, n, noConstFold = isAddr)
|
||||
# inlining of 'var openarray' iterators; bug #19977
|
||||
if n.typ.kind != tyOpenArray and (c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags): return
|
||||
var n = result
|
||||
case n[0].kind
|
||||
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
|
||||
@@ -872,10 +875,6 @@ proc transformCall(c: PTransf, n: PNode): PNode =
|
||||
inc(j)
|
||||
result.add(a)
|
||||
if result.len == 2: result = result[1]
|
||||
elif magic == mAddr:
|
||||
result = newTransNode(nkAddr, n, 1)
|
||||
result[0] = n[1]
|
||||
result = transformAddrDeref(c, result, {nkDerefExpr, nkHiddenDeref})
|
||||
elif magic in {mNBindSym, mTypeOf, mRunnableExamples}:
|
||||
# for bindSym(myconst) we MUST NOT perform constant folding:
|
||||
result = n
|
||||
@@ -970,7 +969,7 @@ proc transformDerefBlock(c: PTransf, n: PNode): PNode =
|
||||
result[i] = e0[i]
|
||||
result[e0.len-1] = newTreeIT(nkHiddenDeref, n.info, n.typ, e0[e0.len-1])
|
||||
|
||||
proc transform(c: PTransf, n: PNode): PNode =
|
||||
proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
when false:
|
||||
var oldDeferAnchor: PNode
|
||||
if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
|
||||
@@ -1037,9 +1036,9 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||
of nkCallKinds:
|
||||
result = transformCall(c, n)
|
||||
of nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, {nkHiddenDeref})
|
||||
result = transformAddrDeref(c, n, {nkHiddenDeref}, isAddr = true)
|
||||
of nkAddr:
|
||||
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref})
|
||||
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}, isAddr = true)
|
||||
of nkDerefExpr:
|
||||
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
|
||||
of nkHiddenDeref:
|
||||
@@ -1119,7 +1118,7 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||
let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
|
||||
n.typ != nil and
|
||||
n.typ.kind == tyPointer
|
||||
if not exprIsPointerCast:
|
||||
if not exprIsPointerCast and not noConstFold:
|
||||
var cnst = getConstExpr(c.module, result, c.idgen, c.graph)
|
||||
# we inline constants if they are not complex constants:
|
||||
if cnst != nil and not dontInlineConstant(n, cnst):
|
||||
|
||||
@@ -133,10 +133,12 @@ proc whichPragma*(n: PNode): TSpecialWord =
|
||||
case key.kind
|
||||
of nkIdent: result = whichKeyword(key.ident)
|
||||
of nkSym: result = whichKeyword(key.sym.name)
|
||||
of nkCast: result = wCast
|
||||
of nkCast: return wCast
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
result = whichPragma(key[0])
|
||||
else: result = wInvalid
|
||||
return whichPragma(key[0])
|
||||
else: return wInvalid
|
||||
if result in nonPragmaWordsLow..nonPragmaWordsHigh:
|
||||
result = wInvalid
|
||||
|
||||
proc isNoSideEffectPragma*(n: PNode): bool =
|
||||
var k = whichPragma(n)
|
||||
|
||||
@@ -26,6 +26,8 @@ type
|
||||
taIsTemplateOrMacro
|
||||
taProcContextIsNotMacro
|
||||
taIsCastable
|
||||
taIsDefaultField
|
||||
taVoid # only allow direct void fields of objects/tuples
|
||||
|
||||
TTypeAllowedFlags* = set[TTypeAllowedFlag]
|
||||
|
||||
@@ -57,6 +59,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
if typ == nil: return nil
|
||||
if containsOrIncl(marker, typ.id): return nil
|
||||
var t = skipTypes(typ, abstractInst-{tyTypeDesc, tySink})
|
||||
|
||||
let flags = if t.kind == tyVoid: flags else: flags-{taVoid}
|
||||
case t.kind
|
||||
of tyVar, tyLent:
|
||||
if kind in {skProc, skFunc, skConst} and (views notin c.features):
|
||||
@@ -112,7 +116,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
of tyStatic:
|
||||
if kind notin {skParam}: result = t
|
||||
of tyVoid:
|
||||
if taField notin flags: result = t
|
||||
if taVoid notin flags: result = t
|
||||
of tyTypeClasses:
|
||||
if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
|
||||
discard
|
||||
@@ -170,7 +174,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
elif kind in {skVar, skLet}:
|
||||
result = t[1]
|
||||
of tyRef:
|
||||
if kind == skConst: result = t
|
||||
if kind == skConst and taIsDefaultField notin flags: result = t
|
||||
else: result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
|
||||
of tyPtr:
|
||||
result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
|
||||
@@ -180,10 +184,10 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
if result != nil: break
|
||||
of tyObject, tyTuple:
|
||||
if kind in {skProc, skFunc, skConst} and
|
||||
t.kind == tyObject and t[0] != nil:
|
||||
t.kind == tyObject and t[0] != nil and taIsDefaultField notin flags:
|
||||
result = t
|
||||
else:
|
||||
let flags = flags+{taField}
|
||||
let flags = flags+{taField, taVoid}
|
||||
for i in 0..<t.len:
|
||||
result = typeAllowedAux(marker, t[i], kind, c, flags)
|
||||
if result != nil: break
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options,
|
||||
lineinfos, int128, modulegraphs, astmsgs
|
||||
lineinfos, int128, modulegraphs, astmsgs, wordrecg
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[assertions, formatfloat]
|
||||
@@ -28,6 +28,8 @@ type
|
||||
preferMixed,
|
||||
# most useful, shows: symbol + resolved symbols if it differs, e.g.:
|
||||
# tuple[a: MyInt{int}, b: float]
|
||||
preferInlayHint,
|
||||
preferInferredEffects,
|
||||
|
||||
TTypeRelation* = enum # order is important!
|
||||
isNone, isConvertible,
|
||||
@@ -62,8 +64,6 @@ proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
|
||||
template `$`*(typ: PType): string = typeToString(typ)
|
||||
|
||||
proc base*(t: PType): PType =
|
||||
result = t[0]
|
||||
|
||||
# ------------------- type iterator: ----------------------------------------
|
||||
type
|
||||
@@ -117,7 +117,7 @@ proc isPureObject*(typ: PType): bool =
|
||||
proc isUnsigned*(t: PType): bool =
|
||||
t.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}
|
||||
|
||||
proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 =
|
||||
proc getOrdValueAux*(n: PNode, err: var bool): Int128 =
|
||||
var k = n.kind
|
||||
if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}:
|
||||
k = nkUIntLit
|
||||
@@ -133,13 +133,22 @@ proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 =
|
||||
toInt128(n.intVal)
|
||||
of nkNilLit:
|
||||
int128.Zero
|
||||
of nkHiddenStdConv: getOrdValue(n[1], onError)
|
||||
of nkHiddenStdConv:
|
||||
getOrdValueAux(n[1], err)
|
||||
else:
|
||||
# XXX: The idea behind the introduction of int128 was to finally
|
||||
# have all calculations numerically far away from any
|
||||
# overflows. This command just introduces such overflows and
|
||||
# should therefore really be revisited.
|
||||
onError
|
||||
err = true
|
||||
int128.Zero
|
||||
|
||||
proc getOrdValue*(n: PNode): Int128 =
|
||||
var err: bool = false
|
||||
result = getOrdValueAux(n, err)
|
||||
#assert err == false
|
||||
|
||||
proc getOrdValue*(n: PNode, onError: Int128): Int128 =
|
||||
var err = false
|
||||
result = getOrdValueAux(n, err)
|
||||
if err:
|
||||
result = onError
|
||||
|
||||
proc getFloatValue*(n: PNode): BiggestFloat =
|
||||
case n.kind
|
||||
@@ -508,7 +517,7 @@ const
|
||||
"void", "iterable"]
|
||||
|
||||
const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo,
|
||||
preferGenericArg, preferResolved, preferMixed}
|
||||
preferGenericArg, preferResolved, preferMixed, preferInlayHint, preferInferredEffects}
|
||||
|
||||
template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
|
||||
tc.add concrete
|
||||
@@ -542,7 +551,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
if prefer in preferToResolveSymbols and t.sym != nil and
|
||||
sfAnon notin t.sym.flags and t.kind != tySequence:
|
||||
if t.kind == tyInt and isIntLit(t):
|
||||
result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
|
||||
if prefer == preferInlayHint:
|
||||
result = t.sym.name.s
|
||||
else:
|
||||
result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
|
||||
elif t.kind == tyAlias and t[0].kind != tyAlias:
|
||||
result = typeToString(t[0])
|
||||
elif prefer in {preferResolved, preferMixed}:
|
||||
@@ -558,7 +570,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
result = t.sym.name.s
|
||||
if prefer == preferMixed and result != t.sym.name.s:
|
||||
result = t.sym.name.s & "{" & result & "}"
|
||||
elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
|
||||
elif prefer in {preferName, preferTypeName, preferInlayHint, preferInferredEffects} or t.sym.owner.isNil:
|
||||
# note: should probably be: {preferName, preferTypeName, preferGenericArg}
|
||||
result = t.sym.name.s
|
||||
if t.kind == tyGenericParam and t.len > 0:
|
||||
@@ -577,8 +589,11 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
if not isIntLit(t) or prefer == preferExported:
|
||||
result = typeToStr[t.kind]
|
||||
else:
|
||||
if prefer == preferGenericArg:
|
||||
case prefer:
|
||||
of preferGenericArg:
|
||||
result = $t.n.intVal
|
||||
of preferInlayHint:
|
||||
result = "int"
|
||||
else:
|
||||
result = "int literal(" & $t.n.intVal & ")"
|
||||
of tyGenericInst, tyGenericInvocation:
|
||||
@@ -751,12 +766,35 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
result.add(')')
|
||||
if t.len > 0 and t[0] != nil: result.add(": " & typeToString(t[0]))
|
||||
var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv
|
||||
var hasImplicitRaises = false
|
||||
if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos:
|
||||
let pragmasNode = t.owner.ast[pragmasPos]
|
||||
let raisesSpec = effectSpec(pragmasNode, wRaises)
|
||||
if not isNil(raisesSpec):
|
||||
addSep(prag)
|
||||
prag.add("raises: ")
|
||||
prag.add($raisesSpec)
|
||||
hasImplicitRaises = true
|
||||
if tfNoSideEffect in t.flags:
|
||||
addSep(prag)
|
||||
prag.add("noSideEffect")
|
||||
if tfThread in t.flags:
|
||||
addSep(prag)
|
||||
prag.add("gcsafe")
|
||||
if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
|
||||
let effects = t.owner.typ.n[0]
|
||||
if effects.kind == nkEffectList and effects.len == effectListLen:
|
||||
var inferredRaisesStr = ""
|
||||
let effs = effects[exceptionEffects]
|
||||
if not isNil(effs):
|
||||
for eff in items(effs):
|
||||
if not isNil(eff):
|
||||
addSep(inferredRaisesStr)
|
||||
inferredRaisesStr.add($eff.typ)
|
||||
addSep(prag)
|
||||
prag.add("raises: <inferred> [")
|
||||
prag.add(inferredRaisesStr)
|
||||
prag.add("]")
|
||||
if prag.len != 0: result.add("{." & prag & ".}")
|
||||
of tyVarargs:
|
||||
result = typeToStr[t.kind] % typeToString(t[0])
|
||||
@@ -807,11 +845,11 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
|
||||
result = firstOrd(conf, lastSon(t))
|
||||
of tyOrdinal:
|
||||
if t.len > 0: result = firstOrd(conf, lastSon(t))
|
||||
else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
|
||||
else: fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')')
|
||||
of tyUncheckedArray, tyCstring:
|
||||
result = Zero
|
||||
else:
|
||||
internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
|
||||
fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')')
|
||||
result = Zero
|
||||
|
||||
proc firstFloat*(t: PType): BiggestFloat =
|
||||
@@ -895,11 +933,11 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 =
|
||||
of tyProxy: result = Zero
|
||||
of tyOrdinal:
|
||||
if t.len > 0: result = lastOrd(conf, lastSon(t))
|
||||
else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
|
||||
else: fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')')
|
||||
of tyUncheckedArray:
|
||||
result = Zero
|
||||
else:
|
||||
internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
|
||||
fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')')
|
||||
result = Zero
|
||||
|
||||
proc lastFloat*(t: PType): BiggestFloat =
|
||||
@@ -960,6 +998,7 @@ type
|
||||
ExactGcSafety
|
||||
AllowCommonBase
|
||||
PickyCAliases # be picky about the distinction between 'cint' and 'int32'
|
||||
IgnoreFlags # used for borrowed functions and methods; ignores the tfVarIsPtr flag
|
||||
|
||||
TTypeCmpFlags* = set[TTypeCmpFlag]
|
||||
|
||||
@@ -1153,6 +1192,13 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
|
||||
proc isGenericAlias*(t: PType): bool =
|
||||
return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst
|
||||
|
||||
proc genericAliasDepth*(t: PType): int =
|
||||
result = 0
|
||||
var it = t
|
||||
while it.isGenericAlias:
|
||||
it = it.lastSon
|
||||
inc result
|
||||
|
||||
proc skipGenericAlias*(t: PType): PType =
|
||||
return if t.isGenericAlias: t.lastSon else: t
|
||||
|
||||
@@ -1172,12 +1218,12 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
if containsOrIncl(c, a, b): return true
|
||||
|
||||
if x == y: return true
|
||||
var a = skipTypes(x, {tyGenericInst, tyAlias})
|
||||
var a = skipTypes(x, {tyAlias})
|
||||
while a.kind == tyUserTypeClass and tfResolved in a.flags:
|
||||
a = skipTypes(a[^1], {tyGenericInst, tyAlias})
|
||||
var b = skipTypes(y, {tyGenericInst, tyAlias})
|
||||
a = skipTypes(a[^1], {tyAlias})
|
||||
var b = skipTypes(y, {tyAlias})
|
||||
while b.kind == tyUserTypeClass and tfResolved in b.flags:
|
||||
b = skipTypes(b[^1], {tyGenericInst, tyAlias})
|
||||
b = skipTypes(b[^1], {tyAlias})
|
||||
assert(a != nil)
|
||||
assert(b != nil)
|
||||
if a.kind != b.kind:
|
||||
@@ -1274,7 +1320,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
cycleCheck()
|
||||
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
|
||||
result = sameChildrenAux(a, b, c)
|
||||
if result:
|
||||
if result and IgnoreFlags notin c.flags:
|
||||
if IgnoreTupleFields in c.flags:
|
||||
result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam}
|
||||
else:
|
||||
@@ -1289,9 +1335,17 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
result = sameTypeOrNilAux(a[0], b[0], c) and
|
||||
sameValue(a.n[0], b.n[0]) and
|
||||
sameValue(a.n[1], b.n[1])
|
||||
of tyGenericInst, tyAlias, tyInferred, tyIterable:
|
||||
of tyAlias, tyInferred, tyIterable:
|
||||
cycleCheck()
|
||||
result = sameTypeAux(a.lastSon, b.lastSon, c)
|
||||
of tyGenericInst:
|
||||
# BUG #23445
|
||||
# The type system must distinguish between `T[int] = object #[empty]#`
|
||||
# and `T[float] = object #[empty]#`!
|
||||
cycleCheck()
|
||||
for ff, aa in underspecifiedPairs(a, b, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
|
||||
of tyNone: result = false
|
||||
of tyConcept:
|
||||
result = exprStructuralEquivalent(a.n, b.n)
|
||||
|
||||
@@ -431,11 +431,15 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
|
||||
|
||||
of nkIfStmt, nkIfExpr:
|
||||
for i in 0..<n.len:
|
||||
inc c.inConditional
|
||||
destMightOwn(c, dest, n[i].lastSon)
|
||||
dec c.inConditional
|
||||
|
||||
of nkCaseStmt:
|
||||
for i in 1..<n.len:
|
||||
inc c.inConditional
|
||||
destMightOwn(c, dest, n[i].lastSon)
|
||||
dec c.inConditional
|
||||
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
if n.len > 0:
|
||||
@@ -488,8 +492,17 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
|
||||
# we know the result is derived from the first argument:
|
||||
var roots: seq[(PSym, int)]
|
||||
allRoots(n[1], roots, RootEscapes)
|
||||
for r in roots:
|
||||
connect(c, dest.sym, r[0], n[1].info)
|
||||
if roots.len == 0 and c.inConditional > 0:
|
||||
# when in a conditional expression,
|
||||
# to ensure that the first argument isn't outlived
|
||||
# by the lvalue, we need find the root, otherwise
|
||||
# it is probably a local temporary
|
||||
# (e.g. a return value from a call),
|
||||
# we should prevent cursorfication
|
||||
dest.flags.incl preventCursor
|
||||
else:
|
||||
for r in roots:
|
||||
connect(c, dest.sym, r[0], n[1].info)
|
||||
|
||||
else:
|
||||
let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone
|
||||
@@ -820,6 +833,10 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
|
||||
registerVariable(c, child[i])
|
||||
#deps(c, child[i], last)
|
||||
|
||||
if c.inLoop > 0 and child[0].kind == nkSym: # bug #22787
|
||||
let vid = variableId(c, child[0].sym)
|
||||
if child[^1].kind != nkEmpty:
|
||||
markAsReassigned(c, vid)
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
computeLiveRanges(c, n[0])
|
||||
computeLiveRanges(c, n[1])
|
||||
@@ -979,7 +996,9 @@ proc computeCursors*(s: PSym; n: PNode; g: ModuleGraph) =
|
||||
if v.flags * {ownsData, preventCursor, isConditionallyReassigned} == {} and
|
||||
v.sym.kind notin {skParam, skResult} and
|
||||
v.sym.flags * {sfThread, sfGlobal} == {} and hasDestructor(v.sym.typ) and
|
||||
v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned:
|
||||
v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned and
|
||||
(getAttachedOp(g, v.sym.typ, attachedAsgn) == nil or
|
||||
sfError notin getAttachedOp(g, v.sym.typ, attachedAsgn).flags):
|
||||
let rid = root(par, i)
|
||||
if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]):
|
||||
discard "cannot cursor into a graph that is mutated"
|
||||
|
||||
@@ -811,6 +811,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
# a[b] = c
|
||||
decodeBC(rkNode)
|
||||
let idx = regs[rb].intVal.int
|
||||
assert regs[ra].kind == rkNode
|
||||
let arr = regs[ra].node
|
||||
case arr.kind
|
||||
of nkTupleConstr: # refer to `opcSlice`
|
||||
@@ -824,7 +825,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of nkStrKinds:
|
||||
src.strVal[int(realIndex)] = char(regs[rc].intVal)
|
||||
of nkBracket:
|
||||
src[int(realIndex)] = regs[rc].node
|
||||
if regs[rc].kind == rkInt:
|
||||
src[int(realIndex)] = newIntNode(nkIntLit, regs[rc].intVal)
|
||||
else:
|
||||
assert regs[rc].kind == rkNode
|
||||
src[int(realIndex)] = regs[rc].node
|
||||
else:
|
||||
stackTrace(c, tos, pc, "opcWrArr internal error")
|
||||
else:
|
||||
@@ -1015,7 +1020,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcLenCstring:
|
||||
decodeBImm(rkInt)
|
||||
assert regs[rb].kind == rkNode
|
||||
regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm
|
||||
if regs[rb].node.kind == nkNilLit:
|
||||
regs[ra].intVal = -imm
|
||||
else:
|
||||
regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm
|
||||
of opcIncl:
|
||||
decodeB(rkNode)
|
||||
let b = regs[rb].regToNode
|
||||
@@ -1213,6 +1221,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcEqStr:
|
||||
decodeBC(rkInt)
|
||||
regs[ra].intVal = ord(regs[rb].node.strVal == regs[rc].node.strVal)
|
||||
of opcEqCString:
|
||||
decodeBC(rkInt)
|
||||
let bNil = regs[rb].node.kind == nkNilLit
|
||||
let cNil = regs[rc].node.kind == nkNilLit
|
||||
regs[ra].intVal = ord((bNil and cNil) or
|
||||
(not bNil and not cNil and regs[rb].node.strVal == regs[rc].node.strVal))
|
||||
of opcLeStr:
|
||||
decodeBC(rkInt)
|
||||
regs[ra].intVal = ord(regs[rb].node.strVal <= regs[rc].node.strVal)
|
||||
@@ -1345,7 +1359,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
else:
|
||||
internalError(c.config, c.debug[pc], "opcParseFloat: Incorrectly created openarray")
|
||||
else:
|
||||
regs[ra].intVal = parseBiggestFloat(regs[ra].node.strVal, rcAddr.floatVal)
|
||||
regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal, rcAddr.floatVal)
|
||||
|
||||
of opcRangeChck:
|
||||
let rb = instr.regB
|
||||
@@ -1496,7 +1510,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
regs[ra].node
|
||||
c.currentExceptionA = raised
|
||||
# Set the `name` field of the exception
|
||||
c.currentExceptionA[2].skipColon.strVal = c.currentExceptionA.typ.sym.name.s
|
||||
var exceptionNameNode = newStrNode(nkStrLit, c.currentExceptionA.typ.sym.name.s)
|
||||
if c.currentExceptionA[2].kind == nkExprColonExpr:
|
||||
exceptionNameNode.typ = c.currentExceptionA[2][1].typ
|
||||
c.currentExceptionA[2][1] = exceptionNameNode
|
||||
else:
|
||||
exceptionNameNode.typ = c.currentExceptionA[2].typ
|
||||
c.currentExceptionA[2] = exceptionNameNode
|
||||
c.exceptionInstr = pc
|
||||
|
||||
var frame = tos
|
||||
@@ -1909,6 +1929,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
|
||||
if error.len == 0 and msg <= errMax:
|
||||
error = formatMsg(conf, info, msg, arg))
|
||||
|
||||
regs[ra].node = newNode(nkEmpty)
|
||||
if error.len > 0:
|
||||
c.errorFlag = error
|
||||
elif ast.len != 1:
|
||||
@@ -1926,6 +1948,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
error = formatMsg(conf, info, msg, arg))
|
||||
if error.len > 0:
|
||||
c.errorFlag = error
|
||||
regs[ra].node = newNode(nkEmpty)
|
||||
else:
|
||||
regs[ra].node = ast
|
||||
of opcQueryErrorFlag:
|
||||
|
||||
@@ -99,7 +99,7 @@ type
|
||||
opcLeFloat, opcLtFloat, opcLeu, opcLtu,
|
||||
opcEqRef, opcEqNimNode, opcSameNodeType,
|
||||
opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
|
||||
opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
|
||||
opcEqStr, opcEqCString, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
|
||||
opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr,
|
||||
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||
opcIsNil, opcOf, opcIs,
|
||||
|
||||
@@ -133,6 +133,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
if inst:
|
||||
if allowRecursion:
|
||||
result = mapTypeToAstR(t.lastSon, info)
|
||||
# keep original type info for getType calls on the output node:
|
||||
result.typ = t
|
||||
else:
|
||||
result = newNodeX(nkBracketExpr)
|
||||
#result.add mapTypeToAst(t.lastSon, info)
|
||||
@@ -141,6 +143,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
result.add mapTypeToAst(t[i], info)
|
||||
else:
|
||||
result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion)
|
||||
# keep original type info for getType calls on the output node:
|
||||
result.typ = t
|
||||
of tyGenericBody:
|
||||
if inst:
|
||||
result = mapTypeToAstR(t.lastSon, info)
|
||||
|
||||
@@ -406,13 +406,19 @@ proc genIf(c: PCtx, n: PNode; dest: var TDest) =
|
||||
c.gen(it[0], tmp)
|
||||
elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false
|
||||
c.clearDest(n, dest)
|
||||
c.gen(it[1], dest) # then part
|
||||
if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `dest`
|
||||
c.gen(it[1])
|
||||
else:
|
||||
c.gen(it[1], dest) # then part
|
||||
if i < n.len-1:
|
||||
endings.add(c.xjmp(it[1], opcJmp, 0))
|
||||
c.patch(elsePos)
|
||||
else:
|
||||
c.clearDest(n, dest)
|
||||
c.gen(it[0], dest)
|
||||
if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `dest`
|
||||
c.gen(it[0])
|
||||
else:
|
||||
c.gen(it[0], dest)
|
||||
for endPos in endings: c.patch(endPos)
|
||||
c.clearDest(n, dest)
|
||||
|
||||
@@ -506,17 +512,25 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) =
|
||||
let it = n[i]
|
||||
if it.len == 1:
|
||||
# else stmt:
|
||||
if it[0].kind != nkNilLit or it[0].typ != nil:
|
||||
let body = it[0]
|
||||
if body.kind != nkNilLit or body.typ != nil:
|
||||
# an nkNilLit with nil for typ implies there is no else branch, this
|
||||
# avoids unused related errors as we've already consumed the dest
|
||||
c.gen(it[0], dest)
|
||||
if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest`
|
||||
c.gen(body)
|
||||
else:
|
||||
c.gen(body, dest)
|
||||
else:
|
||||
let b = rawGenLiteral(c, it)
|
||||
c.gABx(it, opcBranch, tmp, b)
|
||||
let elsePos = c.xjmp(it.lastSon, opcFJmp, tmp)
|
||||
c.gen(it.lastSon, dest)
|
||||
let body = it.lastSon
|
||||
let elsePos = c.xjmp(body, opcFJmp, tmp)
|
||||
if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest`
|
||||
c.gen(body)
|
||||
else:
|
||||
c.gen(body, dest)
|
||||
if i < n.len-1:
|
||||
endings.add(c.xjmp(it.lastSon, opcJmp, 0))
|
||||
endings.add(c.xjmp(body, opcJmp, 0))
|
||||
c.patch(elsePos)
|
||||
c.clearDest(n, dest)
|
||||
for endPos in endings: c.patch(endPos)
|
||||
@@ -532,7 +546,10 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
|
||||
if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
|
||||
var endings: seq[TPosition] = @[]
|
||||
let ehPos = c.xjmp(n, opcTry, 0)
|
||||
c.gen(n[0], dest)
|
||||
if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `dest`
|
||||
c.gen(n[0])
|
||||
else:
|
||||
c.gen(n[0], dest)
|
||||
c.clearDest(n, dest)
|
||||
# Add a jump past the exception handling code
|
||||
let jumpToFinally = c.xjmp(n, opcJmp, 0)
|
||||
@@ -550,7 +567,11 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
|
||||
if it.len == 1:
|
||||
# general except section:
|
||||
c.gABx(it, opcExcept, 0, 0)
|
||||
c.gen(it.lastSon, dest)
|
||||
let body = it.lastSon
|
||||
if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest`
|
||||
c.gen(body)
|
||||
else:
|
||||
c.gen(body, dest)
|
||||
c.clearDest(n, dest)
|
||||
if i < n.len:
|
||||
endings.add(c.xjmp(it, opcJmp, 0))
|
||||
@@ -875,13 +896,15 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
|
||||
const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar}
|
||||
const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool}
|
||||
var signedIntegers = {tyInt..tyInt64}
|
||||
var unsignedIntegers = {tyUInt..tyUInt64, tyChar}
|
||||
var unsignedIntegers = {tyUInt..tyUInt64, tyChar, tyEnum, tyBool}
|
||||
let src = n[1].typ.skipTypes(abstractRange)#.kind
|
||||
let dst = n[0].typ.skipTypes(abstractRange)#.kind
|
||||
let srcSize = getSize(c.config, src)
|
||||
let dstSize = getSize(c.config, dst)
|
||||
const unsupportedCastDifferentSize =
|
||||
"VM does not support 'cast' from $1 with size $2 to $3 with size $4 due to different sizes"
|
||||
if src.kind in allowedIntegers and dst.kind in allowedIntegers:
|
||||
let tmp = c.genx(n[1])
|
||||
if dest < 0: dest = c.getTemp(n[0].typ)
|
||||
@@ -898,8 +921,11 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
|
||||
# is smaller than source.
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(dstSize*8))
|
||||
c.freeTemp(tmp)
|
||||
elif srcSize == dstSize and src.kind in allowedIntegers and
|
||||
dst.kind in {tyFloat, tyFloat32, tyFloat64}:
|
||||
elif src.kind in allowedIntegers and
|
||||
dst.kind in {tyFloat, tyFloat32, tyFloat64}:
|
||||
if srcSize != dstSize:
|
||||
globalError(c.config, n.info, unsupportedCastDifferentSize %
|
||||
[$src.kind, $srcSize, $dst.kind, $dstSize])
|
||||
let tmp = c.genx(n[1])
|
||||
if dest < 0: dest = c.getTemp(n[0].typ)
|
||||
if dst.kind == tyFloat32:
|
||||
@@ -908,8 +934,11 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
|
||||
c.gABC(n, opcCastIntToFloat64, dest, tmp)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
elif srcSize == dstSize and src.kind in {tyFloat, tyFloat32, tyFloat64} and
|
||||
elif src.kind in {tyFloat, tyFloat32, tyFloat64} and
|
||||
dst.kind in allowedIntegers:
|
||||
if srcSize != dstSize:
|
||||
globalError(c.config, n.info, unsupportedCastDifferentSize %
|
||||
[$src.kind, $srcSize, $dst.kind, $dstSize])
|
||||
let tmp = c.genx(n[1])
|
||||
if dest < 0: dest = c.getTemp(n[0].typ)
|
||||
if src.kind == tyFloat32:
|
||||
@@ -1151,7 +1180,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(size*8))
|
||||
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
|
||||
genConv(c, n, n[1], dest)
|
||||
of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr)
|
||||
of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
|
||||
of mEqCString: genBinaryABC(c, n, dest, opcEqCString)
|
||||
of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
|
||||
of mLtStr: genBinaryABC(c, n, dest, opcLtStr)
|
||||
of mEqSet: genBinarySet(c, n, dest, opcEqSet)
|
||||
@@ -1525,6 +1555,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
|
||||
# little hack ahead for bug #12612: assume gensym'ed variables
|
||||
# are in the right scope:
|
||||
if sfGenSym in s.flags and c.prc.sym == nil: discard
|
||||
elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard
|
||||
else: cannotEval(c, n)
|
||||
elif s.kind in {skProc, skFunc, skConverter, skMethod,
|
||||
skIterator} and sfForward in s.flags:
|
||||
@@ -1778,7 +1809,6 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags
|
||||
let setLit = c.genx(checkExpr[1])
|
||||
var rs = c.getTemp(getSysType(c.graph, n.info, tyBool))
|
||||
c.gABC(n, opcContainsSet, rs, setLit, discVal)
|
||||
c.freeTemp(discVal)
|
||||
c.freeTemp(setLit)
|
||||
# If the check fails let the user know
|
||||
let lab1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs)
|
||||
@@ -1791,6 +1821,7 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags
|
||||
strLit.typ = strType
|
||||
c.genLit(strLit, msgReg)
|
||||
c.gABC(n, opcInvalidField, msgReg, discVal)
|
||||
c.freeTemp(discVal)
|
||||
c.freeTemp(msgReg)
|
||||
c.patch(lab1)
|
||||
|
||||
@@ -1864,10 +1895,10 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
|
||||
result = newNodeIT(nkUIntLit, info, t)
|
||||
of tyFloat..tyFloat128:
|
||||
result = newNodeIT(nkFloatLit, info, t)
|
||||
of tyCstring, tyString:
|
||||
of tyString:
|
||||
result = newNodeIT(nkStrLit, info, t)
|
||||
result.strVal = ""
|
||||
of tyVar, tyLent, tyPointer, tyPtr, tyUntyped,
|
||||
of tyCstring, tyVar, tyLent, tyPointer, tyPtr, tyUntyped,
|
||||
tyTyped, tyTypeDesc, tyRef, tyNil:
|
||||
result = newNodeIT(nkNilLit, info, t)
|
||||
of tyProc:
|
||||
@@ -2010,6 +2041,8 @@ proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||
c.freeTemp(a)
|
||||
|
||||
proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||
if tfUnion in n.typ.flags: # bug #22708 # bug #13481
|
||||
globalError(c.config, n.info, "object with '{.union.}' pragmas is not supported by VM")
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
let t = n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc})
|
||||
if t.kind == tyRef:
|
||||
@@ -2079,8 +2112,13 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
let s = n.sym
|
||||
checkCanEval(c, n)
|
||||
case s.kind
|
||||
of skVar, skForVar, skTemp, skLet, skParam, skResult:
|
||||
of skVar, skForVar, skTemp, skLet, skResult:
|
||||
genRdVar(c, n, dest, flags)
|
||||
of skParam:
|
||||
if s.typ.kind == tyTypeDesc:
|
||||
genTypeLit(c, s.typ.skipTypes({tyTypeDesc}), dest)
|
||||
else:
|
||||
genRdVar(c, n, dest, flags)
|
||||
of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator:
|
||||
# 'skTemplate' is only allowed for 'getAst' support:
|
||||
if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure:
|
||||
@@ -2182,8 +2220,6 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
of nkVarSection, nkLetSection:
|
||||
unused(c, n, dest)
|
||||
genVarSection(c, n)
|
||||
of declarativeDefs, nkMacroDef:
|
||||
unused(c, n, dest)
|
||||
of nkLambdaKinds:
|
||||
#let s = n[namePos].sym
|
||||
#discard genProc(c, s)
|
||||
@@ -2203,7 +2239,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
dest = tmp0
|
||||
of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
|
||||
nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
|
||||
nkMixinStmt, nkBindStmt:
|
||||
nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef:
|
||||
unused(c, n, dest)
|
||||
of nkStringToCString, nkCStringToString:
|
||||
gen(c, n[0], dest)
|
||||
|
||||
@@ -286,7 +286,7 @@ proc loadAny(p: var JsonParser, t: PType,
|
||||
|
||||
proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator): PNode =
|
||||
var tab = initTable[BiggestInt, PNode]()
|
||||
var p: JsonParser
|
||||
var p: JsonParser = default(JsonParser)
|
||||
open(p, newStringStream(s), "unknown file")
|
||||
next(p)
|
||||
result = loadAny(p, t, tab, cache, conf, idgen)
|
||||
|
||||
@@ -91,28 +91,36 @@ type
|
||||
wRedefine = "redefine", wCallsite = "callsite",
|
||||
wQuirky = "quirky",
|
||||
|
||||
# codegen keywords, but first the ones that are also pragmas:
|
||||
wExtern = "extern", wGoto = "goto", wRegister = "register",
|
||||
wUnion = "union", wPacked = "packed", wVirtual = "virtual",
|
||||
wVolatile = "volatile", wMember = "member",
|
||||
wByCopy = "bycopy", wByRef = "byref",
|
||||
|
||||
# codegen keywords but not pragmas:
|
||||
wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char",
|
||||
wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default",
|
||||
wDelete = "delete", wDouble = "double", wDynamicCast = "dynamic_cast",
|
||||
wExplicit = "explicit", wExtern = "extern", wFalse = "false", wFloat = "float",
|
||||
wFriend = "friend", wGoto = "goto", wInt = "int", wLong = "long", wMutable = "mutable",
|
||||
wExplicit = "explicit", wFalse = "false", wFloat = "float",
|
||||
wFriend = "friend", wInt = "int", wLong = "long", wMutable = "mutable",
|
||||
wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private",
|
||||
wProtected = "protected", wPublic = "public", wRegister = "register",
|
||||
wProtected = "protected", wPublic = "public",
|
||||
wReinterpretCast = "reinterpret_cast", wRestrict = "restrict", wShort = "short",
|
||||
wSigned = "signed", wSizeof = "sizeof", wStaticCast = "static_cast", wStruct = "struct",
|
||||
wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef",
|
||||
wTypeid = "typeid", wTypeof = "typeof", wTypename = "typename",
|
||||
wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual",
|
||||
wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t",
|
||||
wUnsigned = "unsigned", wVoid = "void",
|
||||
|
||||
wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype",
|
||||
wNullptr = "nullptr", wNoexcept = "noexcept",
|
||||
wThreadLocal = "thread_local", wStaticAssert = "static_assert",
|
||||
wChar16 = "char16_t", wChar32 = "char32_t",
|
||||
wChar16 = "char16_t", wChar32 = "char32_t", wWchar = "wchar_t",
|
||||
|
||||
wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr",
|
||||
|
||||
wInOut = "inout", wByCopy = "bycopy", wByRef = "byref", wOneWay = "oneway",
|
||||
wInOut = "inout", wOneWay = "oneway",
|
||||
# end of codegen keywords
|
||||
|
||||
wBitsize = "bitsize", wImportHidden = "all",
|
||||
wSendable = "sendable"
|
||||
|
||||
@@ -125,12 +133,15 @@ const
|
||||
nimKeywordsLow* = ord(wAsm)
|
||||
nimKeywordsHigh* = ord(wYield)
|
||||
|
||||
ccgKeywordsLow* = ord(wAuto)
|
||||
ccgKeywordsLow* = ord(wExtern)
|
||||
ccgKeywordsHigh* = ord(wOneWay)
|
||||
|
||||
cppNimSharedKeywords* = {
|
||||
wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport,
|
||||
wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing}
|
||||
|
||||
nonPragmaWordsLow* = wAuto
|
||||
nonPragmaWordsHigh* = wOneWay
|
||||
|
||||
|
||||
from std/enumutils import genEnumCaseStmt
|
||||
|
||||
@@ -14,12 +14,12 @@ cc = gcc
|
||||
# additional options always passed to the compiler:
|
||||
--parallel_build: "0" # 0 to auto-detect number of processors
|
||||
|
||||
@if nimHasAmbiguousEnumHint:
|
||||
# not needed if hint is a style check
|
||||
hint[AmbiguousEnum]=off
|
||||
@end
|
||||
#hint[XDeclaredButNotUsed]=off
|
||||
|
||||
@if not nimHasNolineTooLong:
|
||||
hint[LineTooLong]=off
|
||||
@end
|
||||
|
||||
threads:on
|
||||
|
||||
# Examples of how to setup a cross-compiler:
|
||||
|
||||
@@ -237,10 +237,10 @@ doc.file = """<?xml version="1.0" encoding="utf-8" ?>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" type="text/css" href="$nimdoccss">
|
||||
<link rel="stylesheet" type="text/css" href="${nimdoccss}?v=$nimVersion">
|
||||
|
||||
<!-- JS -->
|
||||
<script type="text/javascript" src="$dochackjs"></script>
|
||||
<script type="text/javascript" src="${dochackjs}?v=$nimVersion"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="documentId">
|
||||
|
||||
@@ -41,6 +41,9 @@ written as:
|
||||
for i in 0..<x.len: `=destroy`(x.data[i])
|
||||
dealloc(x.data)
|
||||
|
||||
proc `=wasMoved`*[T](x: var myseq[T]) =
|
||||
x.data = nil
|
||||
|
||||
proc `=trace`[T](x: var myseq[T]; env: pointer) =
|
||||
# `=trace` allows the cycle collector `--mm:orc`
|
||||
# to understand how to trace the object graph.
|
||||
@@ -51,7 +54,7 @@ written as:
|
||||
# do nothing for self-assignments:
|
||||
if a.data == b.data: return
|
||||
`=destroy`(a)
|
||||
wasMoved(a)
|
||||
`=wasMoved`(a)
|
||||
a.len = b.len
|
||||
a.cap = b.cap
|
||||
if b.data != nil:
|
||||
@@ -62,8 +65,7 @@ written as:
|
||||
proc `=dup`*[T](a: myseq[T]): myseq[T] {.nodestroy.} =
|
||||
# an optimized version of `=wasMoved(tmp); `=copy(tmp, src)`
|
||||
# usually present if a custom `=copy` hook is overridden
|
||||
result.len = a.len
|
||||
result.cap = a.cap
|
||||
result = myseq[T](len: a.len, cap: a.cap, data: nil)
|
||||
if a.data != nil:
|
||||
result.data = cast[typeof(result.data)](alloc(result.cap * sizeof(T)))
|
||||
for i in 0..<result.len:
|
||||
@@ -73,7 +75,6 @@ written as:
|
||||
# move assignment, optional.
|
||||
# Compiler is using `=destroy` and `copyMem` when not provided
|
||||
`=destroy`(a)
|
||||
wasMoved(a)
|
||||
a.len = b.len
|
||||
a.cap = b.cap
|
||||
a.data = b.data
|
||||
@@ -94,9 +95,10 @@ written as:
|
||||
x.data[i] = y
|
||||
|
||||
proc createSeq*[T](elems: varargs[T]): myseq[T] =
|
||||
result.cap = elems.len
|
||||
result.len = elems.len
|
||||
result.data = cast[typeof(result.data)](alloc(result.cap * sizeof(T)))
|
||||
result = myseq[T](
|
||||
len: elems.len,
|
||||
cap: elems.len,
|
||||
data: cast[typeof(result.data)](alloc(result.cap * sizeof(T))))
|
||||
for i in 0..<result.len: result.data[i] = elems[i]
|
||||
|
||||
proc len*[T](x: myseq[T]): int {.inline.} = x.len
|
||||
@@ -151,6 +153,25 @@ but this of little utility as a raising destructor is implementation defined
|
||||
behavior. Later versions of the language specification might cover this case precisely.
|
||||
|
||||
|
||||
`=wasMoved` hook
|
||||
----------------
|
||||
|
||||
A `=wasMoved` hook sets the object to a state that signifies to the destructor there is nothing to destroy.
|
||||
|
||||
The prototype of this hook for a type `T` needs to be:
|
||||
|
||||
```nim
|
||||
proc `=wasMoved`(x: var T)
|
||||
```
|
||||
|
||||
Usually some pointer field inside the object is set to `nil`:
|
||||
|
||||
```nim
|
||||
proc `=wasMoved`(x: var T) =
|
||||
x.field = nil
|
||||
```
|
||||
|
||||
|
||||
`=sink` hook
|
||||
------------
|
||||
|
||||
@@ -272,21 +293,11 @@ The general pattern in using `=destroy` with `=trace` looks like:
|
||||
**Note**: The `=trace` hooks (which are only used by `--mm:orc`) are currently more experimental and less refined
|
||||
than the other hooks.
|
||||
|
||||
`=WasMoved` hook
|
||||
----------------
|
||||
|
||||
A `wasMoved` hook resets the memory of an object to its initial (binary zero) value to signify it was "moved" and to signify its destructor should do nothing and ideally be optimized away.
|
||||
|
||||
The prototype of this hook for a type `T` needs to be:
|
||||
|
||||
```nim
|
||||
proc `=wasMoved`(x: var T)
|
||||
```
|
||||
|
||||
`=dup` hook
|
||||
-----------
|
||||
|
||||
A `=dup` hook duplicates the memory of an object. `=dup(x)` can be regarded as an optimization replacing the `wasMoved(dest); =copy(dest, x)` operation.
|
||||
A `=dup` hook duplicates an object. `=dup(x)` can be regarded as an optimization replacing a `wasMoved(dest); =copy(dest, x)` operation.
|
||||
|
||||
The prototype of this hook for a type `T` needs to be:
|
||||
|
||||
@@ -317,6 +328,24 @@ document uses the notation `lastReadOf(x)` to describe that `x` is not
|
||||
used afterward. This property is computed by a static control flow analysis
|
||||
but can also be enforced by using `system.move` explicitly.
|
||||
|
||||
One can query if the analysis is able to perform a move with `system.ensureMove`.
|
||||
`move` enforces a move operation and calls `=wasMoved` whereas `ensureMove` is
|
||||
an annotation that implies no runtime operation. An `ensureMove` annotation leads to a static error
|
||||
if the compiler cannot prove that a move would be safe.
|
||||
|
||||
For example:
|
||||
|
||||
```nim
|
||||
proc main(normalParam: string; sinkParam: sink string) =
|
||||
var x = "abc"
|
||||
# valid:
|
||||
let valid = ensureMove x
|
||||
# invalid:
|
||||
let invalid = ensureMove normalParam
|
||||
# valid:
|
||||
let alsoValid = ensureMove sinkParam
|
||||
```
|
||||
|
||||
|
||||
Swap
|
||||
====
|
||||
@@ -434,7 +463,7 @@ destroyed at the scope exit.
|
||||
x = lastReadOf z
|
||||
------------------ (move-optimization)
|
||||
`=sink`(x, z)
|
||||
wasMoved(z)
|
||||
`=wasMoved`(z)
|
||||
|
||||
|
||||
v = v
|
||||
@@ -461,7 +490,7 @@ destroyed at the scope exit.
|
||||
f_sink(lastReadOf y)
|
||||
----------------------- (move-to-sink)
|
||||
f_sink(y)
|
||||
wasMoved(y)
|
||||
`=wasMoved`(y)
|
||||
|
||||
|
||||
Object and array construction
|
||||
@@ -474,7 +503,7 @@ function has `sink` parameters.
|
||||
Destructor removal
|
||||
==================
|
||||
|
||||
`wasMoved(x);` followed by a `=destroy(x)` operation cancel each other
|
||||
`=wasMoved(x)` followed by a `=destroy(x)` operation cancel each other
|
||||
out. An implementation is encouraged to exploit this in order to improve
|
||||
efficiency and code sizes. The current implementation does perform this
|
||||
optimization.
|
||||
@@ -483,11 +512,11 @@ optimization.
|
||||
Self assignments
|
||||
================
|
||||
|
||||
`=sink` in combination with `wasMoved` can handle self-assignments but
|
||||
`=sink` in combination with `=wasMoved` can handle self-assignments but
|
||||
it's subtle.
|
||||
|
||||
The simple case of `x = x` cannot be turned
|
||||
into `=sink(x, x); wasMoved(x)` because that would lose `x`'s value.
|
||||
into `=sink(x, x); =wasMoved(x)` because that would lose `x`'s value.
|
||||
The solution is that simple self-assignments that consist of
|
||||
|
||||
- Symbols: `x = x`
|
||||
@@ -522,10 +551,10 @@ Is transformed into:
|
||||
try:
|
||||
if cond:
|
||||
`=sink`(result, a)
|
||||
wasMoved(a)
|
||||
`=wasMoved`(a)
|
||||
else:
|
||||
`=sink`(result, b)
|
||||
wasMoved(b)
|
||||
`=wasMoved`(b)
|
||||
finally:
|
||||
`=destroy`(b)
|
||||
`=destroy`(a)
|
||||
@@ -539,10 +568,10 @@ Is transformed into:
|
||||
`=sink`(y, "xyz")
|
||||
`=sink`(x, select(true,
|
||||
let blitTmp = x
|
||||
wasMoved(x)
|
||||
`=wasMoved`(x)
|
||||
blitTmp,
|
||||
let blitTmp = y
|
||||
wasMoved(y)
|
||||
`=wasMoved`(y)
|
||||
blitTmp))
|
||||
echo [x]
|
||||
finally:
|
||||
@@ -578,7 +607,7 @@ for expressions of type `lent T` or of type `var T`.
|
||||
proc construct(kids: sink seq[Tree]): Tree =
|
||||
result = Tree(kids: kids)
|
||||
# converted into:
|
||||
`=sink`(result.kids, kids); wasMoved(kids)
|
||||
`=sink`(result.kids, kids); `=wasMoved`(kids)
|
||||
`=destroy`(kids)
|
||||
|
||||
proc `[]`*(x: Tree; i: int): lent Tree =
|
||||
|
||||
@@ -192,7 +192,7 @@ conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
|
||||
&IND{>} stmt
|
||||
typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue
|
||||
indAndComment?
|
||||
varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')'
|
||||
varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)?
|
||||
varTuple = varTupleLhs '=' optInd expr
|
||||
colonBody = colcom stmt postExprBlocks?
|
||||
variable = (varTuple / identColonEquals) colonBody? indAndComment
|
||||
|
||||
@@ -2634,7 +2634,9 @@ of the argument.
|
||||
range.
|
||||
3. Generic match: `f` is a generic type and `a` matches, for
|
||||
instance `a` is `int` and `f` is a generic (constrained) parameter
|
||||
type (like in `[T]` or `[T: int|char]`).
|
||||
type (like in `[T]` or `[T: int|char]`). Constraints given an alias (as in `T`)
|
||||
shall be used to define `f`, when `f` is composed of `T`, following simple variable
|
||||
substitution.
|
||||
4. Subrange or subtype match: `a` is a `range[T]` and `T`
|
||||
matches `f` exactly. Or: `a` is a subtype of `f`.
|
||||
5. Integral conversion match: `a` is convertible to `f` and `f` and `a`
|
||||
@@ -2646,9 +2648,8 @@ of the argument.
|
||||
There are two major methods of selecting the best matching candidate, namely
|
||||
counting and disambiguation. Counting takes precedence to disambiguation. In counting,
|
||||
each parameter is given a category and the number of parameters in each category is counted.
|
||||
The categories are listed above and are in order of precedence. For example, if
|
||||
a candidate with one exact match is compared to a candidate with multiple generic matches
|
||||
and zero exact matches, the candidate with an exact match will win.
|
||||
For example, if a candidate with one exact match is compared to a candidate with multiple
|
||||
generic matches and zero exact matches, the candidate with an exact match will win.
|
||||
|
||||
In the following, `count(p, m)` counts the number of matches of the matching category `m`
|
||||
for the routine `p`.
|
||||
@@ -2668,10 +2669,12 @@ algorithm returns true:
|
||||
return "ambiguous"
|
||||
```
|
||||
|
||||
When counting is ambiguous, disambiguation begins. Parameters are iterated
|
||||
by position and these parameter pairs are compared for their type relation. The general goal
|
||||
of this comparison is to determine which parameter is more specific. The types considered are
|
||||
not of the inputs from the callsite, but of the competing candidates' parameters.
|
||||
When counting is ambiguous, disambiguation begins. Disambiguation also has two stages, first a
|
||||
hierarchical type relation comparison, and if that is inconclusive, a complexity comparison.
|
||||
Where counting relates the type of the operand to the formal parameter, disambiguation relates the
|
||||
formal parameters with each other to find the most competitive choice.
|
||||
Parameters are iterated by position and these parameter pairs are compared for their type
|
||||
relation. The general goal of this comparison is to determine which parameter is least general.
|
||||
|
||||
|
||||
Some examples:
|
||||
@@ -2691,7 +2694,6 @@ Some examples:
|
||||
```
|
||||
|
||||
|
||||
If this algorithm returns "ambiguous" further disambiguation is performed:
|
||||
If the argument `a` matches both the parameter type `f` of `p`
|
||||
and `g` of `q` via a subtyping relation, the inheritance depth is taken
|
||||
into account:
|
||||
@@ -2733,6 +2735,23 @@ matches) is preferred:
|
||||
gen(ri) # "ref T"
|
||||
```
|
||||
|
||||
Type variables match
|
||||
----------------------
|
||||
|
||||
When overload resolution is considering candidates, the type variable's definition
|
||||
is not overlooked as it is used to define the formal parameter's type via variable substitution.
|
||||
|
||||
For example:
|
||||
```nim
|
||||
type A
|
||||
proc p[T: A](param: T)
|
||||
proc p[T: object](param: T)
|
||||
```
|
||||
|
||||
These signatures are not ambiguous for an instance of `A` even though the formal parameters match ("T" == "T").
|
||||
Instead `T` is treated as a variable in that (`T` ?= `T`) depending on the bound type of `T` at the time of
|
||||
overload resolution.
|
||||
|
||||
|
||||
Overloading based on 'var T'
|
||||
--------------------------------------
|
||||
@@ -3726,9 +3745,6 @@ type conversions to unsigned integers and between unsigned integers. The
|
||||
rationale for this is mostly better interoperability with the C Programming
|
||||
language when algorithms are ported from C to Nim.
|
||||
|
||||
Exception: Values that are converted to an unsigned type at compile time
|
||||
are checked so that code like `byte(-1)` does not compile.
|
||||
|
||||
**Note**: Historically the operations
|
||||
were unchecked and the conversions were sometimes checked but starting with
|
||||
the revision 1.0.4 of this document and the language implementation the
|
||||
@@ -5067,7 +5083,7 @@ It is possible to raise/catch imported C++ exceptions. Types imported using
|
||||
`importcpp` can be raised or caught. Exceptions are raised by value and
|
||||
caught by reference. Example:
|
||||
|
||||
```nim test = "nim cpp -r $1"
|
||||
```nim
|
||||
type
|
||||
CStdException {.importcpp: "std::exception", header: "<exception>", inheritable.} = object
|
||||
## does not inherit from `RootObj`, so we use `inheritable` instead
|
||||
@@ -5358,6 +5374,8 @@ To override the compiler's side effect analysis a `{.noSideEffect.}`
|
||||
**Side effects are usually inferred. The inference for side effects is
|
||||
analogous to the inference for exception tracking.**
|
||||
|
||||
When the compiler cannot infer side effects, as is the case for imported
|
||||
functions, one can annotate them with the `sideEffect` pragma.
|
||||
|
||||
GC safety effect
|
||||
----------------
|
||||
@@ -5423,6 +5441,7 @@ Generics are Nim's means to parametrize procs, iterators or types with
|
||||
`type parameters`:idx:. Depending on the context, the brackets are used either to
|
||||
introduce type parameters or to instantiate a generic proc, iterator, or type.
|
||||
|
||||
|
||||
The following example shows how a generic binary tree can be modeled:
|
||||
|
||||
```nim test = "nim c $1"
|
||||
@@ -5517,7 +5536,7 @@ type Foo[T] = object
|
||||
proc p[H;T: Foo[H]](param: T): H
|
||||
```
|
||||
|
||||
A constraint definition may have more than one symbol defined by seperating each definition by
|
||||
A constraint definition may have more than one symbol defined by separating each definition by
|
||||
a `;`. Notice how `T` is composed of `H` and the return type of `p` is defined as `H`. When this
|
||||
generic proc is instantiated `H` will be bound to a concrete type, thus making `T` concrete and
|
||||
the return type of `p` will be bound to the same concrete type used to define `H`.
|
||||
@@ -5800,7 +5819,7 @@ at definition and the context at instantiation are considered:
|
||||
echo a == b # works!
|
||||
```
|
||||
|
||||
In the example, the generic `==` for tuples (as defined in the system module)
|
||||
In the example, the [generic `==` for tuples](system.html#%3D%3D%2CT%2CT_2) (as defined in the system module)
|
||||
uses the `==` operators of the tuple's components. However, the `==` for
|
||||
the `Index` type is defined *after* the `==` for tuples; yet the example
|
||||
compiles as the instantiation takes the currently defined symbols into account
|
||||
@@ -6152,9 +6171,12 @@ scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas:
|
||||
`gensym`'ed symbols are not exposed but `inject`'ed symbols are.
|
||||
|
||||
The default for symbols of entity `type`, `var`, `let` and `const`
|
||||
is `gensym` and for `proc`, `iterator`, `converter`, `template`,
|
||||
`macro` is `inject`. However, if the name of the entity is passed as a
|
||||
template parameter, it is an `inject`'ed symbol:
|
||||
is `gensym`. For `proc`, `iterator`, `converter`, `template`,
|
||||
`macro`, the default is `inject`, but if a `gensym` symbol with the same name
|
||||
is defined in the same syntax-level scope, it will be `gensym` by default.
|
||||
This can be overriden by marking the routine as `inject`.
|
||||
|
||||
If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol:
|
||||
|
||||
```nim
|
||||
template withFile(f, fn, mode: untyped, actions: untyped): untyped =
|
||||
|
||||
@@ -838,6 +838,7 @@ The concept matches if:
|
||||
|
||||
a) all expressions within the body can be compiled for the tested type
|
||||
b) all statically evaluable boolean expressions in the body are true
|
||||
c) all type modifiers specified match their respective definitions
|
||||
|
||||
The identifiers following the `concept` keyword represent instances of the
|
||||
currently matched type. You can apply any of the standard type modifiers such
|
||||
@@ -2323,3 +2324,97 @@ proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} =
|
||||
In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropiate constructor.
|
||||
|
||||
Notice when calling a constructor in the section of a global variable initialization, it will be called before `NimMain` meaning Nim is not fully initialized.
|
||||
|
||||
Injected symbols in generic procs and templates
|
||||
===============================================
|
||||
|
||||
With the experimental option `openSym`, captured symbols in generic routine and
|
||||
template bodies may 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 options, 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"
|
||||
```
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ where code size matters and you know that your code does not produce cycles, you
|
||||
use `--mm:arc`. Notice that the default `async`:idx: implementation produces cycles
|
||||
and leaks memory with `--mm:arc`, in other words, for `async` you need to use `--mm:orc`.
|
||||
|
||||
Only ARC/ORC support move semantics, destructors, `sink`, `cursor`, `acyclic`.
|
||||
|
||||
|
||||
Other MM modes
|
||||
|
||||
38
doc/nep1.md
38
doc/nep1.md
@@ -256,36 +256,46 @@ Coding Conventions
|
||||
Conventions for multi-line statements and expressions
|
||||
-----------------------------------------------------
|
||||
|
||||
- Tuples which are longer than one line should indent their parameters to
|
||||
align with the parameters above it.
|
||||
- Tuples which are longer than one line should indent their parameters.
|
||||
|
||||
```nim
|
||||
type
|
||||
LongTupleA = tuple[wordyTupleMemberOne: int, wordyTupleMemberTwo: string,
|
||||
wordyTupleMemberThree: float]
|
||||
LongTupleA = tuple[
|
||||
wordyTupleMemberOne: int, wordyTupleMemberTwo: string,
|
||||
wordyTupleMemberThree: float]
|
||||
```
|
||||
|
||||
- Similarly, any procedure and procedure type declarations that are longer
|
||||
than one line should do the same thing.
|
||||
than one line should do the same thing. Double indent may be used to
|
||||
distinguish them from the body that follows - this applies to all constructs
|
||||
with a body (if, while, etc).
|
||||
|
||||
```nim
|
||||
type
|
||||
EventCallback = proc (timeReceived: Time, errorCode: int, event: Event,
|
||||
output: var string)
|
||||
EventCallback = proc(
|
||||
timeReceived: Time, errorCode: int, event: Event,
|
||||
output: var string)
|
||||
|
||||
proc lotsOfArguments(argOne: string, argTwo: int, argThree: float,
|
||||
argFour: proc(), argFive: bool): int
|
||||
{.heyLookALongPragma.} =
|
||||
proc lotsOfArguments(
|
||||
argOne: string, argTwo: int, argThree: float,
|
||||
argFour: proc(), argFive: bool, argSix: int
|
||||
): GenericType[int, string] {.heyLookALongPragma.} =
|
||||
discard
|
||||
```
|
||||
|
||||
- Multi-line procedure calls should continue on the same column as the opening
|
||||
parenthesis (like multi-line procedure declarations).
|
||||
- Multi-line procedure calls should continue indented (like multi-line procedure
|
||||
declarations).
|
||||
|
||||
```nim
|
||||
startProcess(nimExecutable, currentDirectory, compilerArguments
|
||||
environment, processOptions)
|
||||
startProcess(
|
||||
nimExecutable, currentDirectory, compilerArguments
|
||||
environment, processOptions)
|
||||
```
|
||||
|
||||
Previous versions of this guide advocated vertical alignment along the opening
|
||||
brace / parenthesis - both styles are permissible with a preference for the
|
||||
current style in new code.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
||||
|
||||
@@ -503,9 +503,6 @@ To link against ``nimrtl.dll`` use the command:
|
||||
nim c -d:useNimRtl myprog.nim
|
||||
```
|
||||
|
||||
**Note**: Currently the creation of ``nimrtl.dll`` with thread support has
|
||||
never been tested and is unlikely to work!
|
||||
|
||||
|
||||
Additional compilation switches
|
||||
===============================
|
||||
|
||||
@@ -166,7 +166,7 @@ An example:
|
||||
n.strVal = ""
|
||||
```
|
||||
|
||||
As can been seen from the example, an advantage to an object hierarchy is that
|
||||
As can be seen from the example, an advantage to an object hierarchy is that
|
||||
no conversion between different object types is needed. Yet, access to invalid
|
||||
object fields raises an exception.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user