mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 03:13:41 +00:00
Compare commits
207 Commits
v2.0.6
...
version-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10564f25b3 | ||
|
|
fbc7e54e95 | ||
|
|
7f3dbd5656 | ||
|
|
71f3e100b6 | ||
|
|
d2cb099e44 | ||
|
|
7e51e48cac | ||
|
|
19c89622ca | ||
|
|
b1d9af4d5f | ||
|
|
c467532484 | ||
|
|
114d0b8bdc | ||
|
|
f8ce7ddd6d | ||
|
|
cd6dbbd7f3 | ||
|
|
acac0bc1f2 | ||
|
|
82f3a57612 | ||
|
|
7e589bcbe4 | ||
|
|
dbed9310ba | ||
|
|
9524edec60 | ||
|
|
574db65396 | ||
|
|
e2bf3a6f70 | ||
|
|
6f2dbc105e | ||
|
|
63151568b4 | ||
|
|
4610c2b314 | ||
|
|
d01002d8f8 | ||
|
|
bc2fa6fe32 | ||
|
|
41637db18f | ||
|
|
b7eefc31d8 | ||
|
|
5df3bf467e | ||
|
|
ac44139084 | ||
|
|
ac882ea696 | ||
|
|
56f9559c69 | ||
|
|
c08c32a1ed | ||
|
|
03491904a0 | ||
|
|
57f84c5376 | ||
|
|
1df4debbf1 | ||
|
|
1ff69eae17 | ||
|
|
4fa122eb44 | ||
|
|
53782b1404 | ||
|
|
cafe1284ac | ||
|
|
e2ae7d5f43 | ||
|
|
772ebe6715 | ||
|
|
32c99651fc | ||
|
|
2eac7941ff | ||
|
|
cedcb7881d | ||
|
|
117b913458 | ||
|
|
bf4de6a394 | ||
|
|
86f1a2785a | ||
|
|
c7d057d7f9 | ||
|
|
8f44c40b6b | ||
|
|
76935b4c34 | ||
|
|
30abe60cda | ||
|
|
14dfabb230 | ||
|
|
5fb4662ab1 | ||
|
|
535556875e | ||
|
|
95fa7f0f12 | ||
|
|
6bcb078476 | ||
|
|
5c58e7d201 | ||
|
|
5327498547 | ||
|
|
d84d41dc95 | ||
|
|
693b35b59f | ||
|
|
c1bb144fdc | ||
|
|
1f38c3cea8 | ||
|
|
f45ca4fdf4 | ||
|
|
5cbc7a6d1b | ||
|
|
56e7c75e03 | ||
|
|
d51236e9cc | ||
|
|
4d0d848235 | ||
|
|
8859f1ddf7 | ||
|
|
0e2b34ce35 | ||
|
|
ba516c8eb5 | ||
|
|
009a5b0684 | ||
|
|
c786415eef | ||
|
|
33dc54c573 | ||
|
|
6a1d1a8c2b | ||
|
|
0c426e7875 | ||
|
|
4e1b5ee702 | ||
|
|
3a334e09ea | ||
|
|
dbe9c724a0 | ||
|
|
9620d206b9 | ||
|
|
9e5cdc43a6 | ||
|
|
1778b8354a | ||
|
|
f6f72722bb | ||
|
|
90993aeff6 | ||
|
|
15e5ddc675 | ||
|
|
9f43f9fa6e | ||
|
|
ce7c6f4f33 | ||
|
|
b0074121ec | ||
|
|
3713994ef1 | ||
|
|
75bd2d0688 | ||
|
|
eedfcbeb30 | ||
|
|
fe72db98c1 | ||
|
|
a70b4712fc | ||
|
|
76d834c182 | ||
|
|
72fcda1b35 | ||
|
|
fd9d2f5b82 | ||
|
|
d3f7fb3100 | ||
|
|
4acc7a5e18 | ||
|
|
e334aad202 | ||
|
|
67d7a7c124 | ||
|
|
206bbbd940 | ||
|
|
cc4c9251f0 | ||
|
|
8754469f49 | ||
|
|
e2ef322754 | ||
|
|
4fe17a2d87 | ||
|
|
cc887c23f4 | ||
|
|
acd09cec43 | ||
|
|
c8094176f1 | ||
|
|
3c66401dee | ||
|
|
ff46fcfd24 | ||
|
|
e941ee15be | ||
|
|
5f119f0cdf | ||
|
|
0636bd2a04 | ||
|
|
2499336fef | ||
|
|
1ba16876f1 | ||
|
|
3a180df0d8 | ||
|
|
9d65507d20 | ||
|
|
78003e9f7b | ||
|
|
27381cc602 | ||
|
|
dd9cb0425c | ||
|
|
894eee03a5 | ||
|
|
4b8b8fb9d7 | ||
|
|
3214174f06 | ||
|
|
8bb9823fde | ||
|
|
40ee3a950e | ||
|
|
74c4b23e0d | ||
|
|
7180ce6faa | ||
|
|
fd1d008cce | ||
|
|
8bc8347f02 | ||
|
|
33bef682dd | ||
|
|
bfd4b9b7a4 | ||
|
|
7d2fe8086f | ||
|
|
a78834c26c | ||
|
|
7cbf0ee53c | ||
|
|
f86f2928a7 | ||
|
|
5f09bf11de | ||
|
|
25ce25bc42 | ||
|
|
7503bed717 | ||
|
|
b7041b250c | ||
|
|
06d00dfcf6 | ||
|
|
baa8af92af | ||
|
|
f09b612f64 | ||
|
|
7d0cdfc79d | ||
|
|
ae117bd7bc | ||
|
|
b79e603702 | ||
|
|
da2ba43dca | ||
|
|
b5ef14ceda | ||
|
|
dc62ee00df | ||
|
|
b6a8dcd922 | ||
|
|
37965bd591 | ||
|
|
c2b4d8a968 | ||
|
|
8d254a5945 | ||
|
|
4856beae70 | ||
|
|
568a637f0f | ||
|
|
8b421d2203 | ||
|
|
a5237a5053 | ||
|
|
d0a8637872 | ||
|
|
7821aa94eb | ||
|
|
b1e5dd605a | ||
|
|
87ab9b8ac2 | ||
|
|
c18e599f53 | ||
|
|
d1aa568de3 | ||
|
|
352c82af89 | ||
|
|
22149422ee | ||
|
|
d6f7625626 | ||
|
|
3e8dfde2f9 | ||
|
|
3f183f6769 | ||
|
|
1100f550dc | ||
|
|
02eb396f45 | ||
|
|
796aa78562 | ||
|
|
1cc1451ca3 | ||
|
|
dd37bb88fe | ||
|
|
a88bce6106 | ||
|
|
8a4ee2b84f | ||
|
|
7b834b94da | ||
|
|
179ae267e9 | ||
|
|
5d872321b3 | ||
|
|
ff1881a4c1 | ||
|
|
702f8342ff | ||
|
|
92e505577e | ||
|
|
1ce954a0cf | ||
|
|
76e6130f64 | ||
|
|
83455cf1d5 | ||
|
|
be99f2fed8 | ||
|
|
cbcf6c5be6 | ||
|
|
e4db9bffba | ||
|
|
ecc7e3d41d | ||
|
|
22896b3a95 | ||
|
|
eaf0e7ff60 | ||
|
|
5935c3bfa9 | ||
|
|
2114414099 | ||
|
|
3788aa0a99 | ||
|
|
e98c98b46c | ||
|
|
51723bace8 | ||
|
|
a90e687d26 | ||
|
|
4b42170022 | ||
|
|
2eff34f08a | ||
|
|
ba51e7c4d8 | ||
|
|
d259099ef0 | ||
|
|
915cd5b28b | ||
|
|
16bbffcb77 | ||
|
|
73af7c60a1 | ||
|
|
7bdcaada13 | ||
|
|
e77e129a05 | ||
|
|
d0b3b7e1a7 | ||
|
|
2735fd2bf5 | ||
|
|
8d1f03e1da | ||
|
|
d83eb88eb9 | ||
|
|
10907cc4a7 |
2
.github/workflows/ci_bench.yml
vendored
2
.github/workflows/ci_bench.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
os: [ubuntu-24.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
4
.github/workflows/ci_docs.yml
vendored
4
.github/workflows/ci_docs.yml
vendored
@@ -43,11 +43,11 @@ jobs:
|
||||
target: [linux, windows, osx]
|
||||
include:
|
||||
- target: linux
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
os: macos-12
|
||||
os: macos-13
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
76
.github/workflows/ci_gcc14.yml
vendored
Normal file
76
.github/workflows/ci_gcc14.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
name: GCC 14
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt update -qq
|
||||
sudo apt remove needrestart
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
- name: 'Install dependencies (Linux amd64 gcc 14)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo add-apt-repository universe
|
||||
sudo apt update -qq
|
||||
sudo apt install -y gcc-14 g++-14 libpcre3 liblapack-dev
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'NIM_TESTAMENT_DISABLE_SSL'
|
||||
shell: bash
|
||||
run: echo "NIM_TESTAMENT_DISABLE_SSL=1" >> $GITHUB_ENV
|
||||
|
||||
- name: 'System information'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimCiSystemInfo
|
||||
|
||||
- name: 'Build csourcesAny'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
||||
|
||||
- name: 'koch, Run CI'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
3
.github/workflows/ci_packages.yml
vendored
3
.github/workflows/ci_packages.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-12]
|
||||
os: [ubuntu-22.04, macos-13]
|
||||
cpu: [amd64]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
@@ -48,6 +48,7 @@ jobs:
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
# XXX can't find boehm and gtk on macos 13
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
|
||||
17
.github/workflows/ci_publish.yml
vendored
17
.github/workflows/ci_publish.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
os: [ubuntu-22.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -21,10 +21,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
- name: 'Install node.js'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version: ''
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
@@ -34,17 +34,6 @@ jobs:
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-20.04'
|
||||
vmImage: 'ubuntu-24.04'
|
||||
CPU: amd64
|
||||
# regularly breaks, refs bug #17325
|
||||
# Linux_i386:
|
||||
@@ -29,23 +29,23 @@ jobs:
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-12'
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-12'
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
vmImage: 'windows-2019'
|
||||
vmImage: 'windows-2025'
|
||||
CPU: amd64
|
||||
# see also: `NIM_TEST_PACKAGES`
|
||||
NIM_TESTAMENT_BATCH: "0_3"
|
||||
Windows_amd64_batch1_3:
|
||||
vmImage: 'windows-2019'
|
||||
vmImage: 'windows-2025'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "1_3"
|
||||
Windows_amd64_batch2_3:
|
||||
vmImage: 'windows-2019'
|
||||
vmImage: 'windows-2025'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "2_3"
|
||||
|
||||
@@ -80,10 +80,12 @@ jobs:
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo apt-fast update -qq
|
||||
echo_run sudo add-apt-repository universe
|
||||
echo_run sudo apt-get update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
echo_run sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
echo_run sudo apt-get install --no-install-recommends -yq \
|
||||
gcc-14 g++-14 libpcre3 liblapack-dev libpcre3 liblapack-dev libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
echo_run sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
displayName: 'Install dependencies (amd64 Linux)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
|
||||
|
||||
@@ -100,12 +102,12 @@ jobs:
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
|
||||
# echo_run sudo apt-fast update -qq
|
||||
echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
|
||||
# echo_run sudo apt-get update -qq
|
||||
echo_run sudo apt-get update -qq || echo "failed, see bug #17343"
|
||||
# `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
|
||||
# `could not load: libffi.so` during dynamic loading.
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
|
||||
echo_run sudo apt-get install --no-install-recommends --allow-downgrades -yq \
|
||||
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
|
||||
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
|
||||
|
||||
@@ -130,6 +132,7 @@ jobs:
|
||||
- bash: brew install boehmgc make sfml
|
||||
displayName: 'Install dependencies (OSX)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
|
||||
# XXX can't find boehm on macos 13
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
|
||||
@@ -24,6 +24,6 @@ if not exist %nim_csources% (
|
||||
cd ..
|
||||
copy /y bin\nim.exe %nim_csources%
|
||||
)
|
||||
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
|
||||
84
changelog.md
84
changelog.md
@@ -3,6 +3,9 @@
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
@@ -16,6 +19,11 @@
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
@@ -27,28 +35,37 @@ slots when enlarging a sequence.
|
||||
|
||||
|
||||
|
||||
- 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.
|
||||
- The experimental option `--experimental:openSym` has been added to allow
|
||||
captured symbols in generic routine and template bodies respectively to be
|
||||
replaced by symbols injected locally by templates/macros at instantiation
|
||||
time. `bind` may be used to keep the captured symbols over the injected ones
|
||||
regardless of enabling the option, but other methods like renaming the
|
||||
captured symbols should be used instead so that the code is not affected by
|
||||
context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`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.
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped) =
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:genericsOpenSym`
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
{.experimental: "genericsOpenSym".}
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
@@ -60,6 +77,53 @@ slots when enlarging a sequence.
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert barTempl() == "injected" # previously it would be "captured"
|
||||
|
||||
template bazTempl(): string =
|
||||
bind value
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert bazTempl() == "captured"
|
||||
```
|
||||
|
||||
This option also generates a new node kind `nnkOpenSym` which contains
|
||||
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
|
||||
modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
Another experimental switch `genericsOpenSym` exists that enables this behavior
|
||||
at instantiation time, meaning templates etc can enable it specifically when
|
||||
they are being called. However this does not generate `nnkOpenSym` nodes
|
||||
(unless the other switch is enabled) and so doesn't reflect the regular
|
||||
behavior of the switch.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
echo bar[int]() # "injected"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
var res: string
|
||||
foo(123):
|
||||
res = value
|
||||
res
|
||||
assert barTempl() == "injected"
|
||||
```
|
||||
|
||||
## Compiler changes
|
||||
|
||||
177
compiler/ast.nim
177
compiler/ast.nim
@@ -227,11 +227,12 @@ type
|
||||
nkModuleRef # for .rod file support: A (moduleId, itemId) pair
|
||||
nkReplayAction # for .rod file support: A replay action
|
||||
nkNilRodNode # for .rod file support: a 'nil' PNode
|
||||
nkOpenSym # container for captured sym that can be overriden by local symbols
|
||||
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
TSymFlag* = enum # 51 flags!
|
||||
TSymFlag* = enum # 52 flags!
|
||||
sfUsed, # read access of sym (for warnings) or simply used
|
||||
sfExported, # symbol is exported from module
|
||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
||||
@@ -315,6 +316,7 @@ type
|
||||
sfVirtual # proc is a C++ virtual function
|
||||
sfByCopy # param is marked as pass bycopy
|
||||
sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl
|
||||
sfWasGenSym # symbol was 'gensym'ed
|
||||
|
||||
TSymFlags* = set[TSymFlag]
|
||||
|
||||
@@ -516,7 +518,9 @@ type
|
||||
nfFirstWrite # this node is a first write
|
||||
nfHasComment # node has a comment
|
||||
nfSkipFieldChecking # node skips field visable checking
|
||||
nfOpenSym # node is a captured sym but can be overriden by local symbols
|
||||
nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot
|
||||
# because openSym experimental switch is disabled
|
||||
# gives warning instead
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47)
|
||||
@@ -1090,7 +1094,7 @@ const
|
||||
nfFromTemplate, nfDefaultRefsParam,
|
||||
nfExecuteOnReload, nfLastRead,
|
||||
nfFirstWrite, nfSkipFieldChecking,
|
||||
nfOpenSym}
|
||||
nfDisabledOpenSym}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
@@ -1106,7 +1110,7 @@ const
|
||||
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
||||
nkCommand, nkCallStrLit, nkHiddenCallConv}
|
||||
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
|
||||
nkClosedSymChoice}
|
||||
nkClosedSymChoice, nkOpenSym}
|
||||
|
||||
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
|
||||
nkLiterals* = {nkCharLit..nkTripleStrLit}
|
||||
@@ -1134,6 +1138,7 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
|
||||
of nkSym: a.sym.name
|
||||
of nkIdent: a.ident
|
||||
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
|
||||
of nkOpenSym: getPIdent(a.sons[0])
|
||||
else: nil
|
||||
|
||||
const
|
||||
@@ -1253,8 +1258,11 @@ proc getDeclPragma*(n: PNode): PNode =
|
||||
|
||||
proc extractPragma*(s: PSym): PNode =
|
||||
## gets the pragma node of routine/type/var/let/const symbol `s`
|
||||
if s.kind in routineKinds:
|
||||
result = s.ast[pragmasPos]
|
||||
if s.kind in routineKinds: # bug #24167
|
||||
if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
|
||||
result = s.ast[pragmasPos]
|
||||
else:
|
||||
result = nil
|
||||
elif s.kind in {skType, skVar, skLet, skConst}:
|
||||
if s.ast != nil and s.ast.len > 0:
|
||||
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
|
||||
@@ -1452,6 +1460,9 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||
result.typ = sym.typ
|
||||
result.info = info
|
||||
|
||||
proc newOpenSym*(n: PNode): PNode {.inline.} =
|
||||
result = newTreeI(nkOpenSym, n.info, n)
|
||||
|
||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = intVal
|
||||
@@ -1460,7 +1471,42 @@ proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = castToInt64(intVal)
|
||||
|
||||
proc lastSon*(n: Indexable): Indexable = n.sons[^1]
|
||||
proc lastSon*(n: Indexable): Indexable {.inline.} = n.sons[^1]
|
||||
template setLastSon*(n: PNode, s: PNode) = n.sons[^1] = s
|
||||
|
||||
template firstSon*(n: PNode): PNode = n.sons[0]
|
||||
template secondSon*(n: PNode): PNode = n.sons[1]
|
||||
|
||||
template hasSon*(n: PNode): bool = n.len > 0
|
||||
template has2Sons*(n: PNode): bool = n.len > 1
|
||||
|
||||
proc replaceFirstSon*(n, newson: PNode) {.inline.} =
|
||||
n.sons[0] = newson
|
||||
|
||||
proc replaceSon*(n: PNode; i: int; newson: PNode) {.inline.} =
|
||||
n.sons[i] = newson
|
||||
|
||||
proc last*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc elementType*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
proc skipModifier*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc indexType*(n: PType): PType {.inline.} = n.sons[0]
|
||||
proc baseClass*(n: PType): PType {.inline.} = n.sons[0]
|
||||
|
||||
proc base*(t: PType): PType {.inline.} =
|
||||
result = t.sons[0]
|
||||
|
||||
proc returnType*(n: PType): PType {.inline.} = n.sons[0]
|
||||
proc setReturnType*(n, r: PType) {.inline.} = n.sons[0] = r
|
||||
proc setIndexType*(n, idx: PType) {.inline.} = n.sons[0] = idx
|
||||
|
||||
proc firstParamType*(n: PType): PType {.inline.} = n.sons[1]
|
||||
proc firstGenericParam*(n: PType): PType {.inline.} = n.sons[1]
|
||||
|
||||
proc typeBodyImpl*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc genericHead*(n: PType): PType {.inline.} = n.sons[0]
|
||||
|
||||
proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||
## Used throughout the compiler code to test whether a type tree contains or
|
||||
@@ -1468,7 +1514,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||
## last child nodes of a type tree need to be searched. This is a really hot
|
||||
## path within the compiler!
|
||||
result = t
|
||||
while result.kind in kinds: result = lastSon(result)
|
||||
while result.kind in kinds: result = last(result)
|
||||
|
||||
proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
|
||||
let kind = skipTypes(typ, abstractVarRange).kind
|
||||
@@ -1527,6 +1573,107 @@ proc `$`*(s: PSym): string =
|
||||
else:
|
||||
result = "<nil>"
|
||||
|
||||
when false:
|
||||
iterator items*(t: PType): PType =
|
||||
for i in 0..<t.sons.len: yield t.sons[i]
|
||||
|
||||
iterator pairs*(n: PType): tuple[i: int, n: PType] =
|
||||
for i in 0..<n.sons.len: yield (i, n.sons[i])
|
||||
|
||||
when true:
|
||||
proc len*(n: PType): int {.inline.} =
|
||||
result = n.sons.len
|
||||
|
||||
proc sameTupleLengths*(a, b: PType): bool {.inline.} =
|
||||
result = a.sons.len == b.sons.len
|
||||
|
||||
iterator tupleTypePairs*(a, b: PType): (int, PType, PType) =
|
||||
for i in 0 ..< a.sons.len:
|
||||
yield (i, a.sons[i], b.sons[i])
|
||||
|
||||
iterator underspecifiedPairs*(a, b: PType; start = 0; without = 0): (PType, PType) =
|
||||
# XXX Figure out with what typekinds this is called.
|
||||
for i in start ..< min(a.sons.len, b.sons.len) + without:
|
||||
yield (a.sons[i], b.sons[i])
|
||||
|
||||
proc signatureLen*(t: PType): int {.inline.} =
|
||||
result = t.sons.len
|
||||
|
||||
proc paramsLen*(t: PType): int {.inline.} =
|
||||
result = t.sons.len - 1
|
||||
|
||||
proc genericParamsLen*(t: PType): int {.inline.} =
|
||||
assert t.kind == tyGenericInst
|
||||
result = t.sons.len - 2 # without 'head' and 'body'
|
||||
|
||||
proc genericInvocationParamsLen*(t: PType): int {.inline.} =
|
||||
assert t.kind == tyGenericInvocation
|
||||
result = t.sons.len - 1 # without 'head'
|
||||
|
||||
proc kidsLen*(t: PType): int {.inline.} =
|
||||
result = t.sons.len
|
||||
|
||||
proc genericParamHasConstraints*(t: PType): bool {.inline.} = t.sons.len > 0
|
||||
|
||||
proc hasElementType*(t: PType): bool {.inline.} = t.sons.len > 0
|
||||
proc isEmptyTupleType*(t: PType): bool {.inline.} = t.sons.len == 0
|
||||
proc isSingletonTupleType*(t: PType): bool {.inline.} = t.sons.len == 1
|
||||
|
||||
proc genericConstraint*(t: PType): PType {.inline.} = t.sons[0]
|
||||
|
||||
iterator genericInstParams*(t: PType): (bool, PType) =
|
||||
for i in 1..<t.sons.len-1:
|
||||
yield (i!=1, t.sons[i])
|
||||
|
||||
iterator genericInstParamPairs*(a, b: PType): (int, PType, PType) =
|
||||
for i in 1..<min(a.sons.len, b.sons.len)-1:
|
||||
yield (i-1, a.sons[i], b.sons[i])
|
||||
|
||||
iterator genericInvocationParams*(t: PType): (bool, PType) =
|
||||
for i in 1..<t.sons.len:
|
||||
yield (i!=1, t.sons[i])
|
||||
|
||||
iterator genericInvocationAndBodyElements*(a, b: PType): (PType, PType) =
|
||||
for i in 1..<a.sons.len:
|
||||
yield (a.sons[i], b.sons[i-1])
|
||||
|
||||
iterator genericInvocationParamPairs*(a, b: PType): (bool, PType, PType) =
|
||||
for i in 1..<a.sons.len:
|
||||
if i >= b.sons.len:
|
||||
yield (false, nil, nil)
|
||||
else:
|
||||
yield (true, a.sons[i], b.sons[i])
|
||||
|
||||
iterator genericBodyParams*(t: PType): (int, PType) =
|
||||
for i in 0..<t.sons.len-1:
|
||||
yield (i, t.sons[i])
|
||||
|
||||
iterator userTypeClassInstParams*(t: PType): (bool, PType) =
|
||||
for i in 1..<t.sons.len-1:
|
||||
yield (i!=1, t.sons[i])
|
||||
|
||||
iterator ikids*(t: PType): (int, PType) =
|
||||
for i in 0..<t.sons.len: yield (i, t.sons[i])
|
||||
|
||||
const
|
||||
FirstParamAt* = 1
|
||||
FirstGenericParamAt* = 1
|
||||
|
||||
iterator paramTypes*(t: PType): (int, PType) =
|
||||
for i in FirstParamAt..<t.sons.len: yield (i, t.sons[i])
|
||||
|
||||
iterator paramTypePairs*(a, b: PType): (PType, PType) =
|
||||
for i in FirstParamAt..<a.sons.len: yield (a.sons[i], b.sons[i])
|
||||
|
||||
template paramTypeToNodeIndex*(x: int): int = x
|
||||
|
||||
iterator kids*(t: PType): PType =
|
||||
for i in 0..<t.sons.len: yield t.sons[i]
|
||||
|
||||
iterator signature*(t: PType): PType =
|
||||
# yields return type + parameter types
|
||||
for i in 0..<t.sons.len: yield t.sons[i]
|
||||
|
||||
proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
|
||||
result = PType(kind: kind, owner: owner, size: defaultSize,
|
||||
align: defaultAlignment, itemId: id,
|
||||
@@ -1637,7 +1784,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
|
||||
result = t
|
||||
var i = maxIters
|
||||
while result.kind in kinds:
|
||||
result = lastSon(result)
|
||||
result = last(result)
|
||||
dec i
|
||||
if i == 0: return nil
|
||||
|
||||
@@ -1646,7 +1793,7 @@ proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
|
||||
result = t
|
||||
while result != nil and result.kind in kinds:
|
||||
if result.len == 0: return nil
|
||||
result = lastSon(result)
|
||||
result = last(result)
|
||||
|
||||
proc isGCedMem*(t: PType): bool {.inline.} =
|
||||
result = t.kind in {tyString, tyRef, tySequence} or
|
||||
@@ -1915,13 +2062,15 @@ proc skipGenericOwner*(s: PSym): PSym =
|
||||
## Generic instantiations are owned by their originating generic
|
||||
## symbol. This proc skips such owners and goes straight to the owner
|
||||
## of the generic itself (the module or the enclosing proc).
|
||||
result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
|
||||
result = if s.kind == skModule:
|
||||
s
|
||||
elif s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule:
|
||||
s.owner.owner
|
||||
else:
|
||||
s.owner
|
||||
|
||||
proc originatingModule*(s: PSym): PSym =
|
||||
result = s.owner
|
||||
result = s
|
||||
while result.kind != skModule: result = result.owner
|
||||
|
||||
proc isRoutine*(s: PSym): bool {.inline.} =
|
||||
@@ -1983,7 +2132,7 @@ proc toObject*(typ: PType): PType =
|
||||
## cases should be a ``tyObject``).
|
||||
## Otherwise ``typ`` is simply returned as-is.
|
||||
let t = typ.skipTypes({tyAlias, tyGenericInst})
|
||||
if t.kind == tyRef: t.lastSon
|
||||
if t.kind == tyRef: t.elementType
|
||||
else: typ
|
||||
|
||||
proc toObjectFromRefPtrGeneric*(typ: PType): PType =
|
||||
@@ -2000,7 +2149,7 @@ proc toObjectFromRefPtrGeneric*(typ: PType): PType =
|
||||
result = typ
|
||||
while true:
|
||||
case result.kind
|
||||
of tyGenericBody: result = result.lastSon
|
||||
of tyGenericBody: result = result.last
|
||||
of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0]
|
||||
# automatic dereferencing is deep, refs #18298.
|
||||
else: break
|
||||
|
||||
@@ -890,7 +890,7 @@ proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
|
||||
result = nextIter(ti, tab)
|
||||
|
||||
iterator items*(tab: TStrTable): PSym =
|
||||
var it: TTabIter
|
||||
var it: TTabIter = default(TTabIter)
|
||||
var s = initTabIter(it, tab)
|
||||
while s != nil:
|
||||
yield s
|
||||
@@ -1054,14 +1054,6 @@ proc iiTablePut(t: var TIITable, key, val: int) =
|
||||
iiTableRawInsert(t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
proc isAddrNode*(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkAddr, nkHiddenAddr: true
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mAddr: true
|
||||
else: false
|
||||
else: false
|
||||
|
||||
proc listSymbolNames*(symbols: openArray[PSym]): string =
|
||||
for sym in symbols:
|
||||
if result.len > 0:
|
||||
|
||||
@@ -150,12 +150,18 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
line(p, cpsStmts, pl)
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
|
||||
|
||||
proc reifiedOpenArray(n: PNode): bool {.inline.} =
|
||||
var x = n
|
||||
while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}:
|
||||
x = x[0]
|
||||
while true:
|
||||
case x.kind
|
||||
of {nkAddr, nkHiddenAddr, nkHiddenDeref}:
|
||||
x = x[0]
|
||||
of nkHiddenStdConv:
|
||||
x = x[1]
|
||||
else:
|
||||
break
|
||||
if x.kind == nkSym and x.sym.kind == skParam:
|
||||
result = false
|
||||
else:
|
||||
@@ -166,15 +172,15 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||
initLocExpr(p, q[1], a)
|
||||
initLocExpr(p, q[2], b)
|
||||
initLocExpr(p, q[3], c)
|
||||
# but first produce the required index checks:
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, a, b, c)
|
||||
if prepareForMutation:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
# bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
|
||||
# are mapped into ctPtrToArray, the dereference of which is skipped
|
||||
# in the `genref`. We need to skip these ptrs here
|
||||
# in the `genDeref`. We need to skip these ptrs here
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
|
||||
# but first produce the required index checks:
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, a, b, c, ty)
|
||||
if prepareForMutation:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let dest = getTypeDesc(p.module, destType)
|
||||
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
||||
case ty.kind
|
||||
@@ -309,13 +315,23 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
|
||||
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
|
||||
n.kind == nkHiddenAddr:
|
||||
initLocExprSingleUse(p, n[0], a)
|
||||
# bug #23748: we need to introduce a temporary here. The expression type
|
||||
# will be a reference in C++ and we cannot create a temporary reference
|
||||
# variable. Thus, we create a temporary pointer variable instead.
|
||||
let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
|
||||
if needsIndirect:
|
||||
n.typ = n.typ.exactReplica
|
||||
n.typ.flags.incl tfVarIsPtr
|
||||
initLocExprSingleUse(p, n, a)
|
||||
a = withTmpIfNeeded(p, a, needsTmp)
|
||||
if needsIndirect: a.flags.incl lfIndirect
|
||||
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
|
||||
# means '*T'. See posix.nim for lots of examples that do that in the wild.
|
||||
let callee = call[0]
|
||||
if callee.kind == nkSym and
|
||||
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
|
||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
|
||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and
|
||||
needsIndirect:
|
||||
addAddrLoc(p.config, a, result)
|
||||
else:
|
||||
addRdLoc(a, result)
|
||||
@@ -407,9 +423,11 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
||||
if not needTmp[i - 1]:
|
||||
needTmp[i - 1] = potentialAlias(n, potentialWrites)
|
||||
getPotentialWrites(ri[i], false, potentialWrites)
|
||||
if ri[i].kind in {nkHiddenAddr, nkAddr}:
|
||||
# Optimization: don't use a temp, if we would only take the address anyway
|
||||
needTmp[i - 1] = false
|
||||
when false:
|
||||
# this optimization is wrong, see bug #23748
|
||||
if ri[i].kind in {nkHiddenAddr, nkAddr}:
|
||||
# Optimization: don't use a temp, if we would only take the address anyway
|
||||
needTmp[i - 1] = false
|
||||
|
||||
var oldLen = result.len
|
||||
for i in 1..<ri.len:
|
||||
|
||||
@@ -344,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):
|
||||
@@ -602,7 +602,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
# skipping 'range' is correct here as we'll generate a proper range check
|
||||
# later via 'chckRange'
|
||||
let t = e.typ.skipTypes(abstractRange)
|
||||
if optOverflowCheck notin p.options:
|
||||
if optOverflowCheck notin p.options or (m in {mSucc, mPred} and t.kind in {tyUInt..tyUInt64}):
|
||||
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
||||
putIntoDest(p, d, e, res)
|
||||
else:
|
||||
@@ -832,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)
|
||||
@@ -1037,8 +1039,8 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
putIntoDest(p, d, n,
|
||||
ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
|
||||
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
|
||||
let ty = skipTypes(arr.t, abstractVarRange)
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) =
|
||||
let ty = arrTyp
|
||||
case ty.kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
if reifiedOpenArray(arr.lode):
|
||||
@@ -1887,7 +1889,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
initLocExpr(p, a[2], b)
|
||||
initLocExpr(p, a[3], c)
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, m, b, c)
|
||||
genBoundsCheck(p, m, b, c, skipTypes(m.t, abstractVarRange))
|
||||
if op == mHigh:
|
||||
putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)]))
|
||||
else:
|
||||
@@ -2360,7 +2362,7 @@ proc genWasMoved(p: BProc; n: PNode) =
|
||||
|
||||
proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
var a: TLoc
|
||||
initLocExpr(p, n[1].skipAddr, a)
|
||||
initLocExpr(p, n[1].skipAddr, a, {lfEnforceDeref})
|
||||
if n.len == 4:
|
||||
# generated by liftdestructors:
|
||||
var src: TLoc
|
||||
@@ -2393,8 +2395,14 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
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) =
|
||||
@@ -3110,16 +3118,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of nkObjConstr: genObjConstr(p, n, d)
|
||||
of nkCast: genCast(p, n, d)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d)
|
||||
of nkHiddenAddr:
|
||||
if n[0].kind == nkDerefExpr:
|
||||
# addr ( deref ( x )) --> x
|
||||
var x = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
x.typ = n.typ
|
||||
expr(p, x, d)
|
||||
return
|
||||
genAddr(p, n, d)
|
||||
of nkAddr: genAddr(p, n, d)
|
||||
of nkAddr, nkHiddenAddr: genAddr(p, n, d)
|
||||
of nkBracketExpr: genBracketExpr(p, n, d)
|
||||
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
|
||||
of nkDotExpr: genRecordField(p, n, d)
|
||||
@@ -3304,11 +3303,16 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
||||
isConst: bool, info: TLineInfo) =
|
||||
case obj.kind
|
||||
of nkRecList:
|
||||
let isUnion = tfUnion in t.flags
|
||||
for it in obj.sons:
|
||||
getNullValueAux(p, t, it, constOrNil, result, count, isConst, info)
|
||||
if isUnion:
|
||||
# generate only 1 field for default value of union
|
||||
return
|
||||
of nkRecCase:
|
||||
getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info)
|
||||
if count > 0: result.add ", "
|
||||
var res = ""
|
||||
if count > 0: res.add ", "
|
||||
var branch = Zero
|
||||
if constOrNil != nil:
|
||||
## find kind value, default is zero if not specified
|
||||
@@ -3322,18 +3326,21 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
||||
break
|
||||
|
||||
let selectedBranch = caseObjDefaultBranch(obj, branch)
|
||||
result.add "{"
|
||||
res.add "{"
|
||||
var countB = 0
|
||||
let b = lastSon(obj[selectedBranch])
|
||||
# designated initilization is the only way to init non first element of unions
|
||||
# branches are allowed to have no members (b.len == 0), in this case they don't need initializer
|
||||
if b.kind == nkRecList and not isEmptyCaseObjectBranch(b):
|
||||
result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
|
||||
getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info)
|
||||
result.add "}"
|
||||
res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
|
||||
getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info)
|
||||
res.add "}"
|
||||
elif b.kind == nkSym:
|
||||
result.add "." & mangleRecFieldName(p.module, b.sym) & " = "
|
||||
getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info)
|
||||
res.add "." & mangleRecFieldName(p.module, b.sym) & " = "
|
||||
getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info)
|
||||
else:
|
||||
return
|
||||
result.add res
|
||||
result.add "}"
|
||||
|
||||
of nkSym:
|
||||
|
||||
@@ -18,7 +18,7 @@ proc registerTraverseProc(p: BProc, v: PSym) =
|
||||
var traverseProc = ""
|
||||
if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcRefc} and
|
||||
optOwnedRefs notin p.config.globalOptions and
|
||||
containsGarbageCollectedRef(v.loc.t):
|
||||
containsManagedMemory(v.loc.t):
|
||||
# we register a specialized marked proc here; this has the advantage
|
||||
# that it works out of the box for thread local storage then :-)
|
||||
traverseProc = genTraverseProcForGlobal(p.module, v, v.info)
|
||||
|
||||
@@ -273,9 +273,12 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
|
||||
if rettype.isImportedCppType or t.isImportedCppType or
|
||||
(typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc}):
|
||||
# prevents nrvo for cdecl procs; # bug #23401
|
||||
return false
|
||||
result = containsGarbageCollectedRef(t) or
|
||||
(t.kind == tyObject and not isObjLackingTypeField(t))
|
||||
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
|
||||
@@ -757,7 +760,7 @@ proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope =
|
||||
genMemberProcHeader(m, prc, header, false, true)
|
||||
result.addf "$1;$n", [header]
|
||||
if isCtorGen and not isDefaultCtorGen:
|
||||
var ch: IntSet
|
||||
var ch: IntSet = default(IntSet)
|
||||
result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)]
|
||||
|
||||
proc fillObjectFields*(m: BModule; typ: PType) =
|
||||
@@ -765,6 +768,8 @@ proc fillObjectFields*(m: BModule; typ: PType) =
|
||||
# this fact here.
|
||||
var check = initIntSet()
|
||||
discard getRecordFields(m, typ, check)
|
||||
if typ.baseClass != nil:
|
||||
fillObjectFields(m, typ.baseClass.skipTypes(skipPtrs))
|
||||
|
||||
proc mangleDynLibProc(sym: PSym): Rope
|
||||
|
||||
@@ -1432,7 +1437,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]
|
||||
@@ -1591,7 +1596,7 @@ proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TType
|
||||
))
|
||||
)
|
||||
else:
|
||||
let addrOf = newNodeIT(nkAddr, info, theProc.typ[1])
|
||||
let addrOf = newNodeIT(nkHiddenAddr, info, theProc.typ[1])
|
||||
addrOf.add newDeref(newTreeIT(
|
||||
nkCast, info, castType, newNodeIT(nkType, info, castType),
|
||||
newSymNode(dest)
|
||||
|
||||
@@ -406,6 +406,7 @@ proc rdCharLoc(a: TLoc): Rope =
|
||||
type
|
||||
TAssignmentFlag = enum
|
||||
needToCopy
|
||||
needToCopySinkParam
|
||||
needTempForOpenArray
|
||||
TAssignmentFlags = set[TAssignmentFlag]
|
||||
|
||||
@@ -1062,7 +1063,11 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
|
||||
if result != Unknown: return result
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
if n[0].kind == nkSym and n[0].sym.kind == skResult:
|
||||
if not containsResult(n[1]): result = InitSkippable
|
||||
if not containsResult(n[1]):
|
||||
if allPathsAsgnResult(p, n[1]) == InitRequired:
|
||||
result = InitRequired
|
||||
else:
|
||||
result = InitSkippable
|
||||
else: result = InitRequired
|
||||
elif containsResult(n):
|
||||
result = InitRequired
|
||||
@@ -1140,6 +1145,10 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
|
||||
allPathsInBranch(n[i])
|
||||
of nkRaiseStmt:
|
||||
result = InitRequired
|
||||
of nkChckRangeF, nkChckRange64, nkChckRange:
|
||||
# TODO: more checks might need to be covered like overflow, indexDefect etc.
|
||||
# bug #22852
|
||||
result = InitRequired
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
allPathsInBranch(n[i])
|
||||
|
||||
@@ -163,7 +163,7 @@ type
|
||||
|
||||
const
|
||||
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
|
||||
nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
|
||||
nkCommentStmt, nkMixinStmt, nkBindStmt, nkTypeOfExpr} + procDefs
|
||||
|
||||
proc newStateAccess(ctx: var Ctx): PNode =
|
||||
if ctx.stateVarSym.isNil:
|
||||
|
||||
@@ -143,7 +143,6 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasTemplateRedefinitionPragma")
|
||||
defineSymbol("nimHasCstringCase")
|
||||
defineSymbol("nimHasCallsitePragma")
|
||||
defineSymbol("nimHasAmbiguousEnumHint")
|
||||
|
||||
defineSymbol("nimHasWarnCastSizes") # deadcode
|
||||
defineSymbol("nimHasOutParams")
|
||||
@@ -161,3 +160,6 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
defineSymbol("nimHasGenericsOpenSym2")
|
||||
defineSymbol("nimHasNolineTooLong")
|
||||
defineSymbol("nimHasGenericsOpenSym3")
|
||||
|
||||
@@ -821,7 +821,7 @@ proc getName(n: PNode): string =
|
||||
result = "`"
|
||||
for i in 0..<n.len: result.add(getName(n[i]))
|
||||
result = "`"
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getName(n[0])
|
||||
else:
|
||||
result = ""
|
||||
@@ -839,7 +839,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
|
||||
var r = ""
|
||||
for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
|
||||
result = getIdent(cache, r)
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getNameIdent(cache, n[0])
|
||||
else:
|
||||
result = nil
|
||||
@@ -853,7 +853,7 @@ proc getRstName(n: PNode): PRstNode =
|
||||
of nkAccQuoted:
|
||||
result = getRstName(n[0])
|
||||
for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getRstName(n[0])
|
||||
else:
|
||||
result = nil
|
||||
|
||||
@@ -423,8 +423,8 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
|
||||
internalAssert conf, call[0].kind == nkPtrLit
|
||||
|
||||
var cif: TCif
|
||||
var sig: ParamList
|
||||
var cif: TCif = default(TCif)
|
||||
var sig: ParamList = default(ParamList)
|
||||
# use the arguments' types for varargs support:
|
||||
for i in 1..<call.len:
|
||||
sig[i-1] = mapType(conf, call[i].typ)
|
||||
@@ -463,8 +463,8 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||
info: TLineInfo): PNode =
|
||||
internalAssert conf, fn.kind == nkPtrLit
|
||||
|
||||
var cif: TCif
|
||||
var sig: ParamList
|
||||
var cif: TCif = default(TCif)
|
||||
var sig: ParamList = default(ParamList)
|
||||
for i in 0..len-1:
|
||||
var aTyp = args[i+start].typ
|
||||
if aTyp.isNil:
|
||||
|
||||
@@ -56,6 +56,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
# internalAssert c.config, false
|
||||
idTablePut(c.mapping, s, x)
|
||||
if sfGenSym in s.flags:
|
||||
# TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID)
|
||||
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
|
||||
if c.instLines: actual.info else: templ.info)
|
||||
else:
|
||||
|
||||
@@ -452,6 +452,11 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
|
||||
proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
|
||||
result = conf.compileOptions
|
||||
|
||||
if (conf.cCompiler == ccGcc or conf.cCompiler == ccCLang) and
|
||||
conf.selectedGC == gcRefc:
|
||||
# bug #10625
|
||||
addOpt(result, "-fno-omit-frame-pointer")
|
||||
|
||||
for option in conf.compileOptionsCmd:
|
||||
if strutils.find(result, option, 0) < 0:
|
||||
addOpt(result, option)
|
||||
|
||||
@@ -1047,8 +1047,12 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||
if check[1].kind == nkCurly:
|
||||
result = copyTree(check)
|
||||
if access.kind == nkDotExpr:
|
||||
# change the access to the discriminator field access
|
||||
var a = copyTree(access)
|
||||
# set field name to discriminator field name
|
||||
a[1] = check[2]
|
||||
# set discriminator field type: important for `neg`
|
||||
a.typ = check[2].typ
|
||||
result[2] = a
|
||||
# 'access.kind != nkDotExpr' can happen for object constructors
|
||||
# which we don't check yet
|
||||
|
||||
@@ -768,6 +768,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
|
||||
of nkSym:
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand))
|
||||
if result.typ == nil:
|
||||
result.typ = result.sym.typ
|
||||
of directIntLit:
|
||||
result.intVal = tree.nodes[n.int].operand
|
||||
of externIntLit:
|
||||
@@ -782,6 +784,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
assert n2.kind == nkInt32Lit
|
||||
transitionNoneToSym(result)
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
|
||||
if result.typ == nil:
|
||||
result.typ = result.sym.typ
|
||||
else:
|
||||
for n0 in sonsReadonly(tree, n):
|
||||
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
|
||||
|
||||
@@ -141,7 +141,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
|
||||
# for an enumeration we have to add all identifiers
|
||||
if multiImport:
|
||||
# for a overloadable syms add all overloaded routines
|
||||
var it: ModuleIter
|
||||
var it: ModuleIter = default(ModuleIter)
|
||||
var e = initModuleIter(it, c.graph, fromMod, s.name)
|
||||
while e != nil:
|
||||
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
|
||||
|
||||
@@ -313,8 +313,9 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
|
||||
]#
|
||||
result = dest.kind != nkSym
|
||||
|
||||
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc:
|
||||
proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags:
|
||||
# add cyclic flag, but not to sink calls, which IsExplicitSink generates
|
||||
let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if cyclicType(c.graph, t):
|
||||
result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
|
||||
@@ -459,7 +460,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
var newCall = newTreeIT(nkCall, src.info, src.typ,
|
||||
newSymNode(op),
|
||||
src)
|
||||
c.finishCopy(newCall, n, isFromSink = true)
|
||||
c.finishCopy(newCall, n, {}, isFromSink = true)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
newCall
|
||||
@@ -468,7 +469,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
c.finishCopy(m, n, {}, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
message(c.graph.config, n.info, hintPerformance,
|
||||
@@ -496,7 +497,7 @@ proc containsConstSeq(n: PNode): bool =
|
||||
return true
|
||||
result = false
|
||||
case n.kind
|
||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
|
||||
result = containsConstSeq(n[1])
|
||||
of nkObjConstr, nkClosure:
|
||||
for i in 1..<n.len:
|
||||
@@ -754,7 +755,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||
let tmp = c.getTemp(s, n[0].typ, n.info)
|
||||
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
|
||||
m.add p(n[0], c, s, normal)
|
||||
c.finishCopy(m, n[0], isFromSink = false)
|
||||
c.finishCopy(m, n[0], {}, isFromSink = false)
|
||||
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
|
||||
var toDisarm = n[0]
|
||||
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
|
||||
@@ -794,15 +795,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result = passCopyToSink(n, c, s)
|
||||
elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
|
||||
nkCallKinds + nkLiterals:
|
||||
if n.kind in nkCallKinds and n[0].kind == nkSym:
|
||||
if n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
result = p(n, c, s, consumed)
|
||||
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
||||
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
@@ -822,6 +815,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
elif n.kind in {nkObjDownConv, nkObjUpConv}:
|
||||
result = copyTree(n)
|
||||
result[0] = p(n[0], c, s, sinkArg)
|
||||
elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}:
|
||||
result = copyTree(n)
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
elif n.typ == nil:
|
||||
# 'raise X' can be part of a 'case' expression. Deal with it here:
|
||||
result = p(n, c, s, normal)
|
||||
@@ -874,12 +870,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
return
|
||||
|
||||
let inSpawn = c.inSpawn
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
|
||||
c.inSpawn.inc
|
||||
@@ -896,13 +886,19 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
isDangerous = true
|
||||
|
||||
result = shallowCopy(n)
|
||||
for i in 1..<n.len:
|
||||
if i < L and isCompileTimeOnly(parameters[i]):
|
||||
result[i] = n[i]
|
||||
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
|
||||
result[i] = p(n[i], c, s, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
for i in 1..<n.len:
|
||||
if i < L and isCompileTimeOnly(parameters[i]):
|
||||
result[i] = n[i]
|
||||
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
|
||||
result[i] = p(n[i], c, s, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
|
||||
when false:
|
||||
if isDangerous:
|
||||
@@ -1160,7 +1156,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
@@ -1168,7 +1164,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
else:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
@@ -1188,7 +1184,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
@@ -1208,7 +1204,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
|
||||
when false:
|
||||
proc computeUninit(c: var Con) =
|
||||
|
||||
@@ -896,7 +896,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
|
||||
p.body.add("++excHandler;\L")
|
||||
var tmpFramePtr = rope"F"
|
||||
lineF(p, "try {$n", [])
|
||||
var a: TCompRes
|
||||
var a: TCompRes = default(TCompRes)
|
||||
gen(p, n[0], a)
|
||||
moveInto(p, a, r)
|
||||
var generalCatchBranchExists = false
|
||||
@@ -1519,15 +1519,8 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
|
||||
of nkObjDownConv:
|
||||
gen(p, n[0], r)
|
||||
of nkHiddenDeref:
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
gen(p, n[0], r)
|
||||
of nkDerefExpr:
|
||||
var x = n[0]
|
||||
if n.kind == nkHiddenAddr:
|
||||
x = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
x.typ = n.typ
|
||||
gen(p, x, r)
|
||||
of nkHiddenAddr:
|
||||
gen(p, n[0], r)
|
||||
of nkConv:
|
||||
|
||||
@@ -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; isDistinct = false): PSym
|
||||
info: TLineInfo; idgen: IdGenerator): PSym
|
||||
|
||||
proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
idgen: IdGenerator)
|
||||
@@ -217,17 +217,38 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
|
||||
c.filterDiscriminator = oldfilterDiscriminator
|
||||
of nkRecList:
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
# destroys in reverse order #24719
|
||||
if c.kind == attachedDestructor:
|
||||
for i in countdown(n.len-1, 0):
|
||||
fillBodyObj(c, n[i], body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
else:
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
else:
|
||||
illFormedAstLocal(n, c.g.config)
|
||||
|
||||
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
if t.len > 0 and t[0] != nil:
|
||||
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)
|
||||
template fillBase =
|
||||
if t.len > 0 and t[0] != nil:
|
||||
let dest = newNodeIT(nkHiddenSubConv, c.info, t[0])
|
||||
dest.add newNodeI(nkEmpty, c.info)
|
||||
dest.add x
|
||||
var src = y
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
src = newNodeIT(nkHiddenSubConv, c.info, t[0])
|
||||
src.add newNodeI(nkEmpty, c.info)
|
||||
src.add y
|
||||
|
||||
fillBody(c, skipTypes(t[0], abstractPtrs), body, dest, src)
|
||||
template fillFields =
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
if c.kind == attachedDestructor:
|
||||
# destroys in reverse order #24719
|
||||
fillFields()
|
||||
fillBase()
|
||||
else:
|
||||
fillBase()
|
||||
fillFields()
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
var hasCase = isCaseObj(t.n)
|
||||
@@ -1036,9 +1057,7 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ[0]
|
||||
if getAttachedOp(g, baseType, kind) == nil:
|
||||
# TODO: fixme `isDistinct` is a fix for #23552; remove it after
|
||||
# `-d:nimPreviewNonVarDestructor` becomes the default
|
||||
discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
|
||||
discard produceSym(g, c, baseType, kind, info, idgen)
|
||||
result = getAttachedOp(g, baseType, kind)
|
||||
setAttachedOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
@@ -1077,7 +1096,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; isDistinct = false): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym =
|
||||
if kind == attachedDup:
|
||||
return symDupPrototype(g, typ, owner, kind, info, idgen)
|
||||
|
||||
@@ -1086,8 +1105,8 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
|
||||
let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
|
||||
idgen, result, info)
|
||||
|
||||
if kind == attachedDestructor and typ.kind in {tyRef, tyString, tySequence} and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and not isDistinct:
|
||||
if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence})):
|
||||
dest.typ = typ
|
||||
else:
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
@@ -1129,13 +1148,13 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(xx, yy)
|
||||
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
if typ.kind == tyDistinct:
|
||||
return produceSymDistinctType(g, c, typ, kind, info, idgen)
|
||||
|
||||
result = getAttachedOp(g, typ, kind)
|
||||
if result == nil:
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen)
|
||||
|
||||
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
@@ -1173,14 +1192,22 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
# bug #19205: Do not forget to also copy the hidden type field:
|
||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
||||
|
||||
if not a.canRaise: incl result.flags, sfNeverRaises
|
||||
if not a.canRaise:
|
||||
incl result.flags, sfNeverRaises
|
||||
result.ast[pragmasPos] = newNodeI(nkPragma, info)
|
||||
result.ast[pragmasPos].add newTree(nkExprColonExpr,
|
||||
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))
|
||||
|
||||
if kind == attachedDestructor:
|
||||
incl result.options, optQuirky
|
||||
completePartialOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
|
||||
proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
assert(typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject)
|
||||
result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen)
|
||||
# discrimantor assignments needs pointers to destroy fields; alas, we cannot use non-var destructor here
|
||||
result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen, isDiscriminant = true)
|
||||
var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
a.asgnForType = typ
|
||||
|
||||
@@ -91,7 +91,7 @@ type
|
||||
warnStmtListLambda = "StmtListLambda",
|
||||
warnBareExcept = "BareExcept",
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnGenericsIgnoredInjection = "GenericsIgnoredInjection",
|
||||
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
|
||||
warnUser = "User",
|
||||
# hints
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||
@@ -105,7 +105,6 @@ type
|
||||
hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency",
|
||||
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
|
||||
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
|
||||
hintAmbiguousEnum = "AmbiguousEnum",
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
@@ -196,7 +195,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",
|
||||
warnIgnoredSymbolInjection: "$1",
|
||||
warnUser: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
@@ -228,7 +227,6 @@ const
|
||||
hintGCStats: "$1",
|
||||
hintGlobalVar: "global variable declared here",
|
||||
hintExpandMacro: "expanded macro: $1",
|
||||
hintAmbiguousEnum: "$1",
|
||||
hintUser: "$1",
|
||||
hintUserRaw: "$1",
|
||||
hintExtendedContext: "$1",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import std/strutils
|
||||
from std/sugar import dup
|
||||
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages, modulegraphs
|
||||
export packages
|
||||
|
||||
const
|
||||
@@ -95,7 +95,7 @@ template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind)
|
||||
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
|
||||
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
|
||||
sym.kind != skResult and # ignore `result`
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
@@ -136,7 +136,7 @@ template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
|
||||
## Check symbol uses match their definition's style.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
|
||||
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
|
||||
@@ -152,5 +152,5 @@ template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragma
|
||||
## Note: This only applies to builtin pragmas, not user pragmas.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
(sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)): # ignore foreign packages
|
||||
checkPragmaUseImpl(ctx.config, info, w, pragmaName)
|
||||
|
||||
@@ -48,7 +48,7 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
case x.kind
|
||||
of nkIdent: id.add(x.ident.s)
|
||||
of nkSym: id.add(x.sym.name.s)
|
||||
of nkSymChoices:
|
||||
of nkSymChoices, nkOpenSym:
|
||||
if x[0].kind == nkSym:
|
||||
id.add(x[0].sym.name.s)
|
||||
else:
|
||||
@@ -61,6 +61,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
result = n[0].sym.name
|
||||
else:
|
||||
handleError(n, origin)
|
||||
of nkOpenSym:
|
||||
result = considerQuotedIdent(c, n[0], origin)
|
||||
else:
|
||||
handleError(n, origin)
|
||||
|
||||
@@ -135,7 +137,7 @@ proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule;
|
||||
return result
|
||||
|
||||
iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym =
|
||||
var ti: ModuleIter
|
||||
var ti: ModuleIter = default(ModuleIter)
|
||||
var candidate = initIdentIter(ti, marked, im, name, g)
|
||||
while candidate != nil:
|
||||
yield candidate
|
||||
@@ -148,7 +150,7 @@ iterator importedItems*(c: PContext; name: PIdent): PSym =
|
||||
yield s
|
||||
|
||||
proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
|
||||
var ti: TIdentIter
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
result = @[]
|
||||
var res = initIdentIter(ti, c.pureEnumFields, name)
|
||||
while res != nil:
|
||||
@@ -220,7 +222,7 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
|
||||
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
@@ -238,7 +240,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
result = @[]
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
@@ -254,11 +256,23 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc cmpScopes*(ctx: PContext, s: PSym): int =
|
||||
# Do not return a negative number
|
||||
if s.originatingModule == ctx.module:
|
||||
result = 2
|
||||
var owner = s
|
||||
while true:
|
||||
owner = owner.skipGenericOwner
|
||||
if owner.kind == skModule: break
|
||||
inc result
|
||||
else:
|
||||
result = 1
|
||||
|
||||
proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool =
|
||||
result = false
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
var scopeHasCandidate = false
|
||||
while candidate != nil:
|
||||
@@ -289,8 +303,16 @@ proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): boo
|
||||
# imports had a candidate but wasn't ambiguous
|
||||
return false
|
||||
|
||||
proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
proc errorSym*(c: PContext, ident: PIdent, info: TLineInfo): PSym =
|
||||
## creates an error symbol to avoid cascading errors (for IDE support)
|
||||
result = newSym(skError, ident, c.idgen, getCurrOwner(c), info, {})
|
||||
result.typ = errorType(c)
|
||||
incl(result.flags, sfDiscardable)
|
||||
# pretend it's from the top level scope to prevent cascading errors:
|
||||
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
|
||||
c.moduleScope.addSym(result)
|
||||
|
||||
proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
var m = n
|
||||
# ensure that 'considerQuotedIdent' can't fail:
|
||||
if m.kind == nkDotExpr: m = m[1]
|
||||
@@ -298,12 +320,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
considerQuotedIdent(c, m)
|
||||
else:
|
||||
getIdent(c.cache, "err:" & renderTree(m))
|
||||
result = newSym(skError, ident, c.idgen, getCurrOwner(c), n.info, {})
|
||||
result.typ = errorType(c)
|
||||
incl(result.flags, sfDiscardable)
|
||||
# pretend it's from the top level scope to prevent cascading errors:
|
||||
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
|
||||
c.moduleScope.addSym(result)
|
||||
result = errorSym(c, ident, n.info)
|
||||
|
||||
type
|
||||
TOverloadIterMode* = enum
|
||||
@@ -330,7 +347,7 @@ proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string =
|
||||
|
||||
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
|
||||
# check if all symbols have been used and defined:
|
||||
var it: TTabIter
|
||||
var it: TTabIter = default(TTabIter)
|
||||
var s = initTabIter(it, scope.symbols)
|
||||
var missingImpls = 0
|
||||
var unusedSyms: seq[tuple[sym: PSym, key: string]]
|
||||
@@ -485,7 +502,7 @@ proc mustFixSpelling(c: PContext): bool {.inline.} =
|
||||
result = c.config.spellSuggestMax != 0 and c.compilesContextId == 0
|
||||
# don't slowdown inside compiles()
|
||||
|
||||
proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
|
||||
proc fixSpelling(c: PContext, ident: PIdent, result: var string) =
|
||||
## when we cannot find the identifier, suggest nearby spellings
|
||||
var list = initHeapQueue[SpellCandidate]()
|
||||
let name0 = ident.s.nimIdentNormalize
|
||||
@@ -540,10 +557,10 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PS
|
||||
amb = false
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
|
||||
var amb: bool
|
||||
var amb: bool = false
|
||||
discard errorUseQualifier(c, info, s, amb)
|
||||
|
||||
proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
|
||||
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
|
||||
var i = 0
|
||||
for candidate in candidates:
|
||||
@@ -578,11 +595,11 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extr
|
||||
c.recursiveDep = ""
|
||||
localError(c.config, info, errGenerated, err)
|
||||
|
||||
proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym =
|
||||
proc errorUndeclaredIdentifierHint*(c: PContext; ident: PIdent; info: TLineInfo): PSym =
|
||||
var extra = ""
|
||||
if c.mustFixSpelling: fixSpelling(c, n, ident, extra)
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s, extra)
|
||||
result = errorSym(c, n)
|
||||
if c.mustFixSpelling: fixSpelling(c, ident, extra)
|
||||
errorUndeclaredIdentifier(c, info, ident.s, extra)
|
||||
result = errorSym(c, ident, info)
|
||||
|
||||
proc lookUp*(c: PContext, n: PNode): PSym =
|
||||
# Looks up a symbol. Generates an error in case of nil.
|
||||
@@ -590,16 +607,16 @@ proc lookUp*(c: PContext, n: PNode): PSym =
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
result = searchInScopes(c, n.ident, amb)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, n.ident)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n.ident, n.info)
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkAccQuoted:
|
||||
var ident = considerQuotedIdent(c, n)
|
||||
result = searchInScopes(c, ident, amb)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, ident, n.info)
|
||||
else:
|
||||
internalError(c.config, n.info, "lookUp")
|
||||
return
|
||||
return nil
|
||||
if amb:
|
||||
#contains(c.ambiguousSymbols, result.id):
|
||||
result = errorUseQualifier(c, n.info, result, amb)
|
||||
@@ -610,36 +627,45 @@ type
|
||||
TLookupFlag* = enum
|
||||
checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
|
||||
|
||||
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
|
||||
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind]): seq[PSym] =
|
||||
result = searchInScopesFilterBy(c, ident, filter)
|
||||
if result.len == 0:
|
||||
result.add allPureEnumFields(c, ident)
|
||||
|
||||
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
case n.kind
|
||||
of nkIdent, nkAccQuoted:
|
||||
var amb = false
|
||||
var ident = considerQuotedIdent(c, n)
|
||||
if checkModule in flags:
|
||||
result = searchInScopes(c, ident, amb)
|
||||
if result == nil:
|
||||
let candidates = allPureEnumFields(c, ident)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
if amb and checkAmbiguity in flags:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
else:
|
||||
let candidates = searchInScopesFilterBy(c, ident, allExceptModule)
|
||||
let candidates = lookUpCandidates(c, ident, allExceptModule)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
if amb and checkAmbiguity in flags:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
if result == nil:
|
||||
let candidates = allPureEnumFields(c, ident)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
if amb and checkAmbiguity in flags:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
|
||||
else:
|
||||
result = nil
|
||||
if result == nil and checkUndeclared in flags:
|
||||
result = errorUndeclaredIdentifierHint(c, n, ident)
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n.info)
|
||||
elif checkAmbiguity in flags and result != nil and amb:
|
||||
result = errorUseQualifier(c, n.info, result, amb)
|
||||
c.isAmbiguous = amb
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkOpenSym:
|
||||
result = qualifiedLookUp(c, n[0], flags)
|
||||
of nkDotExpr:
|
||||
result = nil
|
||||
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
|
||||
@@ -654,17 +680,17 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
result = strTableGet(c.topLevelScope.symbols, ident)
|
||||
else:
|
||||
if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
|
||||
var amb: bool
|
||||
var amb: bool = false
|
||||
result = errorUseQualifier(c, n.info, m, amb)
|
||||
else:
|
||||
result = someSym(c.graph, m, ident)
|
||||
if result == nil and checkUndeclared in flags:
|
||||
result = errorUndeclaredIdentifierHint(c, n[1], ident)
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
|
||||
elif n[1].kind == nkSym:
|
||||
result = n[1].sym
|
||||
if result.owner != nil and result.owner != m and checkUndeclared in flags:
|
||||
# dotExpr in templates can end up here
|
||||
result = errorUndeclaredIdentifierHint(c, n[1], considerQuotedIdent(c, n[1]))
|
||||
result = errorUndeclaredIdentifierHint(c, result.name, n[1].info)
|
||||
elif checkUndeclared in flags and
|
||||
n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
|
||||
localError(c.config, n[1].info, "identifier expected, but got: " &
|
||||
@@ -676,6 +702,10 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
if result != nil and result.kind == skStub: loadStub(result)
|
||||
|
||||
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
if n.kind == nkOpenSym:
|
||||
# maybe the logic in semexprs should be mirrored here instead
|
||||
# for now it only seems this is called for `pickSym` in `getTypeIdent`
|
||||
return initOverloadIter(o, c, n[0])
|
||||
o.importIdx = -1
|
||||
o.marked = initIntSet()
|
||||
case n.kind
|
||||
|
||||
@@ -195,8 +195,8 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
|
||||
var stream = llStreamOpen(f, fmRead)
|
||||
if stream != nil:
|
||||
var
|
||||
L: Lexer
|
||||
tok: Token
|
||||
L: Lexer = default(Lexer)
|
||||
tok: Token = default(Token)
|
||||
initToken(tok)
|
||||
openLexer(L, f, stream, cache, config)
|
||||
while true:
|
||||
|
||||
@@ -251,7 +251,7 @@ proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
|
||||
iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
|
||||
let importHidden = optImportHidden in m.options
|
||||
if isCachedModule(g, m):
|
||||
var rodIt: RodIter
|
||||
var rodIt: RodIter = default(RodIter)
|
||||
var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden)
|
||||
while r != nil:
|
||||
yield r
|
||||
@@ -272,7 +272,7 @@ proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
|
||||
result = someSym(g, g.systemModule, name)
|
||||
|
||||
iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
|
||||
var mi: ModuleIter
|
||||
var mi: ModuleIter = default(ModuleIter)
|
||||
var r = initModuleIter(mi, g, g.systemModule, name)
|
||||
while r != nil:
|
||||
yield r
|
||||
|
||||
@@ -125,14 +125,14 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool
|
||||
conf.m.filenameToIndexTbl[canon2] = result
|
||||
|
||||
proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
|
||||
var dummy: bool
|
||||
var dummy: bool = false
|
||||
result = fileInfoIdx(conf, filename, dummy)
|
||||
|
||||
proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile; isKnownFile: var bool): FileIndex =
|
||||
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), isKnownFile)
|
||||
|
||||
proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex =
|
||||
var dummy: bool
|
||||
var dummy: bool = false
|
||||
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy)
|
||||
|
||||
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
|
||||
|
||||
@@ -9,6 +9,7 @@ define:nimPreviewSlimSystem
|
||||
define:nimPreviewCstringConversion
|
||||
define:nimPreviewProcConversion
|
||||
define:nimPreviewRangeDefault
|
||||
define:nimPreviewNonVarDestructor
|
||||
threads:off
|
||||
|
||||
#import:"$projectpath/testability"
|
||||
|
||||
@@ -40,7 +40,7 @@ proc selectUniqueSymbol*(i: Interpreter; name: string;
|
||||
assert i != nil
|
||||
assert i.mainModule != nil, "no main module selected"
|
||||
let n = getIdent(i.graph.cache, name)
|
||||
var it: ModuleIter
|
||||
var it: ModuleIter = default(ModuleIter)
|
||||
var s = initModuleIter(it, i.graph, i.mainModule, n)
|
||||
result = nil
|
||||
while s != nil:
|
||||
|
||||
@@ -279,8 +279,8 @@ proc optimize*(n: PNode): PNode =
|
||||
|
||||
Now assume 'use' raises, then we shouldn't do the 'wasMoved(s)'
|
||||
]#
|
||||
var c: Con
|
||||
var b: BasicBlock
|
||||
var c: Con = Con()
|
||||
var b: BasicBlock = default(BasicBlock)
|
||||
analyse(c, b, n)
|
||||
if c.somethingTodo:
|
||||
result = shallowCopy(n)
|
||||
|
||||
@@ -221,6 +221,8 @@ type
|
||||
flexibleOptionalParams,
|
||||
strictDefs,
|
||||
strictCaseObjects,
|
||||
openSym, # remove nfDisabledOpenSym when this is default
|
||||
# alternative to above:
|
||||
genericsOpenSym
|
||||
|
||||
LegacyFeature* = enum
|
||||
|
||||
@@ -51,3 +51,11 @@ func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
conf.mainPackageId == sym.getPackageId
|
||||
|
||||
func belongsToProjectPackageMaybeNil*(conf: ConfigRef, sym: PSym): bool =
|
||||
## Return whether the symbol belongs to the project's package.
|
||||
## Returns `false` if `sym` is nil.
|
||||
##
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
sym != nil and conf.mainPackageId == sym.getPackageId
|
||||
|
||||
@@ -280,8 +280,15 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result = isAssignable(owner, n[0])
|
||||
of nkCallKinds:
|
||||
# builtin slice keeps lvalue-ness:
|
||||
if getMagic(n) in {mArrGet, mSlice}:
|
||||
let m = getMagic(n)
|
||||
if m == mSlice:
|
||||
# builtin slice keeps l-value-ness
|
||||
# except for pointers because slice dereferences
|
||||
if n[1].typ.kind == tyPtr:
|
||||
result = arLValue
|
||||
else:
|
||||
result = isAssignable(owner, n[1])
|
||||
elif m == mArrGet:
|
||||
result = isAssignable(owner, n[1])
|
||||
elif n.typ != nil:
|
||||
case n.typ.kind
|
||||
|
||||
@@ -91,7 +91,7 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
stream: PLLStream): bool =
|
||||
if graph.stopCompile(): return true
|
||||
var
|
||||
p: Parser
|
||||
p: Parser = default(Parser)
|
||||
s: PLLStream
|
||||
fileIdx = module.fileIdx
|
||||
|
||||
|
||||
@@ -862,6 +862,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
|
||||
# number of pragmas increase/decrease with user pragma expansion
|
||||
inc c.instCounter
|
||||
defer: dec c.instCounter
|
||||
if c.instCounter > 100:
|
||||
globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s)
|
||||
|
||||
@@ -871,7 +872,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
pragma(c, sym, userPragma.ast, validPragmas, isStatement)
|
||||
n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
|
||||
i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
|
||||
dec c.instCounter
|
||||
else:
|
||||
let k = whichKeyword(ident)
|
||||
if k in validPragmas:
|
||||
@@ -1295,8 +1295,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
pragmaProposition(c, it)
|
||||
of wEnsures:
|
||||
pragmaEnsures(c, it)
|
||||
of wEnforceNoRaises, wQuirky:
|
||||
of wEnforceNoRaises:
|
||||
sym.flags.incl sfNeverRaises
|
||||
of wQuirky:
|
||||
sym.flags.incl sfNeverRaises
|
||||
if sym.kind in {skProc, skMethod, skConverter, skFunc, skIterator}:
|
||||
sym.options.incl optQuirky
|
||||
of wSystemRaisesDefect:
|
||||
sym.flags.incl sfSystemRaisesDefect
|
||||
of wVirtual:
|
||||
|
||||
@@ -31,7 +31,7 @@ proc equalGenericParams(procA, procB: PNode): bool =
|
||||
proc searchForProcAux(c: PContext, scope: PScope, fn: PSym): PSym =
|
||||
const flags = {ExactGenericParams, ExactTypeDescValues,
|
||||
ExactConstraints, IgnoreCC}
|
||||
var it: TIdentIter
|
||||
var it: TIdentIter = default(TIdentIter)
|
||||
result = initIdentIter(it, scope.symbols, fn.name)
|
||||
while result != nil:
|
||||
if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags):
|
||||
@@ -74,7 +74,7 @@ when false:
|
||||
proc searchForBorrowProc*(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
# Searches for the fn in the symbol table. If the parameter lists are suitable
|
||||
# for borrowing the sym in the symbol table is returned, else nil.
|
||||
var it: TIdentIter
|
||||
var it: TIdentIter = default(TIdentIter)
|
||||
for scope in walkScopes(startScope):
|
||||
result = initIdentIter(it, scope.symbols, fn.Name)
|
||||
while result != nil:
|
||||
|
||||
@@ -515,6 +515,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
if n.len > 0: result += lsub(g, n[0])
|
||||
of nkOpenSym: result = lsub(g, n[0])
|
||||
of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
|
||||
of nkTupleClassTy: result = len("tuple")
|
||||
of nkDotExpr: result = lsons(g, n) + 1
|
||||
@@ -556,8 +557,16 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
of nkIfExpr:
|
||||
result = lsub(g, n[0][0]) + lsub(g, n[0][1]) + lsons(g, n, 1) +
|
||||
len("if_:_")
|
||||
of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
|
||||
of nkElseExpr: result = lsub(g, n[0]) + len("_else:_") # type descriptions
|
||||
of nkElifExpr, nkElifBranch:
|
||||
if isEmptyType(n[1].typ):
|
||||
result = lsons(g, n) + len("elif_:_")
|
||||
else:
|
||||
result = lsons(g, n) + len("_elif_:_")
|
||||
of nkElseExpr, nkElse:
|
||||
if isEmptyType(n[0].typ):
|
||||
result = lsub(g, n[0]) + len("else:_")
|
||||
else:
|
||||
result = lsub(g, n[0]) + len("_else:_") # type descriptions
|
||||
of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n[0]) else: 0)+len("typeof()")
|
||||
of nkRefTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ref")
|
||||
of nkPtrTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ptr")
|
||||
@@ -600,8 +609,6 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
of nkCommentStmt: result = n.comment.len
|
||||
of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
|
||||
of nkImportAs: result = lsub(g, n[0]) + len("_as_") + lsub(g, n[1])
|
||||
of nkElifBranch: result = lsons(g, n) + len("elif_:_")
|
||||
of nkElse: result = lsub(g, n[0]) + len("else:_")
|
||||
of nkFinally: result = lsub(g, n[0]) + len("finally:_")
|
||||
of nkGenericParams: result = lcomma(g, n) + 2
|
||||
of nkFormalParams:
|
||||
@@ -1019,7 +1026,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
|
||||
proc skipHiddenNodes(n: PNode): PNode =
|
||||
result = n
|
||||
while result != nil:
|
||||
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1:
|
||||
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1:
|
||||
result = result[1]
|
||||
elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
|
||||
result.len > 0:
|
||||
@@ -1279,6 +1286,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
|
||||
else:
|
||||
gsub(g, n, 0)
|
||||
of nkOpenSym: gsub(g, n, 0)
|
||||
of nkPar, nkClosure:
|
||||
put(g, tkParLe, "(")
|
||||
gcomma(g, n, c)
|
||||
@@ -1308,10 +1316,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
put(g, tkCustomLit, n[0].strVal)
|
||||
gsub(g, n, 1)
|
||||
else:
|
||||
gsub(g, n, 0)
|
||||
for i in 0..<n.len-1:
|
||||
gsub(g, n, i)
|
||||
put(g, tkDot, ".")
|
||||
assert n.len == 2, $n.len
|
||||
accentedName(g, n[1])
|
||||
if n.len > 1:
|
||||
accentedName(g, n[^1])
|
||||
of nkBind:
|
||||
putWithSpace(g, tkBind, "bind")
|
||||
gsub(g, n, 0)
|
||||
@@ -1456,15 +1465,30 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
putWithSpace(g, tkColon, ":")
|
||||
if n.len > 0: gsub(g, n[0], 1)
|
||||
gsons(g, n, emptyContext, 1)
|
||||
of nkElifExpr:
|
||||
putWithSpace(g, tkElif, " elif")
|
||||
gcond(g, n[0])
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 1)
|
||||
of nkElseExpr:
|
||||
put(g, tkElse, " else")
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 0)
|
||||
of nkElifExpr, nkElifBranch:
|
||||
if isEmptyType(n[1].typ):
|
||||
optNL(g)
|
||||
putWithSpace(g, tkElif, "elif")
|
||||
gsub(g, n, 0)
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n[1], c)
|
||||
else:
|
||||
putWithSpace(g, tkElif, " elif")
|
||||
gcond(g, n[0])
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 1)
|
||||
of nkElseExpr, nkElse:
|
||||
if isEmptyType(n[0].typ):
|
||||
optNL(g)
|
||||
put(g, tkElse, "else")
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n[0], c)
|
||||
else:
|
||||
put(g, tkElse, " else")
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 0)
|
||||
of nkTypeOfExpr:
|
||||
put(g, tkType, "typeof")
|
||||
put(g, tkParLe, "(")
|
||||
@@ -1726,19 +1750,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
of nkMixinStmt:
|
||||
putWithSpace(g, tkMixin, "mixin")
|
||||
gcomma(g, n, c)
|
||||
of nkElifBranch:
|
||||
optNL(g)
|
||||
putWithSpace(g, tkElif, "elif")
|
||||
gsub(g, n, 0)
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n[1], c)
|
||||
of nkElse:
|
||||
optNL(g)
|
||||
put(g, tkElse, "else")
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n[0], c)
|
||||
of nkFinally, nkDefer:
|
||||
optNL(g)
|
||||
if n.kind == nkFinally:
|
||||
|
||||
@@ -319,6 +319,14 @@ proc intersects(s1, s2: IntSet): bool =
|
||||
if s2.contains(a):
|
||||
return true
|
||||
|
||||
proc hasPushOrPopPragma(n: DepN): bool =
|
||||
# Checks if the tree node has some pragmas that do not
|
||||
# play well with reordering, like the push/pop pragma
|
||||
# no crossing for push/pop barrier
|
||||
let a = n.pnode
|
||||
result = a.kind == nkPragma and a[0].kind == nkIdent and
|
||||
(a[0].ident.s == "push" or a[0].ident.s == "pop")
|
||||
|
||||
proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
|
||||
# Build a dependency graph
|
||||
result = newSeqOfCap[DepN](deps.len)
|
||||
@@ -360,6 +368,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
|
||||
for dep in deps[i][0]:
|
||||
if dep in declares:
|
||||
ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
|
||||
elif hasPushOrPopPragma(nj):
|
||||
# Every node that comes after a push/pop pragma must
|
||||
# depend on it; vice versa
|
||||
if j < i:
|
||||
ni.kids.add nj
|
||||
else:
|
||||
nj.kids.add ni
|
||||
else:
|
||||
for d in declares:
|
||||
if uses.contains(d):
|
||||
@@ -393,23 +408,14 @@ proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
|
||||
proc getStrongComponents(g: var DepG): seq[seq[DepN]] =
|
||||
## Tarjan's algorithm. Performs a topological sort
|
||||
## and detects strongly connected components.
|
||||
var s: seq[DepN]
|
||||
result = @[]
|
||||
var s: seq[DepN] = @[]
|
||||
var idx = 0
|
||||
for v in g.mitems:
|
||||
if v.idx < 0:
|
||||
strongConnect(v, idx, s, result)
|
||||
|
||||
proc hasForbiddenPragma(n: PNode): bool =
|
||||
# Checks if the tree node has some pragmas that do not
|
||||
# play well with reordering, like the push/pop pragma
|
||||
for a in n:
|
||||
if a.kind == nkPragma and a[0].kind == nkIdent and
|
||||
a[0].ident.s == "push":
|
||||
return true
|
||||
|
||||
proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
|
||||
if n.hasForbiddenPragma:
|
||||
return n
|
||||
var includedFiles = initIntSet()
|
||||
let mpath = toFullPath(graph.config, module.fileIdx)
|
||||
let n = expandIncludes(graph, module, n, mpath,
|
||||
|
||||
@@ -88,6 +88,11 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
|
||||
changeType(c, x, formal, check=true)
|
||||
result = arg
|
||||
result = skipHiddenSubConv(result, c.graph, c.idgen)
|
||||
# mark inserted converter as used:
|
||||
var a = result
|
||||
if a.kind == nkHiddenDeref: a = a[0]
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
markUsed(c, a.info, a[0].sym)
|
||||
|
||||
|
||||
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
@@ -220,66 +225,7 @@ proc shouldCheckCaseCovered(caseTyp: PType): bool =
|
||||
else:
|
||||
discard
|
||||
|
||||
proc endsInNoReturn(n: PNode): bool =
|
||||
## check if expr ends the block like raising or call of noreturn procs do
|
||||
result = false # assume it does return
|
||||
|
||||
template checkBranch(branch) =
|
||||
if not endsInNoReturn(branch):
|
||||
# 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
|
||||
|
||||
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 endsInNoReturn(n: PNode): bool
|
||||
|
||||
proc commonType*(c: PContext; x: PType, y: PNode): PType =
|
||||
# ignore exception raising branches in case/if expressions
|
||||
@@ -311,6 +257,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
result.owner = getCurrOwner(c)
|
||||
else:
|
||||
result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
|
||||
if find(result.name.s, '`') >= 0:
|
||||
result.flags.incl sfWasGenSym
|
||||
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
|
||||
# incl(result.flags, sfGlobal)
|
||||
when defined(nimsuggest):
|
||||
@@ -320,7 +268,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||
allowed: TSymFlags): PSym
|
||||
# identifier with visibility
|
||||
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
||||
allowed: TSymFlags): PSym
|
||||
allowed: TSymFlags, fromTopLevel = false): PSym
|
||||
|
||||
proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind;
|
||||
flags: TTypeAllowedFlags = {}) =
|
||||
@@ -553,7 +501,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
if retType.kind == tyVoid:
|
||||
result = semStmt(c, result, flags)
|
||||
else:
|
||||
result = semExpr(c, result, flags, expectedType)
|
||||
result = semExpr(c, result, flags)
|
||||
result = fitNode(c, retType, result, result.info)
|
||||
#globalError(s.info, errInvalidParamKindX, typeToString(s.typ[0]))
|
||||
dec(c.config.evalTemplateCounter)
|
||||
|
||||
@@ -68,7 +68,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# `matches` may find new symbols, so keep track of count
|
||||
var symCount = c.currentScope.symbols.counter
|
||||
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
# https://github.com/nim-lang/Nim/issues/21272
|
||||
# prevent mutation during iteration by storing them in a seq
|
||||
# luckily `initCandidateSymbols` does just that
|
||||
@@ -399,7 +399,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||
|
||||
proc bracketNotFoundError(c: PContext; n: PNode) =
|
||||
var errors: CandidateErrors = @[]
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
let headSymbol = n[0]
|
||||
var symx = initOverloadIter(o, c, headSymbol)
|
||||
while symx != nil:
|
||||
@@ -421,7 +421,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
|
||||
# also avoid slowdowns in evaluating `compiles(expr)`.
|
||||
discard
|
||||
else:
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var sym = initOverloadIter(o, c, f)
|
||||
while sym != nil:
|
||||
result &= "\n found $1" % [getSymRepr(c.config, sym)]
|
||||
@@ -465,7 +465,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
filter, result, alt, errors, efExplain in flags,
|
||||
errorsEnabled, flags)
|
||||
|
||||
var dummyErrors: CandidateErrors
|
||||
var dummyErrors: CandidateErrors = @[]
|
||||
template pickSpecialOp(headSymbol) =
|
||||
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
|
||||
filter, result, alt, dummyErrors, efExplain in flags,
|
||||
@@ -556,6 +556,15 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
|
||||
for i in 1..<n.len:
|
||||
instGenericConvertersArg(c, n[i], x)
|
||||
|
||||
proc markConvertersUsed*(c: PContext, n: PNode) =
|
||||
assert n.kind in nkCallKinds
|
||||
for i in 1..<n.len:
|
||||
var a = n[i]
|
||||
if a == nil: continue
|
||||
if a.kind == nkHiddenDeref: a = a[0]
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
markUsed(c, a.info, a[0].sym)
|
||||
|
||||
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
|
||||
var m = newCandidate(c, f)
|
||||
result = paramTypesMatch(m, f, a, arg, nil)
|
||||
@@ -651,8 +660,10 @@ proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
markConvertersUsed(c, result)
|
||||
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
|
||||
result.typ = finalCallee.typ[0]
|
||||
if finalCallee.magic notin {mArrGet, mArrPut}:
|
||||
result.typ = finalCallee.typ[0]
|
||||
updateDefaultParams(result)
|
||||
|
||||
proc canDeref(n: PNode): bool {.inline.} =
|
||||
|
||||
@@ -76,6 +76,7 @@ type
|
||||
efNoDiagnostics,
|
||||
efTypeAllowed # typeAllowed will be called after
|
||||
efWantNoDefaults
|
||||
efAllowSymChoice # symchoice node should not be resolved
|
||||
|
||||
TExprFlags* = set[TExprFlag]
|
||||
|
||||
@@ -169,6 +170,7 @@ type
|
||||
inUncheckedAssignSection*: int
|
||||
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
|
||||
skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
|
||||
inTypeofContext*: int
|
||||
TBorrowState* = enum
|
||||
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ template rejectEmptyNode(n: PNode) =
|
||||
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
rejectEmptyNode(n)
|
||||
# same as 'semExprWithType' but doesn't check for proc vars
|
||||
result = semExpr(c, n, flags + {efOperand})
|
||||
result = semExpr(c, n, flags + {efOperand, efAllowSymChoice})
|
||||
if result.typ != nil:
|
||||
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
|
||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||
@@ -79,42 +79,10 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType
|
||||
# do not produce another redundant error message:
|
||||
result = errorNode(c, n)
|
||||
|
||||
proc ambiguousSymChoice(c: PContext, orig, n: PNode): PNode =
|
||||
let first = n[0].sym
|
||||
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:
|
||||
var err = "ambiguous enum field '" & first.name.s &
|
||||
"' assumed to be of type " & typeToString(first.typ) &
|
||||
" -- use one of the following:\n"
|
||||
for child in n:
|
||||
let candidate = child.sym
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s & "\n"
|
||||
message(c.config, orig.info, hintAmbiguousEnum, err)
|
||||
result = n[0]
|
||||
else:
|
||||
var err = "ambiguous identifier '" & first.name.s &
|
||||
"' -- use one of the following:\n"
|
||||
for child in n:
|
||||
let candidate = child.sym
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
err.add ": " & typeToString(candidate.typ) & "\n"
|
||||
localError(c.config, orig.info, err)
|
||||
n.typ = errorType(c)
|
||||
result = n
|
||||
|
||||
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
result = semExprCheck(c, n, flags-{efTypeAllowed}, expectedType)
|
||||
if result.typ == nil and efInTypeof in flags:
|
||||
result.typ = c.voidType
|
||||
elif (result.typ == nil or result.typ.kind == tyNone) and
|
||||
efTypeAllowed in flags and
|
||||
result.kind == nkClosedSymChoice and result.len > 0:
|
||||
result = ambiguousSymChoice(c, n, result)
|
||||
elif result.typ == nil or result.typ == c.enforceVoidContext:
|
||||
localError(c.config, n.info, errExprXHasNoType %
|
||||
renderTree(result, {renderNoComments}))
|
||||
@@ -147,6 +115,112 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
|
||||
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode
|
||||
|
||||
proc isSymChoice(n: PNode): bool {.inline.} =
|
||||
result = n.kind in nkSymChoices
|
||||
|
||||
proc resolveSymChoice(c: PContext, n: var PNode, flags: TExprFlags = {}, expectedType: PType = nil) =
|
||||
## Attempts to resolve a symchoice `n`, `n` remains a symchoice if
|
||||
## it cannot be resolved (this is the case even when `n.len == 1`).
|
||||
if expectedType != nil:
|
||||
# resolve from type inference, see paramTypesMatch
|
||||
n = fitNode(c, expectedType, n, n.info)
|
||||
if isSymChoice(n) and efAllowSymChoice notin flags:
|
||||
# some contexts might want sym choices preserved for later disambiguation
|
||||
# in general though they are ambiguous
|
||||
let first = n[0].sym
|
||||
var foundSym: PSym = nil
|
||||
if first.kind == skEnumField and
|
||||
not isAmbiguous(c, first.name, {skEnumField}, foundSym) and
|
||||
foundSym == first:
|
||||
# choose the first resolved enum field, i.e. the latest in scope
|
||||
# to mirror behavior before overloadable enums
|
||||
n = n[0]
|
||||
|
||||
proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType,
|
||||
warnDisabled = false): PNode =
|
||||
## sem the child of an `nkOpenSym` node, that is, captured symbols that can be
|
||||
## replaced by newly injected symbols in generics. `s` must be the captured
|
||||
## symbol if the original node is an `nkSym` node; and `nil` if it is an
|
||||
## `nkOpenSymChoice`, in which case only non-overloadable injected symbols
|
||||
## will be considered.
|
||||
let isSym = n.kind == nkSym
|
||||
let ident = n.getPIdent
|
||||
assert ident != nil
|
||||
let id = newIdentNode(ident, n.info)
|
||||
c.isAmbiguous = false
|
||||
let s2 = qualifiedLookUp(c, id, {})
|
||||
# for `nkSym`, the first found symbol being different and unambiguous is
|
||||
# enough to replace the original
|
||||
# for `nkOpenSymChoice`, the first found symbol must be non-overloadable,
|
||||
# since otherwise we have to use regular `nkOpenSymChoice` functionality
|
||||
# but of the overloadable sym kinds, semExpr does not handle skModule, skMacro, skTemplate
|
||||
# as overloaded in the case where `nkIdent` finds them first
|
||||
if s2 != nil and not c.isAmbiguous and
|
||||
((isSym and s2 != n.sym) or
|
||||
(not isSym and s2.kind notin OverloadableSyms-{skModule, skMacro, skTemplate})):
|
||||
# only consider symbols defined under current proc:
|
||||
var o = s2.owner
|
||||
while o != nil:
|
||||
if o == c.p.owner:
|
||||
if not warnDisabled:
|
||||
result = semExpr(c, id, flags, expectedType)
|
||||
return
|
||||
else:
|
||||
var msg =
|
||||
"a new symbol '" & ident.s & "' has been injected during " &
|
||||
# msgContext should show what is being instantiated:
|
||||
"template or generic instantiation, however "
|
||||
if isSym:
|
||||
msg.add(
|
||||
getSymRepr(c.config, n.sym) & " captured at " &
|
||||
"the proc declaration will be used instead; " &
|
||||
"either enable --experimental:openSym to use the injected symbol, " &
|
||||
"or `bind` this captured symbol explicitly")
|
||||
else:
|
||||
msg.add(
|
||||
"overloads of " & ident.s & " will be used instead; " &
|
||||
"either enable --experimental:openSym to use the injected symbol, " &
|
||||
"or `bind` this symbol explicitly")
|
||||
message(c.config, n.info, warnIgnoredSymbolInjection, msg)
|
||||
break
|
||||
o = o.owner
|
||||
# nothing found
|
||||
n.flags.excl nfDisabledOpenSym
|
||||
if not warnDisabled and isSym:
|
||||
result = semExpr(c, n, flags, expectedType)
|
||||
else:
|
||||
result = nil
|
||||
if not isSym:
|
||||
# set symchoice node type back to None
|
||||
n.typ = newTypeS(tyNone, c)
|
||||
|
||||
proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
if n.kind == nkOpenSymChoice:
|
||||
result = semOpenSym(c, n, flags, expectedType,
|
||||
warnDisabled = nfDisabledOpenSym in n.flags and
|
||||
genericsOpenSym notin c.features)
|
||||
if result != nil:
|
||||
return
|
||||
result = n
|
||||
resolveSymChoice(c, result, flags, expectedType)
|
||||
if isSymChoice(result) and result.len == 1:
|
||||
# resolveSymChoice can leave 1 sym
|
||||
result = result[0]
|
||||
if isSymChoice(result) and efAllowSymChoice notin flags:
|
||||
var err = "ambiguous identifier: '" & result[0].sym.name.s &
|
||||
"' -- use one of the following:\n"
|
||||
for child in n:
|
||||
let candidate = child.sym
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
err.add ": " & typeToString(candidate.typ) & "\n"
|
||||
localError(c.config, n.info, err)
|
||||
n.typ = errorType(c)
|
||||
result = n
|
||||
if result.kind == nkSym:
|
||||
result = semSym(c, result, result.sym, flags)
|
||||
|
||||
proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} =
|
||||
result = copyTree(s.astdef)
|
||||
if result.isNil:
|
||||
@@ -215,7 +289,8 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
|
||||
discard "convOk"
|
||||
elif targetTyp.isOrdinalType:
|
||||
if src.kind in nkCharLit..nkUInt64Lit and
|
||||
src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp):
|
||||
src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp) and
|
||||
targetTyp.kind notin {tyUInt..tyUInt64}:
|
||||
result = convNotInRange
|
||||
elif src.kind in nkFloatLit..nkFloat64Lit and
|
||||
(classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or
|
||||
@@ -286,9 +361,6 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
|
||||
if result and src.kind == tyNil:
|
||||
return dst.size <= conf.target.ptrSize
|
||||
|
||||
proc isSymChoice(n: PNode): bool {.inline.} =
|
||||
result = n.kind in nkSymChoices
|
||||
|
||||
proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
|
||||
# XXX: liftParamType started to perform addDecl
|
||||
# we could do that instead in semTypeNode by snooping for added
|
||||
@@ -350,10 +422,10 @@ proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType
|
||||
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], flags * {efDetermineType})
|
||||
if op.kind == nkClosedSymChoice and op.len > 0 and
|
||||
op[0].sym.kind == skEnumField: # resolves overloadedable enums
|
||||
op = ambiguousSymChoice(c, n, op)
|
||||
var op = semExprWithType(c, n[1], flags * {efDetermineType} + {efAllowSymChoice})
|
||||
if isSymChoice(op) and op[0].sym.kind notin routineKinds:
|
||||
# T(foo) disambiguation syntax only allowed for routines
|
||||
op = semSymChoice(c, op)
|
||||
if targetType.kind != tyGenericParam and targetType.isMetaType:
|
||||
let final = inferWithMetatype(c, targetType, op, true)
|
||||
result.add final
|
||||
@@ -548,15 +620,14 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||
lhsType = n[1].typ
|
||||
else:
|
||||
if lhsType.base.kind == tyNone or
|
||||
(c.inGenericContext > 0 and lhsType.base.containsGenericType):
|
||||
if c.inGenericContext > 0 and lhsType.base.containsGenericType:
|
||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||
return
|
||||
|
||||
result = isOpImpl(c, n, flags)
|
||||
|
||||
proc semOpAux(c: PContext, n: PNode) =
|
||||
const flags = {efDetermineType}
|
||||
const flags = {efDetermineType, efAllowSymChoice}
|
||||
for i in 1..<n.len:
|
||||
var a = n[i]
|
||||
if a.kind == nkExprEqExpr and a.len == 2:
|
||||
@@ -898,9 +969,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
n.typ.flags.incl tfUnresolved
|
||||
|
||||
# optimization pass: not necessary for correctness of the semantic pass
|
||||
if callee.kind == skConst or
|
||||
if (callee.kind == skConst or
|
||||
{sfNoSideEffect, sfCompileTime} * callee.flags != {} and
|
||||
{sfForward, sfImportc} * callee.flags == {} and n.typ != nil:
|
||||
{sfForward, sfImportc} * callee.flags == {}) and n.typ != nil:
|
||||
|
||||
if callee.kind != skConst and
|
||||
sfCompileTime notin callee.flags and
|
||||
@@ -908,7 +979,8 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
|
||||
if callee.magic notin ctfeWhitelist: return
|
||||
|
||||
if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine:
|
||||
if callee.kind notin {skProc, skFunc, skConverter, skConst} or
|
||||
callee.isGenericRoutineStrict:
|
||||
return
|
||||
|
||||
if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return
|
||||
@@ -1054,7 +1126,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
|
||||
not (result.typ.kind == tySequence and result.typ[0].kind == tyEmpty):
|
||||
liftTypeBoundOps(c, result.typ, n.info)
|
||||
#result = patchResolvedTypeBoundOp(c, result)
|
||||
if c.matchedConcept == nil:
|
||||
if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone):
|
||||
# don't fold calls in concepts and typeof
|
||||
result = evalAtCompileTime(c, result)
|
||||
|
||||
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
@@ -1074,7 +1147,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
else:
|
||||
n[0] = n0
|
||||
else:
|
||||
n[0] = semExpr(c, n[0], {efInCall})
|
||||
n[0] = semExpr(c, n[0], {efInCall, efAllowSymChoice})
|
||||
let t = n[0].typ
|
||||
if t != nil and t.kind in {tyVar, tyLent}:
|
||||
n[0] = newDeref(n[0])
|
||||
@@ -1132,6 +1205,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
else:
|
||||
result = m.call
|
||||
instGenericConvertersSons(c, result, m)
|
||||
markConvertersUsed(c, result)
|
||||
|
||||
else:
|
||||
result = overloadedCallOpr(c, n) # this uses efNoUndeclared
|
||||
@@ -1271,7 +1345,9 @@ proc readTypeParameter(c: PContext, typ: PType,
|
||||
# This seems semantically correct and then we'll be able
|
||||
# to return the section symbol directly here
|
||||
let foundType = makeTypeDesc(c, def[2].typ)
|
||||
return newSymNode(copySym(def[0].sym, c.idgen).linkTo(foundType), info)
|
||||
let s = copySym(def[0].sym, c.idgen)
|
||||
s.typ = foundType
|
||||
return newSymNode(s, info)
|
||||
|
||||
of nkConstSection:
|
||||
for def in statement:
|
||||
@@ -1296,7 +1372,9 @@ proc readTypeParameter(c: PContext, typ: PType,
|
||||
return c.graph.emptyNode
|
||||
else:
|
||||
let foundTyp = makeTypeDesc(c, rawTyp)
|
||||
return newSymNode(copySym(tParam.sym, c.idgen).linkTo(foundTyp), info)
|
||||
let s = copySym(tParam.sym, c.idgen)
|
||||
s.typ = foundTyp
|
||||
return newSymNode(s, info)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1386,6 +1464,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
|
||||
markUsed(c, n.info, s)
|
||||
onUse(n.info, s)
|
||||
if s.typ == nil:
|
||||
return localErrorNode(c, n, "symbol '$1' has no type" % [s.name.s])
|
||||
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
|
||||
return s.typ.n
|
||||
result = newSymNode(s, n.info)
|
||||
@@ -1479,7 +1559,8 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
|
||||
onUse(n[1].info, s)
|
||||
return
|
||||
|
||||
n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable})
|
||||
# extra flags since LHS may become a call operand:
|
||||
n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable, efAllowSymChoice})
|
||||
#restoreOldStyleType(n[0])
|
||||
var i = considerQuotedIdent(c, n[1], n)
|
||||
var ty = n[0].typ
|
||||
@@ -1633,7 +1714,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
return
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
# signal that generic parameters may be applied after
|
||||
n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric})
|
||||
n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric, efAllowSymChoice})
|
||||
var arr = skipTypes(n[0].typ, {tyGenericInst, tyUserTypeClassInst, tyOwned,
|
||||
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
|
||||
if arr.kind == tyStatic:
|
||||
@@ -1692,7 +1773,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result = nil
|
||||
else:
|
||||
let s = if n[0].kind == nkSym: n[0].sym
|
||||
elif n[0].kind in nkSymChoices: n[0][0].sym
|
||||
elif n[0].kind in nkSymChoices + {nkOpenSym}: n[0][0].sym
|
||||
else: nil
|
||||
if s != nil:
|
||||
case s.kind
|
||||
@@ -1732,7 +1813,7 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
||||
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
|
||||
# nodes?
|
||||
let aOrig = nOrig[0]
|
||||
result = newTreeI(nkCall, n.info, setterId, a[0], semExprWithType(c, n[1]))
|
||||
result = newTreeI(nkCall, n.info, setterId, a[0], n[1])
|
||||
result.flags.incl nfDotSetter
|
||||
let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1])
|
||||
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
|
||||
@@ -1773,6 +1854,8 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
|
||||
else:
|
||||
localError(c.config, n.info, errExprHasNoAddress)
|
||||
result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ))
|
||||
if n.typ.kind in {tyVar, tyLent}:
|
||||
n.typ = n.typ.elementType
|
||||
result.add(n)
|
||||
|
||||
proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
|
||||
@@ -2064,12 +2147,10 @@ proc semYield(c: PContext, n: PNode): PNode =
|
||||
if c.p.owner == nil or c.p.owner.kind != skIterator:
|
||||
localError(c.config, n.info, errYieldNotAllowedHere)
|
||||
elif n[0].kind != nkEmpty:
|
||||
n[0] = semExprWithType(c, n[0]) # check for type compatibility:
|
||||
var iterType = c.p.owner.typ
|
||||
let restype = iterType[0]
|
||||
n[0] = semExprWithType(c, n[0], {}, restype) # check for type compatibility:
|
||||
if restype != nil:
|
||||
if restype.kind != tyUntyped:
|
||||
n[0] = fitNode(c, restype, n[0], n.info)
|
||||
if n[0].typ == nil: internalError(c.config, n.info, "semYield")
|
||||
|
||||
if resultTypeIsInferrable(restype):
|
||||
@@ -2077,6 +2158,8 @@ proc semYield(c: PContext, n: PNode): PNode =
|
||||
iterType[0] = inferred
|
||||
if c.p.resultSym != nil:
|
||||
c.p.resultSym.typ = inferred
|
||||
else:
|
||||
n[0] = fitNode(c, restype, n[0], n.info)
|
||||
|
||||
semYieldVarResult(c, n, restype)
|
||||
else:
|
||||
@@ -2128,6 +2211,8 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
|
||||
result = n.sym
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
result = n[0].sym
|
||||
of nkOpenSym:
|
||||
result = lookUpForDeclared(c, n[0], onlyCurrentScope)
|
||||
else:
|
||||
localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
|
||||
result = nil
|
||||
@@ -2188,7 +2273,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
|
||||
let headSymbol = macroCall[0]
|
||||
var cands = 0
|
||||
var cand: PSym = nil
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var symx = initOverloadIter(o, c, headSymbol)
|
||||
while symx != nil:
|
||||
if symx.kind in {skTemplate, skMacro} and symx.typ.len == macroCall.len:
|
||||
@@ -2439,9 +2524,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P
|
||||
of mAddr:
|
||||
markUsed(c, n.info, s)
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result[0] = newSymNode(s, n[0].info)
|
||||
result[1] = semAddrArg(c, n[1])
|
||||
result.typ = makePtrType(c, result[1].typ)
|
||||
result = semAddr(c, n[1])
|
||||
of mTypeOf:
|
||||
markUsed(c, n.info, s)
|
||||
result = semTypeOf(c, n)
|
||||
@@ -2583,7 +2666,9 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
var typ = commonTypeBegin
|
||||
if n.len in 1..2 and n[0].kind == nkElifBranch and (
|
||||
n.len == 1 or n[1].kind == nkElse):
|
||||
let exprNode = n[0][0]
|
||||
var exprNode = n[0][0]
|
||||
if exprNode.kind == nkOpenSym:
|
||||
exprNode = exprNode[0]
|
||||
if exprNode.kind == nkIdent:
|
||||
whenNimvm = lookUp(c, exprNode).magic == mNimvm
|
||||
elif exprNode.kind == nkSym:
|
||||
@@ -2615,6 +2700,8 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
if semCheck:
|
||||
it[0] = semExpr(c, it[0], flags)
|
||||
typ = commonType(c, typ, it[0].typ)
|
||||
if typ != nil and typ.kind != tyUntyped:
|
||||
it[0] = fitNode(c, typ, it[0], it[0].info)
|
||||
if result == nil:
|
||||
result = it[0]
|
||||
else: illFormedAst(n, c.config)
|
||||
@@ -2759,6 +2846,12 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
|
||||
# can check if field name matches expected type here
|
||||
let expectedElemType = if expected != nil: expected[i] else: nil
|
||||
n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType)
|
||||
if expectedElemType != nil and
|
||||
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
|
||||
# hasEmpty/nil check is to not break existing code like
|
||||
# `const foo = [(1, {}), (2, {false})]`,
|
||||
# `const foo = if true: (0, nil) else: (1, new(int))`
|
||||
n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info)
|
||||
|
||||
if n[i][1].typ.kind == tyTypeDesc:
|
||||
localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.")
|
||||
@@ -2785,6 +2878,12 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT
|
||||
for i in 0..<n.len:
|
||||
let expectedElemType = if expected != nil: expected[i] else: nil
|
||||
n[i] = semExprWithType(c, n[i], {}, expectedElemType)
|
||||
if expectedElemType != nil and
|
||||
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
|
||||
# hasEmpty/nil check is to not break existing code like
|
||||
# `const foo = [(1, {}), (2, {false})]`,
|
||||
# `const foo = if true: (0, nil) else: (1, new(int))`
|
||||
n[i] = fitNode(c, expectedElemType, n[i], n[i].info)
|
||||
addSonSkipIntLit(typ, n[i].typ, c.idgen)
|
||||
result.typ = typ
|
||||
|
||||
@@ -2840,7 +2939,7 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkExportStmt, n.info)
|
||||
for i in 0..<n.len:
|
||||
let a = n[i]
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var s = initOverloadIter(o, c, a)
|
||||
if s == nil:
|
||||
localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a))
|
||||
@@ -2982,7 +3081,7 @@ proc getNilType(c: PContext): PType =
|
||||
c.nilTypeCache = result
|
||||
|
||||
proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode =
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var i = 0
|
||||
var a = initOverloadIter(o, c, n)
|
||||
while a != nil:
|
||||
@@ -3015,6 +3114,48 @@ proc semPragmaStmt(c: PContext; n: PNode) =
|
||||
else:
|
||||
pragma(c, c.p.owner, n, stmtPragmas, true)
|
||||
|
||||
proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode,
|
||||
flags: TExprFlags, expectedType: PType): PSym =
|
||||
# result is nil on error or if a node that can't produce a sym is resolved
|
||||
let ident = considerQuotedIdent(c, n)
|
||||
var filter = {low(TSymKind)..high(TSymKind)}
|
||||
if efNoEvaluateGeneric in flags:
|
||||
# `a[...]` where `a` is a module or package is not possible
|
||||
filter.excl {skModule, skPackage}
|
||||
let candidates = lookUpCandidates(c, ident, filter)
|
||||
if candidates.len == 0:
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n.info)
|
||||
elif candidates.len == 1 or {efNoEvaluateGeneric, efInCall} * flags != {}:
|
||||
# unambiguous, or we don't care about ambiguity
|
||||
result = candidates[0]
|
||||
else:
|
||||
# ambiguous symbols have 1 last chance as a symchoice,
|
||||
# but type symbols cannot participate in symchoices
|
||||
var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c))
|
||||
for c in candidates:
|
||||
if c.kind notin {skType, skModule, skPackage}:
|
||||
choice.add newSymNode(c, n.info)
|
||||
if choice.len == 0:
|
||||
# we know candidates.len > 1, we just couldn't put any in a symchoice
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
return nil
|
||||
resolveSymChoice(c, choice, flags, expectedType)
|
||||
# choice.len == 1 can be true here but as long as it's a symchoice
|
||||
# it's still not resolved
|
||||
if isSymChoice(choice):
|
||||
result = nil
|
||||
if efAllowSymChoice in flags:
|
||||
resultNode = choice
|
||||
else:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
else:
|
||||
if choice.kind == nkSym:
|
||||
result = choice.sym
|
||||
else:
|
||||
# resolution could have generated nkHiddenStdConv etc
|
||||
resultNode = semExpr(c, choice, flags, expectedType)
|
||||
result = nil
|
||||
|
||||
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
when defined(nimCompilerStacktraceHints):
|
||||
setFrameMsg c.config$n.info & " " & $n.kind
|
||||
@@ -3052,25 +3193,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
if nfSem in n.flags: return
|
||||
case n.kind
|
||||
of nkIdent, nkAccQuoted:
|
||||
var s: PSym
|
||||
if expectedType != nil and (
|
||||
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
|
||||
expected.kind == tyEnum):
|
||||
let nameId = considerQuotedIdent(c, n).id
|
||||
for f in expected.n:
|
||||
if f.kind == nkSym and f.sym.name.id == nameId:
|
||||
s = f.sym
|
||||
break
|
||||
let s = resolveIdentToSym(c, n, result, flags, expectedType)
|
||||
if s == nil:
|
||||
let checks = if efNoEvaluateGeneric in flags:
|
||||
{checkUndeclared, checkPureEnumFields}
|
||||
elif efInCall in flags:
|
||||
{checkUndeclared, checkModule, checkPureEnumFields}
|
||||
else:
|
||||
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
|
||||
s = qualifiedLookUp(c, n, checks)
|
||||
if s == nil:
|
||||
return
|
||||
# resolveIdentToSym either errored or gave a result node
|
||||
return
|
||||
if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
|
||||
case s.kind
|
||||
of skProc, skFunc, skMethod, skConverter, skIterator:
|
||||
@@ -3087,43 +3213,32 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
result = enumFieldSymChoice(c, n, s, flags)
|
||||
else:
|
||||
result = semSym(c, n, s, flags)
|
||||
if expectedType != nil and isSymChoice(result):
|
||||
result = fitNode(c, expectedType, result, n.info)
|
||||
if result.kind == nkSym:
|
||||
result = semSym(c, result, result.sym, flags)
|
||||
if isSymChoice(result):
|
||||
result = semSymChoice(c, result, flags, expectedType)
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
result = semSymChoice(c, n, flags, expectedType)
|
||||
of nkSym:
|
||||
let s = n.sym
|
||||
if 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
|
||||
if nfDisabledOpenSym in n.flags:
|
||||
let override = genericsOpenSym in c.features
|
||||
let res = semOpenSym(c, n, flags, expectedType,
|
||||
warnDisabled = not override)
|
||||
if res != nil:
|
||||
assert override
|
||||
return res
|
||||
# because of the changed symbol binding, this does not mean that we
|
||||
# don't have to check the symbol for semantics here again!
|
||||
result = semSym(c, n, s, flags)
|
||||
of nkOpenSym:
|
||||
assert n.len == 1
|
||||
let inner = n[0]
|
||||
result = semOpenSym(c, inner, flags, expectedType)
|
||||
of nkEmpty, nkNone, nkCommentStmt, nkType:
|
||||
discard
|
||||
of nkNilLit:
|
||||
if result.typ == nil:
|
||||
result.typ = getNilType(c)
|
||||
if expectedType != nil:
|
||||
if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}:
|
||||
var m = newCandidate(c, result.typ)
|
||||
if typeRel(m, expectedType, result.typ) >= isSubtype:
|
||||
result.typ = expectedType
|
||||
@@ -3292,8 +3407,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
of nkAddr:
|
||||
result = n
|
||||
checkSonsLen(n, 1, c.config)
|
||||
result[0] = semAddrArg(c, n[0])
|
||||
result.typ = makePtrType(c, result[0].typ)
|
||||
result = semAddr(c, n[0])
|
||||
of nkHiddenAddr, nkHiddenDeref:
|
||||
checkSonsLen(n, 1, c.config)
|
||||
n[0] = semExpr(c, n[0], flags, expectedType)
|
||||
@@ -3313,10 +3427,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
considerGenSyms(c, n)
|
||||
of nkTableConstr:
|
||||
result = semTableConstr(c, n, expectedType)
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
# handling of sym choices is context dependent
|
||||
# the node is left intact for now
|
||||
discard
|
||||
of nkStaticExpr: result = semStaticExpr(c, n[0], expectedType)
|
||||
of nkAsgn, nkFastAsgn: result = semAsgn(c, n)
|
||||
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags, expectedType)
|
||||
|
||||
@@ -18,6 +18,15 @@ type
|
||||
replaceByFieldName: bool
|
||||
c: PContext
|
||||
|
||||
proc wrapNewScope(c: PContext, n: PNode): PNode {.inline.} =
|
||||
# use `if true` to not interfere with `break`
|
||||
# just opening scope via `openScope(c)` isn't enough,
|
||||
# a scope has to be opened in the codegen as well for reused
|
||||
# template instantiations
|
||||
let trueLit = newIntLit(c.graph, n.info, 1)
|
||||
trueLit.typ = getSysType(c.graph, n.info, tyBool)
|
||||
result = newTreeI(nkIfStmt, n.info, newTreeI(nkElifBranch, n.info, trueLit, n))
|
||||
|
||||
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
if c.field != nil and isEmptyType(c.field.typ):
|
||||
result = newNode(nkEmpty)
|
||||
@@ -70,7 +79,9 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
|
||||
fc.replaceByFieldName = c.m == mFieldPairs
|
||||
openScope(c.c)
|
||||
inc c.c.inUnrolledContext
|
||||
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
var body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
# new scope for each field that codegen should know about:
|
||||
body = wrapNewScope(c.c, body)
|
||||
father.add(semStmt(c.c, body, {}))
|
||||
dec c.c.inUnrolledContext
|
||||
closeScope(c.c)
|
||||
@@ -145,6 +156,8 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
fc.c = c
|
||||
fc.replaceByFieldName = m == mFieldPairs
|
||||
var body = instFieldLoopBody(fc, loopBody, n)
|
||||
# new scope for each field that codegen should know about:
|
||||
body = wrapNewScope(c, body)
|
||||
inc c.inUnrolledContext
|
||||
stmts.add(semStmt(c, body, {}))
|
||||
dec c.inUnrolledContext
|
||||
|
||||
@@ -409,14 +409,17 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
|
||||
result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g)
|
||||
of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
|
||||
var val = a.getOrdValue
|
||||
if check: rangeCheck(n, val, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
if dstTyp.kind in {tyUInt..tyUInt64}:
|
||||
result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g)
|
||||
result.transitionIntKind(nkUIntLit)
|
||||
else:
|
||||
if check: rangeCheck(n, val, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
else:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
if check and result.kind in {nkCharLit..nkUInt64Lit}:
|
||||
if check and result.kind in {nkCharLit..nkUInt64Lit} and
|
||||
dstTyp.kind notin {tyUInt..tyUInt64}:
|
||||
rangeCheck(n, getInt(result), g)
|
||||
of tyFloat..tyFloat64:
|
||||
case srcTyp.kind
|
||||
@@ -685,10 +688,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
except DivByZeroDefect:
|
||||
localError(g.config, n.info, "division by zero")
|
||||
of nkAddr:
|
||||
var a = getConstExpr(m, n[0], idgen, g)
|
||||
if a != nil:
|
||||
result = n
|
||||
n[0] = a
|
||||
result = nil # don't fold paths containing nkAddr
|
||||
of nkBracket, nkCurly:
|
||||
result = copyNode(n)
|
||||
for son in n.items:
|
||||
@@ -734,6 +734,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
if leValueConv(n[1], a) and leValueConv(a, n[2]):
|
||||
result = a # a <= x and x <= b
|
||||
result.typ = n.typ
|
||||
elif n.typ.kind in {tyUInt..tyUInt64}:
|
||||
discard "don't check uints"
|
||||
else:
|
||||
localError(g.config, n.info,
|
||||
"conversion from $1 to $2 is invalid" %
|
||||
@@ -755,7 +757,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
of nkCast:
|
||||
var a = getConstExpr(m, n[1], idgen, g)
|
||||
if a == nil: return
|
||||
if n.typ != nil and n.typ.kind in NilableTypes:
|
||||
if n.typ != nil and n.typ.kind in NilableTypes and
|
||||
not (n.typ.kind == tyProc and a.typ.kind == tyProc):
|
||||
# we allow compile-time 'cast' for pointer types:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
|
||||
@@ -71,9 +71,15 @@ 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
|
||||
if canOpenSym(s):
|
||||
if openSym in c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
@@ -102,8 +108,11 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
of skParam:
|
||||
result = n
|
||||
@@ -113,16 +122,22 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
else:
|
||||
result = n
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
|
||||
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
@@ -236,7 +251,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
#var s = qualifiedLookUp(c, n, luf)
|
||||
#if s != nil: result = semGenericStmtSymbol(c, n, s)
|
||||
# XXX for example: ``result.add`` -- ``add`` needs to be looked up here...
|
||||
var dummy: bool
|
||||
var dummy: bool = false
|
||||
result = fuzzyLookup(c, n, flags, ctx, dummy)
|
||||
of nkSym:
|
||||
let a = n.sym
|
||||
@@ -562,7 +577,8 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
else:
|
||||
body = getBody(c.graph, s)
|
||||
else: body = n[bodyPos]
|
||||
n[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
|
||||
let bodyFlags = if n.kind == nkTemplateDef: flags + {withinMixin} else: flags
|
||||
n[bodyPos] = semGenericStmtScope(c, body, bodyFlags, ctx)
|
||||
closeScope(c)
|
||||
of nkPragma, nkPragmaExpr: discard
|
||||
of nkExprColonExpr, nkExprEqExpr:
|
||||
|
||||
@@ -60,8 +60,11 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
|
||||
elif t.kind in {tyGenericParam, tyConcept}:
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif isUnresolvedStatic(t) and c.inGenericContext == 0 and
|
||||
c.matchedConcept == nil:
|
||||
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)
|
||||
@@ -282,7 +285,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
for i in 1..<def.len:
|
||||
def[i] = replaceTypeVarsN(cl, def[i], 1)
|
||||
|
||||
def = semExprWithType(c, def)
|
||||
# allow symchoice since node will be fit later
|
||||
# although expectedType should cover it
|
||||
def = semExprWithType(c, def, {efAllowSymChoice}, typeToFit)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
|
||||
@@ -336,7 +341,12 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
# generates an instantiated proc
|
||||
if c.instCounter > 50:
|
||||
globalError(c.config, info, "generic instantiation too nested")
|
||||
inc(c.instCounter)
|
||||
inc c.instCounter
|
||||
let currentTypeofContext = c.inTypeofContext
|
||||
c.inTypeofContext = 0
|
||||
defer:
|
||||
dec c.instCounter
|
||||
c.inTypeofContext = currentTypeofContext
|
||||
# careful! we copy the whole AST including the possibly nil body!
|
||||
var n = copyTree(fn.ast)
|
||||
# NOTE: for access of private fields within generics from a different module
|
||||
@@ -382,6 +392,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
for j in 1..<result.typ.len:
|
||||
entry.concreteTypes[i] = result.typ[j]
|
||||
inc i
|
||||
#echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len
|
||||
if tfTriggersCompileTime in result.typ.flags:
|
||||
incl(result.flags, sfCompileTime)
|
||||
n[genericParamsPos] = c.graph.emptyNode
|
||||
@@ -404,7 +415,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
if result.magic notin {mSlice, mTypeOf}:
|
||||
# 'toOpenArray' is special and it is allowed to return 'openArray':
|
||||
paramsTypeCheck(c, result.typ)
|
||||
#echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len
|
||||
else:
|
||||
#echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len
|
||||
result = oldPrc
|
||||
popProcCon(c)
|
||||
popInfoContext(c.config)
|
||||
@@ -413,7 +426,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
popOwner(c)
|
||||
c.currentScope = oldScope
|
||||
discard c.friendModules.pop()
|
||||
dec(c.instCounter)
|
||||
c.matchedConcept = oldMatchedConcept
|
||||
if result.kind == skMethod: finishMethod(c, result)
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
of nkObjConstr:
|
||||
let x = t.skipTypes(abstractPtrs)
|
||||
n.typ = t
|
||||
n[0].typ = t
|
||||
for i in 1..<n.len:
|
||||
var j = i-1
|
||||
let field = x.ithField(j)
|
||||
|
||||
@@ -30,13 +30,15 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
|
||||
if asgnExpr.sons.len > 1:
|
||||
result = newTree(nkAsgn, result[1], asgnExpr)
|
||||
|
||||
proc semAddrArg(c: PContext; n: PNode): PNode =
|
||||
proc semAddr(c: PContext; n: PNode): PNode =
|
||||
result = newNodeI(nkAddr, n.info)
|
||||
let x = semExprWithType(c, n)
|
||||
if x.kind == nkSym:
|
||||
x.sym.flags.incl(sfAddrTaken)
|
||||
if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}:
|
||||
localError(c.config, n.info, errExprHasNoAddress)
|
||||
result = x
|
||||
result.add x
|
||||
result.typ = makePtrType(c, x.typ)
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
var m = BiggestInt 1 # typeOfIter
|
||||
@@ -47,6 +49,8 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
else:
|
||||
m = mode.intVal
|
||||
result = newNodeI(nkTypeOfExpr, n.info)
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
result.add typExpr
|
||||
result.typ = makeTypeDesc(c, typExpr.typ)
|
||||
@@ -549,9 +553,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
case n[0].sym.magic
|
||||
of mAddr:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = n
|
||||
result[1] = semAddrArg(c, n[1])
|
||||
result.typ = makePtrType(c, result[1].typ)
|
||||
result = semAddr(c, n[1])
|
||||
of mTypeOf:
|
||||
result = semTypeOf(c, n)
|
||||
of mSizeOf:
|
||||
|
||||
@@ -11,7 +11,7 @@ import
|
||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||
wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
|
||||
modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling, tables,
|
||||
semstrictfuncs
|
||||
semstrictfuncs, lowerings
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
@@ -89,10 +89,11 @@ const
|
||||
errXCannotBeAssignedTo = "'$1' cannot be assigned to"
|
||||
errLetNeedsInit = "'let' symbol requires an initialization"
|
||||
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
|
||||
if typ == nil or sfGeneratedOp in tracked.owner.flags:
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit = false) =
|
||||
if typ == nil or (sfGeneratedOp in tracked.owner.flags and not explicit):
|
||||
# don't create type bound ops for anything in a function with a `nodestroy` pragma
|
||||
# bug #21987
|
||||
# unless this is an explicit call, bug #24626
|
||||
return
|
||||
when false:
|
||||
let realType = typ.skipTypes(abstractInst)
|
||||
@@ -177,6 +178,7 @@ proc varDecl(a: PEffects; n: PNode) {.inline.} =
|
||||
proc skipHiddenDeref(n: PNode): PNode {.inline.} =
|
||||
result = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
|
||||
|
||||
proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
|
||||
let n = skipHiddenDeref(n)
|
||||
if n.kind != nkSym: return
|
||||
@@ -581,7 +583,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||
if paramType != nil and tfNotNil in paramType.flags and n.typ != nil:
|
||||
let ntyp = n.typ.skipTypesOrNil({tyVar, tyLent, tySink})
|
||||
if ntyp != nil and tfNotNil notin ntyp.flags:
|
||||
if isAddrNode(n):
|
||||
if n.kind in {nkAddr, nkHiddenAddr}:
|
||||
# addr(x[]) can't be proven, but addr(x) can:
|
||||
if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
|
||||
elif (n.kind == nkSym and n.sym.kind in routineKinds) or
|
||||
@@ -925,10 +927,17 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
# rebind type bounds operations after createTypeBoundOps call
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar})
|
||||
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
|
||||
createTypeBoundOps(tracked, t, n.info)
|
||||
createTypeBoundOps(tracked, t, n.info, explicit = true)
|
||||
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
|
||||
if op != nil:
|
||||
n[0].sym = op
|
||||
if TTypeAttachedOp(opKind) == attachedDestructor and
|
||||
op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
|
||||
if n[1].kind == nkSym and n[1].sym.kind == skParam and
|
||||
n[1].typ.kind == tyVar:
|
||||
n[1] = genDeref(n[1])
|
||||
else:
|
||||
n[1] = skipAddr(n[1])
|
||||
|
||||
if op != nil and op.kind == tyProc:
|
||||
for i in 1..<min(n.safeLen, op.len):
|
||||
@@ -1057,7 +1066,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
|
||||
tracked.owner.flags.incl sfInjectDestructors
|
||||
# bug #15038: ensure consistency
|
||||
if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ
|
||||
if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ = n.sym.typ
|
||||
of nkHiddenAddr, nkAddr:
|
||||
if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym):
|
||||
useVarNoInitCheck(tracked, n[0], n[0].sym)
|
||||
@@ -1144,7 +1153,12 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
let last = lastSon(child)
|
||||
track(tracked, last)
|
||||
of nkCaseStmt: trackCase(tracked, n)
|
||||
of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
|
||||
of nkWhen: # This should be a "when nimvm" node.
|
||||
let oldState = tracked.init.len
|
||||
track(tracked, n[0][1])
|
||||
tracked.init.setLen(oldState)
|
||||
track(tracked, n[1][0])
|
||||
of nkIfStmt, nkIfExpr: trackIf(tracked, n)
|
||||
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
|
||||
of nkWhileStmt:
|
||||
# 'while true' loop?
|
||||
|
||||
@@ -129,17 +129,141 @@ proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode
|
||||
closeScope(c)
|
||||
|
||||
const
|
||||
skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
|
||||
nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch,
|
||||
skipForDiscardable = {nkStmtList, nkStmtListExpr,
|
||||
nkOfBranch, nkElse, nkFinally, nkExceptBranch,
|
||||
nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr,
|
||||
nkHiddenStdConv, nkHiddenDeref}
|
||||
nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref}
|
||||
|
||||
proc implicitlyDiscardable(n: PNode): bool =
|
||||
var n = n
|
||||
while n.kind in skipForDiscardable: n = n.lastSon
|
||||
result = n.kind in nkLastBlockStmts or
|
||||
(isCallExpr(n) and n[0].kind == nkSym and
|
||||
sfDiscardable in n[0].sym.flags)
|
||||
# same traversal as endsInNoReturn
|
||||
template checkBranch(branch) =
|
||||
if not implicitlyDiscardable(branch):
|
||||
return false
|
||||
|
||||
var it = n
|
||||
# skip these beforehand, no special handling needed
|
||||
while it.kind in skipForDiscardable and it.len > 0:
|
||||
it = it.lastSon
|
||||
|
||||
case it.kind
|
||||
of nkIfExpr, nkIfStmt:
|
||||
for branch in it:
|
||||
checkBranch:
|
||||
if branch.len == 2:
|
||||
branch[1]
|
||||
elif branch.len == 1:
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `if` statement during implicitlyDiscardable"
|
||||
# all branches are discardable
|
||||
result = true
|
||||
of nkCaseStmt:
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
checkBranch:
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
branch[^1]
|
||||
of nkElifBranch:
|
||||
branch[1]
|
||||
of nkElse:
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `case` statement in endsInNoReturn"
|
||||
# all branches are discardable
|
||||
result = true
|
||||
of nkTryStmt:
|
||||
checkBranch(it[0])
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
if branch.kind != nkFinally:
|
||||
checkBranch(branch[^1])
|
||||
# all branches are discardable
|
||||
result = true
|
||||
of nkCallKinds:
|
||||
result = it[0].kind == nkSym and {sfDiscardable, sfNoReturn} * it[0].sym.flags != {}
|
||||
of nkLastBlockStmts:
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc endsInNoReturn(n: PNode, returningNode: var PNode; discardableCheck = false): bool =
|
||||
## check if expr ends the block like raising or call of noreturn procs do
|
||||
result = false # assume it does return
|
||||
|
||||
template checkBranch(branch) =
|
||||
if not endsInNoReturn(branch, returningNode, discardableCheck):
|
||||
# proved a branch returns
|
||||
return false
|
||||
|
||||
var it = n
|
||||
# skip these beforehand, no special handling needed
|
||||
let skips = if discardableCheck: skipForDiscardable else: skipForDiscardable-{nkBlockExpr, nkBlockStmt}
|
||||
while it.kind in skips and it.len > 0:
|
||||
it = it.lastSon
|
||||
|
||||
case it.kind
|
||||
of nkIfExpr, nkIfStmt:
|
||||
var hasElse = false
|
||||
for branch in it:
|
||||
checkBranch:
|
||||
if branch.len == 2:
|
||||
branch[1]
|
||||
elif branch.len == 1:
|
||||
hasElse = true
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `if` statement during endsInNoReturn"
|
||||
# none of the branches returned
|
||||
result = hasElse # Only truly a no-return when it's exhaustive
|
||||
of nkCaseStmt:
|
||||
let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc})
|
||||
# semCase should already have checked for exhaustiveness in this case
|
||||
# effectively the same as having an else
|
||||
var hasElse = caseTyp.shouldCheckCaseCovered()
|
||||
|
||||
# actual noreturn checks
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
checkBranch:
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
branch[^1]
|
||||
of nkElifBranch:
|
||||
branch[1]
|
||||
of nkElse:
|
||||
hasElse = true
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `case` statement in endsInNoReturn"
|
||||
# Can only guarantee a noreturn if there is an else or it's exhaustive
|
||||
result = hasElse
|
||||
of nkTryStmt:
|
||||
checkBranch(it[0])
|
||||
var lastIndex = it.len - 1
|
||||
if it[lastIndex].kind == nkFinally:
|
||||
# if finally is noreturn, then the entire statement is noreturn
|
||||
if endsInNoReturn(it[lastIndex][^1], returningNode, discardableCheck):
|
||||
return true
|
||||
dec lastIndex
|
||||
for i in 1 .. lastIndex:
|
||||
let branch = it[i]
|
||||
checkBranch(branch[^1])
|
||||
# none of the branches returned
|
||||
result = true
|
||||
of nkLastBlockStmts:
|
||||
result = true
|
||||
of nkCallKinds:
|
||||
result = it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
|
||||
if not result:
|
||||
returningNode = it
|
||||
else:
|
||||
result = false
|
||||
returningNode = it
|
||||
|
||||
proc endsInNoReturn(n: PNode): bool =
|
||||
var dummy: PNode = nil
|
||||
result = endsInNoReturn(n, dummy)
|
||||
|
||||
proc fixNilType(c: PContext; n: PNode) =
|
||||
if isAtom(n):
|
||||
@@ -162,13 +286,9 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
|
||||
localError(c.config, result.info, "expression has no type: " &
|
||||
renderTree(result, {renderNoComments}))
|
||||
else:
|
||||
var n = result
|
||||
while n.kind in skipForDiscardable:
|
||||
if n.kind == nkTryStmt: n = n[0]
|
||||
else: n = n.lastSon
|
||||
|
||||
# Ignore noreturn procs since they don't have a type
|
||||
if n.endsInNoReturn:
|
||||
var n = result
|
||||
if result.endsInNoReturn(n, discardableCheck = true):
|
||||
return
|
||||
|
||||
var s = "expression '" & $n & "' is of type '" &
|
||||
@@ -357,7 +477,7 @@ proc identWithin(n: PNode, s: PIdent): bool =
|
||||
|
||||
proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym =
|
||||
if isTopLevel(c):
|
||||
result = semIdentWithPragma(c, kind, n, {sfExported})
|
||||
result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true)
|
||||
incl(result.flags, sfGlobal)
|
||||
#if kind in {skVar, skLet}:
|
||||
# echo "global variable here ", n.info, " ", result.name.s
|
||||
@@ -660,6 +780,24 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
|
||||
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
var b: PNode
|
||||
result = copyNode(n)
|
||||
|
||||
# transform var x, y = 12 into var x = 12; var y = 12
|
||||
# bug #18104; transformation should be finished before templates expansion
|
||||
# TODO: move warnings for tuple here
|
||||
var transformed = copyNode(n)
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
if a.kind == nkIdentDefs and a.len > 3 and a[^1].kind != nkEmpty:
|
||||
for j in 0..<a.len-2:
|
||||
var b = newNodeI(nkIdentDefs, a.info)
|
||||
b.add a[j]
|
||||
b.add a[^2]
|
||||
b.add copyTree(a[^1])
|
||||
transformed.add b
|
||||
else:
|
||||
transformed.add a
|
||||
let n = transformed
|
||||
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
|
||||
@@ -1039,7 +1177,7 @@ proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string;
|
||||
if maType == nil: return
|
||||
|
||||
let headSymbol = selector[0]
|
||||
var o: TOverloadIter
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var match: PSym = nil
|
||||
var symx = initOverloadIter(o, c, headSymbol)
|
||||
while symx != nil:
|
||||
@@ -1073,7 +1211,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
toResolve.add newIdentNode(getIdent(c.cache, "case"), n.info)
|
||||
toResolve.add n[0]
|
||||
|
||||
var errors: CandidateErrors
|
||||
var errors: CandidateErrors = @[]
|
||||
var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoDiagnostics},
|
||||
errors, false)
|
||||
if r.state == csMatch:
|
||||
@@ -1581,6 +1719,17 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
# check the style here after the pragmas have been processed:
|
||||
styleCheckDef(c, s)
|
||||
# compute the type's size and check for illegal recursions:
|
||||
if a[0].kind == nkPragmaExpr:
|
||||
let pragmas = a[0][1]
|
||||
for i in 0 ..< pragmas.len:
|
||||
if pragmas[i].kind == nkExprColonExpr and
|
||||
pragmas[i][0].kind == nkIdent and
|
||||
whichKeyword(pragmas[i][0].ident) == wSize:
|
||||
if s.typ.kind != tyEnum and sfImportc notin s.flags:
|
||||
# EventType* {.size: sizeof(uint32).} = enum
|
||||
# AtomicFlag* {.importc: "atomic_flag", header: "<stdatomic.h>", size: 1.} = object
|
||||
localError(c.config, pragmas[i].info, "size pragma only allowed for enum types and imported types")
|
||||
|
||||
if a[1].kind == nkEmpty:
|
||||
var x = a[2]
|
||||
if x.kind in nkCallKinds and nfSem in x.flags:
|
||||
@@ -2133,6 +2282,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
of nkEmpty:
|
||||
s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info)
|
||||
s.flags.incl sfUsed
|
||||
s.flags.incl sfGenSym
|
||||
n[namePos] = newSymNode(s)
|
||||
of nkSym:
|
||||
s = n[namePos].sym
|
||||
|
||||
@@ -51,7 +51,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
|
||||
isField = false): PNode =
|
||||
var
|
||||
a: PSym
|
||||
o: TOverloadIter
|
||||
o: TOverloadIter = default(TOverloadIter)
|
||||
var i = 0
|
||||
a = initOverloadIter(o, c, n)
|
||||
while a != nil:
|
||||
@@ -66,7 +66,12 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
|
||||
# for instance 'nextTry' is both in tables.nim and astalgo.nim ...
|
||||
if not isField or sfGenSym notin s.flags:
|
||||
result = newSymNode(s, info)
|
||||
markUsed(c, info, s)
|
||||
if isField:
|
||||
# possibly not final field sym
|
||||
incl(s.flags, sfUsed)
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
else:
|
||||
markUsed(c, info, s)
|
||||
onUse(info, s)
|
||||
else:
|
||||
result = n
|
||||
@@ -217,63 +222,82 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
if k == skParam and c.inTemplateHeader > 0:
|
||||
local.flags.incl sfTemplateParam
|
||||
|
||||
proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
|
||||
proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField: bool): PNode =
|
||||
incl(s.flags, sfUsed)
|
||||
# bug #12885; ideally sem'checking is performed again afterwards marking
|
||||
# the symbol as used properly, but the nfSem mechanism currently prevents
|
||||
# that from happening, so we mark the module as used here already:
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
markOwnerModuleAsUsed(c.c, s)
|
||||
# we do not call onUse here, as the identifier is not really
|
||||
# resolved here. We will fixup the used identifiers later.
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
result = n
|
||||
of OverloadableSyms-{skTemplate,skMacro}:
|
||||
result = symChoice(c, n, s, scOpen, isField)
|
||||
of skTemplate, skMacro:
|
||||
result = symChoice(c, n, s, scOpen, isField)
|
||||
if result.kind == nkSym:
|
||||
# template/macro symbols might need to be semchecked again
|
||||
# prepareOperand etc don't do this without setting the type to nil
|
||||
result.typ = nil
|
||||
of OverloadableSyms:
|
||||
result = symChoice(c.c, n, s, scOpen, isField)
|
||||
if not isField and result.kind in {nkSym, nkOpenSymChoice}:
|
||||
if openSym in c.c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
of skGenericParam:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
|
||||
if not isField and s.owner != c.owner:
|
||||
if openSym in c.c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
of skParam:
|
||||
result = n
|
||||
of skType:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
|
||||
else:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else: result = newSymNode(s, n.info)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if not isField:
|
||||
if openSym in c.c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ = nil
|
||||
# Issue #12832
|
||||
when defined(nimsuggest):
|
||||
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
|
||||
suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false)
|
||||
# field access (dot expr) will be handled by builtinFieldAccess
|
||||
if not isField:
|
||||
styleCheckUse(c, n.info, s)
|
||||
styleCheckUse(c.c, n.info, s)
|
||||
|
||||
proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
|
||||
proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode =
|
||||
result = n
|
||||
if n.kind == nkIdent:
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and s.kind == skParam:
|
||||
if s.owner == c.owner and (s.kind == skParam or
|
||||
(sfGenSym in s.flags and not explicitInject)):
|
||||
incl(s.flags, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
result[i] = semRoutineInTemplName(c, n[i])
|
||||
result[i] = semRoutineInTemplName(c, n[i], explicitInject)
|
||||
|
||||
proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
|
||||
result = n
|
||||
checkSonsLen(n, bodyPos + 1, c.c.config)
|
||||
if n.kind notin nkLambdaKinds:
|
||||
# routines default to 'inject':
|
||||
if symBinding(n[pragmasPos]) == spGenSym:
|
||||
let binding = symBinding(n[pragmasPos])
|
||||
if binding == spGenSym:
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
|
||||
if not hasParam:
|
||||
var s = newGenSym(k, ident, c)
|
||||
@@ -285,7 +309,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
|
||||
else:
|
||||
n[namePos] = ident
|
||||
else:
|
||||
n[namePos] = semRoutineInTemplName(c, n[namePos])
|
||||
n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject)
|
||||
# open scope for parameters
|
||||
openScope(c)
|
||||
for i in patternPos..paramsPos-1:
|
||||
@@ -361,7 +385,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
else:
|
||||
if s.kind in {skType, skVar, skLet, skConst}:
|
||||
discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
|
||||
result = semTemplSymbol(c.c, n, s, c.noGenSym > 0)
|
||||
result = semTemplSymbol(c, n, s, c.noGenSym > 0)
|
||||
of nkBind:
|
||||
result = semTemplBody(c, n[0])
|
||||
of nkBindStmt:
|
||||
@@ -571,7 +595,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
else:
|
||||
if s.kind in {skType, skVar, skLet, skConst}:
|
||||
discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
|
||||
return semTemplSymbol(c.c, n, s, c.noGenSym > 0)
|
||||
return semTemplSymbol(c, n, s, c.noGenSym > 0)
|
||||
if n.kind == nkDotExpr:
|
||||
result = n
|
||||
result[0] = semTemplBody(c, n[0])
|
||||
|
||||
@@ -228,6 +228,22 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
|
||||
else:
|
||||
return false
|
||||
|
||||
proc annotateClosureConv(n: PNode) =
|
||||
case n.kind
|
||||
of {nkNone..nkNilLit}:
|
||||
discard
|
||||
of nkTupleConstr:
|
||||
if n.typ.kind == tyProc and n.typ.callConv == ccClosure and
|
||||
n[0].typ.kind == tyProc and n[0].typ.callConv != ccClosure:
|
||||
# restores `transf.generateThunk`
|
||||
n[0] = newTreeIT(nkHiddenSubConv, n[0].info, n.typ,
|
||||
newNodeI(nkEmpty, n[0].info), n[0])
|
||||
n.transitionSonsKind(nkClosure)
|
||||
n.flags.incl nfTransf
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
annotateClosureConv(n[i])
|
||||
|
||||
proc fitDefaultNode(c: PContext, n: PNode): PType =
|
||||
inc c.inStaticContext
|
||||
let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
|
||||
@@ -243,6 +259,7 @@ proc fitDefaultNode(c: PContext, n: PNode): PType =
|
||||
# `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
|
||||
annotateClosureConv(n)
|
||||
result = n[^1].typ
|
||||
else:
|
||||
result = n[^1].typ
|
||||
@@ -521,7 +538,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)
|
||||
@@ -536,11 +553,15 @@ 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)
|
||||
of skLet: implicitPragmas(c, result, n.info, letPragmas)
|
||||
of skConst: implicitPragmas(c, result, n.info, constPragmas)
|
||||
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) =
|
||||
@@ -1247,7 +1268,7 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
|
||||
result = semTypeNode(c, n[0], nil)
|
||||
constraint = semNodeKindConstraints(n, c.config, 1)
|
||||
elif n.kind == nkCall and
|
||||
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and
|
||||
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and
|
||||
considerQuotedIdent(c, n[0]).s == "{}":
|
||||
result = semTypeNode(c, n[1], nil)
|
||||
constraint = semNodeKindConstraints(n, c.config, 2)
|
||||
@@ -1325,7 +1346,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
def.typ = makeTypeFromExpr(c, def.copyTree)
|
||||
break determineType
|
||||
|
||||
def = semExprWithType(c, def, {efDetermineType}, defTyp)
|
||||
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, defTyp)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
|
||||
@@ -1609,7 +1630,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
recomputeFieldPositions(tx, tx.n, position)
|
||||
|
||||
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
||||
if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody} and prev != nil:
|
||||
if prev != nil and (prev.kind == tyGenericBody or
|
||||
typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}):
|
||||
result = newTypeS(tyAlias, c)
|
||||
result.rawAddSon typeExpr
|
||||
result.sym = prev.sym
|
||||
@@ -1802,6 +1824,8 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
|
||||
openScope(c)
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let t = semExprWithType(c, n, {efInTypeof})
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
@@ -1816,6 +1840,8 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
|
||||
else:
|
||||
m = mode.intVal
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
@@ -1864,7 +1890,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
return errorSym(c, n)
|
||||
if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}:
|
||||
# this implements the wanted ``var v: V, x: V`` feature ...
|
||||
var ov: TOverloadIter
|
||||
var ov: TOverloadIter = default(TOverloadIter)
|
||||
var amb = initOverloadIter(ov, c, n)
|
||||
while amb != nil and amb.kind != skType:
|
||||
amb = nextOverloadIter(ov, c, n)
|
||||
@@ -1907,11 +1933,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of nkTupleConstr: result = semAnonTuple(c, n, prev)
|
||||
of nkCallKinds:
|
||||
let x = n[0]
|
||||
let ident = case x.kind
|
||||
of nkIdent: x.ident
|
||||
of nkSym: x.sym.name
|
||||
of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name
|
||||
else: nil
|
||||
let ident = x.getPIdent
|
||||
if ident != nil and ident.s == "[]":
|
||||
let b = newNodeI(nkBracketExpr, n.info)
|
||||
for i in 1..<n.len: b.add(n[i])
|
||||
@@ -2001,7 +2023,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
elif op.id == ord(wType):
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = semTypeOf(c, n[1], prev)
|
||||
elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf:
|
||||
elif op.s == "typeof" and (
|
||||
(n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or
|
||||
(n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)):
|
||||
result = semTypeOf2(c, n, prev)
|
||||
elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
|
||||
result = semTypeExpr(c, n[1], prev)
|
||||
@@ -2088,7 +2112,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if s.kind != skError: localError(c.config, n.info, errTypeExpected)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
|
||||
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
|
||||
internalAssert c.config, s.typ.base.kind != tyNone
|
||||
result = s.typ.base
|
||||
elif prev == nil:
|
||||
result = s.typ
|
||||
@@ -2112,7 +2136,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if s.kind == skType:
|
||||
s.typ
|
||||
else:
|
||||
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
|
||||
internalAssert c.config, s.typ.base.kind != tyNone
|
||||
s.typ.base
|
||||
let alias = maybeAliasType(c, t, prev)
|
||||
if alias != nil:
|
||||
@@ -2172,6 +2196,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of nkType: result = n.typ
|
||||
of nkStmtListType: result = semStmtListType(c, n, prev)
|
||||
of nkBlockType: result = semBlockType(c, n, prev)
|
||||
of nkOpenSym: result = semTypeNode(c, n[0], prev)
|
||||
else:
|
||||
result = semTypeExpr(c, n, prev)
|
||||
when false:
|
||||
|
||||
@@ -136,9 +136,13 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
else:
|
||||
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
|
||||
let isCall = result.kind in nkCallKinds
|
||||
# don't try to instantiate symchoice symbols, they can be
|
||||
# generic procs which the compiler will think are uninstantiated
|
||||
# because their type will contain uninstantiated params
|
||||
let isSymChoice = result.kind in nkSymChoices
|
||||
for i in 0..<n.safeLen:
|
||||
# XXX HACK: ``f(a, b)``, avoid to instantiate `f`
|
||||
if isCall and i == 0: result.add(n[i])
|
||||
if isSymChoice or (isCall and i == 0): result.add(n[i])
|
||||
else: result.add(prepareNode(cl, n[i]))
|
||||
|
||||
proc isTypeParam(n: PNode): bool =
|
||||
@@ -574,6 +578,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
result.kind = tyUserTypeClassInst
|
||||
|
||||
of tyGenericBody:
|
||||
if cl.allowMetaTypes: return
|
||||
localError(
|
||||
cl.c.config,
|
||||
cl.info,
|
||||
@@ -647,7 +652,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
|
||||
for i in 0..<result.len:
|
||||
if result[i] != nil:
|
||||
if result[i].kind == tyGenericBody:
|
||||
if result[i].kind == tyGenericBody and not cl.allowMetaTypes:
|
||||
localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info,
|
||||
"cannot instantiate '" &
|
||||
typeToString(result[i], preferDesc) &
|
||||
|
||||
@@ -260,7 +260,8 @@ when defined(debugSigHashes):
|
||||
# (select hash from sighashes group by hash having count(*) > 1) order by hash;
|
||||
|
||||
proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): SigHash =
|
||||
var c: MD5Context
|
||||
result = default(SigHash)
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init c
|
||||
hashType c, t, flags+{CoOwnerSig}, conf
|
||||
md5Final c, result.MD5Digest
|
||||
@@ -269,7 +270,8 @@ proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}):
|
||||
typeToString(t), $result)
|
||||
|
||||
proc hashProc(s: PSym; conf: ConfigRef): SigHash =
|
||||
var c: MD5Context
|
||||
result = default(SigHash)
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init c
|
||||
hashType c, s.typ, {CoProc}, conf
|
||||
|
||||
@@ -289,7 +291,8 @@ proc hashProc(s: PSym; conf: ConfigRef): SigHash =
|
||||
md5Final c, result.MD5Digest
|
||||
|
||||
proc hashNonProc*(s: PSym): SigHash =
|
||||
var c: MD5Context
|
||||
result = default(SigHash)
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init c
|
||||
hashSym(c, s)
|
||||
var it = s
|
||||
@@ -305,7 +308,8 @@ proc hashNonProc*(s: PSym): SigHash =
|
||||
md5Final c, result.MD5Digest
|
||||
|
||||
proc hashOwner*(s: PSym): SigHash =
|
||||
var c: MD5Context
|
||||
result = default(SigHash)
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init c
|
||||
var m = s
|
||||
while m.kind != skModule: m = m.owner
|
||||
@@ -378,7 +382,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
|
||||
graph.symBodyHashes.withValue(sym.id, value):
|
||||
return value[]
|
||||
|
||||
var c: MD5Context
|
||||
var c: MD5Context = default(MD5Context)
|
||||
md5Init(c)
|
||||
c.hashType(sym.typ, {CoProc}, graph.config)
|
||||
c &= char(sym.kind)
|
||||
|
||||
@@ -78,7 +78,8 @@ type
|
||||
# or when the explain pragma is used. may be
|
||||
# triggered with an idetools command in the
|
||||
# future.
|
||||
inheritancePenalty: int # to prefer closest father object type
|
||||
# to prefer closest father object type
|
||||
inheritancePenalty: int
|
||||
firstMismatch*: MismatchInfo # mismatch info for better error messages
|
||||
diagnosticsEnabled*: bool
|
||||
|
||||
@@ -93,6 +94,7 @@ type
|
||||
|
||||
const
|
||||
isNilConversion = isConvertible # maybe 'isIntConv' fits better?
|
||||
maxInheritancePenalty = high(int) div 2
|
||||
|
||||
proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true)
|
||||
proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
|
||||
@@ -113,7 +115,7 @@ proc initCandidateAux(ctx: PContext,
|
||||
c.call = nil
|
||||
c.baseTypeMatch = false
|
||||
c.genericConverter = false
|
||||
c.inheritancePenalty = 0
|
||||
c.inheritancePenalty = -1
|
||||
|
||||
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
|
||||
initCandidateAux(ctx, c, callee)
|
||||
@@ -140,15 +142,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
|
||||
initCandidateAux(ctx, c, callee.typ)
|
||||
c.calleeSym = callee
|
||||
if callee.kind in skProcKinds and calleeScope == -1:
|
||||
if callee.originatingModule == ctx.module:
|
||||
c.calleeScope = 2
|
||||
var owner = callee
|
||||
while true:
|
||||
owner = owner.skipGenericOwner
|
||||
if owner.kind == skModule: break
|
||||
inc c.calleeScope
|
||||
else:
|
||||
c.calleeScope = 1
|
||||
c.calleeScope = cmpScopes(ctx, callee)
|
||||
else:
|
||||
c.calleeScope = calleeScope
|
||||
c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
|
||||
@@ -175,19 +169,19 @@ proc newCandidate*(ctx: PContext, callee: PSym,
|
||||
proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
|
||||
initCandidate(ctx, result, callee)
|
||||
|
||||
proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
||||
a.c = b.c
|
||||
a.exactMatches = b.exactMatches
|
||||
a.subtypeMatches = b.subtypeMatches
|
||||
a.convMatches = b.convMatches
|
||||
a.intConvMatches = b.intConvMatches
|
||||
a.genericMatches = b.genericMatches
|
||||
a.state = b.state
|
||||
a.callee = b.callee
|
||||
a.calleeSym = b.calleeSym
|
||||
a.call = copyTree(b.call)
|
||||
a.baseTypeMatch = b.baseTypeMatch
|
||||
copyIdTable(a.bindings, b.bindings)
|
||||
proc copyCandidate(dest: var TCandidate, src: TCandidate) =
|
||||
dest.c = src.c
|
||||
dest.exactMatches = src.exactMatches
|
||||
dest.subtypeMatches = src.subtypeMatches
|
||||
dest.convMatches = src.convMatches
|
||||
dest.intConvMatches = src.intConvMatches
|
||||
dest.genericMatches = src.genericMatches
|
||||
dest.state = src.state
|
||||
dest.callee = src.callee
|
||||
dest.calleeSym = src.calleeSym
|
||||
dest.call = copyTree(src.call)
|
||||
dest.baseTypeMatch = src.baseTypeMatch
|
||||
copyIdTable(dest.bindings, src.bindings)
|
||||
|
||||
proc typeRel*(c: var TCandidate, f, aOrig: PType,
|
||||
flags: TTypeRelFlags = {}): TTypeRelation
|
||||
@@ -202,10 +196,10 @@ proc checkGeneric(a, b: TCandidate): int =
|
||||
let tra = typeRel(ma, bb[i], aa[i], {trDontBind})
|
||||
var mb = newCandidate(c, aa[i])
|
||||
let trb = typeRel(mb, aa[i], bb[i], {trDontBind})
|
||||
if tra == isGeneric and trb == isNone:
|
||||
if tra == isGeneric and trb in {isNone, isInferred, isInferredConvertible}:
|
||||
if winner == -1: return 0
|
||||
winner = 1
|
||||
if trb == isGeneric and tra == isNone:
|
||||
if trb == isGeneric and tra in {isNone, isInferred, isInferredConvertible}:
|
||||
if winner == 1: return 0
|
||||
winner = -1
|
||||
result = winner
|
||||
@@ -215,14 +209,24 @@ proc sumGeneric(t: PType): int =
|
||||
# and Foo[T] has the value 2 so that we know Foo[Foo[T]] is more
|
||||
# specific than Foo[T].
|
||||
var t = t
|
||||
var isvar = 0
|
||||
while true:
|
||||
case t.kind
|
||||
of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray,
|
||||
tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody,
|
||||
tyLent, tyOwned:
|
||||
of tyAlias, tySink, tyNot: t = t.lastSon
|
||||
of tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray,
|
||||
tyOpenArray, tyVarargs, tySet, tyRange, tySequence,
|
||||
tyLent, tyOwned, tyVar:
|
||||
t = t.lastSon
|
||||
inc result
|
||||
of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyVoid,
|
||||
tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||
tyUInt..tyUInt64, tyCompositeTypeClass, tyBuiltInTypeClass:
|
||||
inc result
|
||||
break
|
||||
of tyGenericBody:
|
||||
t = t.typeBodyImpl
|
||||
of tyGenericInst, tyStatic:
|
||||
t = t[0]
|
||||
inc result
|
||||
of tyOr:
|
||||
var maxBranch = 0
|
||||
for branch in t.sons:
|
||||
@@ -230,32 +234,25 @@ proc sumGeneric(t: PType): int =
|
||||
if branchSum > maxBranch: maxBranch = branchSum
|
||||
inc result, maxBranch
|
||||
break
|
||||
of tyVar:
|
||||
t = t[0]
|
||||
inc result
|
||||
inc isvar
|
||||
of tyTypeDesc:
|
||||
t = t.lastSon
|
||||
if t.kind == tyEmpty: break
|
||||
inc result
|
||||
of tyGenericParam:
|
||||
if t.len > 0:
|
||||
t = t.skipModifier
|
||||
else:
|
||||
inc result
|
||||
break
|
||||
of tyUntyped, tyTyped: break
|
||||
of tyGenericInvocation, tyTuple, tyProc, tyAnd:
|
||||
result += ord(t.kind in {tyGenericInvocation, tyAnd})
|
||||
result += ord(t.kind == tyAnd)
|
||||
for i in 0..<t.len:
|
||||
if t[i] != nil:
|
||||
result += sumGeneric(t[i])
|
||||
break
|
||||
of tyStatic:
|
||||
return sumGeneric(t[0]) + 1
|
||||
of tyGenericParam, tyUntyped, tyTyped: break
|
||||
of tyAlias, tySink: t = t.lastSon
|
||||
of tyBool, tyChar, tyEnum, tyObject, tyPointer,
|
||||
tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
|
||||
tyUInt..tyUInt64, tyCompositeTypeClass:
|
||||
return isvar + 1
|
||||
of tyBuiltInTypeClass:
|
||||
return isvar
|
||||
else:
|
||||
return 0
|
||||
break
|
||||
|
||||
proc complexDisambiguation(a, b: PType): int =
|
||||
# 'a' matches better if *every* argument matches better or equal than 'b'.
|
||||
@@ -290,7 +287,16 @@ proc writeMatches*(c: TCandidate) =
|
||||
echo " conv matches: ", c.convMatches
|
||||
echo " inheritance: ", c.inheritancePenalty
|
||||
|
||||
proc cmpCandidates*(a, b: TCandidate): int =
|
||||
proc cmpInheritancePenalty(a, b: int): int =
|
||||
var eb = b
|
||||
var ea = a
|
||||
if b < 0:
|
||||
eb = maxInheritancePenalty # defacto max penalty
|
||||
if a < 0:
|
||||
ea = maxInheritancePenalty
|
||||
eb - ea
|
||||
|
||||
proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
|
||||
result = a.exactMatches - b.exactMatches
|
||||
if result != 0: return
|
||||
result = a.genericMatches - b.genericMatches
|
||||
@@ -301,16 +307,16 @@ proc cmpCandidates*(a, b: TCandidate): int =
|
||||
if result != 0: return
|
||||
result = a.convMatches - b.convMatches
|
||||
if result != 0: return
|
||||
# the other way round because of other semantics:
|
||||
result = b.inheritancePenalty - a.inheritancePenalty
|
||||
result = cmpInheritancePenalty(a.inheritancePenalty, b.inheritancePenalty)
|
||||
if result != 0: return
|
||||
# check for generic subclass relation
|
||||
result = checkGeneric(a, b)
|
||||
if isFormal:
|
||||
# check for generic subclass relation
|
||||
result = checkGeneric(a, b)
|
||||
if result != 0: return
|
||||
# prefer more specialized generic over more general generic:
|
||||
result = complexDisambiguation(a.callee, b.callee)
|
||||
if result != 0: return
|
||||
# prefer more specialized generic over more general generic:
|
||||
result = complexDisambiguation(a.callee, b.callee)
|
||||
# only as a last resort, consider scoping:
|
||||
if result != 0: return
|
||||
result = a.calleeScope - b.calleeScope
|
||||
|
||||
proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
|
||||
@@ -330,16 +336,27 @@ template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = p
|
||||
result.add renderTree(n[i][0])
|
||||
result.add ": "
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
|
||||
# XXX we really need to 'tryExpr' here!
|
||||
arg = c.semOperand(c, n[i][1])
|
||||
n[i].typ = arg.typ
|
||||
n[i][1] = arg
|
||||
arg = c.semTryExpr(c, n[i][1])
|
||||
if arg == nil:
|
||||
arg = n[i][1]
|
||||
arg.typ = newTypeS(tyUntyped, c)
|
||||
else:
|
||||
if arg.typ == nil:
|
||||
arg.typ = newTypeS(tyVoid, c)
|
||||
n[i].typ = arg.typ
|
||||
n[i][1] = arg
|
||||
else:
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
|
||||
nkOfBranch, nkElifBranch,
|
||||
nkExceptBranch}:
|
||||
arg = c.semOperand(c, n[i])
|
||||
n[i] = arg
|
||||
arg = c.semTryExpr(c, n[i])
|
||||
if arg == nil:
|
||||
arg = n[i]
|
||||
arg.typ = newTypeS(tyUntyped, c)
|
||||
else:
|
||||
if arg.typ == nil:
|
||||
arg.typ = newTypeS(tyVoid, c)
|
||||
n[i] = arg
|
||||
if arg.typ != nil and arg.typ.kind == tyError: return
|
||||
result.add argTypeToString(arg, prefer)
|
||||
|
||||
@@ -362,6 +379,7 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
|
||||
else: result = t
|
||||
of tyGenericParam, tyAnything, tyConcept:
|
||||
result = t
|
||||
if c.isNoCall: return
|
||||
while true:
|
||||
result = PType(idTableGet(c.bindings, t))
|
||||
if result == nil:
|
||||
@@ -456,33 +474,40 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
|
||||
else: result = isIntConv
|
||||
else: result = isNone
|
||||
|
||||
proc getObjectTypeOrNil(f: PType): PType =
|
||||
proc reduceToBase(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.
|
||||
Returns the lowest order (most general) type that that is compatible with the input.
|
||||
E.g.
|
||||
A[T] = ptr object ... A -> ptr object
|
||||
A[N: static[int]] = array[N, int] ... A -> array
|
||||
]#
|
||||
if f == nil: return nil
|
||||
case f.kind:
|
||||
of tyGenericInvocation, tyCompositeTypeClass, tyAlias:
|
||||
if f.len <= 0 or f[0] == nil:
|
||||
result = nil
|
||||
of tyGenericParam:
|
||||
if f.len <= 0 or f.skipModifier == nil:
|
||||
result = f
|
||||
else:
|
||||
result = getObjectTypeOrNil(f[0])
|
||||
of tyGenericBody, tyGenericInst:
|
||||
result = getObjectTypeOrNil(f.lastSon)
|
||||
result = reduceToBase(f.skipModifier)
|
||||
of tyGenericInvocation:
|
||||
result = reduceToBase(f.baseClass)
|
||||
of tyCompositeTypeClass, tyAlias:
|
||||
if f.len <= 0 or f[0] == nil:
|
||||
result = f
|
||||
else:
|
||||
result = reduceToBase(f.elementType)
|
||||
of tyGenericInst:
|
||||
result = reduceToBase(f.skipModifier)
|
||||
of tyGenericBody:
|
||||
result = reduceToBase(f.typeBodyImpl)
|
||||
of tyUserTypeClass:
|
||||
if f.isResolvedUserTypeClass:
|
||||
result = f.base # ?? idk if this is right
|
||||
else:
|
||||
result = f.lastSon
|
||||
of tyStatic, tyOwned, tyVar, tyLent, tySink:
|
||||
result = getObjectTypeOrNil(f.base)
|
||||
result = reduceToBase(f.base)
|
||||
of tyInferred:
|
||||
# This is not true "After a candidate type is selected"
|
||||
result = getObjectTypeOrNil(f.base)
|
||||
of tyTyped, tyUntyped, tyFromExpr:
|
||||
result = nil
|
||||
result = reduceToBase(f.base)
|
||||
of tyRange:
|
||||
result = f.lastSon
|
||||
else:
|
||||
@@ -566,7 +591,7 @@ proc minRel(a, b: TTypeRelation): TTypeRelation =
|
||||
if a <= b: result = a
|
||||
else: result = b
|
||||
|
||||
proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
proc recordRel(c: var TCandidate, f, a: PType, flags: TTypeRelFlags): TTypeRelation =
|
||||
result = isNone
|
||||
if sameType(f, a):
|
||||
result = isEqual
|
||||
@@ -575,8 +600,13 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
let firstField = if f.kind == tyTuple: 0
|
||||
else: 1
|
||||
for i in firstField..<f.len:
|
||||
var m = typeRel(c, f[i], a[i])
|
||||
var m = typeRel(c, f[i], a[i], flags)
|
||||
if m < isSubtype: return isNone
|
||||
if m == isSubtype and a[i].kind != tyNil and c.inheritancePenalty > -1:
|
||||
# we can't process individual element type conversions from a
|
||||
# type conversion for the whole tuple
|
||||
# subtype relations need type conversions when inheritance is used
|
||||
return isNone
|
||||
result = minRel(result, m)
|
||||
if f.n != nil and a.n != nil:
|
||||
for i in 0..<f.n.len:
|
||||
@@ -586,7 +616,7 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
else:
|
||||
var x = f.n[i].sym
|
||||
var y = a.n[i].sym
|
||||
if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype:
|
||||
if f.kind == tyObject and typeRel(c, x.typ, y.typ, flags) < isSubtype:
|
||||
return isNone
|
||||
if x.name.id != y.name.id: return isNone
|
||||
|
||||
@@ -968,9 +998,21 @@ proc inferStaticsInRange(c: var TCandidate,
|
||||
doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete))
|
||||
|
||||
template subtypeCheck() =
|
||||
if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {
|
||||
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
|
||||
case result
|
||||
of isIntConv:
|
||||
result = isNone
|
||||
of isSubrange:
|
||||
discard # XXX should be isNone with preview define, warnings
|
||||
of isConvertible:
|
||||
if f.lastSon.skipTypes(abstractInst).kind != tyOpenArray:
|
||||
# exclude var openarray which compiler supports
|
||||
result = isNone
|
||||
of isSubtype:
|
||||
if f.lastSon.skipTypes(abstractInst).kind in {
|
||||
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
|
||||
# compiler can't handle subtype conversions with pointer indirection
|
||||
result = isNone
|
||||
else: discard
|
||||
|
||||
proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
|
||||
# this proc is always called for a pair of matching types
|
||||
@@ -1179,10 +1221,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
|
||||
of tyFromExpr:
|
||||
if c.c.inGenericContext > 0:
|
||||
# generic type bodies can sometimes compile call expressions
|
||||
# prevent expressions with unresolved types from
|
||||
# being passed as parameters
|
||||
return isNone
|
||||
if not c.isNoCall:
|
||||
# generic type bodies can sometimes compile call expressions
|
||||
# prevent expressions with unresolved types from
|
||||
# being passed as parameters
|
||||
return isNone
|
||||
else:
|
||||
# Foo[templateCall(T)] shouldn't fail early if Foo has a constraint
|
||||
# and we can't evaluate `templateCall(T)` yet
|
||||
return isGeneric
|
||||
else: discard
|
||||
case f.kind
|
||||
of tyEnum:
|
||||
@@ -1241,13 +1288,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
|
||||
subtypeCheck()
|
||||
of tyArray:
|
||||
case a.kind
|
||||
of tyArray:
|
||||
a = reduceToBase(a)
|
||||
if a.kind == tyArray:
|
||||
var fRange = f[0]
|
||||
var aRange = a[0]
|
||||
if fRange.kind in {tyGenericParam, tyAnything}:
|
||||
var prev = PType(idTableGet(c.bindings, fRange))
|
||||
if prev == nil:
|
||||
if typeRel(c, fRange, aRange) == isNone:
|
||||
return isNone
|
||||
put(c, fRange, a[0])
|
||||
fRange = a
|
||||
else:
|
||||
@@ -1260,7 +1309,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, ff, aa, flags)
|
||||
|
||||
if result < isGeneric:
|
||||
if nimEnableCovariance and
|
||||
trNoCovariance notin flags and
|
||||
@@ -1271,6 +1319,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
return isNone
|
||||
|
||||
if fRange.rangeHasUnresolvedStatic:
|
||||
if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange:
|
||||
return
|
||||
return inferStaticsInRange(c, fRange, a)
|
||||
elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
|
||||
return inferStaticsInRange(c, aRange, f)
|
||||
@@ -1279,7 +1329,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
else:
|
||||
if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange):
|
||||
result = isNone
|
||||
else: discard
|
||||
of tyUncheckedArray:
|
||||
if a.kind == tyUncheckedArray:
|
||||
result = typeRel(c, base(f), base(a), flags)
|
||||
@@ -1361,21 +1410,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
skipOwned(a)
|
||||
if a.kind == f.kind: result = isEqual
|
||||
of tyTuple:
|
||||
if a.kind == tyTuple: result = recordRel(c, f, a)
|
||||
if a.kind == tyTuple: result = recordRel(c, f, a, flags)
|
||||
of tyObject:
|
||||
let effectiveArgType = if useTypeLoweringRuleInTypeClass:
|
||||
a
|
||||
else:
|
||||
getObjectTypeOrNil(a)
|
||||
if effectiveArgType == nil: return isNone
|
||||
reduceToBase(a)
|
||||
if effectiveArgType.kind == tyObject:
|
||||
if sameObjectTypes(f, effectiveArgType):
|
||||
c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
|
||||
result = isEqual
|
||||
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
|
||||
elif trIsOutParam notin flags:
|
||||
var depth = isObjectSubtype(c, effectiveArgType, f, nil)
|
||||
if depth > 0:
|
||||
inc(c.inheritancePenalty, depth)
|
||||
c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil)
|
||||
if c.inheritancePenalty > 0:
|
||||
result = isSubtype
|
||||
of tyDistinct:
|
||||
a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
|
||||
@@ -1398,7 +1446,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
result = isNone
|
||||
|
||||
of tyPtr, tyRef:
|
||||
skipOwned(a)
|
||||
a = reduceToBase(a)
|
||||
if a.kind == f.kind:
|
||||
# ptr[R, T] can be passed to ptr[T], but not the other way round:
|
||||
if a.len < f.len: return isNone
|
||||
@@ -1548,7 +1596,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if aAsObject.kind == tyObject and trIsOutParam notin flags:
|
||||
let baseType = aAsObject.base
|
||||
if baseType != nil:
|
||||
c.inheritancePenalty += 1
|
||||
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
|
||||
let ret = typeRel(c, f, baseType, flags)
|
||||
return if ret in {isEqual,isGeneric}: isSubtype else: ret
|
||||
|
||||
@@ -1647,7 +1695,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
depth = -1
|
||||
|
||||
if depth >= 0:
|
||||
c.inheritancePenalty += depth
|
||||
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
|
||||
# bug #4863: We still need to bind generic alias crap, so
|
||||
# we cannot return immediately:
|
||||
result = if depth == 0: isGeneric else: isSubtype
|
||||
@@ -1666,20 +1714,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
considerPreviousT:
|
||||
result = isNone
|
||||
let oldInheritancePenalty = c.inheritancePenalty
|
||||
var maxInheritance = 0
|
||||
var minInheritance = maxInheritancePenalty
|
||||
for branch in f.sons:
|
||||
c.inheritancePenalty = 0
|
||||
c.inheritancePenalty = -1
|
||||
let x = typeRel(c, branch, aOrig, flags)
|
||||
maxInheritance = max(maxInheritance, c.inheritancePenalty)
|
||||
# 'or' implies maximum matching result:
|
||||
if x > result: result = x
|
||||
if x >= result:
|
||||
if c.inheritancePenalty > -1:
|
||||
minInheritance = min(minInheritance, c.inheritancePenalty)
|
||||
result = x
|
||||
if result >= isIntConv:
|
||||
if minInheritance < maxInheritancePenalty:
|
||||
c.inheritancePenalty = oldInheritancePenalty + minInheritance
|
||||
if result > isGeneric: result = isGeneric
|
||||
bindingRet result
|
||||
else:
|
||||
result = isNone
|
||||
c.inheritancePenalty = oldInheritancePenalty + maxInheritance
|
||||
|
||||
of tyNot:
|
||||
considerPreviousT:
|
||||
for branch in f.sons:
|
||||
@@ -1699,11 +1748,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
considerPreviousT:
|
||||
let target = f[0]
|
||||
let targetKind = target.kind
|
||||
var effectiveArgType = a.getObjectTypeOrNil()
|
||||
if effectiveArgType == nil: return isNone
|
||||
var effectiveArgType = reduceToBase(a)
|
||||
# the skipped child of tyBuiltInTypeClass can be structured differently,
|
||||
# newConstraint constructs them with no children
|
||||
let typeClassArg = effectiveArgType.kind == tyBuiltInTypeClass
|
||||
effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
|
||||
if targetKind == effectiveArgType.kind:
|
||||
if effectiveArgType.isEmptyContainer:
|
||||
if not typeClassArg and effectiveArgType.isEmptyContainer:
|
||||
return isNone
|
||||
if targetKind == tyProc:
|
||||
if target.flags * {tfIterator} != effectiveArgType.flags * {tfIterator}:
|
||||
@@ -1797,18 +1848,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
else:
|
||||
# check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)'
|
||||
if f.len > 0 and f[0].kind != tyNone:
|
||||
let oldInheritancePenalty = c.inheritancePenalty
|
||||
result = typeRel(c, f[0], a, flags + {trDontBind,trBindGenericParam})
|
||||
if doBindGP and result notin {isNone, isGeneric}:
|
||||
let concrete = concreteType(c, a, f)
|
||||
if concrete == nil: return isNone
|
||||
put(c, f, concrete)
|
||||
# bug #6526
|
||||
if result in {isEqual, isSubtype}:
|
||||
# 'T: Class' is a *better* match than just 'T'
|
||||
# but 'T: Subclass' is even better:
|
||||
c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty -
|
||||
100 * ord(result == isEqual)
|
||||
result = isGeneric
|
||||
elif a.kind == tyTypeDesc:
|
||||
# somewhat special typing rule, the following is illegal:
|
||||
@@ -1824,10 +1869,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
a.sym.transitionGenericParamToType()
|
||||
a.flags.excl tfWildcard
|
||||
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.
|
||||
# careful: `trDontDont` (set by `checkGeneric`) is not always respected in this call graph.
|
||||
# typRel having two different modes (binding and non-binding) can make things harder to
|
||||
# reason about and maintain. Refactoring typeRel to not be responsible for setting, or
|
||||
# at least validating, bindings can have multiple benefits. This is debatable. I'm not 100% sure.
|
||||
# A design that allows a proper complexity analysis of types like `tyOr` would be ideal.
|
||||
concrete = concreteType(c, a, f)
|
||||
if concrete == nil:
|
||||
return isNone
|
||||
@@ -1840,7 +1886,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
elif x.kind == tyGenericParam:
|
||||
result = isGeneric
|
||||
else:
|
||||
# This is the bound type - can't benifit from these tallies
|
||||
let
|
||||
inheritancePenaltyOld = c.inheritancePenalty
|
||||
result = typeRel(c, x, a, flags) # check if it fits
|
||||
c.inheritancePenalty = inheritancePenaltyOld
|
||||
if result > isGeneric: result = isGeneric
|
||||
of tyStatic:
|
||||
let prev = PType(idTableGet(c.bindings, f))
|
||||
@@ -2054,7 +2104,9 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
dest = generateTypeInstance(c, m.bindings, arg, dest)
|
||||
let fdest = typeRel(m, f, dest)
|
||||
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
|
||||
markUsed(c, arg.info, c.converters[i])
|
||||
# can't fully mark used yet, may not be used in final call
|
||||
incl(c.converters[i].flags, sfUsed)
|
||||
markOwnerModuleAsUsed(c, c.converters[i])
|
||||
var s = newSymNode(c.converters[i])
|
||||
s.typ = c.converters[i].typ
|
||||
s.info = arg.info
|
||||
@@ -2259,8 +2311,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
inc(m.genericMatches)
|
||||
if arg.typ == nil:
|
||||
result = arg
|
||||
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or
|
||||
m.inheritancePenalty > oldInheritancePenalty:
|
||||
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or cmpInheritancePenalty(oldInheritancePenalty, m.inheritancePenalty) > 0:
|
||||
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
||||
elif arg.typ.isEmptyContainer:
|
||||
result = arg.copyTree
|
||||
@@ -2346,56 +2397,76 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||
if arg == nil or arg.kind notin nkSymChoices:
|
||||
result = paramTypesMatchAux(m, f, a, arg, argOrig)
|
||||
else:
|
||||
# CAUTION: The order depends on the used hashing scheme. Thus it is
|
||||
# incorrect to simply use the first fitting match. However, to implement
|
||||
# this correctly is inefficient. We have to copy `m` here to be able to
|
||||
# roll back the side effects of the unification algorithm.
|
||||
let c = m.c
|
||||
var
|
||||
x = newCandidate(c, m.callee)
|
||||
y = newCandidate(c, m.callee)
|
||||
z = newCandidate(c, m.callee)
|
||||
x.calleeSym = m.calleeSym
|
||||
y.calleeSym = m.calleeSym
|
||||
z.calleeSym = m.calleeSym
|
||||
# symbol kinds that don't participate in symchoice type disambiguation:
|
||||
let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage, skType}
|
||||
|
||||
var best = -1
|
||||
for i in 0..<arg.len:
|
||||
if arg[i].sym.kind in {skProc, skFunc, skMethod, skConverter,
|
||||
skIterator, skMacro, skTemplate, skEnumField}:
|
||||
copyCandidate(z, m)
|
||||
z.callee = arg[i].typ
|
||||
if tfUnresolved in z.callee.flags: continue
|
||||
z.calleeSym = arg[i].sym
|
||||
# XXX this is still all wrong: (T, T) should be 2 generic matches
|
||||
# and (int, int) 2 exact matches, etc. Essentially you cannot call
|
||||
# typeRel here and expect things to work!
|
||||
let r = typeRel(z, f, arg[i].typ)
|
||||
incMatches(z, r, 2)
|
||||
if r != isNone:
|
||||
z.state = csMatch
|
||||
case x.state
|
||||
of csEmpty, csNoMatch:
|
||||
x = z
|
||||
best = i
|
||||
of csMatch:
|
||||
let cmp = cmpCandidates(x, z)
|
||||
if cmp < 0:
|
||||
best = i
|
||||
x = z
|
||||
elif cmp == 0:
|
||||
y = z # z is as good as x
|
||||
result = arg
|
||||
|
||||
if x.state == csEmpty:
|
||||
result = nil
|
||||
elif y.state == csMatch and cmpCandidates(x, y) == 0:
|
||||
if x.state != csMatch:
|
||||
internalError(m.c.graph.config, arg.info, "x.state is not csMatch")
|
||||
# ambiguous: more than one symbol fits!
|
||||
# See tsymchoice_for_expr as an example. 'f.kind == tyUntyped' should match
|
||||
# anyway:
|
||||
if f.kind in {tyUntyped, tyTyped}: result = arg
|
||||
else: result = nil
|
||||
if f.kind in {tyTyped, tyUntyped}:
|
||||
var
|
||||
bestScope = -1
|
||||
counts = 0
|
||||
for i in 0..<arg.len:
|
||||
if arg[i].sym.kind in matchSet:
|
||||
let thisScope = cmpScopes(m.c, arg[i].sym)
|
||||
if thisScope > bestScope:
|
||||
best = i
|
||||
bestScope = thisScope
|
||||
counts = 0
|
||||
elif thisScope == bestScope:
|
||||
inc counts
|
||||
if best == -1:
|
||||
result = nil
|
||||
elif counts > 0:
|
||||
best = -1
|
||||
else:
|
||||
# CAUTION: The order depends on the used hashing scheme. Thus it is
|
||||
# incorrect to simply use the first fitting match. However, to implement
|
||||
# this correctly is inefficient. We have to copy `m` here to be able to
|
||||
# roll back the side effects of the unification algorithm.
|
||||
let c = m.c
|
||||
var
|
||||
x = newCandidate(c, m.callee) # potential "best"
|
||||
y = newCandidate(c, m.callee) # potential competitor with x
|
||||
z = newCandidate(c, m.callee) # buffer for copies of m
|
||||
x.calleeSym = m.calleeSym
|
||||
y.calleeSym = m.calleeSym
|
||||
z.calleeSym = m.calleeSym
|
||||
|
||||
for i in 0..<arg.len:
|
||||
if arg[i].sym.kind in matchSet:
|
||||
copyCandidate(z, m)
|
||||
z.callee = arg[i].typ
|
||||
if tfUnresolved in z.callee.flags: continue
|
||||
z.calleeSym = arg[i].sym
|
||||
z.calleeScope = cmpScopes(m.c, arg[i].sym)
|
||||
# XXX this is still all wrong: (T, T) should be 2 generic matches
|
||||
# and (int, int) 2 exact matches, etc. Essentially you cannot call
|
||||
# typeRel here and expect things to work!
|
||||
let r = typeRel(z, f, arg[i].typ)
|
||||
incMatches(z, r, 2)
|
||||
if r != isNone:
|
||||
z.state = csMatch
|
||||
case x.state
|
||||
of csEmpty, csNoMatch:
|
||||
x = z
|
||||
best = i
|
||||
of csMatch:
|
||||
let cmp = cmpCandidates(x, z, isFormal=false)
|
||||
if cmp < 0:
|
||||
best = i
|
||||
x = z
|
||||
elif cmp == 0:
|
||||
y = z # z is as good as x
|
||||
|
||||
if x.state == csEmpty:
|
||||
result = nil
|
||||
elif y.state == csMatch and cmpCandidates(x, y, isFormal=false) == 0:
|
||||
if x.state != csMatch:
|
||||
internalError(m.c.graph.config, arg.info, "x.state is not csMatch")
|
||||
result = nil
|
||||
if best > -1 and result != nil:
|
||||
# only one valid interpretation found:
|
||||
markUsed(m.c, arg.info, arg[best].sym)
|
||||
onUse(arg.info, arg[best].sym)
|
||||
@@ -2455,7 +2526,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)
|
||||
|
||||
@@ -57,7 +57,7 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache
|
||||
else:
|
||||
inc(i, 2)
|
||||
while i < line.len and line[i] in Whitespace: inc(i)
|
||||
var p: Parser
|
||||
var p: Parser = default(Parser)
|
||||
openParser(p, filename, llStreamOpen(substr(line, i)), cache, config)
|
||||
result = parseAll(p)
|
||||
closeParser(p)
|
||||
|
||||
@@ -100,12 +100,19 @@ proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
|
||||
else:
|
||||
result = newSymNode(r)
|
||||
|
||||
proc transform(c: PTransf, n: PNode): PNode
|
||||
proc transform(c: PTransf, n: PNode, noConstFold = false): PNode
|
||||
|
||||
proc transformSons(c: PTransf, n: PNode): PNode =
|
||||
proc transformSons(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
result = newTransNode(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = transform(c, n[i])
|
||||
result[i] = transform(c, n[i], noConstFold)
|
||||
|
||||
proc transformSonsAfterType(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
result = newTransNode(n)
|
||||
assert n.len != 0
|
||||
result[0] = copyTree(n[0])
|
||||
for i in 1..<n.len:
|
||||
result[i] = transform(c, n[i], noConstFold)
|
||||
|
||||
proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode =
|
||||
result = newTransNode(kind, ri.info, 2)
|
||||
@@ -470,9 +477,10 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
result.add(introduceNewLocalVars(c, c.transCon.forLoopBody))
|
||||
c.isIntroducingNewLocalVars = false
|
||||
|
||||
proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
|
||||
result = transformSons(c, n)
|
||||
if c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags: return
|
||||
proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds, isAddr = false): PNode =
|
||||
result = transformSons(c, n, noConstFold = isAddr)
|
||||
# inlining of 'var openarray' iterators; bug #19977
|
||||
if n.typ.kind != tyOpenArray and (c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags): return
|
||||
var n = result
|
||||
case n[0].kind
|
||||
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
|
||||
@@ -496,7 +504,17 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
|
||||
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
|
||||
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
|
||||
else:
|
||||
if n[0].kind in kinds:
|
||||
if n[0].kind in kinds and
|
||||
not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and
|
||||
n[0][0].typ.skipTypes(abstractVar).kind == tyTuple
|
||||
) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and
|
||||
n.typ.kind == tyVar and
|
||||
n.typ.skipTypes(abstractVar).kind == tyOpenArray and
|
||||
n[0][0].typ.skipTypes(abstractVar).kind == tyString) and
|
||||
not (isAddr and n.typ.kind == tyVar and n[0][0].typ.kind == tyRef and
|
||||
n[0][0].kind == nkObjConstr)
|
||||
: # elimination is harmful to `for tuple unpack` because of newTupleAccess
|
||||
# it is also harmful to openArrayLoc (var openArray) for strings
|
||||
# addr ( deref ( x )) --> x
|
||||
result = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
@@ -824,9 +842,18 @@ proc transformArrayAccess(c: PTransf, n: PNode): PNode =
|
||||
if n[0].kind == nkSym and n[0].sym.kind == skType:
|
||||
result = n
|
||||
else:
|
||||
result = newTransNode(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = transform(c, skipConv(n[i]))
|
||||
result = transformSons(c, n)
|
||||
if n.len >= 2 and result[1].kind in {nkChckRange, nkChckRange64} and
|
||||
n[1].kind in {nkHiddenStdConv, nkHiddenSubConv}:
|
||||
# implicit conversion, was transformed into range check
|
||||
# remove in favor of index check if conversion to array index type
|
||||
# has to be done here because the array index type needs to be relaxed
|
||||
# i.e. a uint32 index can implicitly convert to range[0..3] but not int
|
||||
let arr = skipTypes(n[0].typ, abstractVarRange)
|
||||
if arr.kind == tyArray and
|
||||
firstOrd(c.graph.config, arr) == getOrdValue(result[1][1]) and
|
||||
lastOrd(c.graph.config, arr) == getOrdValue(result[1][2]):
|
||||
result[1] = result[1].skipConv
|
||||
|
||||
proc getMergeOp(n: PNode): PSym =
|
||||
case n.kind
|
||||
@@ -874,10 +901,6 @@ proc transformCall(c: PTransf, n: PNode): PNode =
|
||||
inc(j)
|
||||
result.add(a)
|
||||
if result.len == 2: result = result[1]
|
||||
elif magic == mAddr:
|
||||
result = newTransNode(nkAddr, n, 1)
|
||||
result[0] = n[1]
|
||||
result = transformAddrDeref(c, result, {nkDerefExpr, nkHiddenDeref})
|
||||
elif magic in {mNBindSym, mTypeOf, mRunnableExamples}:
|
||||
# for bindSym(myconst) we MUST NOT perform constant folding:
|
||||
result = n
|
||||
@@ -972,7 +995,7 @@ proc transformDerefBlock(c: PTransf, n: PNode): PNode =
|
||||
result[i] = e0[i]
|
||||
result[e0.len-1] = newTreeIT(nkHiddenDeref, n.info, n.typ, e0[e0.len-1])
|
||||
|
||||
proc transform(c: PTransf, n: PNode): PNode =
|
||||
proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
when false:
|
||||
var oldDeferAnchor: PNode
|
||||
if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
|
||||
@@ -1038,10 +1061,8 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||
of nkBreakStmt: result = transformBreak(c, n)
|
||||
of nkCallKinds:
|
||||
result = transformCall(c, n)
|
||||
of nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, {nkHiddenDeref})
|
||||
of nkAddr:
|
||||
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref})
|
||||
of nkAddr, nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}, isAddr = true)
|
||||
of nkDerefExpr:
|
||||
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
|
||||
of nkHiddenDeref:
|
||||
@@ -1053,6 +1074,9 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = transformConv(c, n)
|
||||
of nkObjConstr, nkCast:
|
||||
# don't try to transform type node
|
||||
result = transformSonsAfterType(c, n)
|
||||
of nkDiscardStmt:
|
||||
result = n
|
||||
if n[0].kind != nkEmpty:
|
||||
@@ -1121,7 +1145,7 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||
let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
|
||||
n.typ != nil and
|
||||
n.typ.kind == tyPointer
|
||||
if not exprIsPointerCast:
|
||||
if not exprIsPointerCast and not noConstFold:
|
||||
var cnst = getConstExpr(c.module, result, c.idgen, c.graph)
|
||||
# we inline constants if they are not complex constants:
|
||||
if cnst != nil and not dontInlineConstant(n, cnst):
|
||||
|
||||
@@ -213,8 +213,7 @@ proc stupidStmtListExpr*(n: PNode): bool =
|
||||
proc dontInlineConstant*(orig, cnst: PNode): bool {.inline.} =
|
||||
# symbols that expand to a complex constant (array, etc.) should not be
|
||||
# inlined, unless it's the empty array:
|
||||
result = orig.kind != cnst.kind and
|
||||
cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and
|
||||
result = cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and
|
||||
cnst.len > ord(cnst.kind == nkObjConstr)
|
||||
|
||||
proc isRunnableExamples*(n: PNode): bool =
|
||||
|
||||
@@ -64,8 +64,6 @@ proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
|
||||
template `$`*(typ: PType): string = typeToString(typ)
|
||||
|
||||
proc base*(t: PType): PType =
|
||||
result = t[0]
|
||||
|
||||
# ------------------- type iterator: ----------------------------------------
|
||||
type
|
||||
@@ -119,7 +117,7 @@ proc isPureObject*(typ: PType): bool =
|
||||
proc isUnsigned*(t: PType): bool =
|
||||
t.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}
|
||||
|
||||
proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 =
|
||||
proc getOrdValueAux*(n: PNode, err: var bool): Int128 =
|
||||
var k = n.kind
|
||||
if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}:
|
||||
k = nkUIntLit
|
||||
@@ -135,13 +133,22 @@ proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 =
|
||||
toInt128(n.intVal)
|
||||
of nkNilLit:
|
||||
int128.Zero
|
||||
of nkHiddenStdConv: getOrdValue(n[1], onError)
|
||||
of nkHiddenStdConv:
|
||||
getOrdValueAux(n[1], err)
|
||||
else:
|
||||
# XXX: The idea behind the introduction of int128 was to finally
|
||||
# have all calculations numerically far away from any
|
||||
# overflows. This command just introduces such overflows and
|
||||
# should therefore really be revisited.
|
||||
onError
|
||||
err = true
|
||||
int128.Zero
|
||||
|
||||
proc getOrdValue*(n: PNode): Int128 =
|
||||
var err: bool = false
|
||||
result = getOrdValueAux(n, err)
|
||||
#assert err == false
|
||||
|
||||
proc getOrdValue*(n: PNode, onError: Int128): Int128 =
|
||||
var err = false
|
||||
result = getOrdValueAux(n, err)
|
||||
if err:
|
||||
result = onError
|
||||
|
||||
proc getFloatValue*(n: PNode): BiggestFloat =
|
||||
case n.kind
|
||||
@@ -1211,24 +1218,24 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
if containsOrIncl(c, a, b): return true
|
||||
|
||||
if x == y: return true
|
||||
var a = skipTypes(x, {tyGenericInst, tyAlias})
|
||||
var a = skipTypes(x, {tyAlias})
|
||||
while a.kind == tyUserTypeClass and tfResolved in a.flags:
|
||||
a = skipTypes(a[^1], {tyGenericInst, tyAlias})
|
||||
var b = skipTypes(y, {tyGenericInst, tyAlias})
|
||||
a = skipTypes(a[^1], {tyAlias})
|
||||
var b = skipTypes(y, {tyAlias})
|
||||
while b.kind == tyUserTypeClass and tfResolved in b.flags:
|
||||
b = skipTypes(b[^1], {tyGenericInst, tyAlias})
|
||||
b = skipTypes(b[^1], {tyAlias})
|
||||
assert(a != nil)
|
||||
assert(b != nil)
|
||||
if a.kind != b.kind:
|
||||
case c.cmp
|
||||
of dcEq: return false
|
||||
of dcEqIgnoreDistinct:
|
||||
a = a.skipTypes({tyDistinct, tyGenericInst})
|
||||
b = b.skipTypes({tyDistinct, tyGenericInst})
|
||||
if a.kind != b.kind: return false
|
||||
of dcEqOrDistinctOf:
|
||||
a = a.skipTypes({tyDistinct, tyGenericInst})
|
||||
if a.kind != b.kind: return false
|
||||
case c.cmp
|
||||
of dcEq:
|
||||
if a.kind != b.kind: return false
|
||||
of dcEqIgnoreDistinct:
|
||||
a = a.skipTypes({tyDistinct, tyGenericInst})
|
||||
b = b.skipTypes({tyDistinct, tyGenericInst})
|
||||
if a.kind != b.kind: return false
|
||||
of dcEqOrDistinctOf:
|
||||
a = a.skipTypes({tyDistinct, tyGenericInst})
|
||||
if a.kind != b.kind: return false
|
||||
|
||||
#[
|
||||
The following code should not run in the case either side is an generic alias,
|
||||
@@ -1236,11 +1243,12 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
objects ie `type A[T] = SomeObject`
|
||||
]#
|
||||
# this is required by tunique_type but makes no sense really:
|
||||
if tyDistinct notin {x.kind, y.kind} and x.kind == tyGenericInst and IgnoreTupleFields notin c.flags:
|
||||
if c.cmp == dcEq and x.kind == tyGenericInst and
|
||||
IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
|
||||
let
|
||||
lhs = x.skipGenericAlias
|
||||
rhs = y.skipGenericAlias
|
||||
if rhs.kind != tyGenericInst or lhs.base != rhs.base:
|
||||
if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen:
|
||||
return false
|
||||
for i in 1..<lhs.len - 1:
|
||||
let ff = rhs[i]
|
||||
@@ -1328,9 +1336,17 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
result = sameTypeOrNilAux(a[0], b[0], c) and
|
||||
sameValue(a.n[0], b.n[0]) and
|
||||
sameValue(a.n[1], b.n[1])
|
||||
of tyGenericInst, tyAlias, tyInferred, tyIterable:
|
||||
of tyAlias, tyInferred, tyIterable:
|
||||
cycleCheck()
|
||||
result = sameTypeAux(a.lastSon, b.lastSon, c)
|
||||
of tyGenericInst:
|
||||
# BUG #23445
|
||||
# The type system must distinguish between `T[int] = object #[empty]#`
|
||||
# and `T[float] = object #[empty]#`!
|
||||
cycleCheck()
|
||||
for ff, aa in underspecifiedPairs(a, b, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
|
||||
of tyNone: result = false
|
||||
of tyConcept:
|
||||
result = exprStructuralEquivalent(a.n, b.n)
|
||||
|
||||
@@ -431,11 +431,15 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
|
||||
|
||||
of nkIfStmt, nkIfExpr:
|
||||
for i in 0..<n.len:
|
||||
inc c.inConditional
|
||||
destMightOwn(c, dest, n[i].lastSon)
|
||||
dec c.inConditional
|
||||
|
||||
of nkCaseStmt:
|
||||
for i in 1..<n.len:
|
||||
inc c.inConditional
|
||||
destMightOwn(c, dest, n[i].lastSon)
|
||||
dec c.inConditional
|
||||
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
if n.len > 0:
|
||||
@@ -488,8 +492,17 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
|
||||
# we know the result is derived from the first argument:
|
||||
var roots: seq[(PSym, int)]
|
||||
allRoots(n[1], roots, RootEscapes)
|
||||
for r in roots:
|
||||
connect(c, dest.sym, r[0], n[1].info)
|
||||
if roots.len == 0 and c.inConditional > 0:
|
||||
# when in a conditional expression,
|
||||
# to ensure that the first argument isn't outlived
|
||||
# by the lvalue, we need find the root, otherwise
|
||||
# it is probably a local temporary
|
||||
# (e.g. a return value from a call),
|
||||
# we should prevent cursorfication
|
||||
dest.flags.incl preventCursor
|
||||
else:
|
||||
for r in roots:
|
||||
connect(c, dest.sym, r[0], n[1].info)
|
||||
|
||||
else:
|
||||
let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
import semmacrosanity
|
||||
import
|
||||
std/[strutils, tables, parseutils],
|
||||
std/[strutils, tables, intsets, parseutils],
|
||||
msgs, vmdef, vmgen, nimsets, types,
|
||||
parser, vmdeps, idents, trees, renderer, options, transf,
|
||||
gorgeimpl, lineinfos, btrees, macrocacheimpl,
|
||||
@@ -1359,7 +1359,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
else:
|
||||
internalError(c.config, c.debug[pc], "opcParseFloat: Incorrectly created openarray")
|
||||
else:
|
||||
regs[ra].intVal = parseBiggestFloat(regs[ra].node.strVal, rcAddr.floatVal)
|
||||
regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal, rcAddr.floatVal)
|
||||
|
||||
of opcRangeChck:
|
||||
let rb = instr.regB
|
||||
@@ -1837,7 +1837,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
|
||||
else:
|
||||
stackTrace(c, tos, pc, "node has no type")
|
||||
else:
|
||||
of 3:
|
||||
# getTypeImpl opcode:
|
||||
ensureKind(rkNode)
|
||||
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
|
||||
@@ -1846,6 +1846,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
|
||||
else:
|
||||
stackTrace(c, tos, pc, "node has no type")
|
||||
else:
|
||||
# getTypeInstSkipAlias opcode:
|
||||
ensureKind(rkNode)
|
||||
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
|
||||
regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc], c.idgen, skipAlias = true)
|
||||
elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil:
|
||||
regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen, skipAlias = true)
|
||||
else:
|
||||
stackTrace(c, tos, pc, "node has no type")
|
||||
of opcNGetSize:
|
||||
decodeBImm(rkInt)
|
||||
let n = regs[rb].node
|
||||
@@ -2375,8 +2384,11 @@ proc evalConstExprAux(module: PSym; idgen: IdGenerator;
|
||||
setupGlobalCtx(module, g, idgen)
|
||||
var c = PCtx g.vm
|
||||
let oldMode = c.mode
|
||||
let oldLocals = c.locals
|
||||
c.mode = mode
|
||||
c.locals = initIntSet()
|
||||
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
|
||||
c.locals = oldLocals
|
||||
if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
|
||||
assert c.code[start].opcode != opcEof
|
||||
when debugEchoCode: c.echoCode start
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## This module contains the type definitions for the new evaluation engine.
|
||||
## An instruction is 1-3 int32s in memory, it is a register based VM.
|
||||
|
||||
import std/[tables, strutils]
|
||||
import std/[tables, strutils, intsets]
|
||||
|
||||
import ast, idents, options, modulegraphs, lineinfos
|
||||
|
||||
@@ -270,6 +270,7 @@ type
|
||||
templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst
|
||||
vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC)
|
||||
procToCodePos*: Table[int, int]
|
||||
locals*: IntSet
|
||||
|
||||
PStackFrame* = ref TStackFrame
|
||||
TStackFrame* {.acyclic.} = object
|
||||
|
||||
@@ -40,7 +40,7 @@ proc atomicTypeX(s: PSym; info: TLineInfo): PNode =
|
||||
result.info = info
|
||||
|
||||
proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator;
|
||||
inst=false; allowRecursionX=false): PNode
|
||||
inst=false; allowRecursionX=false; skipAlias = false): PNode
|
||||
|
||||
proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo;
|
||||
idgen: IdGenerator;
|
||||
@@ -68,7 +68,7 @@ proc objectNode(cache: IdentCache; n: PNode; idgen: IdGenerator): PNode =
|
||||
|
||||
proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
idgen: IdGenerator;
|
||||
inst=false; allowRecursionX=false): PNode =
|
||||
inst=false; allowRecursionX=false; skipAlias = false): PNode =
|
||||
var allowRecursion = allowRecursionX
|
||||
template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info, idgen)
|
||||
template atomicType(s): untyped = atomicTypeX(s, info)
|
||||
@@ -89,7 +89,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
id
|
||||
template newIdentDefs(s): untyped = newIdentDefs(s, s.typ)
|
||||
|
||||
if inst and not allowRecursion and t.sym != nil:
|
||||
if inst and not allowRecursion and t.sym != nil and
|
||||
not (skipAlias and t.kind == tyAlias):
|
||||
# getTypeInst behavior: return symbol
|
||||
return atomicType(t.sym)
|
||||
|
||||
@@ -122,7 +123,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
if t.base != nil:
|
||||
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
|
||||
result.add atomicType("typeDesc", mTypeDesc)
|
||||
result.add mapTypeToAst(t.base, info)
|
||||
result.add mapTypeToAstX(cache, t.base, info, idgen, inst, skipAlias = skipAlias)
|
||||
else:
|
||||
result = atomicType("typeDesc", mTypeDesc)
|
||||
of tyGenericInvocation:
|
||||
@@ -151,7 +152,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
else:
|
||||
result = mapTypeToAst(t.lastSon, info)
|
||||
of tyAlias:
|
||||
result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion)
|
||||
result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion, skipAlias = skipAlias)
|
||||
of tyOrdinal:
|
||||
result = mapTypeToAst(t.lastSon, info)
|
||||
of tyDistinct:
|
||||
@@ -316,8 +317,9 @@ proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGene
|
||||
|
||||
# the "Inst" version includes generic parameters in the resulting type tree
|
||||
# and also tries to look like the corresponding Nim type declaration
|
||||
proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode =
|
||||
result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=false)
|
||||
proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator; skipAlias = false): PNode =
|
||||
# skipAlias: skips aliases and typedesc
|
||||
result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=false, skipAlias = skipAlias)
|
||||
|
||||
# the "Impl" version includes generic parameters in the resulting type tree
|
||||
# and also tries to look like the corresponding Nim type implementation
|
||||
|
||||
@@ -698,6 +698,9 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
||||
let dest = c.genx(le, {gfNodeAddr})
|
||||
c.gABC(le, opcWrDeref, dest, 0, value)
|
||||
c.freeTemp(dest)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
if sameBackendType(le.typ, le[1].typ):
|
||||
genAsgnPatch(c, le[1], value)
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -866,7 +869,7 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||
genBinaryABC(c, n, dest, opc)
|
||||
c.genNarrow(n, dest)
|
||||
|
||||
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
|
||||
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest, flags: TGenFlags = {}; opc=opcConv) =
|
||||
let t2 = n.typ.skipTypes({tyDistinct})
|
||||
let targ2 = arg.typ.skipTypes({tyDistinct})
|
||||
|
||||
@@ -878,7 +881,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
|
||||
return true
|
||||
|
||||
if implicitConv():
|
||||
gen(c, arg, dest)
|
||||
gen(c, arg, dest, flags)
|
||||
return
|
||||
|
||||
let tmp = c.genx(arg)
|
||||
@@ -1040,7 +1043,7 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode =
|
||||
else:
|
||||
(if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex)
|
||||
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) =
|
||||
case m
|
||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||
of mOr: c.genAndOr(n, opcTJmp, dest)
|
||||
@@ -1179,7 +1182,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(size*8))
|
||||
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
|
||||
genConv(c, n, n[1], dest)
|
||||
genConv(c, n, n[1], dest, flags)
|
||||
of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
|
||||
of mEqCString: genBinaryABC(c, n, dest, opcEqCString)
|
||||
of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
|
||||
@@ -1321,7 +1324,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
of "getType": 0
|
||||
of "typeKind": 1
|
||||
of "getTypeInst": 2
|
||||
else: 3 # "getTypeImpl"
|
||||
of "getTypeImpl": 3 # "getTypeImpl"
|
||||
else: 4 # getTypeInstSkipAlias
|
||||
c.gABC(n, opcNGetType, dest, tmp, rc)
|
||||
c.freeTemp(tmp)
|
||||
#genUnaryABC(c, n, dest, opcNGetType)
|
||||
@@ -1556,6 +1560,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
|
||||
# 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
|
||||
elif s.kind in {skVar, skLet} and s.id in c.locals: discard
|
||||
else: cannotEval(c, n)
|
||||
elif s.kind in {skProc, skFunc, skConverter, skMethod,
|
||||
skIterator} and sfForward in s.flags:
|
||||
@@ -1645,6 +1650,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||
c.freeTemp(cc)
|
||||
else:
|
||||
gen(c, ri, dest)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
if sameBackendType(le.typ, le[1].typ):
|
||||
genAsgn(c, le[1], ri, requiresCopy)
|
||||
else:
|
||||
let dest = c.genx(le, {gfNodeAddr})
|
||||
genAsgn(c, dest, ri, requiresCopy)
|
||||
@@ -1942,7 +1950,7 @@ proc genVarSection(c: PCtx; n: PNode) =
|
||||
c.gen(lowerTupleUnpacking(c.graph, a, c.idgen, c.getOwner))
|
||||
elif a[0].kind == nkSym:
|
||||
let s = a[0].sym
|
||||
checkCanEval(c, a[0])
|
||||
c.locals.incl(s.id)
|
||||
if s.isGlobal:
|
||||
let runtimeAccessToCompileTime = c.mode == emRepl and
|
||||
sfCompileTime in s.flags and s.position > 0
|
||||
@@ -2007,7 +2015,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||
c.gABx(n, opcLdNull, dest, c.genType(n.typ))
|
||||
|
||||
let intType = getSysType(c.graph, n.info, tyInt)
|
||||
let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
|
||||
let seqType = n.typ.skipTypes(abstractVar+{tyStatic}-{tyTypeDesc})
|
||||
if seqType.kind == tySequence:
|
||||
var tmp = c.getTemp(intType)
|
||||
c.gABx(n, opcLdImmInt, tmp, n.len)
|
||||
@@ -2155,7 +2163,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
if n[0].kind == nkSym:
|
||||
let s = n[0].sym
|
||||
if s.magic != mNone:
|
||||
genMagic(c, n, dest, s.magic)
|
||||
genMagic(c, n, dest, flags, s.magic)
|
||||
elif s.kind == skMethod:
|
||||
localError(c.config, n.info, "cannot call method " & s.name.s &
|
||||
" at compile time")
|
||||
@@ -2212,11 +2220,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
unused(c, n, dest)
|
||||
gen(c, n[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
genConv(c, n, n[1], dest)
|
||||
genConv(c, n, n[1], dest, flags)
|
||||
of nkObjDownConv:
|
||||
genConv(c, n, n[0], dest)
|
||||
genConv(c, n, n[0], dest, flags)
|
||||
of nkObjUpConv:
|
||||
genConv(c, n, n[0], dest)
|
||||
genConv(c, n, n[0], dest, flags)
|
||||
of nkVarSection, nkLetSection:
|
||||
unused(c, n, dest)
|
||||
genVarSection(c, n)
|
||||
@@ -2249,7 +2257,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest)
|
||||
of nkCast:
|
||||
if allowCast in c.features:
|
||||
genConv(c, n, n[1], dest, opcCast)
|
||||
genConv(c, n, n[1], dest, flags, opcCast)
|
||||
else:
|
||||
genCastIntFloat(c, n, dest)
|
||||
of nkTypeOfExpr:
|
||||
|
||||
@@ -286,7 +286,7 @@ proc loadAny(p: var JsonParser, t: PType,
|
||||
|
||||
proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator): PNode =
|
||||
var tab = initTable[BiggestInt, PNode]()
|
||||
var p: JsonParser
|
||||
var p: JsonParser = default(JsonParser)
|
||||
open(p, newStringStream(s), "unknown file")
|
||||
next(p)
|
||||
result = loadAny(p, t, tab, cache, conf, idgen)
|
||||
|
||||
@@ -14,12 +14,12 @@ cc = gcc
|
||||
# additional options always passed to the compiler:
|
||||
--parallel_build: "0" # 0 to auto-detect number of processors
|
||||
|
||||
@if nimHasAmbiguousEnumHint:
|
||||
# not needed if hint is a style check
|
||||
hint[AmbiguousEnum]=off
|
||||
@end
|
||||
#hint[XDeclaredButNotUsed]=off
|
||||
|
||||
@if not nimHasNolineTooLong:
|
||||
hint[LineTooLong]=off
|
||||
@end
|
||||
|
||||
threads:on
|
||||
|
||||
# Examples of how to setup a cross-compiler:
|
||||
|
||||
@@ -2634,7 +2634,9 @@ of the argument.
|
||||
range.
|
||||
3. Generic match: `f` is a generic type and `a` matches, for
|
||||
instance `a` is `int` and `f` is a generic (constrained) parameter
|
||||
type (like in `[T]` or `[T: int|char]`).
|
||||
type (like in `[T]` or `[T: int|char]`). Constraints given an alias (as in `T`)
|
||||
shall be used to define `f`, when `f` is composed of `T`, following simple variable
|
||||
substitution.
|
||||
4. Subrange or subtype match: `a` is a `range[T]` and `T`
|
||||
matches `f` exactly. Or: `a` is a subtype of `f`.
|
||||
5. Integral conversion match: `a` is convertible to `f` and `f` and `a`
|
||||
@@ -2646,9 +2648,8 @@ of the argument.
|
||||
There are two major methods of selecting the best matching candidate, namely
|
||||
counting and disambiguation. Counting takes precedence to disambiguation. In counting,
|
||||
each parameter is given a category and the number of parameters in each category is counted.
|
||||
The categories are listed above and are in order of precedence. For example, if
|
||||
a candidate with one exact match is compared to a candidate with multiple generic matches
|
||||
and zero exact matches, the candidate with an exact match will win.
|
||||
For example, if a candidate with one exact match is compared to a candidate with multiple
|
||||
generic matches and zero exact matches, the candidate with an exact match will win.
|
||||
|
||||
In the following, `count(p, m)` counts the number of matches of the matching category `m`
|
||||
for the routine `p`.
|
||||
@@ -2668,10 +2669,12 @@ algorithm returns true:
|
||||
return "ambiguous"
|
||||
```
|
||||
|
||||
When counting is ambiguous, disambiguation begins. Parameters are iterated
|
||||
by position and these parameter pairs are compared for their type relation. The general goal
|
||||
of this comparison is to determine which parameter is more specific. The types considered are
|
||||
not of the inputs from the callsite, but of the competing candidates' parameters.
|
||||
When counting is ambiguous, disambiguation begins. Disambiguation also has two stages, first a
|
||||
hierarchical type relation comparison, and if that is inconclusive, a complexity comparison.
|
||||
Where counting relates the type of the operand to the formal parameter, disambiguation relates the
|
||||
formal parameters with each other to find the most competitive choice.
|
||||
Parameters are iterated by position and these parameter pairs are compared for their type
|
||||
relation. The general goal of this comparison is to determine which parameter is least general.
|
||||
|
||||
|
||||
Some examples:
|
||||
@@ -2691,7 +2694,6 @@ Some examples:
|
||||
```
|
||||
|
||||
|
||||
If this algorithm returns "ambiguous" further disambiguation is performed:
|
||||
If the argument `a` matches both the parameter type `f` of `p`
|
||||
and `g` of `q` via a subtyping relation, the inheritance depth is taken
|
||||
into account:
|
||||
@@ -2733,6 +2735,23 @@ matches) is preferred:
|
||||
gen(ri) # "ref T"
|
||||
```
|
||||
|
||||
Type variables match
|
||||
----------------------
|
||||
|
||||
When overload resolution is considering candidates, the type variable's definition
|
||||
is not overlooked as it is used to define the formal parameter's type via variable substitution.
|
||||
|
||||
For example:
|
||||
```nim
|
||||
type A
|
||||
proc p[T: A](param: T)
|
||||
proc p[T: object](param: T)
|
||||
```
|
||||
|
||||
These signatures are not ambiguous for an instance of `A` even though the formal parameters match ("T" == "T").
|
||||
Instead `T` is treated as a variable in that (`T` ?= `T`) depending on the bound type of `T` at the time of
|
||||
overload resolution.
|
||||
|
||||
|
||||
Overloading based on 'var T'
|
||||
--------------------------------------
|
||||
@@ -3726,9 +3745,6 @@ type conversions to unsigned integers and between unsigned integers. The
|
||||
rationale for this is mostly better interoperability with the C Programming
|
||||
language when algorithms are ported from C to Nim.
|
||||
|
||||
Exception: Values that are converted to an unsigned type at compile time
|
||||
are checked so that code like `byte(-1)` does not compile.
|
||||
|
||||
**Note**: Historically the operations
|
||||
were unchecked and the conversions were sometimes checked but starting with
|
||||
the revision 1.0.4 of this document and the language implementation the
|
||||
@@ -5067,7 +5083,7 @@ It is possible to raise/catch imported C++ exceptions. Types imported using
|
||||
`importcpp` can be raised or caught. Exceptions are raised by value and
|
||||
caught by reference. Example:
|
||||
|
||||
```nim test = "nim cpp -r $1"
|
||||
```nim
|
||||
type
|
||||
CStdException {.importcpp: "std::exception", header: "<exception>", inheritable.} = object
|
||||
## does not inherit from `RootObj`, so we use `inheritable` instead
|
||||
@@ -5080,22 +5096,22 @@ caught by reference. Example:
|
||||
proc fn() =
|
||||
let a = initRuntimeError("foo")
|
||||
doAssert $a.what == "foo"
|
||||
var b: cstring
|
||||
var b = ""
|
||||
try: raise initRuntimeError("foo2")
|
||||
except CStdException as e:
|
||||
doAssert e is CStdException
|
||||
b = e.what()
|
||||
doAssert $b == "foo2"
|
||||
b = $e.what()
|
||||
doAssert b == "foo2"
|
||||
|
||||
try: raise initStdException()
|
||||
except CStdException: discard
|
||||
|
||||
try: raise initRuntimeError("foo3")
|
||||
except CRuntimeError as e:
|
||||
b = e.what()
|
||||
b = $e.what()
|
||||
except CStdException:
|
||||
doAssert false
|
||||
doAssert $b == "foo3"
|
||||
doAssert b == "foo3"
|
||||
|
||||
fn()
|
||||
```
|
||||
@@ -5425,6 +5441,7 @@ Generics are Nim's means to parametrize procs, iterators or types with
|
||||
`type parameters`:idx:. Depending on the context, the brackets are used either to
|
||||
introduce type parameters or to instantiate a generic proc, iterator, or type.
|
||||
|
||||
|
||||
The following example shows how a generic binary tree can be modeled:
|
||||
|
||||
```nim test = "nim c $1"
|
||||
@@ -6154,9 +6171,12 @@ scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas:
|
||||
`gensym`'ed symbols are not exposed but `inject`'ed symbols are.
|
||||
|
||||
The default for symbols of entity `type`, `var`, `let` and `const`
|
||||
is `gensym` and for `proc`, `iterator`, `converter`, `template`,
|
||||
`macro` is `inject`. However, if the name of the entity is passed as a
|
||||
template parameter, it is an `inject`'ed symbol:
|
||||
is `gensym`. For `proc`, `iterator`, `converter`, `template`,
|
||||
`macro`, the default is `inject`, but if a `gensym` symbol with the same name
|
||||
is defined in the same syntax-level scope, it will be `gensym` by default.
|
||||
This can be overriden by marking the routine as `inject`.
|
||||
|
||||
If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol:
|
||||
|
||||
```nim
|
||||
template withFile(f, fn, mode: untyped, actions: untyped): untyped =
|
||||
|
||||
@@ -2324,3 +2324,97 @@ proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} =
|
||||
In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropiate constructor.
|
||||
|
||||
Notice when calling a constructor in the section of a global variable initialization, it will be called before `NimMain` meaning Nim is not fully initialized.
|
||||
|
||||
Injected symbols in generic procs and templates
|
||||
===============================================
|
||||
|
||||
With the experimental option `openSym`, captured symbols in generic routine and
|
||||
template bodies may be replaced by symbols injected locally by templates/macros
|
||||
at instantiation time. `bind` may be used to keep the captured symbols over the
|
||||
injected ones regardless of enabling the options, but other methods like
|
||||
renaming the captured symbols should be used instead so that the code is not
|
||||
affected by context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert barTempl() == "injected" # previously it would be "captured"
|
||||
|
||||
template bazTempl(): string =
|
||||
bind value
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert bazTempl() == "captured"
|
||||
```
|
||||
|
||||
This option also generates a new node kind `nnkOpenSym` which contains
|
||||
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
|
||||
modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
Another experimental switch `genericsOpenSym` exists that enables this behavior
|
||||
at instantiation time, meaning templates etc can enable it specifically when
|
||||
they are being called. However this does not generate `nnkOpenSym` nodes
|
||||
(unless the other switch is enabled) and so doesn't reflect the regular
|
||||
behavior of the switch.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
echo bar[int]() # "injected"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
var res: string
|
||||
foo(123):
|
||||
res = value
|
||||
res
|
||||
assert barTempl() == "injected"
|
||||
```
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ where code size matters and you know that your code does not produce cycles, you
|
||||
use `--mm:arc`. Notice that the default `async`:idx: implementation produces cycles
|
||||
and leaks memory with `--mm:arc`, in other words, for `async` you need to use `--mm:orc`.
|
||||
|
||||
Only ARC/ORC support move semantics, destructors, `sink`, `cursor`, `acyclic`.
|
||||
|
||||
|
||||
Other MM modes
|
||||
|
||||
36
koch.nim
36
koch.nim
@@ -1,23 +1,23 @@
|
||||
#
|
||||
#
|
||||
# Maintenance program for Nim
|
||||
# (c) Copyright 2017 Andreas Rumpf
|
||||
# (c) Copyright 2024 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
# See doc/koch.txt for documentation.
|
||||
# See doc/koch.md for documentation.
|
||||
#
|
||||
|
||||
const
|
||||
# examples of possible values for repos: Head, ea82b54
|
||||
NimbleStableCommit = "f8bd7b5fa6ea7a583b411b5959b06e6b5eb23667" # master
|
||||
AtlasStableCommit = "7b780811a168f3f32bff4822369dda46a7f87f9a"
|
||||
ChecksumsStableCommit = "b4c73320253f78e3a265aec6d9e8feb83f97c77b"
|
||||
NimbleStableCommit = "6a2486b597132340ea7422b078c769b58f21d16d" # 0.20.0
|
||||
AtlasStableCommit = "5faec3e9a33afe99a7d22377dd1b45a5391f5504"
|
||||
ChecksumsStableCommit = "f8f6bd34bfa3fe12c64b919059ad856a96efcba0" # 2.0.1
|
||||
SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
|
||||
|
||||
# examples of possible values for fusion: #head, #ea82b54, 1.2.3
|
||||
FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014"
|
||||
FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734"
|
||||
HeadHash = "#head"
|
||||
when not defined(windows):
|
||||
const
|
||||
@@ -52,7 +52,7 @@ const
|
||||
+-----------------------------------------------------------------+
|
||||
| Maintenance program for Nim |
|
||||
| Version $1|
|
||||
| (c) 2017 Andreas Rumpf |
|
||||
| (c) 2024 Andreas Rumpf |
|
||||
+-----------------------------------------------------------------+
|
||||
Build time: $2, $3
|
||||
|
||||
@@ -77,6 +77,7 @@ Possible Commands:
|
||||
doesn't require network connectivity
|
||||
nimble builds the Nimble tool
|
||||
atlas builds the Atlas tool
|
||||
checksums installs the checksums dependency
|
||||
fusion installs fusion via Nimble
|
||||
|
||||
Boot options:
|
||||
@@ -155,21 +156,22 @@ proc bundleNimbleExe(latest: bool, args: string) =
|
||||
let commit = if latest: "HEAD" else: NimbleStableCommit
|
||||
cloneDependency(distDir, "https://github.com/nim-lang/nimble.git",
|
||||
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
|
||||
updateSubmodules(distDir / "nimble", allowBundled = true)
|
||||
nimCompile("dist/nimble/src/nimble.nim",
|
||||
options = "-d:release -d:nimNimbleBootstrap --mm:refc --noNimblePath " & args)
|
||||
options = "-d:release --mm:refc --noNimblePath " & args)
|
||||
const zippyTests = "dist/nimble/vendor/zippy/tests"
|
||||
if dirExists(zippyTests):
|
||||
removeDir(zippyTests)
|
||||
|
||||
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",
|
||||
@@ -323,8 +325,7 @@ proc boot(args: string, skipIntegrityCheck: bool) =
|
||||
let smartNimcache = (if "release" in args or "danger" in args: "nimcache/r_" else: "nimcache/d_") &
|
||||
hostOS & "_" & hostCPU
|
||||
|
||||
if not dirExists("dist/checksums"):
|
||||
bundleChecksums(false)
|
||||
bundleChecksums(false)
|
||||
|
||||
let nimStart = findStartNim().quoteShell()
|
||||
let times = 2 - ord(skipIntegrityCheck)
|
||||
@@ -479,8 +480,7 @@ proc temp(args: string) =
|
||||
result[1].add " " & quoteShell(args[i])
|
||||
inc i
|
||||
|
||||
if not dirExists("dist/checksums"):
|
||||
bundleChecksums(false)
|
||||
bundleChecksums(false)
|
||||
|
||||
let d = getAppDir()
|
||||
let output = d / "compiler" / "nim".exe
|
||||
|
||||
@@ -93,6 +93,8 @@ type
|
||||
nnkFuncDef,
|
||||
nnkTupleConstr,
|
||||
nnkError, ## erroneous AST node
|
||||
nnkModuleRef, nnkReplayAction, nnkNilRodNode ## internal IC nodes
|
||||
nnkOpenSym
|
||||
|
||||
NimNodeKinds* = set[NimNodeKind]
|
||||
NimTypeKind* = enum # some types are no longer used, see ast.nim
|
||||
@@ -1407,7 +1409,7 @@ proc `$`*(node: NimNode): string =
|
||||
result = node.basename.strVal & "*"
|
||||
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent:
|
||||
result = node.strVal
|
||||
of nnkOpenSymChoice, nnkClosedSymChoice:
|
||||
of nnkOpenSymChoice, nnkClosedSymChoice, nnkOpenSym:
|
||||
result = $node[0]
|
||||
of nnkAccQuoted:
|
||||
result = ""
|
||||
@@ -1572,10 +1574,13 @@ proc extractTypeImpl(n: NimNode): NimNode =
|
||||
result = n[2]
|
||||
else: error("Invalid node to retrieve type implementation of: " & $n.kind)
|
||||
|
||||
|
||||
proc getTypeInstSkipAlias(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
|
||||
|
||||
proc customPragmaNode(n: NimNode): NimNode =
|
||||
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr})
|
||||
let
|
||||
typ = n.getTypeInst()
|
||||
|
||||
let typ = n.getTypeInstSkipAlias()
|
||||
|
||||
if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
|
||||
return typ[1][1]
|
||||
|
||||
@@ -14,6 +14,7 @@ __GNUC__
|
||||
__TINYC__
|
||||
__clang__
|
||||
__AVR__
|
||||
__arm__
|
||||
*/
|
||||
|
||||
|
||||
@@ -573,9 +574,16 @@ NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "P
|
||||
#define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
|
||||
|
||||
#if NIM_INTBITS == 32
|
||||
#define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
|
||||
#define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
|
||||
#define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
|
||||
#if defined(__arm__) && defined(__GNUC__)
|
||||
/* arm-none-eabi-gcc targets defines int32_t as long int */
|
||||
#define nimAddInt(a, b, res) __builtin_saddl_overflow(a, b, res)
|
||||
#define nimSubInt(a, b, res) __builtin_ssubl_overflow(a, b, res)
|
||||
#define nimMulInt(a, b, res) __builtin_smull_overflow(a, b, res)
|
||||
#else
|
||||
#define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
|
||||
#define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
|
||||
#define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
|
||||
#endif
|
||||
#else
|
||||
/* map it to the 'long long' variant */
|
||||
#define nimAddInt(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
|
||||
|
||||
@@ -215,6 +215,11 @@ when defined(osx): # 2001 POSIX evidently does not concern Apple
|
||||
# present size & has no good reason to call this unless it is growing.
|
||||
if fcntl(a1, F_PREALLOCATE, fst.addr) != cint(-1): ftruncate(a1, a2 + a3)
|
||||
else: cint(-1)
|
||||
elif defined(openbsd):
|
||||
proc posix_fallocate*(a1: cint, a2, a3: Off): cint =
|
||||
# above assumption: "has no good reason to call this unless it is growing."
|
||||
# man ftruncate "it will be extended as if by writing bytes with the value zero."
|
||||
return ftruncate(a1, a2 + a3)
|
||||
else:
|
||||
proc posix_fallocate*(a1: cint, a2, a3: Off): cint {.
|
||||
importc, header: "<fcntl.h>".}
|
||||
@@ -503,7 +508,7 @@ proc pthread_spin_unlock*(a1: ptr Pthread_spinlock): cint {.
|
||||
proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
|
||||
|
||||
|
||||
proc exitnow*(code: int) {.importc: "_exit", header: "<unistd.h>".}
|
||||
proc exitnow*(code: cint) {.importc: "_exit", header: "<unistd.h>", noreturn.}
|
||||
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
|
||||
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
|
||||
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
|
||||
@@ -1095,7 +1100,21 @@ proc setprotoent*(a1: cint) {.importc, header: "<netdb.h>".}
|
||||
proc setservent*(a1: cint) {.importc, header: "<netdb.h>".}
|
||||
|
||||
when not defined(lwip):
|
||||
proc poll*(a1: ptr TPollfd, a2: Tnfds, a3: int): cint {.
|
||||
# Linux and Haiku emulate SVR4, which used unsigned long.
|
||||
# Meanwhile, BSD derivatives had used unsigned int; we will use this
|
||||
# for the else case, because it is more widely cloned than SVR4's
|
||||
# behavior.
|
||||
when defined(linux) or defined(haiku):
|
||||
type
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
|
||||
elif defined(zephyr):
|
||||
type
|
||||
Tnfds* = distinct cint
|
||||
else:
|
||||
type
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = cuint
|
||||
|
||||
proc poll*(a1: ptr TPollfd, a2: Tnfds, a3: cint): cint {.
|
||||
importc, header: "<poll.h>", sideEffect.}
|
||||
|
||||
proc realpath*(name, resolved: cstring): cstring {.
|
||||
|
||||
@@ -519,8 +519,6 @@ type
|
||||
events*: cshort ## The input event flags (see below).
|
||||
revents*: cshort ## The output event flags (see below).
|
||||
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
|
||||
|
||||
var
|
||||
errno* {.importc, header: "<errno.h>".}: cint ## error variable
|
||||
h_errno* {.importc, header: "<netdb.h>".}: cint
|
||||
|
||||
@@ -563,8 +563,6 @@ type
|
||||
events*: cshort ## The input event flags (see below).
|
||||
revents*: cshort ## The output event flags (see below).
|
||||
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
|
||||
|
||||
var
|
||||
errno* {.importc, header: "<errno.h>".}: cint ## error variable
|
||||
h_errno* {.importc, header: "<netdb.h>".}: cint
|
||||
|
||||
@@ -125,7 +125,7 @@ type
|
||||
Dev* {.importc: "dev_t", header: "<sys/types.h>".} = int32
|
||||
Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = int
|
||||
Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = int
|
||||
Gid* {.importc: "gid_t", header: "<sys/types.h>".} = int32
|
||||
Gid* {.importc: "gid_t", header: "<sys/types.h>".} = uint32
|
||||
Id* {.importc: "id_t", header: "<sys/types.h>".} = int
|
||||
Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int
|
||||
Key* {.importc: "key_t", header: "<sys/types.h>".} = int
|
||||
@@ -167,7 +167,7 @@ type
|
||||
Trace_event_set* {.importc: "trace_event_set_t",
|
||||
header: "<sys/types.h>".} = int
|
||||
Trace_id* {.importc: "trace_id_t", header: "<sys/types.h>".} = int
|
||||
Uid* {.importc: "uid_t", header: "<sys/types.h>".} = int32
|
||||
Uid* {.importc: "uid_t", header: "<sys/types.h>".} = uint32
|
||||
Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = int
|
||||
|
||||
Utsname* {.importc: "struct utsname",
|
||||
@@ -462,9 +462,9 @@ type
|
||||
header: "<netinet/in.h>".} = object ## struct sockaddr_in6
|
||||
sin6_family*: TSa_Family ## AF_INET6.
|
||||
sin6_port*: InPort ## Port number.
|
||||
sin6_flowinfo*: int32 ## IPv6 traffic class and flow information.
|
||||
sin6_flowinfo*: uint32 ## IPv6 traffic class and flow information.
|
||||
sin6_addr*: In6Addr ## IPv6 address.
|
||||
sin6_scope_id*: int32 ## Set of interfaces for a scope.
|
||||
sin6_scope_id*: uint32 ## Set of interfaces for a scope.
|
||||
|
||||
Tipv6_mreq* {.importc: "struct ipv6_mreq", pure, final,
|
||||
header: "<netinet/in.h>".} = object ## struct ipv6_mreq
|
||||
@@ -491,7 +491,7 @@ type
|
||||
## alternative network names, terminated by a
|
||||
## null pointer.
|
||||
n_addrtype*: cint ## The address type of the network.
|
||||
n_net*: int32 ## The network number, in host byte order.
|
||||
n_net*: uint32 ## The network number, in host byte order.
|
||||
|
||||
Protoent* {.importc: "struct protoent", pure, final,
|
||||
header: "<netdb.h>".} = object ## struct protoent
|
||||
@@ -529,8 +529,6 @@ type
|
||||
events*: cshort ## The input event flags (see below).
|
||||
revents*: cshort ## The output event flags (see below).
|
||||
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = cint
|
||||
|
||||
var
|
||||
errno* {.importc, header: "<errno.h>".}: cint ## error variable
|
||||
h_errno* {.importc, header: "<netdb.h>".}: cint
|
||||
|
||||
@@ -484,8 +484,6 @@ type
|
||||
events*: cshort ## The input event flags (see below).
|
||||
revents*: cshort ## The output event flags (see below).
|
||||
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
|
||||
|
||||
var
|
||||
errno* {.importc, header: "<errno.h>".}: cint ## error variable
|
||||
h_errno* {.importc, header: "<netdb.h>".}: cint
|
||||
|
||||
@@ -134,7 +134,7 @@ type
|
||||
Dev* {.importc: "dev_t", header: "<sys/types.h>".} = int32
|
||||
Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = int
|
||||
Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = int
|
||||
Gid* {.importc: "gid_t", header: "<sys/types.h>".} = int32
|
||||
Gid* {.importc: "gid_t", header: "<sys/types.h>".} = uint32
|
||||
Id* {.importc: "id_t", header: "<sys/types.h>".} = int
|
||||
Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int
|
||||
Key* {.importc: "key_t", header: "<sys/types.h>".} = int
|
||||
@@ -171,7 +171,7 @@ type
|
||||
Trace_event_set* {.importc: "trace_event_set_t",
|
||||
header: "<sys/types.h>".} = int
|
||||
Trace_id* {.importc: "trace_id_t", header: "<sys/types.h>".} = int
|
||||
Uid* {.importc: "uid_t", header: "<sys/types.h>".} = int32
|
||||
Uid* {.importc: "uid_t", header: "<sys/types.h>".} = uint32
|
||||
Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = int
|
||||
|
||||
Utsname* {.importc: "struct utsname",
|
||||
@@ -446,9 +446,9 @@ type
|
||||
header: "<netinet/in.h>".} = object ## struct sockaddr_in6
|
||||
sin6_family*: TSa_Family ## AF_INET6.
|
||||
sin6_port*: InPort ## Port number.
|
||||
sin6_flowinfo*: int32 ## IPv6 traffic class and flow information.
|
||||
sin6_flowinfo*: uint32 ## IPv6 traffic class and flow information.
|
||||
sin6_addr*: In6Addr ## IPv6 address.
|
||||
sin6_scope_id*: int32 ## Set of interfaces for a scope.
|
||||
sin6_scope_id*: uint32 ## Set of interfaces for a scope.
|
||||
|
||||
Tipv6_mreq* {.importc: "struct ipv6_mreq", pure, final,
|
||||
header: "<netinet/in.h>".} = object ## struct ipv6_mreq
|
||||
@@ -475,7 +475,7 @@ type
|
||||
## alternative network names, terminated by a
|
||||
## null pointer.
|
||||
n_addrtype*: cint ## The address type of the network.
|
||||
n_net*: int32 ## The network number, in host byte order.
|
||||
n_net*: uint32 ## The network number, in host byte order.
|
||||
|
||||
Protoent* {.importc: "struct protoent", pure, final,
|
||||
header: "<netdb.h>".} = object ## struct protoent
|
||||
@@ -513,8 +513,6 @@ type
|
||||
events*: cshort ## The input event flags (see below).
|
||||
revents*: cshort ## The output event flags (see below).
|
||||
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = cint
|
||||
|
||||
var
|
||||
errno* {.importc, header: "<errno.h>".}: cint ## error variable
|
||||
h_errno* {.importc, header: "<netdb.h>".}: cint
|
||||
|
||||
@@ -604,13 +604,6 @@ when not defined(lwip):
|
||||
events*: cshort ## The input event flags (see below).
|
||||
revents*: cshort ## The output event flags (see below).
|
||||
|
||||
when defined(zephyr):
|
||||
type
|
||||
Tnfds* = distinct culong
|
||||
else:
|
||||
type
|
||||
Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
|
||||
|
||||
var
|
||||
errno* {.importc, header: "<errno.h>".}: cint ## error variable
|
||||
h_errno* {.importc, header: "<netdb.h>".}: cint
|
||||
|
||||
@@ -1414,7 +1414,7 @@ proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||
doAssert a.hasKey('a') == true
|
||||
doAssert a.hasKey('z') == false
|
||||
|
||||
var hc: Hash
|
||||
var hc: Hash = default(Hash)
|
||||
result = rawGet(t, key, hc) >= 0
|
||||
|
||||
proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||
|
||||
@@ -50,8 +50,7 @@ runnableExamples:
|
||||
flag.clear(moRelaxed)
|
||||
assert not flag.testAndSet
|
||||
|
||||
|
||||
when defined(cpp) or defined(nimdoc):
|
||||
when (defined(cpp) and not defined(nimUseCAtomics)) or defined(nimdoc):
|
||||
# For the C++ backend, types and operations map directly to C++11 atomics.
|
||||
|
||||
{.push, header: "<atomic>".}
|
||||
@@ -274,10 +273,17 @@ else:
|
||||
cast[T](interlockedXor(addr(location.value), cast[nonAtomicType(T)](value)))
|
||||
|
||||
else:
|
||||
{.push, header: "<stdatomic.h>".}
|
||||
when defined(cpp):
|
||||
{.push, header: "<atomic>".}
|
||||
template maybeWrapStd(x: string): string =
|
||||
"std::" & x
|
||||
else:
|
||||
{.push, header: "<stdatomic.h>".}
|
||||
template maybeWrapStd(x: string): string =
|
||||
x
|
||||
|
||||
type
|
||||
MemoryOrder* {.importc: "memory_order".} = enum
|
||||
MemoryOrder* {.importc: "memory_order".maybeWrapStd.} = enum
|
||||
moRelaxed
|
||||
moConsume
|
||||
moAcquire
|
||||
@@ -285,16 +291,25 @@ else:
|
||||
moAcquireRelease
|
||||
moSequentiallyConsistent
|
||||
|
||||
type
|
||||
# Atomic*[T] {.importcpp: "_Atomic('0)".} = object
|
||||
when defined(cpp):
|
||||
type
|
||||
# Atomic*[T] {.importcpp: "_Atomic('0)".} = object
|
||||
|
||||
AtomicInt8 {.importc: "_Atomic NI8".} = int8
|
||||
AtomicInt16 {.importc: "_Atomic NI16".} = int16
|
||||
AtomicInt32 {.importc: "_Atomic NI32".} = int32
|
||||
AtomicInt64 {.importc: "_Atomic NI64".} = int64
|
||||
AtomicInt8 {.importc: "std::atomic<NI8>".} = int8
|
||||
AtomicInt16 {.importc: "std::atomic<NI16>".} = int16
|
||||
AtomicInt32 {.importc: "std::atomic<NI32>".} = int32
|
||||
AtomicInt64 {.importc: "std::atomic<NI64>".} = int64
|
||||
else:
|
||||
type
|
||||
# Atomic*[T] {.importcpp: "_Atomic('0)".} = object
|
||||
|
||||
AtomicInt8 {.importc: "_Atomic NI8".} = int8
|
||||
AtomicInt16 {.importc: "_Atomic NI16".} = int16
|
||||
AtomicInt32 {.importc: "_Atomic NI32".} = int32
|
||||
AtomicInt64 {.importc: "_Atomic NI64".} = int64
|
||||
|
||||
type
|
||||
AtomicFlag* {.importc: "atomic_flag", size: 1.} = object
|
||||
AtomicFlag* {.importc: "atomic_flag".maybeWrapStd, size: 1.} = object
|
||||
|
||||
Atomic*[T] = object
|
||||
when T is Trivial:
|
||||
@@ -308,27 +323,27 @@ else:
|
||||
guard: AtomicFlag
|
||||
|
||||
#proc init*[T](location: var Atomic[T]; value: T): T {.importcpp: "atomic_init(@)".}
|
||||
proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc.}
|
||||
proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc.}
|
||||
proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
|
||||
proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
|
||||
proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc: "atomic_load_explicit".maybeWrapStd.}
|
||||
proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_store_explicit".maybeWrapStd.}
|
||||
proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_exchange_explicit".maybeWrapStd.}
|
||||
proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_strong_explicit".maybeWrapStd.}
|
||||
proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_weak_explicit".maybeWrapStd.}
|
||||
|
||||
# Numerical operations
|
||||
proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_add_explicit".maybeWrapStd.}
|
||||
proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_sub_explicit".maybeWrapStd.}
|
||||
proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_and_explicit".maybeWrapStd.}
|
||||
proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_or_explicit".maybeWrapStd.}
|
||||
proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_xor_explicit".maybeWrapStd.}
|
||||
|
||||
# Flag operations
|
||||
# var ATOMIC_FLAG_INIT {.importc, nodecl.}: AtomicFlag
|
||||
# proc init*(location: var AtomicFlag) {.inline.} = location = ATOMIC_FLAG_INIT
|
||||
proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".}
|
||||
proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".}
|
||||
proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".maybeWrapStd.}
|
||||
proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".maybeWrapStd.}
|
||||
|
||||
proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".}
|
||||
proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".}
|
||||
proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".maybeWrapStd.}
|
||||
proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".maybeWrapStd.}
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
## The types, vars and procs are bindings for the C standard library
|
||||
## [<fenv.h>](https://en.cppreference.com/w/c/numeric/fenv) header.
|
||||
|
||||
when defined(posix) and not defined(genode):
|
||||
when defined(posix) and not defined(genode) and not defined(macosx):
|
||||
{.passl: "-lm".}
|
||||
|
||||
var
|
||||
|
||||
@@ -231,7 +231,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
|
||||
verifySelectParams(timeout)
|
||||
|
||||
s.withPollLock():
|
||||
let count = posix.poll(addr(s.pollfds[0]), Tnfds(s.pollcnt), timeout)
|
||||
let count = posix.poll(addr(s.pollfds[0]), Tnfds(s.pollcnt), cint(timeout))
|
||||
if count < 0:
|
||||
result = 0
|
||||
let err = osLastError()
|
||||
|
||||
@@ -155,7 +155,7 @@ func fac*(n: int): int =
|
||||
|
||||
{.push checks: off, line_dir: off, stack_trace: off.}
|
||||
|
||||
when defined(posix) and not defined(genode):
|
||||
when defined(posix) and not defined(genode) and not defined(macosx):
|
||||
{.passl: "-lm".}
|
||||
|
||||
const
|
||||
|
||||
@@ -100,32 +100,19 @@ proc decode(dest: var openArray[uint8], src: openArray[uint32]) =
|
||||
dest[i+3] = uint8(src[j] shr 24 and 0xff'u32)
|
||||
inc(i, 4)
|
||||
|
||||
template slice(s: string, a, b): openArray[uint8] =
|
||||
when nimvm:
|
||||
# toOpenArray is not implemented in VM
|
||||
var s2 = newSeq[uint8](s.len)
|
||||
for i in 0 ..< s2.len:
|
||||
s2[i] = uint8(s[i])
|
||||
s2
|
||||
else:
|
||||
s.toOpenArrayByte(a, b)
|
||||
|
||||
template slice(s: cstring, a, b): openArray[uint8] =
|
||||
when nimvm:
|
||||
# toOpenArray is not implemented in VM
|
||||
slice($s, a, b)
|
||||
toOpenArrayByte($s, a, b)
|
||||
else:
|
||||
when defined(js):
|
||||
# toOpenArrayByte for cstring is not implemented in JS
|
||||
slice($s, a, b)
|
||||
toOpenArrayByte($s, a, b)
|
||||
else:
|
||||
s.toOpenArrayByte(a, b)
|
||||
|
||||
template slice(s: openArray[uint8], a, b): openArray[uint8] =
|
||||
when nimvm:
|
||||
s[a .. b]
|
||||
else:
|
||||
s.toOpenArray(a, b)
|
||||
s.toOpenArray(a, b)
|
||||
|
||||
const useMem = declared(copyMem)
|
||||
|
||||
|
||||
@@ -35,12 +35,12 @@ proc newEIO(msg: string): ref IOError =
|
||||
new(result)
|
||||
result.msg = msg
|
||||
|
||||
proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode =
|
||||
## Set the size of open file pointed to by `fh` to `newFileSize` if != -1.
|
||||
## Space is only allocated if that is cheaper than writing to the file. This
|
||||
## routine returns the last OSErrorCode found rather than raising to support
|
||||
## old rollback/clean-up code style. [ Should maybe move to std/osfiles. ]
|
||||
if newFileSize == -1:
|
||||
proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode =
|
||||
## Set the size of open file pointed to by `fh` to `newFileSize` if != -1,
|
||||
## allocating | freeing space from the file system. This routine returns the
|
||||
## last OSErrorCode found rather than raising to support old rollback/clean-up
|
||||
## code style. [ Should maybe move to std/osfiles. ]
|
||||
if newFileSize < 0 or newFileSize == oldSize:
|
||||
return
|
||||
when defined(windows):
|
||||
var sizeHigh = int32(newFileSize shr 32)
|
||||
@@ -51,14 +51,18 @@ proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode =
|
||||
setEndOfFile(fh) == 0:
|
||||
result = lastErr
|
||||
else:
|
||||
var e: cint # posix_fallocate truncates up when needed.
|
||||
when declared(posix_fallocate):
|
||||
while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
|
||||
discard
|
||||
if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1:
|
||||
result = osLastError() # fallback arguable; Most portable, but allows SEGV
|
||||
elif e != 0:
|
||||
result = osLastError()
|
||||
if newFileSize > oldSize: # grow the file
|
||||
var e: cint # posix_fallocate truncates up when needed.
|
||||
when declared(posix_fallocate):
|
||||
while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
|
||||
discard
|
||||
if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1:
|
||||
result = osLastError() # fallback arguable; Most portable BUT allows SEGV
|
||||
elif e != 0:
|
||||
result = osLastError()
|
||||
else: # shrink the file
|
||||
if ftruncate(fh.cint, newFileSize) == -1:
|
||||
result = osLastError()
|
||||
|
||||
type
|
||||
MemFile* = object ## represents a memory mapped file
|
||||
@@ -254,41 +258,31 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
flags = flags or O_CREAT or O_TRUNC
|
||||
var permissionsMode = S_IRUSR or S_IWUSR
|
||||
result.handle = open(filename, flags, permissionsMode)
|
||||
if result.handle != -1:
|
||||
if (let e = setFileSize(result.handle.FileHandle, newFileSize);
|
||||
e != 0.OSErrorCode): fail(e, "error setting file size")
|
||||
else:
|
||||
result.handle = open(filename, flags)
|
||||
|
||||
if result.handle == -1:
|
||||
# XXX: errno is supposed to be set here
|
||||
# Is there an exception that wraps it?
|
||||
fail(osLastError(), "error opening file")
|
||||
|
||||
if (let e = setFileSize(result.handle.FileHandle, newFileSize);
|
||||
e != 0.OSErrorCode): fail(e, "error setting file size")
|
||||
|
||||
if mappedSize != -1:
|
||||
result.size = mappedSize
|
||||
else:
|
||||
var stat: Stat
|
||||
if mappedSize != -1: #XXX Logic here differs from `when windows` branch ..
|
||||
result.size = mappedSize #.. which always fstats&Uses min(mappedSize, st).
|
||||
else: # if newFileSize!=-1: result.size=newFileSize # if trust setFileSize
|
||||
var stat: Stat #^^.. BUT some FSes (eg. Linux HugeTLBfs) round to 2MiB.
|
||||
if fstat(result.handle, stat) != -1:
|
||||
# XXX: Hmm, this could be unsafe
|
||||
# Why is mmap taking int anyway?
|
||||
result.size = int(stat.st_size)
|
||||
result.size = stat.st_size.int # int may be 32-bit-unsafe for 2..<4 GiB
|
||||
else:
|
||||
fail(osLastError(), "error getting file size")
|
||||
|
||||
result.flags = if mapFlags == cint(-1): MAP_SHARED else: mapFlags
|
||||
#Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set
|
||||
# Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set
|
||||
if int(result.flags and MAP_PRIVATE) == 0:
|
||||
result.flags = result.flags or MAP_SHARED
|
||||
|
||||
result.mem = mmap(
|
||||
nil,
|
||||
result.size,
|
||||
if readonly: PROT_READ else: PROT_READ or PROT_WRITE,
|
||||
result.flags,
|
||||
result.handle,
|
||||
offset)
|
||||
|
||||
let pr = if readonly: PROT_READ else: PROT_READ or PROT_WRITE
|
||||
result.mem = mmap(nil, result.size, pr, result.flags, result.handle, offset)
|
||||
if result.mem == cast[pointer](MAP_FAILED):
|
||||
fail(osLastError(), "file mapping failed")
|
||||
|
||||
@@ -352,7 +346,7 @@ proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} =
|
||||
raise newException(IOError,
|
||||
"Cannot resize MemFile opened with allowRemap=false")
|
||||
if newFileSize != f.size:
|
||||
if (let e = setFileSize(f.handle.FileHandle, newFileSize);
|
||||
if (let e = setFileSize(f.handle.FileHandle, newFileSize, f.size);
|
||||
e != 0.OSErrorCode): raiseOSError(e)
|
||||
when defined(linux): #Maybe NetBSD, too?
|
||||
# On Linux this can be over 100 times faster than a munmap,mmap cycle.
|
||||
|
||||
@@ -1031,6 +1031,8 @@ const mimes* = {
|
||||
"movie": "video/x-sgi-movie",
|
||||
"smv": "video/x-smv",
|
||||
"ice": "x-conference/x-cooltalk",
|
||||
"avif": "image/avif",
|
||||
"avifs": "image/avif"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ when defined(nimHasStyleChecks):
|
||||
when defined(posix) and not defined(lwip):
|
||||
from posix import TPollfd, POLLIN, POLLPRI, POLLOUT, POLLWRBAND, Tnfds
|
||||
|
||||
template monitorPollEvent(x: var SocketHandle, y: cint, timeout: int): int =
|
||||
template monitorPollEvent(x: var SocketHandle, y, timeout: cint): int =
|
||||
var tpollfd: TPollfd
|
||||
tpollfd.fd = cast[cint](x)
|
||||
tpollfd.events = y
|
||||
@@ -222,14 +222,14 @@ proc timeoutRead(fd: var SocketHandle, timeout = 500): int =
|
||||
var fds = @[fd]
|
||||
selectRead(fds, timeout)
|
||||
else:
|
||||
monitorPollEvent(fd, POLLIN or POLLPRI, timeout)
|
||||
monitorPollEvent(fd, POLLIN or POLLPRI, cint(timeout))
|
||||
|
||||
proc timeoutWrite(fd: var SocketHandle, timeout = 500): int =
|
||||
when defined(windows) or defined(lwip):
|
||||
var fds = @[fd]
|
||||
selectWrite(fds, timeout)
|
||||
else:
|
||||
monitorPollEvent(fd, POLLOUT or POLLWRBAND, timeout)
|
||||
monitorPollEvent(fd, POLLOUT or POLLWRBAND, cint(timeout))
|
||||
|
||||
proc socketError*(socket: Socket, err: int = -1, async = false,
|
||||
lastError = (-1).OSErrorCode,
|
||||
@@ -2040,8 +2040,10 @@ proc dial*(address: string, port: Port,
|
||||
if success:
|
||||
result = newSocket(lastFd, domain, sockType, protocol, buffered)
|
||||
elif lastError != 0.OSErrorCode:
|
||||
lastFd.close()
|
||||
raiseOSError(lastError)
|
||||
else:
|
||||
lastFd.close()
|
||||
raise newException(IOError, "Couldn't resolve address: " & address)
|
||||
|
||||
proc connect*(socket: Socket, address: string,
|
||||
|
||||
@@ -692,7 +692,10 @@ proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdT
|
||||
|
||||
proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTarget.} =
|
||||
## Sleeps `milsecs` milliseconds.
|
||||
## A negative `milsecs` causes sleep to return immediately.
|
||||
when defined(windows):
|
||||
if milsecs < 0:
|
||||
return # fixes #23732
|
||||
winlean.sleep(int32(milsecs))
|
||||
else:
|
||||
var a, b: Timespec
|
||||
|
||||
@@ -1416,6 +1416,8 @@ elif not defined(useNimRtl):
|
||||
tmspec.tv_nsec = (timeout * 1_000_000)
|
||||
|
||||
try:
|
||||
if not running(p):
|
||||
return exitStatusLikeShell(p.exitStatus)
|
||||
if clock_gettime(CLOCK_REALTIME, stspec) == -1:
|
||||
raiseOSError(osLastError())
|
||||
while true:
|
||||
|
||||
@@ -514,7 +514,6 @@ proc parseSaturatedNatural*(s: openArray[char], b: var int): int {.
|
||||
proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int =
|
||||
var
|
||||
res = 0.BiggestUInt
|
||||
prev = 0.BiggestUInt
|
||||
i = 0
|
||||
if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}:
|
||||
integerOutOfRangeError()
|
||||
@@ -522,8 +521,11 @@ proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int =
|
||||
if i < s.len and s[i] in {'0'..'9'}:
|
||||
b = 0
|
||||
while i < s.len and s[i] in {'0'..'9'}:
|
||||
prev = res
|
||||
res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
|
||||
if res > BiggestUInt.high div 10: # Highest value that you can multiply 10 without overflow
|
||||
integerOutOfRangeError()
|
||||
res = res * 10
|
||||
let prev = res
|
||||
res += (ord(s[i]) - ord('0')).BiggestUInt
|
||||
if prev > res:
|
||||
integerOutOfRangeError()
|
||||
inc(i)
|
||||
|
||||
@@ -1045,7 +1045,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) =
|
||||
`enter hdPostf`(s, pegNode, start)
|
||||
else:
|
||||
discard
|
||||
let hdPostf = ident(substr(strVal(pegKind), 2))
|
||||
let hdPostf = ident(substr($pegKind, 2))
|
||||
getAst(mkDoEnter(hdPostf, s, pegNode, start))
|
||||
|
||||
macro leave(pegKind, s, pegNode, start, length: untyped): untyped =
|
||||
@@ -1056,7 +1056,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) =
|
||||
`leave hdPostf`(s, pegNode, start, length)
|
||||
else:
|
||||
discard
|
||||
let hdPostf = ident(substr(strVal(pegKind), 2))
|
||||
let hdPostf = ident(substr($pegKind, 2))
|
||||
getAst(mkDoLeave(hdPostf, s, pegNode, start, length))
|
||||
|
||||
matchOrParse(parseIt)
|
||||
|
||||
@@ -203,7 +203,7 @@ proc freshIdentNodes(ast: NimNode): NimNode =
|
||||
# see also https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458
|
||||
proc inspect(node: NimNode): NimNode =
|
||||
case node.kind:
|
||||
of nnkIdent, nnkSym:
|
||||
of nnkIdent, nnkSym, nnkOpenSymChoice, nnkClosedSymChoice, nnkOpenSym:
|
||||
result = ident($node)
|
||||
of nnkEmpty, nnkLiterals:
|
||||
result = node
|
||||
@@ -345,9 +345,10 @@ proc collectImpl(init, body: NimNode): NimNode {.since: (1, 1).} =
|
||||
let res = genSym(nskVar, "collectResult")
|
||||
var bracketExpr: NimNode
|
||||
if init != nil:
|
||||
expectKind init, {nnkCall, nnkIdent, nnkSym}
|
||||
expectKind init, {nnkCall, nnkIdent, nnkSym, nnkClosedSymChoice, nnkOpenSymChoice}
|
||||
bracketExpr = newTree(nnkBracketExpr,
|
||||
if init.kind == nnkCall: freshIdentNodes(init[0]) else: freshIdentNodes(init))
|
||||
if init.kind in {nnkCall, nnkClosedSymChoice, nnkOpenSymChoice, nnkOpenSym}:
|
||||
freshIdentNodes(init[0]) else: freshIdentNodes(init))
|
||||
else:
|
||||
bracketExpr = newTree(nnkBracketExpr)
|
||||
let (resBody, keyType, valueType) = trans(body, res, bracketExpr)
|
||||
|
||||
@@ -19,9 +19,8 @@ template volatileLoad*[T](src: ptr T): T =
|
||||
when defined(js):
|
||||
src[]
|
||||
else:
|
||||
var res: T
|
||||
{.emit: [res, " = (*(", typeof(src[]), " volatile*)", src, ");"].}
|
||||
res
|
||||
result = default(T)
|
||||
{.emit: [result, " = (*(", typeof(src[]), " volatile*)", src, ");"].}
|
||||
|
||||
template volatileStore*[T](dest: ptr T, val: T) =
|
||||
## Generates a volatile store into the container `dest` of the value
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user