mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 03:13:41 +00:00
Compare commits
307 Commits
pr_extend_
...
v2.0.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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-12
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
8
.github/workflows/ci_packages.yml
vendored
8
.github/workflows/ci_packages.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-11]
|
||||
os: [ubuntu-20.04, macos-12]
|
||||
cpu: [amd64]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
@@ -32,10 +32,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 16.x'
|
||||
uses: actions/setup-node@v3
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
|
||||
6
.github/workflows/ci_publish.yml
vendored
6
.github/workflows/ci_publish.yml
vendored
@@ -21,10 +21,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 16.x'
|
||||
uses: actions/setup-node@v3
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
|
||||
@@ -29,10 +29,10 @@ jobs:
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-11'
|
||||
vmImage: 'macOS-12'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-11'
|
||||
vmImage: 'macOS-12'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
|
||||
45
changelog.md
45
changelog.md
@@ -11,6 +11,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 +27,45 @@
|
||||
|
||||
|
||||
|
||||
- An experimental option `genericsOpenSym` has been added to allow captured
|
||||
symbols in generic routine bodies to be replaced by symbols injected locally
|
||||
by templates/macros at instantiation time. `bind` may be used to keep the
|
||||
captured symbols over the injected ones regardless of enabling the option.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`genericsOpenSym` needs to be enabled, and a warning is given in the case
|
||||
where an injected symbol would replace a captured symbol not bound by `bind`
|
||||
and the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped) =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:genericsOpenSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
{.experimental: "genericsOpenSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
```
|
||||
|
||||
## Compiler changes
|
||||
|
||||
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# abstract syntax tree + symbol table
|
||||
|
||||
import
|
||||
lineinfos, hashes, options, ropes, idents, int128, tables
|
||||
lineinfos, hashes, options, ropes, idents, int128, tables, wordrecg
|
||||
from strutils import toLowerAscii
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
@@ -231,7 +231,7 @@ type
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
TSymFlag* = enum # 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 +315,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 +517,7 @@ type
|
||||
nfFirstWrite # this node is a first write
|
||||
nfHasComment # node has a comment
|
||||
nfSkipFieldChecking # node skips field visable checking
|
||||
nfOpenSym # node is a captured sym but can be overriden by local symbols
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47)
|
||||
@@ -589,6 +591,7 @@ type
|
||||
tfEffectSystemWorkaround
|
||||
tfIsOutParam
|
||||
tfSendable
|
||||
tfImplicitStatic
|
||||
|
||||
TTypeFlags* = set[TTypeFlag]
|
||||
|
||||
@@ -638,7 +641,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 +901,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 +1090,8 @@ const
|
||||
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
|
||||
nfFromTemplate, nfDefaultRefsParam,
|
||||
nfExecuteOnReload, nfLastRead,
|
||||
nfFirstWrite, nfSkipFieldChecking}
|
||||
nfFirstWrite, nfSkipFieldChecking,
|
||||
nfOpenSym}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
@@ -2014,7 +2019,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 +2027,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 +2070,12 @@ proc isClosureIterator*(typ: PType): bool {.inline.} =
|
||||
proc isClosure*(typ: PType): bool {.inline.} =
|
||||
typ.kind == tyProc and typ.callConv == ccClosure
|
||||
|
||||
proc isNimcall*(s: PSym): bool {.inline.} =
|
||||
s.typ.callConv == ccNimCall
|
||||
|
||||
proc isExplicitCallConv*(s: PSym): bool {.inline.} =
|
||||
tfExplicitCallConv in s.typ.flags
|
||||
|
||||
proc isSinkParam*(s: PSym): bool {.inline.} =
|
||||
s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)
|
||||
|
||||
|
||||
@@ -24,6 +24,12 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
||||
result.add "]"
|
||||
|
||||
proc addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
result.add " [$1" % typ.kind.toHumanStr
|
||||
if typ.sym != nil:
|
||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
||||
result.add "]"
|
||||
|
||||
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ proc canRaiseDisp(p: BProc; n: PNode): bool =
|
||||
|
||||
proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
|
||||
proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
|
||||
result = false
|
||||
var n = le
|
||||
while true:
|
||||
# do NOT follow nkHiddenDeref here!
|
||||
@@ -82,6 +83,10 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if typ[0] != nil:
|
||||
var flags: TAssignmentFlags = {}
|
||||
if typ[0].kind in {tyOpenArray, tyVarargs}:
|
||||
# perhaps generate no temp if the call doesn't have side effects
|
||||
flags.incl needTempForOpenArray
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if params.len != 0: pl.add(", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
@@ -129,7 +134,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
genAssignment(p, d, list, flags) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
@@ -137,7 +142,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, tmp, list, {}) # no need for deep copying
|
||||
genAssignment(p, tmp, list, flags) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
else:
|
||||
@@ -166,7 +171,10 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||
genBoundsCheck(p, a, b, c)
|
||||
if prepareForMutation:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr})
|
||||
# bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
|
||||
# are mapped into ctPtrToArray, the dereference of which is skipped
|
||||
# in the `genref`. We need to skip these ptrs here
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
|
||||
let dest = getTypeDesc(p.module, destType)
|
||||
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
||||
case ty.kind
|
||||
@@ -301,7 +309,16 @@ 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]
|
||||
@@ -313,6 +330,11 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
addRdLoc(a, result)
|
||||
else:
|
||||
initLocExprSingleUse(p, n, a)
|
||||
if param.typ.kind in abstractPtrs:
|
||||
let typ = skipTypes(param.typ, abstractPtrs)
|
||||
if typ.sym != nil and sfImportc in typ.sym.flags:
|
||||
a.r = "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
|
||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
||||
#assert result != nil
|
||||
|
||||
@@ -394,9 +416,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",
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -1876,9 +1891,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, m, b, c)
|
||||
if op == mHigh:
|
||||
putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)", [rdLoc(b), rdLoc(c)]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)]))
|
||||
else:
|
||||
putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1)+1)", [rdLoc(b), rdLoc(c)]))
|
||||
else:
|
||||
if not reifiedOpenArray(a):
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
|
||||
@@ -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]
|
||||
|
||||
@@ -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])
|
||||
@@ -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]
|
||||
@@ -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,7 +1059,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
for it in n:
|
||||
result = allPathsAsgnResult(it)
|
||||
result = allPathsAsgnResult(p, it)
|
||||
if result != Unknown: return result
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
if n[0].kind == nkSym and n[0].sym.kind == skResult:
|
||||
@@ -1056,6 +1067,8 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
else: result = InitRequired
|
||||
elif containsResult(n):
|
||||
result = InitRequired
|
||||
else:
|
||||
result = allPathsAsgnResult(p, n[1])
|
||||
of nkReturnStmt:
|
||||
if n.len > 0:
|
||||
if n[0].kind == nkEmpty and result != InitSkippable:
|
||||
@@ -1064,7 +1077,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 +1103,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 +1130,17 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
allPathsInBranch(n[0])
|
||||
for i in 1..<n.len:
|
||||
if n[i].kind == nkFinally:
|
||||
result = allPathsAsgnResult(n[i].lastSon)
|
||||
result = allPathsAsgnResult(p, n[i].lastSon)
|
||||
else:
|
||||
allPathsInBranch(n[i].lastSon)
|
||||
of nkCallKinds:
|
||||
if canRaiseDisp(p, n[0]):
|
||||
result = InitRequired
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
allPathsInBranch(n[i])
|
||||
of nkRaiseStmt:
|
||||
result = InitRequired
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
allPathsInBranch(n[i])
|
||||
@@ -1175,7 +1196,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 +1216,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 +1802,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 +1843,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 +1907,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 +1958,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 +2020,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
|
||||
|
||||
@@ -158,3 +158,6 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimAllowNonVarDestructor")
|
||||
defineSymbol("nimHasQuirky")
|
||||
defineSymbol("nimHasEnsureMove")
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
@@ -312,7 +315,7 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
|
||||
|
||||
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc:
|
||||
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
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 +408,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,18 +441,19 @@ 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,
|
||||
@@ -463,7 +470,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
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 +479,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
|
||||
@@ -587,7 +594,9 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
|
||||
# tricky because you would have to intercept moveOrCopy at a certain point
|
||||
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
|
||||
tmp.sym.flags = tmpFlags
|
||||
let cpy = if hasDestructor(c, ret.typ):
|
||||
let cpy = if hasDestructor(c, ret.typ) and
|
||||
ret.typ.kind notin {tyOpenArray, tyVarargs}:
|
||||
# bug #23247 we don't own the data, so it's harmful to destroy it
|
||||
s.parent[].final.add c.genDestroy(tmp)
|
||||
moveOrCopy(tmp, ret, c, s, {IsDecl})
|
||||
else:
|
||||
@@ -760,7 +769,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)
|
||||
@@ -850,7 +871,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 +917,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 +968,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
||||
cycleCheck(n, c)
|
||||
assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn}
|
||||
let flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
|
||||
var flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
|
||||
if inReturn:
|
||||
flags.incl(IsReturn)
|
||||
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
|
||||
elif isDiscriminantField(n[0]):
|
||||
result = c.genDiscriminantAsgn(s, n)
|
||||
@@ -1028,7 +1054,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
of nkReturnStmt:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = p(n[i], c, s, mode)
|
||||
result[i] = p(n[i], c, s, mode, inReturn=true)
|
||||
s.needsTry = true
|
||||
of nkCast:
|
||||
result = shallowCopy(n)
|
||||
|
||||
@@ -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,
|
||||
@@ -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; isDistinct = false): PSym =
|
||||
if kind == attachedDup:
|
||||
return symDupPrototype(g, typ, owner, kind, info, idgen)
|
||||
|
||||
@@ -1069,7 +1087,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
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 typ.kind in {tyRef, tyString, tySequence} and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and not isDistinct:
|
||||
dest.typ = typ
|
||||
else:
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
@@ -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)
|
||||
@@ -1214,7 +1232,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
|
||||
else:
|
||||
localError(g.config, info, "unresolved generic parameter")
|
||||
|
||||
proc isTrival(s: PSym): bool {.inline.} =
|
||||
proc isTrival*(s: PSym): bool {.inline.} =
|
||||
s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
|
||||
|
||||
proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
|
||||
@@ -91,6 +91,7 @@ type
|
||||
warnStmtListLambda = "StmtListLambda",
|
||||
warnBareExcept = "BareExcept",
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnGenericsIgnoredInjection = "GenericsIgnoredInjection",
|
||||
warnUser = "User",
|
||||
# hints
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||
@@ -108,6 +109,7 @@ type
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
hintUnknownHint = "UnknownHint"
|
||||
|
||||
const
|
||||
MsgKindToStr*: array[TMsgKind, string] = [
|
||||
@@ -185,7 +187,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 +196,7 @@ const
|
||||
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
|
||||
warnBareExcept: "$1",
|
||||
warnImplicitDefaultValue: "$1",
|
||||
warnGenericsIgnoredInjection: "$1",
|
||||
warnUser: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
@@ -231,6 +234,7 @@ const
|
||||
hintExtendedContext: "$1",
|
||||
hintMsgOrigin: "$1",
|
||||
hintDeclaredLoc: "$1",
|
||||
hintUnknownHint: "unknown hint: $1"
|
||||
]
|
||||
|
||||
const
|
||||
|
||||
@@ -254,6 +254,41 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool =
|
||||
result = false
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
var scopeHasCandidate = false
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
if scopeHasCandidate:
|
||||
# 2 candidates in same scope, ambiguous
|
||||
return true
|
||||
else:
|
||||
scopeHasCandidate = true
|
||||
sym = candidate
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
if scopeHasCandidate:
|
||||
# scope had a candidate but wasn't ambiguous
|
||||
return false
|
||||
|
||||
var importsHaveCandidate = false
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
if importsHaveCandidate:
|
||||
# 2 candidates among imports, ambiguous
|
||||
return true
|
||||
else:
|
||||
importsHaveCandidate = true
|
||||
sym = s
|
||||
if importsHaveCandidate:
|
||||
# imports had a candidate but wasn't ambiguous
|
||||
return false
|
||||
|
||||
proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
## creates an error symbol to avoid cascading errors (for IDE support)
|
||||
var m = n
|
||||
@@ -531,7 +566,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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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,8 @@ type
|
||||
unicodeOperators, # deadcode
|
||||
flexibleOptionalParams,
|
||||
strictDefs,
|
||||
strictCaseObjects
|
||||
strictCaseObjects,
|
||||
genericsOpenSym
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
@@ -280,9 +281,24 @@ type
|
||||
version*: int
|
||||
endLine*: uint16
|
||||
endCol*: int
|
||||
inlayHintInfo*: SuggestInlayHint
|
||||
|
||||
Suggestions* = seq[Suggest]
|
||||
|
||||
SuggestInlayHintKind* = enum
|
||||
sihkType = "Type",
|
||||
sihkParameter = "Parameter"
|
||||
|
||||
SuggestInlayHint* = ref object
|
||||
kind*: SuggestInlayHintKind
|
||||
line*: int # Starts at 1
|
||||
column*: int # Starts at 0
|
||||
label*: string
|
||||
paddingLeft*: bool
|
||||
paddingRight*: bool
|
||||
allowInsert*: bool
|
||||
tooltip*: string
|
||||
|
||||
ProfileInfo* = object
|
||||
time*: float
|
||||
count*: int
|
||||
@@ -417,6 +433,8 @@ type
|
||||
expandNodeResult*: string
|
||||
expandPosition*: TLineInfo
|
||||
|
||||
clientProcessId*: int
|
||||
|
||||
|
||||
proc parseNimVersion*(a: string): NimVer =
|
||||
# could be moved somewhere reusable
|
||||
@@ -1057,6 +1075,7 @@ proc `$`*(c: IdeCmd): string =
|
||||
of ideRecompile: "recompile"
|
||||
of ideChanged: "changed"
|
||||
of ideType: "type"
|
||||
of ideInlayHints: "inlayHints"
|
||||
|
||||
proc floatInt64Align*(conf: ConfigRef): int16 =
|
||||
## Returns either 4 or 8 depending on reasons.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
@@ -848,7 +848,7 @@ proc gcase(g: var TSrcGen, n: PNode) =
|
||||
gsub(g, n[^1], c)
|
||||
|
||||
proc genSymSuffix(result: var string, s: PSym) {.inline.} =
|
||||
if sfGenSym in s.flags:
|
||||
if sfGenSym in s.flags and s.name.id != ord(wUnderscore):
|
||||
result.add '_'
|
||||
result.addInt s.id
|
||||
|
||||
@@ -874,7 +874,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 +971,9 @@ proc gident(g: var TSrcGen, n: PNode) =
|
||||
s.addInt localId
|
||||
if sfCursor in n.sym.flags:
|
||||
s.add "_cursor"
|
||||
elif n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp):
|
||||
elif n.kind == nkSym and (renderIds in g.flags or
|
||||
(sfGenSym in n.sym.flags and n.sym.name.id != ord(wUnderscore)) or
|
||||
n.sym.kind == skTemp):
|
||||
s.add '_'
|
||||
s.addInt n.sym.id
|
||||
when defined(debugMagics):
|
||||
@@ -1524,17 +1526,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)
|
||||
|
||||
@@ -26,7 +26,10 @@ when not defined(leanCompiler):
|
||||
import spawn
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/formatfloat
|
||||
import std/[
|
||||
formatfloat,
|
||||
assertions,
|
||||
]
|
||||
|
||||
# implementation
|
||||
|
||||
@@ -205,13 +208,78 @@ proc commonType*(c: PContext; x, y: PType): PType =
|
||||
result = newType(k, nextTypeId(c.idgen), r.owner)
|
||||
result.addSonSkipIntLit(r, c.idgen)
|
||||
|
||||
const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
|
||||
proc shouldCheckCaseCovered(caseTyp: PType): bool =
|
||||
result = false
|
||||
case caseTyp.kind
|
||||
of shouldChckCovered:
|
||||
result = true
|
||||
of tyRange:
|
||||
if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
|
||||
result = true
|
||||
else:
|
||||
discard
|
||||
|
||||
proc endsInNoReturn(n: PNode): bool =
|
||||
# check if expr ends in raise exception or call of noreturn proc
|
||||
## check if expr ends the block like raising or call of noreturn procs do
|
||||
result = false # assume it does return
|
||||
|
||||
template checkBranch(branch) =
|
||||
if not endsInNoReturn(branch):
|
||||
# proved a branch returns
|
||||
return false
|
||||
|
||||
var it = n
|
||||
# skip these beforehand, no special handling needed
|
||||
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
|
||||
it = it.lastSon
|
||||
result = it.kind in nkLastBlockStmts or
|
||||
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
|
||||
|
||||
case it.kind
|
||||
of nkIfStmt:
|
||||
var hasElse = false
|
||||
for branch in it:
|
||||
checkBranch:
|
||||
if branch.len == 2:
|
||||
branch[1]
|
||||
elif branch.len == 1:
|
||||
hasElse = true
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `if` statement during endsInNoReturn"
|
||||
# none of the branches returned
|
||||
result = hasElse # Only truly a no-return when it's exhaustive
|
||||
of nkCaseStmt:
|
||||
let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc})
|
||||
# semCase should already have checked for exhaustiveness in this case
|
||||
# effectively the same as having an else
|
||||
var hasElse = caseTyp.shouldCheckCaseCovered()
|
||||
|
||||
# actual noreturn checks
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
checkBranch:
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
branch[^1]
|
||||
of nkElifBranch:
|
||||
branch[1]
|
||||
of nkElse:
|
||||
hasElse = true
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `case` statement in endsInNoReturn"
|
||||
# Can only guarantee a noreturn if there is an else or it's exhaustive
|
||||
result = hasElse
|
||||
of nkTryStmt:
|
||||
checkBranch(it[0])
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
checkBranch(branch[^1])
|
||||
# none of the branches returned
|
||||
result = true
|
||||
else:
|
||||
result = it.kind in nkLastBlockStmts or
|
||||
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
|
||||
|
||||
proc commonType*(c: PContext; x: PType, y: PNode): PType =
|
||||
# ignore exception raising branches in case/if expressions
|
||||
@@ -243,6 +311,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 +322,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 +421,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 +436,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 +541,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)
|
||||
|
||||
@@ -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
|
||||
@@ -595,7 +640,12 @@ proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
else:
|
||||
x.call.add c.graph.emptyNode
|
||||
of skType:
|
||||
x.call.add newSymNode(s, n.info)
|
||||
var tn = newSymNode(s, n.info)
|
||||
# this node will be used in template substitution,
|
||||
# pretend this is an untyped node and let regular sem handle the type
|
||||
# to prevent problems where a generic parameter is treated as a value
|
||||
tn.typ = nil
|
||||
x.call.add tn
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
|
||||
@@ -665,14 +715,18 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
|
||||
onUse(info, s)
|
||||
result = newSymNode(newInst, info)
|
||||
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
assert n.kind == nkBracketExpr
|
||||
proc setGenericParams(c: PContext, n: PNode) =
|
||||
## sems generic params in subscript expression
|
||||
for i in 1..<n.len:
|
||||
let e = semExprWithType(c, n[i])
|
||||
if e.typ == nil:
|
||||
n[i].typ = errorType(c)
|
||||
else:
|
||||
n[i].typ = e.typ.skipTypes({tyTypeDesc})
|
||||
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
assert n.kind == nkBracketExpr
|
||||
setGenericParams(c, n)
|
||||
var s = s
|
||||
var a = n[0]
|
||||
if a.kind == nkSym:
|
||||
@@ -707,17 +761,25 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
else:
|
||||
result = explicitGenericInstError(c, n)
|
||||
|
||||
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] =
|
||||
# Searches for the fn in the symbol table. If the parameter lists are suitable
|
||||
# for borrowing the sym in the symbol table is returned, else nil.
|
||||
# New approach: generate fn(x, y, z) where x, y, z have the proper types
|
||||
# and use the overloading resolution mechanism:
|
||||
const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct}
|
||||
|
||||
template getType(isDistinct: bool; t: PType):untyped =
|
||||
if isDistinct: t.baseOfDistinct(c.graph, c.idgen) else: t
|
||||
|
||||
result = default(tuple[s: PSym, state: TBorrowState])
|
||||
var call = newNodeI(nkCall, fn.info)
|
||||
var hasDistinct = false
|
||||
var isDistinct: bool
|
||||
var x: PType
|
||||
var t: PType
|
||||
call.add(newIdentNode(fn.name, fn.info))
|
||||
for i in 1..<fn.typ.n.len:
|
||||
let param = fn.typ.n[i]
|
||||
const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct}
|
||||
#[.
|
||||
# We only want the type not any modifiers such as `ptr`, `var`, `ref` ...
|
||||
# tyCompositeTypeClass is here for
|
||||
@@ -726,22 +788,31 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
proc `$`(f: Foo): string {.borrow.}
|
||||
# We want to skip the `Foo` to get `int`
|
||||
]#
|
||||
let t = skipTypes(param.typ, desiredTypes)
|
||||
if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true
|
||||
var x: PType
|
||||
t = skipTypes(param.typ, desiredTypes)
|
||||
isDistinct = t.kind == tyDistinct or param.typ.kind == tyDistinct
|
||||
if t.kind == tyGenericInvocation and t[0].lastSon.kind == tyDistinct:
|
||||
result.state = bsGeneric
|
||||
return
|
||||
if isDistinct: hasDistinct = true
|
||||
if param.typ.kind == tyVar:
|
||||
x = newTypeS(param.typ.kind, c)
|
||||
x.addSonSkipIntLit(t.baseOfDistinct(c.graph, c.idgen), c.idgen)
|
||||
x.addSonSkipIntLit(getType(isDistinct, t), c.idgen)
|
||||
else:
|
||||
x = t.baseOfDistinct(c.graph, c.idgen)
|
||||
call.add(newNodeIT(nkEmpty, fn.info, x))
|
||||
x = getType(isDistinct, t)
|
||||
var s = copySym(param.sym, c.idgen)
|
||||
s.typ = x
|
||||
s.info = param.info
|
||||
call.add(newSymNode(s))
|
||||
if hasDistinct:
|
||||
let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}
|
||||
var resolved = semOverloadedCall(c, call, call, filter, {})
|
||||
if resolved != nil:
|
||||
result = resolved[0].sym
|
||||
if not compareTypes(result.typ[0], fn.typ[0], dcEqIgnoreDistinct):
|
||||
result = nil
|
||||
elif result.magic in {mArrPut, mArrGet}:
|
||||
result.s = resolved[0].sym
|
||||
result.state = bsMatch
|
||||
if not compareTypes(result.s.typ[0], fn.typ[0], dcEqIgnoreDistinct, {IgnoreFlags}):
|
||||
result.state = bsReturnNotMatch
|
||||
elif result.s.magic in {mArrPut, mArrGet}:
|
||||
# cannot borrow these magics for now
|
||||
result = nil
|
||||
result.state = bsNotSupported
|
||||
else:
|
||||
result.state = bsNoDistinct
|
||||
|
||||
@@ -168,7 +168,9 @@ 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.
|
||||
TBorrowState* = enum
|
||||
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
|
||||
|
||||
template config*(c: PContext): ConfigRef = c.graph.config
|
||||
|
||||
|
||||
@@ -54,17 +54,6 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# same as 'semExprWithType' but doesn't check for proc vars
|
||||
result = semExpr(c, n, flags + {efOperand})
|
||||
if result.typ != nil:
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
|
||||
#and tfUnresolved in result.typ.flags:
|
||||
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)
|
||||
@@ -92,7 +81,10 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType
|
||||
|
||||
proc ambiguousSymChoice(c: PContext, orig, n: PNode): PNode =
|
||||
let first = n[0].sym
|
||||
if first.kind == skEnumField:
|
||||
var foundSym: PSym = nil
|
||||
if first.kind == skEnumField and
|
||||
not isAmbiguous(c, first.name, {skEnumField}, foundSym) and
|
||||
foundSym == first:
|
||||
# choose the first resolved enum field, i.e. the latest in scope
|
||||
# to mirror behavior before overloadable enums
|
||||
if hintAmbiguousEnum in c.config.notes:
|
||||
@@ -238,6 +230,9 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
|
||||
result = convNotInRange
|
||||
else:
|
||||
# we use d, s here to speed up that operation a bit:
|
||||
if d.kind == tyFromExpr:
|
||||
result = convNotLegal
|
||||
return
|
||||
case cmpTypes(c, d, s)
|
||||
of isNone, isGeneric:
|
||||
if not compareTypes(targetTyp.skipTypes(abstractVar), srcTyp.skipTypes({tyOwned}), dcEqIgnoreDistinct):
|
||||
@@ -308,7 +303,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool =
|
||||
let s = qualifiedLookUp(c, n, {})
|
||||
result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned"
|
||||
|
||||
proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
if n.len != 2:
|
||||
localError(c.config, n.info, "a type conversion takes exactly one argument")
|
||||
return n
|
||||
@@ -355,7 +350,7 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
if n[1].kind == nkExprEqExpr and
|
||||
targetType.skipTypes(abstractPtrs).kind == tyObject:
|
||||
localError(c.config, n.info, "object construction uses ':', not '='")
|
||||
var op = semExprWithType(c, n[1])
|
||||
var op = semExprWithType(c, n[1], flags * {efDetermineType})
|
||||
if op.kind == nkClosedSymChoice and op.len > 0 and
|
||||
op[0].sym.kind == skEnumField: # resolves overloadedable enums
|
||||
op = ambiguousSymChoice(c, n, op)
|
||||
@@ -370,7 +365,9 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
# here or needs to be overwritten too then.
|
||||
result.add op
|
||||
|
||||
if targetType.kind == tyGenericParam:
|
||||
if targetType.kind == tyGenericParam or
|
||||
(op.typ != nil and op.typ.kind == tyFromExpr and c.inGenericContext > 0):
|
||||
# expression is compiled early in a generic body
|
||||
result.typ = makeTypeFromExpr(c, copyTree(result))
|
||||
return result
|
||||
|
||||
@@ -384,7 +381,8 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple:
|
||||
op = fitNode(c, targetType, op, result.info)
|
||||
of convNotNeedeed:
|
||||
message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
||||
if efNoSem2Check notin flags:
|
||||
message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
||||
of convNotLegal:
|
||||
result = fitNode(c, result.typ, result[1], result.info)
|
||||
if result == nil:
|
||||
@@ -514,8 +512,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result.typ = n.typ
|
||||
|
||||
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
if n.len != 3:
|
||||
if n.len != 3 or n[2].kind == nkEmpty:
|
||||
localError(c.config, n.info, "'is' operator takes 2 arguments")
|
||||
return errorNode(c, n)
|
||||
|
||||
let boolType = getSysType(c.graph, n.info, tyBool)
|
||||
result = n
|
||||
@@ -615,7 +614,7 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
|
||||
a.add m
|
||||
changeType(m, tup[i], check)
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if check and n.kind != nkUInt64Lit and not sameType(n.typ, newType):
|
||||
if check and n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType):
|
||||
let value = n.intVal
|
||||
if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
|
||||
localError(c.config, n.info, "cannot convert " & $value &
|
||||
@@ -996,9 +995,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 +1043,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
|
||||
of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
|
||||
of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
|
||||
else:
|
||||
semFinishOperands(c, result)
|
||||
semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut})
|
||||
activate(c, result)
|
||||
fixAbstractType(c, result)
|
||||
analyseIfAddressTakenInCall(c, result)
|
||||
@@ -1069,7 +1092,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct})
|
||||
if t != nil and t.kind == tyTypeDesc:
|
||||
if n.len == 1: return semObjConstr(c, n, flags, expectedType)
|
||||
return semConv(c, n)
|
||||
return semConv(c, n, flags)
|
||||
|
||||
let nOrig = n.copyTree
|
||||
semOpAux(c, n)
|
||||
@@ -1106,7 +1129,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
msg.addDeclaredLocMaybe(c.config, typ)
|
||||
localError(c.config, n.info, msg)
|
||||
return errorNode(c, n)
|
||||
result = nil
|
||||
else:
|
||||
result = m.call
|
||||
instGenericConvertersSons(c, result, m)
|
||||
@@ -1817,6 +1839,39 @@ proc goodLineInfo(arg: PNode): TLineInfo =
|
||||
else:
|
||||
arg.info
|
||||
|
||||
proc makeTupleAssignments(c: PContext; n: PNode): PNode =
|
||||
## expand tuple unpacking assignment into series of assignments
|
||||
##
|
||||
## mirrored with semstmts.makeVarTupleSection
|
||||
let lhs = n[0]
|
||||
let value = semExprWithType(c, n[1], {efTypeAllowed})
|
||||
if value.typ.kind != tyTuple:
|
||||
localError(c.config, n[1].info, errTupleUnpackingTupleExpected %
|
||||
[typeToString(value.typ, preferDesc)])
|
||||
elif lhs.len != value.typ.len:
|
||||
localError(c.config, n.info, errTupleUnpackingDifferentLengths %
|
||||
[$lhs.len, typeToString(value.typ, preferDesc), $value.typ.len])
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
|
||||
temp.typ = value.typ
|
||||
temp.flags.incl(sfGenSym)
|
||||
var v = newNodeI(nkLetSection, value.info)
|
||||
let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
||||
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
||||
vpart[0] = tempNode
|
||||
vpart[1] = c.graph.emptyNode
|
||||
vpart[2] = value
|
||||
v.add vpart
|
||||
result.add(v)
|
||||
|
||||
for i in 0..<lhs.len:
|
||||
if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
|
||||
# skip _ assignments if we are using a temp as they are already evaluated
|
||||
discard
|
||||
else:
|
||||
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
|
||||
|
||||
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
checkSonsLen(n, 2, c.config)
|
||||
var a = n[0]
|
||||
@@ -1845,7 +1900,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
result.add(n[1])
|
||||
if mode == noOverloadedSubscript:
|
||||
bracketNotFoundError(c, result)
|
||||
return n
|
||||
return errorNode(c, n)
|
||||
else:
|
||||
result = semExprNoType(c, result)
|
||||
return result
|
||||
@@ -1859,7 +1914,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:
|
||||
@@ -2108,6 +2163,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 =
|
||||
@@ -2244,7 +2300,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)
|
||||
@@ -2512,9 +2568,10 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
# If semCheck is set to false, ``when`` will return the verbatim AST of
|
||||
# the correct branch. Otherwise the AST will be passed through semStmt.
|
||||
result = nil
|
||||
let flags = if semCheck: {efWantStmt} else: {}
|
||||
|
||||
template setResult(e: untyped) =
|
||||
if semCheck: result = semExpr(c, e) # do not open a new scope!
|
||||
if semCheck: result = semExpr(c, e, flags) # do not open a new scope!
|
||||
else: result = e
|
||||
|
||||
# Check if the node is "when nimvm"
|
||||
@@ -2540,7 +2597,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
checkSonsLen(it, 2, c.config)
|
||||
if whenNimvm:
|
||||
if semCheck:
|
||||
it[1] = semExpr(c, it[1])
|
||||
it[1] = semExpr(c, it[1], flags)
|
||||
typ = commonType(c, typ, it[1].typ)
|
||||
result = n # when nimvm is not elimited until codegen
|
||||
else:
|
||||
@@ -2556,7 +2613,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
checkSonsLen(it, 1, c.config)
|
||||
if result == nil or whenNimvm:
|
||||
if semCheck:
|
||||
it[0] = semExpr(c, it[0])
|
||||
it[0] = semExpr(c, it[0], flags)
|
||||
typ = commonType(c, typ, it[0].typ)
|
||||
if result == nil:
|
||||
result = it[0]
|
||||
@@ -2863,6 +2920,18 @@ proc asBracketExpr(c: PContext; n: PNode): PNode =
|
||||
return result
|
||||
return nil
|
||||
|
||||
proc isOpenArraySym(x: PNode): bool =
|
||||
var x = x
|
||||
while true:
|
||||
case x.kind
|
||||
of {nkAddr, nkHiddenAddr}:
|
||||
x = x[0]
|
||||
of {nkHiddenStdConv, nkHiddenDeref}:
|
||||
x = x[1]
|
||||
else:
|
||||
break
|
||||
result = x.kind == nkSym
|
||||
|
||||
proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) =
|
||||
# This takes care of complicated signatures such as:
|
||||
# proc foo(a: int, b = a)
|
||||
@@ -2883,7 +2952,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,7 +2981,7 @@ proc getNilType(c: PContext): PType =
|
||||
result.align = c.config.target.ptrSize.int16
|
||||
c.nilTypeCache = result
|
||||
|
||||
proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
|
||||
proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode =
|
||||
var o: TOverloadIter
|
||||
var i = 0
|
||||
var a = initOverloadIter(o, c, n)
|
||||
@@ -2922,7 +2994,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
|
||||
if i <= 1:
|
||||
if sfGenSym notin s.flags:
|
||||
result = newSymNode(s, info)
|
||||
markUsed(c, info, s)
|
||||
markUsed(c, info, s, efInCall notin flags)
|
||||
onUse(info, s)
|
||||
else:
|
||||
result = n
|
||||
@@ -3012,7 +3084,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
of skEnumField:
|
||||
result = enumFieldSymChoice(c, n, s)
|
||||
result = enumFieldSymChoice(c, n, s, flags)
|
||||
else:
|
||||
result = semSym(c, n, s, flags)
|
||||
if expectedType != nil and isSymChoice(result):
|
||||
@@ -3020,9 +3092,32 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
if result.kind == nkSym:
|
||||
result = semSym(c, result, result.sym, flags)
|
||||
of nkSym:
|
||||
let s = n.sym
|
||||
if nfOpenSym in n.flags:
|
||||
let id = newIdentNode(s.name, n.info)
|
||||
c.isAmbiguous = false
|
||||
let s2 = qualifiedLookUp(c, id, {})
|
||||
if s2 != nil and s2 != s and not c.isAmbiguous:
|
||||
# only consider symbols defined under current proc:
|
||||
var o = s2.owner
|
||||
while o != nil:
|
||||
if o == c.p.owner:
|
||||
if genericsOpenSym in c.features:
|
||||
result = semExpr(c, id, flags, expectedType)
|
||||
return
|
||||
else:
|
||||
message(c.config, n.info, warnGenericsIgnoredInjection,
|
||||
"a new symbol '" & s.name.s & "' has been injected during " &
|
||||
"instantiation of " & c.p.owner.name.s & ", " &
|
||||
"however " & getSymRepr(c.config, s) & " captured at " &
|
||||
"the proc declaration will be used instead; " &
|
||||
"either enable --experimental:genericsOpenSym to use the " &
|
||||
"injected symbol or `bind` this captured symbol explicitly")
|
||||
break
|
||||
o = o.owner
|
||||
# because of the changed symbol binding, this does not mean that we
|
||||
# don't have to check the symbol for semantics here again!
|
||||
result = semSym(c, n, n.sym, flags)
|
||||
result = semSym(c, n, s, flags)
|
||||
of nkEmpty, nkNone, nkCommentStmt, nkType:
|
||||
discard
|
||||
of nkNilLit:
|
||||
@@ -3040,7 +3135,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
expected.kind in {tyInt..tyInt64,
|
||||
tyUInt..tyUInt64,
|
||||
tyFloat..tyFloat128}):
|
||||
result.typ = expected
|
||||
if expected.kind in {tyFloat..tyFloat128}:
|
||||
n.transitionIntToFloatKind(nkFloatLit)
|
||||
changeType(c, result, expectedType, check=true)
|
||||
@@ -3112,7 +3206,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
# XXX think about this more (``set`` procs)
|
||||
let ambig = c.isAmbiguous
|
||||
if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2:
|
||||
result = semConv(c, n, expectedType)
|
||||
result = semConv(c, n, flags, expectedType)
|
||||
elif ambig and n.len == 1:
|
||||
errorUseQualifier(c, n.info, s)
|
||||
elif n.len == 1:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,9 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result.transitionSonsKind(nkClosedSymChoice)
|
||||
else:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
if result.kind == nkSym and canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
@@ -95,6 +101,9 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result = n
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
of skParam:
|
||||
result = n
|
||||
@@ -103,11 +112,17 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
if (s.typ != nil) and
|
||||
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
else:
|
||||
result = n
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
|
||||
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
@@ -147,6 +162,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 +183,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 +203,18 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
|
||||
styleCheckDef(c, n.info, s, kind)
|
||||
onDef(n.info, s)
|
||||
|
||||
proc addTempDeclToIdents(c: PContext; n: PNode; kind: TSymKind; inCall: bool) =
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
if inCall:
|
||||
addTempDecl(c, n, kind)
|
||||
of nkCallKinds:
|
||||
for s in n:
|
||||
addTempDeclToIdents(c, s, kind, true)
|
||||
else:
|
||||
for s in n:
|
||||
addTempDeclToIdents(c, s, kind, inCall)
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode,
|
||||
flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
|
||||
result = n
|
||||
@@ -348,7 +387,9 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
var a = n[i]
|
||||
checkMinSonsLen(a, 1, c.config)
|
||||
for j in 0..<a.len-1:
|
||||
a[j] = semGenericStmt(c, a[j], flags, ctx)
|
||||
a[j] = semGenericStmt(c, a[j], flags+{withinMixin}, ctx)
|
||||
addTempDeclToIdents(c, a[j], skVar, false)
|
||||
|
||||
a[^1] = semGenericStmtScope(c, a[^1], flags, ctx)
|
||||
closeScope(c)
|
||||
of nkForStmt, nkParForStmt:
|
||||
@@ -445,8 +486,47 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
of nkIdent: a = n[i]
|
||||
else: illFormedAst(n, c.config)
|
||||
addDecl(c, newSymS(skUnknown, getIdentNode(c, a), c))
|
||||
of nkObjectTy, nkTupleTy, nkTupleClassTy:
|
||||
discard
|
||||
of nkTupleTy:
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
case a.kind:
|
||||
of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
|
||||
of nkIdentDefs:
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
|
||||
a[^1] = semGenericStmt(c, a[^1], flags, ctx)
|
||||
for j in 0..<a.len-2:
|
||||
addTempDecl(c, getIdentNode(c, a[j]), skField)
|
||||
else:
|
||||
illFormedAst(a, c.config)
|
||||
of nkObjectTy:
|
||||
if n.len > 0:
|
||||
openScope(c)
|
||||
for i in 0..<n.len:
|
||||
result[i] = semGenericStmt(c, n[i], flags, ctx)
|
||||
closeScope(c)
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
case a.kind:
|
||||
of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
|
||||
of nkIdentDefs:
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
|
||||
a[^1] = semGenericStmt(c, a[^1], flags, ctx)
|
||||
for j in 0..<a.len-2:
|
||||
addTempDecl(c, getIdentNode(c, a[j]), skField)
|
||||
of nkRecCase, nkRecWhen:
|
||||
n[i] = semGenericStmt(c, a, flags, ctx)
|
||||
else:
|
||||
illFormedAst(a, c.config)
|
||||
of nkRecCase:
|
||||
checkSonsLen(n[0], 3, c.config)
|
||||
n[0][^2] = semGenericStmt(c, n[0][^2], flags+{withinTypeDesc}, ctx)
|
||||
n[0][^1] = semGenericStmt(c, n[0][^1], flags, ctx)
|
||||
addTempDecl(c, getIdentNode(c, n[0][0]), skField)
|
||||
for i in 1..<n.len:
|
||||
n[i] = semGenericStmt(c, n[i], flags, ctx)
|
||||
of nkFormalParams:
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
for i in 1..<n.len:
|
||||
|
||||
@@ -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,9 +281,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
# call head symbol, because this leads to infinite recursion.
|
||||
if oldParam.ast != nil:
|
||||
var def = oldParam.ast.copyTree
|
||||
if def.kind == nkCall:
|
||||
if def.kind in nkCallKinds:
|
||||
for i in 1..<def.len:
|
||||
def[i] = replaceTypeVarsN(cl, def[i])
|
||||
def[i] = replaceTypeVarsN(cl, def[i], 1)
|
||||
|
||||
def = semExprWithType(c, def)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
@@ -364,10 +373,13 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
# see ttypeor.nim test.
|
||||
var i = 0
|
||||
newSeq(entry.concreteTypes, fn.typ.len+gp.len-1)
|
||||
# let param instantiation know we are in a concept for unresolved statics:
|
||||
c.matchedConcept = oldMatchedConcept
|
||||
for s in instantiateGenericParamList(c, gp, pt):
|
||||
addDecl(c, s)
|
||||
entry.concreteTypes[i] = s.typ
|
||||
inc i
|
||||
c.matchedConcept = nil
|
||||
pushProcCon(c, result)
|
||||
instantiateProcType(c, pt, result, info)
|
||||
for j in 1..<result.typ.len:
|
||||
|
||||
@@ -66,7 +66,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
|
||||
bracketNotFoundError(c, x)
|
||||
#localError(c.config, n.info, "could not resolve: " & $n)
|
||||
result = n
|
||||
result = errorNode(c, n)
|
||||
|
||||
proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
# rewrite `[]=`(a, i, x) back to ``a[i] = x``.
|
||||
@@ -519,7 +519,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 =
|
||||
@@ -668,7 +670,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])
|
||||
@@ -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}:
|
||||
@@ -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
|
||||
|
||||
@@ -166,6 +166,11 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
|
||||
while n.kind in skipForDiscardable:
|
||||
if n.kind == nkTryStmt: n = n[0]
|
||||
else: n = n.lastSon
|
||||
|
||||
# Ignore noreturn procs since they don't have a type
|
||||
if n.endsInNoReturn:
|
||||
return
|
||||
|
||||
var s = "expression '" & $n & "' is of type '" &
|
||||
result.typ.typeToString & "' and has to be used (or discarded)"
|
||||
if result.info.line != n.info.line or
|
||||
@@ -331,6 +336,8 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
|
||||
result.typ = typ
|
||||
if not floatRangeCheck(result.floatVal, typ):
|
||||
localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)])
|
||||
elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring:
|
||||
discard "keep nkHiddenStdConv for cstring conversions"
|
||||
else:
|
||||
changeType(c, r1, typ, check=true)
|
||||
result = r1
|
||||
@@ -350,7 +357,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 +597,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 +620,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 +650,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 +672,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
addToVarSection(c, result, b)
|
||||
continue
|
||||
|
||||
var hasUserSpecifiedType = false
|
||||
var typ: PType = nil
|
||||
if a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
hasUserSpecifiedType = true
|
||||
|
||||
var typFlags: TTypeAllowedFlags
|
||||
|
||||
@@ -730,6 +748,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 +823,11 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
addToVarSection(c, result, b)
|
||||
continue
|
||||
|
||||
var hasUserSpecifiedType = false
|
||||
var typ: PType = nil
|
||||
if a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
hasUserSpecifiedType = true
|
||||
|
||||
var typFlags: TTypeAllowedFlags
|
||||
|
||||
@@ -846,14 +868,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 +894,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 +923,10 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
if iterAfterVarLent.kind != tyTuple or n.len == 3:
|
||||
if n.len == 3:
|
||||
if n[0].kind == nkVarTuple:
|
||||
if n[0].len-1 != iterAfterVarLent.len:
|
||||
if iterAfterVarLent.kind != tyTuple:
|
||||
return localErrorNode(c, n, n[0].info, errTupleUnpackingTupleExpected %
|
||||
[typeToString(n[1].typ, preferDesc)])
|
||||
elif n[0].len-1 != iterAfterVarLent.len:
|
||||
return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables)
|
||||
|
||||
for i in 0..<n[0].len-1:
|
||||
@@ -1113,20 +1145,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 +1487,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 +1538,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 +1566,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
|
||||
tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink, tyOwned:
|
||||
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
|
||||
for i in start..<t.len:
|
||||
checkMeta(c, n, t[i])
|
||||
checkMeta(c, n, t[i], hasError, t)
|
||||
else:
|
||||
checkMeta(c, n, t)
|
||||
checkMeta(c, n, t, hasError, nil)
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
|
||||
@@ -1568,9 +1603,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 +1685,27 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind) =
|
||||
|
||||
proc semBorrow(c: PContext, n: PNode, s: PSym) =
|
||||
# search for the correct alias:
|
||||
var b = searchForBorrowProc(c, c.currentScope.parent, s)
|
||||
if b != nil:
|
||||
# store the alias:
|
||||
n[bodyPos] = newSymNode(b)
|
||||
# Carry over the original symbol magic, this is necessary in order to ensure
|
||||
# the semantic pass is correct
|
||||
s.magic = b.magic
|
||||
if b.typ != nil and b.typ.len > 0:
|
||||
s.typ.n[0] = b.typ.n[0]
|
||||
s.typ.flags = b.typ.flags
|
||||
else:
|
||||
localError(c.config, n.info, errNoSymbolToBorrowFromFound)
|
||||
var (b, state) = searchForBorrowProc(c, c.currentScope.parent, s)
|
||||
case state
|
||||
of bsMatch:
|
||||
# store the alias:
|
||||
n[bodyPos] = newSymNode(b)
|
||||
# Carry over the original symbol magic, this is necessary in order to ensure
|
||||
# the semantic pass is correct
|
||||
s.magic = b.magic
|
||||
if b.typ != nil and b.typ.len > 0:
|
||||
s.typ.n[0] = b.typ.n[0]
|
||||
s.typ.flags = b.typ.flags
|
||||
of bsNoDistinct:
|
||||
localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless")
|
||||
of bsReturnNotMatch:
|
||||
localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ[0]))
|
||||
of bsGeneric:
|
||||
localError(c.config, n.info, "borrow with generic parameter is not supported")
|
||||
of bsNotSupported:
|
||||
localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s)
|
||||
else:
|
||||
localError(c.config, n.info, errNoSymbolToBorrowFromFound)
|
||||
|
||||
proc swapResult(n: PNode, sRes: PSym, dNode: PNode) =
|
||||
## Swap nodes that are (skResult) symbols to d(estination)Node.
|
||||
@@ -1767,7 +1813,6 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||
n[genericParamsPos] = c.graph.emptyNode
|
||||
# for LL we need to avoid wrong aliasing
|
||||
let params = copyTree n.typ.n
|
||||
n[paramsPos] = params
|
||||
s.typ = n.typ
|
||||
for i in 1..<params.len:
|
||||
if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
|
||||
@@ -1876,7 +1921,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 +1929,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 +1945,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 +1966,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 +2668,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
|
||||
var m = n[i]
|
||||
while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates
|
||||
m = m.lastSon
|
||||
if m.kind in nkLastBlockStmts or
|
||||
m.kind in nkCallKinds and m[0].kind == nkSym and
|
||||
sfNoReturn in m[0].sym.flags:
|
||||
if endsInNoReturn(m):
|
||||
for j in i + 1..<n.len:
|
||||
case n[j].kind
|
||||
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkState: discard
|
||||
|
||||
@@ -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,8 +216,6 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
replaceIdentBySym(c.c, n, newSymNode(local, n.info))
|
||||
if k == skParam and c.inTemplateHeader > 0:
|
||||
local.flags.incl sfTemplateParam
|
||||
else:
|
||||
replaceIdentBySym(c.c, n, ident)
|
||||
|
||||
proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
|
||||
incl(s.flags, sfUsed)
|
||||
@@ -281,7 +260,7 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
|
||||
if n.kind == nkIdent:
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and (s.kind == skParam or sfGenSym in s.flags):
|
||||
if s.owner == c.owner and s.kind == skParam:
|
||||
incl(s.flags, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
@@ -292,20 +271,21 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
|
||||
proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
|
||||
result = n
|
||||
checkSonsLen(n, bodyPos + 1, c.c.config)
|
||||
# routines default to 'inject':
|
||||
if n.kind notin nkLambdaKinds and symBinding(n[pragmasPos]) == spGenSym:
|
||||
let ident = getIdentNode(c, n[namePos])
|
||||
if not isTemplParam(c, ident):
|
||||
var s = newGenSym(k, ident, c)
|
||||
s.ast = n
|
||||
addPrelimDecl(c.c, s)
|
||||
styleCheckDef(c.c, n.info, s)
|
||||
onDef(n.info, s)
|
||||
n[namePos] = newSymNode(s, n[namePos].info)
|
||||
if n.kind notin nkLambdaKinds:
|
||||
# routines default to 'inject':
|
||||
if symBinding(n[pragmasPos]) == spGenSym:
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
|
||||
if not hasParam:
|
||||
var s = newGenSym(k, ident, c)
|
||||
s.ast = n
|
||||
addPrelimDecl(c.c, s)
|
||||
styleCheckDef(c.c, n.info, s)
|
||||
onDef(n.info, s)
|
||||
n[namePos] = newSymNode(s, n[namePos].info)
|
||||
else:
|
||||
n[namePos] = ident
|
||||
else:
|
||||
n[namePos] = ident
|
||||
else:
|
||||
n[namePos] = semRoutineInTemplName(c, n[namePos])
|
||||
n[namePos] = semRoutineInTemplName(c, n[namePos])
|
||||
# open scope for parameters
|
||||
openScope(c)
|
||||
for i in patternPos..paramsPos-1:
|
||||
@@ -526,14 +506,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
if x.kind == nkExprColonExpr:
|
||||
x[1] = semTemplBody(c, x[1])
|
||||
of nkBracketExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
if n.typ == nil:
|
||||
# if a[b] is nested inside a typed expression, don't convert it
|
||||
# back to `[]`(a, b), prepareOperand will not typecheck it again
|
||||
# and so `[]` will not be resolved
|
||||
# checking if a[b] is typed should be enough to cover this case
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkCurlyExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
if n.typ == nil:
|
||||
# see nkBracketExpr case for explanation
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
checkSonsLen(n, 2, c.c.config)
|
||||
@@ -543,17 +530,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
let k = a.kind
|
||||
case k
|
||||
of nkBracketExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
if a.typ == nil:
|
||||
# see nkBracketExpr case above for explanation
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
let a0 = semTemplBody(c, a[0])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkCurlyExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
if a.typ == nil:
|
||||
# see nkBracketExpr case above for explanation
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
result = semTemplBodySons(c, result)
|
||||
else:
|
||||
result = semTemplBodySons(c, n)
|
||||
@@ -566,6 +557,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
# so we use the generic code for nkDotExpr too
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
# mirror the nkIdent case
|
||||
# do not symchoice a quoted template parameter (bug #2390):
|
||||
if s.owner == c.owner and s.kind == skParam and
|
||||
n.kind == nkAccQuoted and n.len == 1:
|
||||
@@ -577,7 +569,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.c, n, s, c.noGenSym > 0)
|
||||
if n.kind == nkDotExpr:
|
||||
result = n
|
||||
result[0] = semTemplBody(c, n[0])
|
||||
@@ -673,6 +667,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
|
||||
@@ -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)
|
||||
@@ -1960,6 +2011,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = semTypeExpr(c, n[1], prev)
|
||||
else:
|
||||
if c.inGenericContext > 0 and n.kind == nkCall:
|
||||
let n = semGenericStmt(c, n)
|
||||
result = makeTypeFromExpr(c, n.copyTree)
|
||||
else:
|
||||
result = semTypeExpr(c, n, prev)
|
||||
@@ -2113,8 +2165,13 @@ 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)
|
||||
|
||||
@@ -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,7 +129,12 @@ 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
|
||||
for i in 0..<n.safeLen:
|
||||
# XXX HACK: ``f(a, b)``, avoid to instantiate `f`
|
||||
@@ -204,7 +210,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 +218,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 +229,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 +264,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 +275,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 +316,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 +561,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 +706,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:
|
||||
|
||||
@@ -94,7 +94,7 @@ type
|
||||
const
|
||||
isNilConversion = isConvertible # maybe 'isIntConv' fits better?
|
||||
|
||||
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
|
||||
@@ -456,6 +456,38 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
|
||||
else: result = isIntConv
|
||||
else: result = isNone
|
||||
|
||||
proc getObjectTypeOrNil(f: PType): PType =
|
||||
#[
|
||||
Returns a type that is f's effective typeclass. This is usually just one level deeper
|
||||
in the hierarchy of generality for a type. `object`, `ref object`, `enum` and user defined
|
||||
tyObjects are common return values.
|
||||
]#
|
||||
if f == nil: return nil
|
||||
case f.kind:
|
||||
of tyGenericInvocation, tyCompositeTypeClass, tyAlias:
|
||||
if f.len <= 0 or f[0] == nil:
|
||||
result = nil
|
||||
else:
|
||||
result = getObjectTypeOrNil(f[0])
|
||||
of tyGenericBody, tyGenericInst:
|
||||
result = getObjectTypeOrNil(f.lastSon)
|
||||
of tyUserTypeClass:
|
||||
if f.isResolvedUserTypeClass:
|
||||
result = f.base # ?? idk if this is right
|
||||
else:
|
||||
result = f.lastSon
|
||||
of tyStatic, tyOwned, tyVar, tyLent, tySink:
|
||||
result = getObjectTypeOrNil(f.base)
|
||||
of tyInferred:
|
||||
# This is not true "After a candidate type is selected"
|
||||
result = getObjectTypeOrNil(f.base)
|
||||
of tyTyped, tyUntyped, tyFromExpr:
|
||||
result = nil
|
||||
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 +502,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
|
||||
@@ -810,7 +843,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 +853,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 =
|
||||
@@ -991,8 +1027,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 +1184,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 +1200,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})
|
||||
@@ -1207,7 +1245,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
of 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:
|
||||
put(c, fRange, a[0])
|
||||
@@ -1236,6 +1274,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
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
|
||||
@@ -1323,12 +1363,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
of tyTuple:
|
||||
if a.kind == tyTuple: result = recordRel(c, f, a)
|
||||
of tyObject:
|
||||
if a.kind == tyObject:
|
||||
if sameObjectTypes(f, a):
|
||||
let effectiveArgType = if useTypeLoweringRuleInTypeClass:
|
||||
a
|
||||
else:
|
||||
getObjectTypeOrNil(a)
|
||||
if effectiveArgType == nil: return isNone
|
||||
if effectiveArgType.kind == tyObject:
|
||||
if sameObjectTypes(f, effectiveArgType):
|
||||
result = isEqual
|
||||
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
|
||||
elif trIsOutParam notin flags:
|
||||
var depth = isObjectSubtype(c, a, f, nil)
|
||||
var depth = isObjectSubtype(c, effectiveArgType, f, nil)
|
||||
if depth > 0:
|
||||
inc(c.inheritancePenalty, depth)
|
||||
result = isSubtype
|
||||
@@ -1447,8 +1492,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:
|
||||
@@ -1520,6 +1569,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 +1581,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 +1609,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 +1634,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
|
||||
@@ -1647,8 +1699,9 @@ 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 = a.getObjectTypeOrNil()
|
||||
if effectiveArgType == nil: return isNone
|
||||
effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
|
||||
if targetKind == effectiveArgType.kind:
|
||||
if effectiveArgType.isEmptyContainer:
|
||||
return isNone
|
||||
@@ -1658,7 +1711,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
|
||||
@@ -1770,7 +1823,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if tfWildcard in a.flags:
|
||||
a.sym.transitionGenericParamToType()
|
||||
a.flags.excl tfWildcard
|
||||
else:
|
||||
elif doBind:
|
||||
# The mechanics of `doBind` being a flag that also denotes sig cmp via
|
||||
# negation is potentially problematic. `IsNone` is appropriate for
|
||||
# preventing illegal bindings, but it is not necessarily appropriate
|
||||
# before the bindings have been finalized.
|
||||
concrete = concreteType(c, a, f)
|
||||
if concrete == nil:
|
||||
return isNone
|
||||
@@ -1885,6 +1942,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)
|
||||
@@ -2042,7 +2102,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 +2129,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 +2193,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,23 +2246,15 @@ 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:
|
||||
@@ -2210,8 +2268,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:
|
||||
@@ -2395,7 +2455,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 +2683,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 =
|
||||
|
||||
@@ -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
|
||||
# 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:
|
||||
|
||||
@@ -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,
|
||||
@@ -508,7 +510,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 +544,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 +563,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 +582,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 +759,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 +838,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 +926,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 +991,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 +1185,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
|
||||
|
||||
@@ -1274,7 +1313,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:
|
||||
|
||||
@@ -820,6 +820,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 +983,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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5358,6 +5358,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
|
||||
----------------
|
||||
@@ -5517,7 +5519,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 +5802,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
15
koch.nim
15
koch.nim
@@ -11,9 +11,10 @@
|
||||
|
||||
const
|
||||
# examples of possible values for repos: Head, ea82b54
|
||||
NimbleStableCommit = "168416290e49023894fc26106799d6f1fc964a2d" # master
|
||||
AtlasStableCommit = "7b780811a168f3f32bff4822369dda46a7f87f9a"
|
||||
ChecksumsStableCommit = "b4c73320253f78e3a265aec6d9e8feb83f97c77b"
|
||||
NimbleStableCommit = "be2f1309b35a6189ff5eb34a007793e6d3f94157" # master
|
||||
AtlasStableCommit = "5faec3e9a33afe99a7d22377dd1b45a5391f5504"
|
||||
ChecksumsStableCommit = "025bcca3915a1b9f19878cea12ad68f9884648fc"
|
||||
SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
|
||||
|
||||
# examples of possible values for fusion: #head, #ea82b54, 1.2.3
|
||||
FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014"
|
||||
@@ -156,17 +157,21 @@ proc bundleNimbleExe(latest: bool, args: string) =
|
||||
commit = commit, allowBundled = true)
|
||||
cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/checksums.git",
|
||||
commit = ChecksumsStableCommit, allowBundled = true) # or copy it from dist?
|
||||
cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/sat.git",
|
||||
commit = SatStableCommit, allowBundled = true)
|
||||
# installer.ini expects it under $nim/bin
|
||||
nimCompile("dist/nimble/src/nimble.nim",
|
||||
options = "-d:release --mm:refc --noNimblePath " & args)
|
||||
options = "-d:release -d:nimNimbleBootstrap --mm:refc --noNimblePath " & args)
|
||||
|
||||
proc bundleAtlasExe(latest: bool, args: string) =
|
||||
let commit = if latest: "HEAD" else: AtlasStableCommit
|
||||
cloneDependency(distDir, "https://github.com/nim-lang/atlas.git",
|
||||
commit = commit, allowBundled = true)
|
||||
cloneDependency(distDir / "atlas" / distDir, "https://github.com/nim-lang/sat.git",
|
||||
commit = SatStableCommit, allowBundled = true)
|
||||
# installer.ini expects it under $nim/bin
|
||||
nimCompile("dist/atlas/src/atlas.nim",
|
||||
options = "-d:release --noNimblePath " & args)
|
||||
options = "-d:release --noNimblePath -d:nimAtlasBootstrap " & args)
|
||||
|
||||
proc bundleNimsuggest(args: string) =
|
||||
nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim",
|
||||
|
||||
@@ -347,8 +347,7 @@ proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
|
||||
newLit(x.getTypeImpl.repr)
|
||||
let t = """
|
||||
object
|
||||
arr: array[0 .. 3, float32]
|
||||
"""
|
||||
arr: array[0 .. 3, float32]"""
|
||||
doAssert(dumpTypeImpl(a) == t)
|
||||
doAssert(dumpTypeImpl(b) == t)
|
||||
doAssert(dumpTypeImpl(c) == t)
|
||||
@@ -427,7 +426,12 @@ proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} =
|
||||
let x = 12
|
||||
echo x
|
||||
|
||||
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
|
||||
when defined(nimHasNoReturnError):
|
||||
{.pragma: errorNoReturn, noreturn.}
|
||||
else:
|
||||
{.pragma: errorNoReturn.}
|
||||
|
||||
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign, errorNoReturn.}
|
||||
## Writes an error message at compile time. The optional `n: NimNode`
|
||||
## parameter is used as the source for file and line number information in
|
||||
## the compilation error message.
|
||||
|
||||
@@ -129,7 +129,9 @@ when not defined(gcDestructors):
|
||||
else:
|
||||
proc nimNewObj(size, align: int): pointer {.importCompilerProc.}
|
||||
proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc.}
|
||||
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
|
||||
proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
|
||||
importCompilerProc.}
|
||||
proc zeroNewElements(len: int; p: pointer; addlen, elemSize, elemAlign: int) {.
|
||||
importCompilerProc.}
|
||||
|
||||
template `+!!`(a, b): untyped = cast[pointer](cast[int](a) + b)
|
||||
@@ -221,7 +223,8 @@ proc extendSeq*(x: Any) =
|
||||
var s = cast[ptr NimSeqV2Reimpl](x.value)
|
||||
let elem = x.rawType.base
|
||||
if s.p == nil or s.p.cap < s.len+1:
|
||||
s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align))
|
||||
s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAddUninit(s.len, s.p, 1, elem.size, elem.align))
|
||||
zeroNewElements(s.len, s.p, 1, elem.size, elem.align)
|
||||
inc s.len
|
||||
else:
|
||||
var y = cast[ptr PGenSeq](x.value)[]
|
||||
|
||||
@@ -217,9 +217,11 @@ type
|
||||
## code.
|
||||
|
||||
proc destroyRegex(pattern: Regex) =
|
||||
`=destroy`(pattern.pattern)
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
`=destroy`(pattern.captureNameToId)
|
||||
|
||||
proc getinfo[T](pattern: Regex, opt: cint): T =
|
||||
let retcode = pcre.fullinfo(pattern.pcreObj, pattern.pcreExtra, opt, addr result)
|
||||
|
||||
@@ -378,8 +378,8 @@ type
|
||||
kind: FootnoteType # discriminator
|
||||
number: int # valid for fnManualNumber (always) and fnAutoNumber,
|
||||
# fnAutoNumberLabel after resolveSubs is called
|
||||
autoNumIdx: int # order of occurence: fnAutoNumber, fnAutoNumberLabel
|
||||
autoSymIdx: int # order of occurence: fnAutoSymbol
|
||||
autoNumIdx: int # order of occurrence: fnAutoNumber, fnAutoNumberLabel
|
||||
autoSymIdx: int # order of occurrence: fnAutoSymbol
|
||||
label: string # valid for fnAutoNumberLabel
|
||||
RstFileTable* = object
|
||||
filenameToIdx*: Table[string, FileIndex]
|
||||
|
||||
@@ -14,7 +14,8 @@ when defined(nimPreviewSlimSystem):
|
||||
# Structure describing an inotify event.
|
||||
type
|
||||
InotifyEvent* {.pure, final, importc: "struct inotify_event",
|
||||
header: "<sys/inotify.h>".} = object ## An Inotify event.
|
||||
header: "<sys/inotify.h>",
|
||||
completeStruct.} = object ## An Inotify event.
|
||||
wd* {.importc: "wd".}: FileHandle ## Watch descriptor.
|
||||
mask* {.importc: "mask".}: uint32 ## Watch mask.
|
||||
cookie* {.importc: "cookie".}: uint32 ## Cookie to synchronize two events.
|
||||
|
||||
@@ -465,6 +465,7 @@ const MSG_EOR* = cint(128)
|
||||
const MSG_OOB* = cint(1)
|
||||
const SCM_RIGHTS* = cint(1)
|
||||
const SO_ACCEPTCONN* = cint(30)
|
||||
const SO_BINDTODEVICE* = cint(25)
|
||||
const SO_BROADCAST* = cint(6)
|
||||
const SO_DEBUG* = cint(1)
|
||||
const SO_DONTROUTE* = cint(5)
|
||||
|
||||
@@ -606,10 +606,10 @@ when not defined(lwip):
|
||||
|
||||
when defined(zephyr):
|
||||
type
|
||||
Tnfds* = distinct cint
|
||||
Tnfds* = distinct culong
|
||||
else:
|
||||
type
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = cint
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
|
||||
|
||||
var
|
||||
errno* {.importc, header: "<errno.h>".}: cint ## error variable
|
||||
|
||||
@@ -482,6 +482,7 @@ var MSG_EOR* {.importc: "MSG_EOR", header: "<sys/socket.h>".}: cint
|
||||
var MSG_OOB* {.importc: "MSG_OOB", header: "<sys/socket.h>".}: cint
|
||||
var SCM_RIGHTS* {.importc: "SCM_RIGHTS", header: "<sys/socket.h>".}: cint
|
||||
var SO_ACCEPTCONN* {.importc: "SO_ACCEPTCONN", header: "<sys/socket.h>".}: cint
|
||||
var SO_BINDTODEVICE* {.importc: "SO_BINDTODEVICE", header: "<sys/socket.h>".}: cint
|
||||
var SO_BROADCAST* {.importc: "SO_BROADCAST", header: "<sys/socket.h>".}: cint
|
||||
var SO_DEBUG* {.importc: "SO_DEBUG", header: "<sys/socket.h>".}: cint
|
||||
var SO_DONTROUTE* {.importc: "SO_DONTROUTE", header: "<sys/socket.h>".}: cint
|
||||
|
||||
@@ -34,7 +34,7 @@ proc uname*(): Uname =
|
||||
|
||||
var u: Utsname
|
||||
if uname(u) != 0:
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
|
||||
result.sysname = charArrayToString u.sysname
|
||||
result.nodename = charArrayToString u.nodename
|
||||
@@ -45,17 +45,17 @@ proc uname*(): Uname =
|
||||
proc fsync*(fd: int) =
|
||||
## synchronize a file's buffer cache to the storage device
|
||||
if fsync(fd.cint) != 0:
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
|
||||
proc stat*(path: string): Stat =
|
||||
## Returns file status in a `Stat` structure
|
||||
if stat(path.cstring, result) != 0:
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
|
||||
proc memoryLock*(a1: pointer, a2: int) =
|
||||
## Locks pages starting from a1 for a1 bytes and prevent them from being swapped.
|
||||
if mlock(a1, a2) != 0:
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
|
||||
proc memoryLockAll*(flags: int) =
|
||||
## Locks all memory for the running process to prevent swapping.
|
||||
@@ -65,23 +65,23 @@ proc memoryLockAll*(flags: int) =
|
||||
## memoryLockAll(MCL_CURRENT or MCL_FUTURE)
|
||||
## ```
|
||||
if mlockall(flags.cint) != 0:
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
|
||||
proc memoryUnlock*(a1: pointer, a2: int) =
|
||||
## Unlock pages starting from a1 for a1 bytes and allow them to be swapped.
|
||||
if munlock(a1, a2) != 0:
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
|
||||
proc memoryUnlockAll*() =
|
||||
## Unlocks all memory for the running process to allow swapping.
|
||||
if munlockall() != 0:
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
|
||||
proc sendSignal*(pid: Pid, signal: int) =
|
||||
## Sends a signal to a running process by calling `kill`.
|
||||
## Raise exception in case of failure e.g. process not running.
|
||||
if kill(pid, signal.cint) != 0:
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
|
||||
proc mkstemp*(prefix: string, suffix=""): (string, File) =
|
||||
## Creates a unique temporary file from a prefix string. A six-character string
|
||||
@@ -103,14 +103,14 @@ proc mkstemp*(prefix: string, suffix=""): (string, File) =
|
||||
var f: File
|
||||
if open(f, fd, fmReadWrite):
|
||||
return ($tmpl, f)
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
|
||||
proc mkdtemp*(prefix: string): string =
|
||||
## Creates a unique temporary directory from a prefix string. Adds a six chars suffix.
|
||||
## The directory is created with permissions 0700. Returns the directory name.
|
||||
var tmpl = cstring(prefix & "XXXXXX")
|
||||
if mkdtemp(tmpl) == nil:
|
||||
raise newException(OSError, $strerror(errno))
|
||||
raiseOSError(OSErrorCode(errno))
|
||||
return $tmpl
|
||||
|
||||
proc osReleaseFile*(): Config {.since: (1, 5).} =
|
||||
|
||||
@@ -533,7 +533,7 @@ when defined(windows) or defined(nimdoc):
|
||||
if flags.isDisconnectionError(errcode):
|
||||
retFuture.complete("")
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
if dataBuf.buf != nil:
|
||||
dealloc dataBuf.buf
|
||||
dataBuf.buf = nil
|
||||
@@ -551,7 +551,7 @@ when defined(windows) or defined(nimdoc):
|
||||
if flags.isDisconnectionError(err):
|
||||
retFuture.complete("")
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
retFuture.fail(newOSError(err))
|
||||
elif ret == 0:
|
||||
# Request completed immediately.
|
||||
if bytesReceived != 0:
|
||||
@@ -603,7 +603,7 @@ when defined(windows) or defined(nimdoc):
|
||||
if flags.isDisconnectionError(errcode):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
if dataBuf.buf != nil:
|
||||
dataBuf.buf = nil
|
||||
)
|
||||
@@ -619,7 +619,7 @@ when defined(windows) or defined(nimdoc):
|
||||
if flags.isDisconnectionError(err):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
retFuture.fail(newOSError(err))
|
||||
elif ret == 0:
|
||||
# Request completed immediately.
|
||||
if bytesReceived != 0:
|
||||
@@ -667,7 +667,7 @@ when defined(windows) or defined(nimdoc):
|
||||
if flags.isDisconnectionError(err):
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
retFuture.fail(newOSError(err))
|
||||
else:
|
||||
retFuture.complete()
|
||||
# We don't deallocate `ol` here because even though this completed
|
||||
@@ -702,7 +702,7 @@ when defined(windows) or defined(nimdoc):
|
||||
if errcode == OSErrorCode(-1):
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
)
|
||||
|
||||
let ret = WSASendTo(socket.SocketHandle, addr dataBuf, 1, addr bytesSent,
|
||||
@@ -712,7 +712,7 @@ when defined(windows) or defined(nimdoc):
|
||||
let err = osLastError()
|
||||
if err.int32 != ERROR_IO_PENDING:
|
||||
GC_unref(ol)
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
retFuture.fail(newOSError(err))
|
||||
else:
|
||||
retFuture.complete()
|
||||
# We don't deallocate `ol` here because even though this completed
|
||||
@@ -746,7 +746,7 @@ when defined(windows) or defined(nimdoc):
|
||||
else:
|
||||
# datagram sockets don't have disconnection,
|
||||
# so we can just raise an exception
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
)
|
||||
|
||||
let res = WSARecvFrom(socket.SocketHandle, addr dataBuf, 1,
|
||||
@@ -757,7 +757,7 @@ when defined(windows) or defined(nimdoc):
|
||||
let err = osLastError()
|
||||
if err.int32 != ERROR_IO_PENDING:
|
||||
GC_unref(ol)
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
retFuture.fail(newOSError(err))
|
||||
else:
|
||||
# Request completed immediately.
|
||||
if bytesReceived != 0:
|
||||
@@ -808,7 +808,7 @@ when defined(windows) or defined(nimdoc):
|
||||
else:
|
||||
retFuture.complete(newAcceptFut.read)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
|
||||
template completeAccept() {.dirty.} =
|
||||
var listenSock = socket
|
||||
@@ -1468,7 +1468,7 @@ else:
|
||||
if flags.isDisconnectionError(lastError):
|
||||
retFuture.complete("")
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
elif res == 0:
|
||||
@@ -1497,7 +1497,7 @@ else:
|
||||
if flags.isDisconnectionError(lastError):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
else:
|
||||
@@ -1563,7 +1563,7 @@ else:
|
||||
let lastError = osLastError()
|
||||
if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and
|
||||
lastError.int32 != EAGAIN:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
else:
|
||||
@@ -1589,7 +1589,7 @@ else:
|
||||
let lastError = osLastError()
|
||||
if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and
|
||||
lastError.int32 != EAGAIN:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
result = false
|
||||
else:
|
||||
@@ -1630,7 +1630,7 @@ else:
|
||||
if flags.isDisconnectionError(lastError):
|
||||
return false
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
try:
|
||||
let address = getAddrString(cast[ptr SockAddr](addr sockAddress))
|
||||
@@ -1763,7 +1763,7 @@ when defined(windows) or defined(nimdoc):
|
||||
socket.SocketHandle.setSockOptInt(SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, 1) # 15022
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
)
|
||||
|
||||
let ret = connectEx(socket.SocketHandle, addrInfo.ai_addr,
|
||||
@@ -1781,7 +1781,7 @@ when defined(windows) or defined(nimdoc):
|
||||
# With ERROR_IO_PENDING `ol` will be deallocated in `poll`,
|
||||
# and the future will be completed/failed there, too.
|
||||
GC_unref(ol)
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): owned(Future[void]) =
|
||||
let retFuture = newFuture[void]("doConnect")
|
||||
@@ -1798,7 +1798,7 @@ else:
|
||||
# interrupted, keep waiting
|
||||
return false
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
|
||||
retFuture.fail(newOSError(OSErrorCode(ret)))
|
||||
return true
|
||||
|
||||
let ret = connect(socket.SocketHandle,
|
||||
@@ -1812,7 +1812,7 @@ else:
|
||||
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
||||
addWrite(socket, cb)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
|
||||
template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
|
||||
protocol: Protocol = IPPROTO_RAW) =
|
||||
|
||||
@@ -146,7 +146,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
|
||||
if errcode.int32 == ERROR_HANDLE_EOF:
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
)
|
||||
ol.offset = DWORD(f.offset and 0xffffffff)
|
||||
ol.offsetHigh = DWORD(f.offset shr 32)
|
||||
@@ -162,7 +162,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
|
||||
# This happens in Windows Server 2003
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
retFuture.fail(newOSError(err))
|
||||
else:
|
||||
# Request completed immediately.
|
||||
var bytesRead: DWORD
|
||||
@@ -173,7 +173,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
|
||||
if err.int32 == ERROR_HANDLE_EOF:
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
retFuture.fail(newOSError(osLastError()))
|
||||
else:
|
||||
assert bytesRead > 0
|
||||
assert bytesRead <= size
|
||||
@@ -186,7 +186,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
|
||||
if res < 0:
|
||||
let lastError = osLastError()
|
||||
if lastError.int32 != EAGAIN:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
elif res == 0:
|
||||
@@ -228,7 +228,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
if errcode.int32 == ERROR_HANDLE_EOF:
|
||||
retFuture.complete("")
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
if buffer != nil:
|
||||
dealloc buffer
|
||||
buffer = nil
|
||||
@@ -251,7 +251,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
# This happens in Windows Server 2003
|
||||
retFuture.complete("")
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
retFuture.fail(newOSError(err))
|
||||
else:
|
||||
# Request completed immediately.
|
||||
var bytesRead: DWORD
|
||||
@@ -262,7 +262,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
if err.int32 == ERROR_HANDLE_EOF:
|
||||
retFuture.complete("")
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
retFuture.fail(newOSError(osLastError()))
|
||||
else:
|
||||
assert bytesRead > 0
|
||||
assert bytesRead <= size
|
||||
@@ -279,7 +279,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
if res < 0:
|
||||
let lastError = osLastError()
|
||||
if lastError.int32 != EAGAIN:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
elif res == 0:
|
||||
@@ -348,7 +348,7 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
|
||||
assert bytesCount == size.int32
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
)
|
||||
# passing -1 here should work according to MSDN, but doesn't. For more
|
||||
# information see
|
||||
@@ -365,14 +365,14 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
|
||||
let err = osLastError()
|
||||
if err.int32 != ERROR_IO_PENDING:
|
||||
GC_unref(ol)
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
retFuture.fail(newOSError(err))
|
||||
else:
|
||||
# Request completed immediately.
|
||||
var bytesWritten: DWORD
|
||||
let overlappedRes = getOverlappedResult(f.fd.Handle,
|
||||
cast[POVERLAPPED](ol), bytesWritten, false.WINBOOL)
|
||||
if not overlappedRes.bool:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
retFuture.fail(newOSError(osLastError()))
|
||||
else:
|
||||
assert bytesWritten == size.int32
|
||||
retFuture.complete()
|
||||
@@ -387,7 +387,7 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
|
||||
if res < 0:
|
||||
let lastError = osLastError()
|
||||
if lastError.int32 != EAGAIN:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
else:
|
||||
@@ -421,7 +421,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
assert bytesCount == data.len.int32
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
if buffer != nil:
|
||||
dealloc buffer
|
||||
buffer = nil
|
||||
@@ -440,14 +440,14 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
dealloc buffer
|
||||
buffer = nil
|
||||
GC_unref(ol)
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
retFuture.fail(newOSError(err))
|
||||
else:
|
||||
# Request completed immediately.
|
||||
var bytesWritten: DWORD
|
||||
let overlappedRes = getOverlappedResult(f.fd.Handle,
|
||||
cast[POVERLAPPED](ol), bytesWritten, false.WINBOOL)
|
||||
if not overlappedRes.bool:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
retFuture.fail(newOSError(osLastError()))
|
||||
else:
|
||||
assert bytesWritten == data.len.int32
|
||||
retFuture.complete()
|
||||
@@ -468,7 +468,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
if res < 0:
|
||||
let lastError = osLastError()
|
||||
if lastError.int32 != EAGAIN:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
else:
|
||||
|
||||
@@ -682,7 +682,7 @@ when defined(posix) and not useNimNetLite:
|
||||
elif ret == EINTR:
|
||||
return false
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
|
||||
retFuture.fail(newOSError(OSErrorCode(ret)))
|
||||
return true
|
||||
|
||||
var socketAddr = makeUnixAddr(path)
|
||||
@@ -696,7 +696,7 @@ when defined(posix) and not useNimNetLite:
|
||||
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
||||
addWrite(AsyncFD(socket.fd), cb)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
|
||||
proc bindUnix*(socket: AsyncSocket, path: string) {.
|
||||
tags: [ReadIOEffect].} =
|
||||
|
||||
@@ -66,14 +66,10 @@ template cbBase(a, b): untyped = [
|
||||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', a, b]
|
||||
|
||||
let
|
||||
const
|
||||
cb64 = cbBase('+', '/')
|
||||
cb64safe = cbBase('-', '_')
|
||||
|
||||
const
|
||||
cb64VM = cbBase('+', '/')
|
||||
cb64safeVM = cbBase('-', '_')
|
||||
|
||||
const
|
||||
invalidChar = 255
|
||||
|
||||
@@ -134,14 +130,10 @@ template encodeInternal(s, alphabet: typed): untyped =
|
||||
result.setLen(outputIndex)
|
||||
|
||||
template encodeImpl() {.dirty.} =
|
||||
when nimvm:
|
||||
block:
|
||||
let lookupTableVM = if safe: cb64safeVM else: cb64VM
|
||||
encodeInternal(s, lookupTableVM)
|
||||
if safe:
|
||||
encodeInternal(s, cb64safe)
|
||||
else:
|
||||
block:
|
||||
let lookupTable = if safe: unsafeAddr(cb64safe) else: unsafeAddr(cb64)
|
||||
encodeInternal(s, lookupTable)
|
||||
encodeInternal(s, cb64)
|
||||
|
||||
proc encode*[T: byte|char](s: openArray[T], safe = false): string =
|
||||
## Encodes `s` into base64 representation.
|
||||
@@ -252,7 +244,7 @@ proc decode*(s: string): string =
|
||||
inputLen = s.len
|
||||
inputEnds = 0
|
||||
# strip trailing characters
|
||||
while s[inputLen - 1] in {'\n', '\r', ' ', '='}:
|
||||
while inputLen > 0 and s[inputLen - 1] in {'\n', '\r', ' ', '='}:
|
||||
dec inputLen
|
||||
# hot loop: read 4 characters at at time
|
||||
inputEnds = inputLen - 4
|
||||
|
||||
@@ -58,7 +58,10 @@ proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
|
||||
template genHashImpl(key, hc: typed) =
|
||||
hc = hash(key)
|
||||
if hc == 0: # This almost never taken branch should be very predictable.
|
||||
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
|
||||
when sizeof(int) < 4:
|
||||
hc = 31415 # Value doesn't matter; Any non-zero favorite is fine <= 16-bit.
|
||||
else:
|
||||
hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
|
||||
|
||||
template genHash(key: typed): Hash =
|
||||
var res: Hash
|
||||
|
||||
@@ -165,28 +165,6 @@ proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
|
||||
new(result)
|
||||
result.value = value
|
||||
|
||||
func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} =
|
||||
## Creates a new `SinglyLinkedList` from the members of `elems`.
|
||||
runnableExamples:
|
||||
from std/sequtils import toSeq
|
||||
let a = [1, 2, 3, 4, 5].toSinglyLinkedList
|
||||
assert a.toSeq == [1, 2, 3, 4, 5]
|
||||
|
||||
result = initSinglyLinkedList[T]()
|
||||
for elem in elems.items:
|
||||
result.add(elem)
|
||||
|
||||
func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} =
|
||||
## Creates a new `DoublyLinkedList` from the members of `elems`.
|
||||
runnableExamples:
|
||||
from std/sequtils import toSeq
|
||||
let a = [1, 2, 3, 4, 5].toDoublyLinkedList
|
||||
assert a.toSeq == [1, 2, 3, 4, 5]
|
||||
|
||||
result = initDoublyLinkedList[T]()
|
||||
for elem in elems.items:
|
||||
result.add(elem)
|
||||
|
||||
template itemsListImpl() {.dirty.} =
|
||||
var it {.cursor.} = L.head
|
||||
while it != nil:
|
||||
@@ -993,3 +971,25 @@ proc appendMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} =
|
||||
## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_
|
||||
## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_
|
||||
a.addMoved(b)
|
||||
|
||||
func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} =
|
||||
## Creates a new `SinglyLinkedList` from the members of `elems`.
|
||||
runnableExamples:
|
||||
from std/sequtils import toSeq
|
||||
let a = [1, 2, 3, 4, 5].toSinglyLinkedList
|
||||
assert a.toSeq == [1, 2, 3, 4, 5]
|
||||
|
||||
result = initSinglyLinkedList[T]()
|
||||
for elem in elems.items:
|
||||
result.add(elem)
|
||||
|
||||
func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} =
|
||||
## Creates a new `DoublyLinkedList` from the members of `elems`.
|
||||
runnableExamples:
|
||||
from std/sequtils import toSeq
|
||||
let a = [1, 2, 3, 4, 5].toDoublyLinkedList
|
||||
assert a.toSeq == [1, 2, 3, 4, 5]
|
||||
|
||||
result = initDoublyLinkedList[T]()
|
||||
for elem in elems.items:
|
||||
result.add(elem)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user