mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 19:03:42 +00:00
Compare commits
1 Commits
version-22
...
ringabout-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1b62d4442 |
11
.github/workflows/bisects.yml
vendored
11
.github/workflows/bisects.yml
vendored
@@ -15,16 +15,9 @@ jobs:
|
||||
name: ${{ matrix.platform }}-bisects
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install OpenSSL (Windows)
|
||||
if: |
|
||||
runner.os == 'Windows'
|
||||
run: choco install openssl.light --version=1.1.1.0 # OpenSSL 3.x removed SSL_library_init
|
||||
shell: 'powershell'
|
||||
|
||||
# v2 wont work here, because uses "hardcoded" nim versions, action "dynamically" finds version with bug.
|
||||
- uses: jiro4989/setup-nim-action@v1
|
||||
- uses: jiro4989/setup-nim-action@v1
|
||||
with:
|
||||
nim-version: 'devel'
|
||||
|
||||
|
||||
6
.github/workflows/ci_docs.yml
vendored
6
.github/workflows/ci_docs.yml
vendored
@@ -41,11 +41,11 @@ jobs:
|
||||
target: [linux, windows, osx]
|
||||
include:
|
||||
- target: linux
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-20.04
|
||||
- target: windows
|
||||
os: windows-latest
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
os: macos-15
|
||||
os: macos-12
|
||||
|
||||
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
|
||||
18
.github/workflows/ci_packages.yml
vendored
18
.github/workflows/ci_packages.yml
vendored
@@ -4,7 +4,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
- 'version-2-2'
|
||||
- 'version-2-0'
|
||||
- 'version-1-6'
|
||||
- 'version-1-2'
|
||||
@@ -18,13 +17,9 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
batch: ["0_3", "1_3", "2_3"] # list of `index_num`
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
cpu: amd64
|
||||
- os: macos-latest
|
||||
cpu: arm64
|
||||
os: [ubuntu-20.04, macos-12]
|
||||
cpu: [amd64]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
@@ -45,15 +40,14 @@ jobs:
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt-get install --no-install-recommends -yq \
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev liblapack-dev libpcre3 xorg-dev
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
# XXX can't find boehm and gtk on macos 13
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
|
||||
20
.github/workflows/ci_publish.yml
vendored
20
.github/workflows/ci_publish.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
os: [ubuntu-20.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -21,10 +21,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js'
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ''
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
@@ -34,6 +34,17 @@ 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
|
||||
@@ -60,7 +71,7 @@ jobs:
|
||||
run: nim c -r -d:release ci/action.nim
|
||||
|
||||
- name: 'Comment'
|
||||
uses: actions/github-script@v9
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
@@ -76,3 +87,4 @@ jobs:
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v11
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-pr-stale: 365
|
||||
days-before-pr-close: 30
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -68,7 +68,6 @@ testament.db
|
||||
/csources
|
||||
/csources_v1
|
||||
/csources_v2
|
||||
/csources_v3
|
||||
|
||||
/dist/
|
||||
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-24.04'
|
||||
vmImage: 'ubuntu-20.04'
|
||||
CPU: amd64
|
||||
# regularly breaks, refs bug #17325
|
||||
# Linux_i386:
|
||||
@@ -28,24 +28,24 @@ jobs:
|
||||
# # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_arm64:
|
||||
vmImage: 'macos-15'
|
||||
CPU: arm64
|
||||
OSX_arm64_cpp:
|
||||
vmImage: 'macos-15'
|
||||
CPU: arm64
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-12'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-12'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
vmImage: 'windows-2025'
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
# see also: `NIM_TEST_PACKAGES`
|
||||
NIM_TESTAMENT_BATCH: "0_3"
|
||||
Windows_amd64_batch1_3:
|
||||
vmImage: 'windows-2025'
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "1_3"
|
||||
Windows_amd64_batch2_3:
|
||||
vmImage: 'windows-2025'
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "2_3"
|
||||
|
||||
@@ -80,12 +80,10 @@ jobs:
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo add-apt-repository universe
|
||||
echo_run sudo apt-get update -qq
|
||||
echo_run sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
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
|
||||
echo_run sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
displayName: 'Install dependencies (amd64 Linux)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
|
||||
|
||||
@@ -102,16 +100,15 @@ jobs:
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
|
||||
# echo_run sudo apt-get update -qq
|
||||
echo_run sudo apt-get update -qq || echo "failed, see bug #17343"
|
||||
# echo_run sudo apt-fast update -qq
|
||||
echo_run sudo apt-fast 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-get install --no-install-recommends --allow-downgrades -yq \
|
||||
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
|
||||
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
|
||||
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
|
||||
|
||||
|
||||
cat << EOF > bin/gcc
|
||||
#!/bin/bash
|
||||
|
||||
@@ -133,7 +130,6 @@ 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
|
||||
|
||||
292
changelog.md
292
changelog.md
@@ -1,154 +1,220 @@
|
||||
# v2.x.x - yyyy-mm-dd
|
||||
# v2.2.0 - yyyy-mm-dd
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimPreviewFloatRoundtrip` becomes the default. `system.addFloat` and `system.$` now can produce string representations of
|
||||
floating point numbers that are minimal in size and possess round-trip and correct
|
||||
rounding guarantees (via the
|
||||
[Dragonbox](https://raw.githubusercontent.com/jk-jeon/dragonbox/master/other_files/Dragonbox.pdf) algorithm). Use `-d:nimLegacySprintf` to emulate old behaviors.
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to be in the same module where their type has been defined.
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
|
||||
```nim
|
||||
var a, b: int
|
||||
(a, b) = (1, 2, 3, 4)
|
||||
```
|
||||
will no longer compile.
|
||||
- `internalNew` is removed from system, use `new` instead.
|
||||
|
||||
- The `default` parameter of `tables.getOrDefault` has been renamed to `def` to
|
||||
avoid conflicts with `system.default`, so named argument usage for this
|
||||
parameter like `getOrDefault(..., default = ...)` will have to be changed.
|
||||
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
|
||||
|
||||
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
|
||||
- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behavior.
|
||||
|
||||
- Unknown warnings and hints now gives warnings `warnUnknownNotes` instead of
|
||||
errors.
|
||||
- JS backend now supports closure iterators.
|
||||
|
||||
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
|
||||
- `owner` in `std/macros` is deprecated.
|
||||
|
||||
- The bare `except:` now panics on `Defect`. Use `except Exception:` or `except Defect:` to catch `Defect`. `--legacy:noPanicOnExcept` is provided for a transition period.
|
||||
- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
|
||||
Previously; in templates they would error immediately at the template definition,
|
||||
and in generic procs a type symbol would arbitrarily be captured, losing the
|
||||
information of the other symbols. This means that generic code can now give
|
||||
errors for ambiguous type symbols, and macros operating on generic proc AST
|
||||
may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
|
||||
|
||||
- With `-d:nimPreviewCStringComparisons`, comparsions (`<`, `>`, `<=`, `>=`) between cstrings switch from reference semantics to value semantics like `==` and `!=`.
|
||||
- Partial generic instantiation of routines is no longer allowed. Previously
|
||||
it compiled in niche situations due to bugs in the compiler.
|
||||
|
||||
- `std/parsesql` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
```nim
|
||||
proc foo[T, U](x: T, y: U) = echo (x, y)
|
||||
proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
|
||||
|
||||
- With `-d:nimPreviewDuplicateModuleError`, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Use `import foo as foo1` (or other aliases) to avoid collisions.
|
||||
proc bar[T]() =
|
||||
foo[float](1, "abc")
|
||||
|
||||
- Adds the switch `--mangle:nim|cpp`, which selects `nim` or `cpp` style name mangling when used with `debuginfo` on, defaults to `cpp`.
|
||||
bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
|
||||
```
|
||||
|
||||
- The second parameter of `succ`, `pred`, `inc`, and `dec` in `system` now accepts `SomeInteger` (previously `Ordinal`).
|
||||
- `const` values now open a new scope for each constant, meaning symbols
|
||||
declared in them can no longer be used outside or in the value of
|
||||
other constants.
|
||||
|
||||
- Bitshift operators (`shl`, `shr`, `ashr`) now apply bitmasking to the right operand in the C/C++/VM/JS backends.
|
||||
```nim
|
||||
const foo = (var a = 1; a)
|
||||
const bar = a # error
|
||||
let baz = a # error
|
||||
```
|
||||
- The following POSIX wrappers have had their types changed from signed to
|
||||
unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
|
||||
- `Gid` (was `int32`, is now `uint32`)
|
||||
- `Uid` (was `int32`, is now `uint32`)
|
||||
- `Dev` (was `int32`, is now `uint32` on FreeBSD)
|
||||
- `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
|
||||
- `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
|
||||
(were `int32`, are now `uint32`)
|
||||
- `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
|
||||
|
||||
- Adds a new warning `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts do not trigger warnings. `int` to `Natural` and `Positive` conversions do not trigger warnings, which can be enabled with `--warning:systemRangeConversion`.
|
||||
|
||||
- Procedure compatibility also checks the backend representation of the
|
||||
parameter and result types, not just their source-level shape. Use
|
||||
`--legacy:procParamTypeBackendAliases` to restore the older behavior.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- `setutils.symmetricDifference` along with its operator version
|
||||
`` setutils.`-+-` `` and in-place version `setutils.toggle` have been added
|
||||
to more efficiently calculate the symmetric difference of bitsets.
|
||||
- `strutils.multiReplace` overload for character set replacements in a single pass.
|
||||
Useful for string sanitation. Follows existing multiReplace semantics.
|
||||
|
||||
- `std/files` adds:
|
||||
- Exports `CopyFlag` enum and `FilePermission` type for fine-grained control of file operations
|
||||
- New file operation procs with `Path` support:
|
||||
- `getFilePermissions`, `setFilePermissions` for managing permissions
|
||||
- `tryRemoveFile` for file deletion
|
||||
- `copyFile` with configurable buffer size and symlink handling
|
||||
- `copyFileWithPermissions` to preserve file attributes
|
||||
- `copyFileToDir` for copying files into directories
|
||||
|
||||
- `std/dirs` adds:
|
||||
- New directory operation procs with `Path` support:
|
||||
- `copyDir` with special file handling options
|
||||
- `copyDirWithPermissions` to recursively preserve attributes
|
||||
|
||||
- `system.setLenUninit` now supports refc, JS and VM backends.
|
||||
- `system.setLenUninit` for the `string` type. Allows setting length without initializing new memory on growth.
|
||||
|
||||
- `std/parseopt` now supports multiple parser modes via a `CliMode` enum.
|
||||
Modes include `Nim` (default, fully compatible) and two new experimental modes:
|
||||
`Lax` and `Gnu` for different option parsing behaviors.
|
||||
|
||||
- `std/symlinks.expandSymlink` now supports Windows symlinks and junctions with
|
||||
POSIX-like single-hop `readlink` semantics.
|
||||
- `std/nre2` is added to replace deprecated NRE.
|
||||
|
||||
- `system.typeof` adds a new parameter `modifierMode` to specify how type modifiers are handled.
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
|
||||
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
|
||||
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
|
||||
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
|
||||
- `std/re` and `std/nre` are deprecated as PCRE library is obsolete.
|
||||
Use https://github.com/nitely/nim-regex or `std/nre2`.
|
||||
See: https://github.com/nim-lang/Nim/issues/23668.
|
||||
- `std/pegs` now correctly lexes UTF-8 bytes inside bare identifier-style
|
||||
terminals, so case-insensitive matching of non-ASCII terms (e.g. ``\i café``)
|
||||
works without single-quoting.
|
||||
- `std/uri`: The `?` operator now appends query parameters to an existing query
|
||||
string instead of replacing it. Fixes [#19782](https://github.com/nim-lang/Nim/issues/19782).
|
||||
- `std/jsonutils`: `fromJson` now throws an exception when converting to `array`/`seq` if the JSON isn't an array instead of silently failing
|
||||
- Changed `std/osfiles.copyFile` to allow to specify `bufferSize` instead of a hardcoded one.
|
||||
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
|
||||
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to system, which doesn't initialize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
|
||||
convert a value with a range type to its base type.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
|
||||
on Google's Farm Hash) which is also often faster than the present one. Define
|
||||
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
|
||||
produces the old values. This may impact your automated tests if they depend
|
||||
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
|
||||
is often an easy workaround.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
|
||||
|
||||
[//]: # "Removals:"
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- An experimental option `--experimental:typeBoundOps` has been added that
|
||||
implements the RFC https://github.com/nim-lang/RFCs/issues/380.
|
||||
This makes the behavior of interfaces like `hash`, `$`, `==` etc. more
|
||||
reliable for nominal types across indirect/restricted imports.
|
||||
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
|
||||
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
|
||||
- `member` can be used to attach a procedure to a C++ type.
|
||||
- C++ `constructor` now reuses `result` instead creating `this`.
|
||||
|
||||
- Tuple unpacking changes:
|
||||
- Tuple unpacking assignment now supports using underscores to discard values.
|
||||
```nim
|
||||
var a, c: int
|
||||
(a, _, c) = (1, 2, 3)
|
||||
```
|
||||
- Tuple unpacking variable declarations now support type annotations, but
|
||||
only for the entire tuple.
|
||||
```nim
|
||||
let (a, b): (int, int) = (1, 2)
|
||||
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
|
||||
```
|
||||
|
||||
- The experimental option `--experimental:openSym` has been added to allow
|
||||
captured symbols in generic routine and template bodies respectively to be
|
||||
replaced by symbols injected locally by templates/macros at instantiation
|
||||
time. `bind` may be used to keep the captured symbols over the injected ones
|
||||
regardless of enabling the option, but other methods like renaming the
|
||||
captured symbols should be used instead so that the code is not affected by
|
||||
context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
# objs.nim
|
||||
import std/hashes
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
type
|
||||
Obj* = object
|
||||
x*, y*: int
|
||||
z*: string # to be ignored for equality
|
||||
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"
|
||||
|
||||
proc `==`*(a, b: Obj): bool =
|
||||
a.x == b.x and a.y == b.y
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
proc hash*(a: Obj): Hash =
|
||||
$!(hash(a.x) &! hash(a.y))
|
||||
{.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
|
||||
# main.nim
|
||||
{.experimental: "typeBoundOps".}
|
||||
from objs import Obj # objs.hash, objs.`==` not imported
|
||||
import std/tables
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
var t: Table[Obj, int]
|
||||
t[Obj(x: 3, y: 4, z: "debug")] = 34
|
||||
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
|
||||
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"
|
||||
```
|
||||
|
||||
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#typeminusbound-overloads)
|
||||
for more information.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- Fixed a bug where `sizeof(T)` inside a `typedesc` template called from a generic type's
|
||||
`when` clause would error with "'sizeof' requires '.importc' types to be '.completeStruct'".
|
||||
The issue was that `hasValuelessStatics` in `semtypinst.nim` didn't recognize
|
||||
`tyTypeDesc(tyGenericParam)` as an unresolved generic parameter.
|
||||
|
||||
- The JS backend now implements write-through for `var openArray` parameters that
|
||||
receive a `toOpenArray` view (bug #15952): mutations reach the caller's storage
|
||||
instead of silently writing to a copy. Fixed homogeneous numeric arrays
|
||||
(`array[N, T]`, JS typed arrays) slice via `subarray`; `seq` and non-numeric
|
||||
arrays slice via a `{base, off, len}` view. This also covers seq/non-numeric-array
|
||||
write-through, pass-through, re-slicing and `@` (openArray-to-seq) of such views.
|
||||
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
|
||||
|
||||
## Tool changes
|
||||
|
||||
- Added `--raw` flag when generating JSON docs to not render markup.
|
||||
- Added `--stdinfile` flag to name of the file used when running program from stdin (defaults to `stdinfile.nim`)
|
||||
- Added `--styleCheck:warning` flag to treat style check violations as warnings.
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.
|
||||
|
||||
## Documentation changes
|
||||
|
||||
- Added documentation for the `completeStruct` pragma in the manual.
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
- [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
|
||||
|
||||
- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
|
||||
now produces a `CountTable` with values corresponding to the number of occurrences
|
||||
now produces a `CountTable` with values correspoding to the number of occurrences
|
||||
of the key in the input. It used to produce a table with all values set to `1`.
|
||||
|
||||
Counting occurrences in a sequence used to be:
|
||||
@@ -339,7 +339,7 @@ for i in a..b:
|
||||
|
||||
- Fixed "ReraiseError when using try/except within finally block"
|
||||
([#5871](https://github.com/nim-lang/Nim/issues/5871))
|
||||
- Fixed "Range type inference leads to counter-intuitive behaviour"
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "JSON % operator can fail in extern procs with dynamic types"
|
||||
([#6385](https://github.com/nim-lang/Nim/issues/6385))
|
||||
@@ -403,7 +403,7 @@ for i in a..b:
|
||||
([#6589](https://github.com/nim-lang/Nim/issues/6589))
|
||||
- Fixed "Generated c code calls function twice"
|
||||
([#6292](https://github.com/nim-lang/Nim/issues/6292))
|
||||
- Fixed "Range type inference leads to counter-intuitive behaviour"
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "New backward indexing is too limited"
|
||||
([#6631](https://github.com/nim-lang/Nim/issues/6631))
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
- We removed `unicode.Rune16` without any deprecation period as the name
|
||||
was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
|
||||
and we didn't find any usage of it in the wild. If you still need it, add this
|
||||
and we didn't find any usages of it in the wild. If you still need it, add this
|
||||
piece of code to your project:
|
||||
```nim
|
||||
type
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
|
||||
* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
|
||||
* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
|
||||
* Fix how `relativePath` handles case sensitivity
|
||||
* Fix how `relativePath` handle case sensitiviy
|
||||
* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
|
||||
* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
|
||||
* VM: no special casing for big endian machines
|
||||
@@ -43,7 +43,7 @@
|
||||
* threadpool: fix link in docs (#12258)
|
||||
* Fix spellings (#12277)
|
||||
* fix #12278, don't expose internal PCRE documentation
|
||||
* Fixed "Documentation of quitprocs is wrong" [#12279](https://github.com/nim-lang/Nim/issues/12279)
|
||||
* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
|
||||
* Fix typo in docs
|
||||
* Fix reference to parseSpec proc in readme
|
||||
* [doc/tut1] removed discard discussion in comments
|
||||
|
||||
@@ -169,7 +169,7 @@ echo f
|
||||
- The Nim compiler now supports a new pragma called ``.localPassc`` to
|
||||
pass specific compiler options to the C(++) backend for the C(++) file
|
||||
that was produced from the current Nim module.
|
||||
- The compiler now infers "sink parameters". To disable this for a specific routine,
|
||||
- The compiler now inferes "sink parameters". To disable this for a specific routine,
|
||||
annotate it with `.nosinks`. To disable it for a section of code, use
|
||||
`{.push sinkInference: off.}`...`{.pop.}`.
|
||||
- The compiler now supports a new switch `--panics:on` that turns runtime
|
||||
@@ -261,7 +261,7 @@ echo f
|
||||
([#12812](https://github.com/nim-lang/Nim/issues/12812))
|
||||
- Fixed "Produce static/const initializations for variables when possible"
|
||||
([#12216](https://github.com/nim-lang/Nim/issues/12216))
|
||||
- Fixed "Assigning discriminator field leads to internal assert with --gc:destructors"
|
||||
- Fixed "Assigning descriminator field leads to internal assert with --gc:destructors"
|
||||
([#12821](https://github.com/nim-lang/Nim/issues/12821))
|
||||
- Fixed "nimsuggest `use` command does not return all instances of symbol"
|
||||
([#12832](https://github.com/nim-lang/Nim/issues/12832))
|
||||
|
||||
@@ -283,7 +283,7 @@ The definition of `"strictFuncs"` was changed.
|
||||
The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
|
||||
The new definition is: "A store to a ref/ptr deref is forbidden."
|
||||
|
||||
This new definition is much easier to understand, the price is some expressiveness. The following code used to be
|
||||
This new definition is much easier to understand, the price is some expressitivity. The following code used to be
|
||||
accepted:
|
||||
|
||||
```nim
|
||||
|
||||
@@ -1,248 +1,12 @@
|
||||
# v2.2.0 - 2024-10-02
|
||||
|
||||
# v2.2.0 - 2023-mm-dd
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` is out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backward compatibility.
|
||||
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
|
||||
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to the same module where their type has been defined.
|
||||
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
|
||||
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
|
||||
```nim
|
||||
var a, b: int
|
||||
(a, b) = (1, 2, 3, 4)
|
||||
```
|
||||
will no longer compile.
|
||||
|
||||
- `internalNew` is removed from the `system` module, use `new` instead.
|
||||
|
||||
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
|
||||
|
||||
- The JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors.
|
||||
|
||||
- The JS backend now supports closure iterators.
|
||||
|
||||
- `owner` in `std/macros` is deprecated.
|
||||
|
||||
- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
|
||||
Previously; in templates they would error immediately at the template definition,
|
||||
and in generic procs a type symbol would arbitrarily be captured, losing the
|
||||
information of the other symbols. This means that generic code can now give
|
||||
errors for ambiguous type symbols, and macros operating on generic proc AST
|
||||
may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
|
||||
|
||||
- Partial generic instantiation of routines is no longer allowed. Previously
|
||||
it compiled in niche situations due to bugs in the compiler.
|
||||
|
||||
```nim
|
||||
proc foo[T, U](x: T, y: U) = echo (x, y)
|
||||
proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
|
||||
|
||||
proc bar[T]() =
|
||||
foo[float](1, "abc")
|
||||
|
||||
bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
|
||||
```
|
||||
|
||||
- `const` values now open a new scope for each constant, meaning symbols
|
||||
declared in them can no longer be used outside or in the value of
|
||||
other constants.
|
||||
|
||||
```nim
|
||||
const foo = (var a = 1; a)
|
||||
const bar = a # error
|
||||
let baz = a # error
|
||||
```
|
||||
|
||||
- The following POSIX wrappers have had their types changed from signed to
|
||||
unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
|
||||
- `Gid` (was `int32`, is now `uint32`)
|
||||
- `Uid` (was `int32`, is now `uint32`)
|
||||
- `Dev` (was `int32`, is now `uint32` on FreeBSD)
|
||||
- `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
|
||||
- `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
|
||||
(were `int32`, are now `uint32`)
|
||||
- `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
|
||||
|
||||
- The `Atomic[T]` type on C++ now uses C11 primitives by default instead of
|
||||
`std::atomic`. To use `std::atomic` instead, `-d:nimUseCppAtomics` can be defined.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
|
||||
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
|
||||
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- Added `newStringUninit` to the `system` module, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to the `system` module, which doesn't initialize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
|
||||
convert a value with a range type to its base type.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
|
||||
on Google's Farm Hash) which is also often faster than the present one. Define
|
||||
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
|
||||
produces the old values. This may impact your automated tests if they depend
|
||||
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
|
||||
is often an easy workaround.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
|
||||
|
||||
[//]: # "Removals:"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
|
||||
|
||||
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
|
||||
|
||||
- `member` can be used to attach a procedure to a C++ type.
|
||||
|
||||
- Inside a C++ constructor, `result` can be used to access the created object rather than `this`.
|
||||
|
||||
- Tuple unpacking changes:
|
||||
- Tuple unpacking assignment now supports using underscores to discard values.
|
||||
```nim
|
||||
var a, c: int
|
||||
(a, _, c) = (1, 2, 3)
|
||||
```
|
||||
- Tuple unpacking variable declarations now support type annotations, but
|
||||
only for the entire tuple.
|
||||
```nim
|
||||
let (a, b): (int, int) = (1, 2)
|
||||
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
|
||||
```
|
||||
|
||||
- The experimental option `--experimental:openSym` has been added to allow
|
||||
captured symbols in generic routine and template bodies respectively to be
|
||||
replaced by symbols injected locally by templates/macros at instantiation
|
||||
time. `bind` may be used to keep the captured symbols over the injected ones
|
||||
regardless of enabling the option, but other methods like renaming the
|
||||
captured symbols should be used instead so that the code is not affected by
|
||||
context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert barTempl() == "injected" # previously it would be "captured"
|
||||
|
||||
template bazTempl(): string =
|
||||
bind value
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert bazTempl() == "captured"
|
||||
```
|
||||
|
||||
This option also generates a new node kind `nnkOpenSym` which contains
|
||||
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
|
||||
modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
Another experimental switch `genericsOpenSym` exists that enables this behavior
|
||||
at instantiation time, meaning templates etc can enable it specifically when
|
||||
they are being called. However this does not generate `nnkOpenSym` nodes
|
||||
(unless the other switch is enabled) and so doesn't reflect the regular
|
||||
behavior of the switch.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
echo bar[int]() # "injected"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
var res: string
|
||||
foo(123):
|
||||
res = value
|
||||
res
|
||||
assert barTempl() == "injected"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# v2.xx.x - yyyy-mm-dd
|
||||
# v1.xx.x - yyyy-mm-dd
|
||||
|
||||
This is an example file.
|
||||
The changes should go to changelog.md!
|
||||
|
||||
@@ -8,7 +8,7 @@ const
|
||||
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
|
||||
nkAddr, nkHiddenAddr,
|
||||
nkObjDownConv, nkObjUpConv}
|
||||
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv, nkCast}
|
||||
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
|
||||
|
||||
proc skipConvDfa*(n: PNode): PNode =
|
||||
result = n
|
||||
@@ -125,3 +125,4 @@ proc aliases*(obj, field: PNode): AliasKind =
|
||||
else:
|
||||
result = maybe
|
||||
else: assert false # unreachable
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
if a.kind == b.kind:
|
||||
case a.kind
|
||||
of nkSym:
|
||||
const varKinds = {skVar, skTemp, skResult, skProc, skFunc}
|
||||
const varKinds = {skVar, skTemp, skProc, skFunc}
|
||||
# same symbol: aliasing:
|
||||
if a.sym.id == b.sym.id: result = arYes
|
||||
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
|
||||
|
||||
117
compiler/ast.nim
117
compiler/ast.nim
@@ -279,7 +279,7 @@ const
|
||||
GcTypeKinds* = {tyRef, tySequence, tyString}
|
||||
|
||||
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyConcept,
|
||||
tyUserTypeClass, tyUserTypeClassInst,
|
||||
tyAnd, tyOr, tyNot, tyAnything}
|
||||
|
||||
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
|
||||
@@ -447,8 +447,6 @@ const
|
||||
tfReturnsNew* = tfInheritable
|
||||
tfNonConstExpr* = tfExplicitCallConv
|
||||
## tyFromExpr where the expression shouldn't be evaluated as a static value
|
||||
tfGenericHasDestructor* = tfExplicitCallConv
|
||||
## tyGenericBody where an instance has a generated destructor
|
||||
skError* = skUnknown
|
||||
|
||||
var
|
||||
@@ -493,14 +491,13 @@ type
|
||||
mAnd, mOr,
|
||||
mImplies, mIff, mExists, mForall, mOld,
|
||||
mEqStr, mLeStr, mLtStr,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mConStrStr, mSlice,
|
||||
mDotDot, # this one is only necessary to give nice compile time warnings
|
||||
mFields, mFieldPairs, mOmpParFor,
|
||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInSet, mRepr, mExit,
|
||||
mSetLengthStr, mSetLengthSeq,
|
||||
mSetLengthSeqUninit,
|
||||
mIsPartOf, mAstToStr, mParallel,
|
||||
mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
|
||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||
@@ -562,7 +559,7 @@ const
|
||||
mStrToStr, mEnumToStr,
|
||||
mAnd, mOr,
|
||||
mEqStr, mLeStr, mLtStr,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInSet, mRepr, mOpenArrayToSeq}
|
||||
|
||||
@@ -594,7 +591,7 @@ type
|
||||
TNode*{.final, acyclic.} = object # on a 32bit machine, this takes 32 bytes
|
||||
when defined(useNodeIds):
|
||||
id*: int
|
||||
typField: PType
|
||||
typ*: PType
|
||||
info*: TLineInfo
|
||||
flags*: TNodeFlags
|
||||
case kind*: TNodeKind
|
||||
@@ -685,7 +682,6 @@ type
|
||||
symbols*: TStrTable
|
||||
parent*: PScope
|
||||
allowPrivateAccess*: seq[PSym] # # enable access to private fields
|
||||
optionStackLen*: int
|
||||
|
||||
PScope* = ref TScope
|
||||
|
||||
@@ -710,7 +706,7 @@ type
|
||||
when defined(nimsuggest):
|
||||
endInfo*: TLineInfo
|
||||
hasUserSpecifiedType*: bool # used for determining whether to display inlay type hints
|
||||
ownerField: PSym
|
||||
owner*: PSym
|
||||
flags*: TSymFlags
|
||||
ast*: PNode # syntax tree of proc, iterator, etc.:
|
||||
# the whole proc including header; this is used
|
||||
@@ -779,7 +775,7 @@ type
|
||||
# formal param list
|
||||
# for concepts, the concept body
|
||||
# else: unused
|
||||
ownerField: PSym # the 'owner' of the type
|
||||
owner*: PSym # the 'owner' of the type
|
||||
sym*: PSym # types have the sym associated with them
|
||||
# it is used for converting types to strings
|
||||
size*: BiggestInt # the size of the type in bytes
|
||||
@@ -797,15 +793,6 @@ type
|
||||
|
||||
TPairSeq* = seq[TPair]
|
||||
|
||||
TIdPair*[T] = object
|
||||
key*: ItemId
|
||||
val*: T
|
||||
|
||||
TIdPairSeq*[T] = seq[TIdPair[T]]
|
||||
TIdTable*[T] = object
|
||||
counter*: int
|
||||
data*: TIdPairSeq[T]
|
||||
|
||||
TNodePair* = object
|
||||
h*: Hash # because it is expensive to compute!
|
||||
key*: PNode
|
||||
@@ -816,7 +803,6 @@ type
|
||||
# nodes are compared by structure!
|
||||
counter*: int
|
||||
data*: TNodePairSeq
|
||||
ignoreTypes*: bool
|
||||
|
||||
TObjectSeq* = seq[RootRef]
|
||||
TObjectSet* = object
|
||||
@@ -828,15 +814,6 @@ type
|
||||
|
||||
template nodeId(n: PNode): int = cast[int](n)
|
||||
|
||||
template typ*(n: PNode): PType =
|
||||
n.typField
|
||||
|
||||
proc owner*(s: PSym|PType): PSym {.inline.} =
|
||||
result = s.ownerField
|
||||
|
||||
proc setOwner*(s: PSym|PType, owner: PSym) {.inline.} =
|
||||
s.ownerField = owner
|
||||
|
||||
type Gconfig = object
|
||||
# we put comments in a side channel to avoid increasing `sizeof(TNode)`, which
|
||||
# reduces memory usage given that `PNode` is the most allocated type by far.
|
||||
@@ -945,17 +922,16 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
|
||||
case a.kind
|
||||
of nkSym: a.sym.name
|
||||
of nkIdent: a.ident
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: a.sons[0].sym.name
|
||||
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
|
||||
of nkOpenSym: getPIdent(a.sons[0])
|
||||
else: nil
|
||||
|
||||
const
|
||||
moduleShift = when defined(cpu32): 20 else: 24
|
||||
|
||||
template toId*(a: ItemId): int =
|
||||
template id*(a: PType | PSym): int =
|
||||
let x = a
|
||||
(x.module.int shl moduleShift) + x.item.int
|
||||
|
||||
template id*(a: PType | PSym): int = toId(a.itemId)
|
||||
(x.itemId.module.int shl moduleShift) + x.itemId.item.int
|
||||
|
||||
type
|
||||
IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
|
||||
@@ -1156,7 +1132,7 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
||||
## new node with line info, type, and no children
|
||||
result = newNode(kind)
|
||||
result.info = info
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
|
||||
proc newNode*(kind: TNodeKind, info: TLineInfo): PNode =
|
||||
## new node with line info, no type, and no children
|
||||
@@ -1221,7 +1197,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
|
||||
assert not name.isNil
|
||||
let id = nextSymId idgen
|
||||
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
|
||||
options: options, ownerField: owner, offset: defaultOffset,
|
||||
options: options, owner: owner, offset: defaultOffset,
|
||||
disamb: getOrDefault(idgen.disambTable, name).int32)
|
||||
idgen.disambTable.inc name
|
||||
when false:
|
||||
@@ -1282,11 +1258,6 @@ proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
|
||||
setLen(dest.data, src.data.len)
|
||||
for i in 0..high(src.data): dest.data[i] = src.data[i]
|
||||
|
||||
proc copyIdTable*[T](dest: var TIdTable[T], src: TIdTable[T]) =
|
||||
dest.counter = src.counter
|
||||
newSeq(dest.data, src.data.len)
|
||||
for i in 0..high(src.data): dest.data[i] = src.data[i]
|
||||
|
||||
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
|
||||
dest.counter = src.counter
|
||||
setLen(dest.data, src.data.len)
|
||||
@@ -1307,13 +1278,13 @@ proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode =
|
||||
proc newSymNode*(sym: PSym): PNode =
|
||||
result = newNode(nkSym)
|
||||
result.sym = sym
|
||||
result.typ() = sym.typ
|
||||
result.typ = sym.typ
|
||||
result.info = sym.info
|
||||
|
||||
proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||
result = newNode(nkSym)
|
||||
result.sym = sym
|
||||
result.typ() = sym.typ
|
||||
result.typ = sym.typ
|
||||
result.info = info
|
||||
|
||||
proc newOpenSym*(n: PNode): PNode {.inline.} =
|
||||
@@ -1393,7 +1364,7 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
|
||||
result = newNode(nkIntLit)
|
||||
else: raiseAssert $kind
|
||||
result.intVal = intVal
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
|
||||
proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
|
||||
# XXX: introduce range check
|
||||
@@ -1532,7 +1503,7 @@ iterator signature*(t: PType): PType =
|
||||
|
||||
proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType =
|
||||
let id = nextTypeId idgen
|
||||
result = PType(kind: kind, ownerField: owner, size: defaultSize,
|
||||
result = PType(kind: kind, owner: owner, size: defaultSize,
|
||||
align: defaultAlignment, itemId: id,
|
||||
uniqueId: id, sons: @[])
|
||||
if son != nil: result.sons.add son
|
||||
@@ -1587,7 +1558,7 @@ proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
|
||||
result.sym = t.sym # backend-info should not be copied
|
||||
|
||||
proc exactReplica*(t: PType): PType =
|
||||
result = PType(kind: t.kind, ownerField: t.owner, size: defaultSize,
|
||||
result = PType(kind: t.kind, owner: t.owner, size: defaultSize,
|
||||
align: defaultAlignment, itemId: t.itemId,
|
||||
uniqueId: t.uniqueId)
|
||||
assignType(result, t)
|
||||
@@ -1625,22 +1596,12 @@ proc initStrTable*(): TStrTable =
|
||||
result = TStrTable(counter: 0)
|
||||
newSeq(result.data, StartSize)
|
||||
|
||||
proc initIdTable*[T](): TIdTable[T] =
|
||||
result = TIdTable[T](counter: 0)
|
||||
newSeq(result.data, StartSize)
|
||||
|
||||
proc resetIdTable*[T](x: var TIdTable[T]) =
|
||||
x.counter = 0
|
||||
# clear and set to old initial size:
|
||||
setLen(x.data, 0)
|
||||
setLen(x.data, StartSize)
|
||||
|
||||
proc initObjectSet*(): TObjectSet =
|
||||
result = TObjectSet(counter: 0)
|
||||
newSeq(result.data, StartSize)
|
||||
|
||||
proc initNodeTable*(ignoreTypes=false): TNodeTable =
|
||||
result = TNodeTable(counter: 0, ignoreTypes: ignoreTypes)
|
||||
proc initNodeTable*(): TNodeTable =
|
||||
result = TNodeTable(counter: 0)
|
||||
newSeq(result.data, StartSize)
|
||||
|
||||
proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
|
||||
@@ -1675,7 +1636,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||
if mask != {} and propagateHasAsgn:
|
||||
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if o2.kind in {tyTuple, tyObject, tyArray,
|
||||
tySequence, tyString, tySet, tyDistinct}:
|
||||
tySequence, tySet, tyDistinct}:
|
||||
o2.flags.incl mask
|
||||
owner.flags.incl mask
|
||||
|
||||
@@ -1705,7 +1666,7 @@ proc copyNode*(src: PNode): PNode =
|
||||
return nil
|
||||
result = newNode(src.kind)
|
||||
result.info = src.info
|
||||
result.typ() = src.typ
|
||||
result.typ = src.typ
|
||||
result.flags = src.flags * PersistentNodeFlags
|
||||
result.comment = src.comment
|
||||
when defined(useNodeIds):
|
||||
@@ -1723,7 +1684,7 @@ proc copyNode*(src: PNode): PNode =
|
||||
|
||||
template transitionNodeKindCommon(k: TNodeKind) =
|
||||
let obj {.inject.} = n[]
|
||||
n[] = TNode(kind: k, typField: n.typ, info: obj.info, flags: obj.flags)
|
||||
n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags)
|
||||
# n.comment = obj.comment # shouldn't be needed, the address doesnt' change
|
||||
when defined(useNodeIds):
|
||||
n.id = obj.id
|
||||
@@ -1746,7 +1707,7 @@ proc transitionNoneToSym*(n: PNode) =
|
||||
template transitionSymKindCommon*(k: TSymKind) =
|
||||
let obj {.inject.} = s[]
|
||||
s[] = TSym(kind: k, itemId: obj.itemId, magic: obj.magic, typ: obj.typ, name: obj.name,
|
||||
info: obj.info, ownerField: obj.ownerField, flags: obj.flags, ast: obj.ast,
|
||||
info: obj.info, owner: obj.owner, flags: obj.flags, ast: obj.ast,
|
||||
options: obj.options, position: obj.position, offset: obj.offset,
|
||||
loc: obj.loc, annex: obj.annex, constraint: obj.constraint)
|
||||
when hasFFI:
|
||||
@@ -1774,7 +1735,7 @@ template copyNodeImpl(dst, src, processSonsStmt) =
|
||||
dst.info = src.info
|
||||
when defined(nimsuggest):
|
||||
result.endInfo = src.endInfo
|
||||
dst.typ() = src.typ
|
||||
dst.typ = src.typ
|
||||
dst.flags = src.flags * PersistentNodeFlags
|
||||
dst.comment = src.comment
|
||||
when defined(useNodeIds):
|
||||
@@ -2117,20 +2078,14 @@ proc canRaise*(fn: PNode): bool =
|
||||
result = false
|
||||
elif fn.kind == nkSym and fn.sym.magic == mEcho:
|
||||
result = true
|
||||
elif fn.typ != nil and fn.typ.kind == tyProc and fn.typ.n != nil:
|
||||
else:
|
||||
# TODO check for n having sons? or just return false for now if not
|
||||
if fn.typ.n[0].kind == nkSym:
|
||||
if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym:
|
||||
result = false
|
||||
else:
|
||||
# A proc-typed value with no explicit raises slot still has
|
||||
# unspecified effects, which sempass2 treats conservatively.
|
||||
# Codegen needs to do the same in order to keep goto-exception
|
||||
# checks after indirect/closure calls.
|
||||
result = ((fn.typ.n[0].len < effectListLen) or
|
||||
fn.typ.n[0][exceptionEffects] == nil or
|
||||
fn.typ.n[0][exceptionEffects].safeLen > 0)
|
||||
else:
|
||||
result = false
|
||||
result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
|
||||
(fn.typ.n[0][exceptionEffects] != nil and
|
||||
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
||||
|
||||
proc toHumanStrImpl[T](kind: T, num: static int): string =
|
||||
result = $kind
|
||||
@@ -2167,8 +2122,14 @@ proc isTrue*(n: PNode): bool =
|
||||
n.kind == nkIntLit and n.intVal != 0
|
||||
|
||||
type
|
||||
TypeMapping* = TIdTable[PType]
|
||||
SymMapping* = TIdTable[PSym]
|
||||
TypeMapping* = Table[ItemId, PType]
|
||||
SymMapping* = Table[ItemId, PSym]
|
||||
|
||||
template initSymMapping*(): SymMapping = initIdTable[PSym]()
|
||||
template initTypeMapping*(): TypeMapping = initIdTable[PType]()
|
||||
template idTableGet*(tab: typed; key: PSym | PType): untyped = tab.getOrDefault(key.itemId)
|
||||
template idTablePut*(tab: typed; key, val: PSym | PType) = tab[key.itemId] = val
|
||||
|
||||
template initSymMapping*(): Table[ItemId, PSym] = initTable[ItemId, PSym]()
|
||||
template initTypeMapping*(): Table[ItemId, PType] = initTable[ItemId, PType]()
|
||||
|
||||
template resetIdTable*(tab: Table[ItemId, PSym]) = tab.clear()
|
||||
template resetIdTable*(tab: Table[ItemId, PType]) = tab.clear()
|
||||
|
||||
@@ -713,70 +713,6 @@ iterator items*(tab: TStrTable): PSym =
|
||||
yield s
|
||||
s = nextIter(it, tab)
|
||||
|
||||
proc isNil(x: ItemId): bool {.inline.} =
|
||||
x.module == 0 and x.item == 0
|
||||
|
||||
proc hasEmptySlot[T](data: TIdPairSeq[T]): bool =
|
||||
for h in 0..high(data):
|
||||
if isNil(data[h].key):
|
||||
return true
|
||||
result = false
|
||||
|
||||
proc idTableRawGet[T](t: TIdTable[T], key: int): int =
|
||||
var h: Hash
|
||||
h = key and high(t.data) # start with real hash value
|
||||
while not isNil(t.data[h].key):
|
||||
if toId(t.data[h].key) == key:
|
||||
return h
|
||||
h = nextTry(h, high(t.data))
|
||||
result = - 1
|
||||
|
||||
proc getOrDefault*[T](t: TIdTable[T], key: ItemId): T =
|
||||
var index = idTableRawGet(t, toId(key))
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = default(T)
|
||||
|
||||
template idTableGet*[T](t: TIdTable[T], key: PType | PSym): T =
|
||||
getOrDefault(t, key.itemId)
|
||||
|
||||
proc idTableRawInsert[T](data: var TIdPairSeq[T], key: ItemId, val: T) =
|
||||
var h: Hash
|
||||
let keyId = toId(key)
|
||||
h = keyId and high(data)
|
||||
while not isNil(data[h].key):
|
||||
assert(toId(data[h].key) != keyId)
|
||||
h = nextTry(h, high(data))
|
||||
assert(isNil(data[h].key))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc `[]=`*[T](t: var TIdTable[T], key: ItemId, val: T) =
|
||||
var
|
||||
index: int
|
||||
n: TIdPairSeq[T]
|
||||
index = idTableRawGet(t, toId(key))
|
||||
if index >= 0:
|
||||
assert(not isNil(t.data[index].key))
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(t.data.len, t.counter):
|
||||
newSeq(n, t.data.len * GrowthFactor)
|
||||
for i in 0..high(t.data):
|
||||
if not isNil(t.data[i].key):
|
||||
idTableRawInsert(n, t.data[i].key, t.data[i].val)
|
||||
assert(hasEmptySlot(n))
|
||||
swap(t.data, n)
|
||||
idTableRawInsert(t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
template idTablePut*[T](t: var TIdTable[T], key: PType | PSym, val: T) =
|
||||
t[key.itemId] = val
|
||||
|
||||
iterator idTablePairs*[T](t: TIdTable[T]): tuple[key: ItemId, val: T] =
|
||||
for i in 0..high(t.data):
|
||||
if not isNil(t.data[i].key):
|
||||
yield (t.data[i].key, t.data[i].val)
|
||||
|
||||
proc initIITable(x: var TIITable) =
|
||||
x.counter = 0
|
||||
newSeq(x.data, StartSize)
|
||||
|
||||
@@ -43,13 +43,13 @@ proc flagsToStr[T](flags: set[T]): string =
|
||||
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): string =
|
||||
result = "["
|
||||
result.addYamlString(toFilename(conf, info))
|
||||
result.addf ", $1, $2]", toLinenumber(info), toColumn(info)
|
||||
result.addf ", $1, $2]", [toLinenumber(info), toColumn(info)]
|
||||
|
||||
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; nl: bool, indent, maxRecDepth: int)
|
||||
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; nl: bool, indent, maxRecDepth: int)
|
||||
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; nl: bool, indent, maxRecDepth: int)
|
||||
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; indent, maxRecDepth: int)
|
||||
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; indent, maxRecDepth: int)
|
||||
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; indent, maxRecDepth: int)
|
||||
|
||||
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; nl: bool, indent: int; maxRecDepth: int) =
|
||||
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; indent: int; maxRecDepth: int) =
|
||||
if n == nil:
|
||||
res.add("null")
|
||||
elif containsOrIncl(marker, n.id):
|
||||
@@ -57,12 +57,10 @@ proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet;
|
||||
else:
|
||||
let istr = spaces(indent * 4)
|
||||
|
||||
if nl:
|
||||
res.addf("\n$1", istr)
|
||||
res.addf("kind: $1", [makeYamlString($n.kind)])
|
||||
res.addf("\n$1name: $2", [istr, makeYamlString(n.name.s)])
|
||||
res.addf("\n$1typ: ", [istr])
|
||||
res.typeToYamlAux(conf, n.typ, marker, true, indent + 1, maxRecDepth - 1)
|
||||
res.typeToYamlAux(conf, n.typ, marker, indent + 1, maxRecDepth - 1)
|
||||
if conf != nil:
|
||||
# if we don't pass the config, we probably don't care about the line info
|
||||
res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
|
||||
@@ -70,7 +68,7 @@ proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet;
|
||||
res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
|
||||
res.addf("\n$1magic: $2", [istr, makeYamlString($n.magic)])
|
||||
res.addf("\n$1ast: ", [istr])
|
||||
res.treeToYamlAux(conf, n.ast, marker, true, indent + 1, maxRecDepth - 1)
|
||||
res.treeToYamlAux(conf, n.ast, marker, indent + 1, maxRecDepth - 1)
|
||||
res.addf("\n$1options: $2", [istr, flagsToStr(n.options)])
|
||||
res.addf("\n$1position: $2", [istr, $n.position])
|
||||
res.addf("\n$1k: $2", [istr, makeYamlString($n.loc.k)])
|
||||
@@ -78,57 +76,53 @@ proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet;
|
||||
if card(n.loc.flags) > 0:
|
||||
res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
|
||||
res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
|
||||
res.addf("\n$1lode: ", [istr])
|
||||
res.treeToYamlAux(conf, n.loc.lode, marker, true, indent + 1, maxRecDepth - 1)
|
||||
res.addf("\n$1lode: $2", [istr])
|
||||
res.treeToYamlAux(conf, n.loc.lode, marker, indent + 1, maxRecDepth - 1)
|
||||
|
||||
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; nl: bool, indent: int; maxRecDepth: int) =
|
||||
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; indent: int; maxRecDepth: int) =
|
||||
if n == nil:
|
||||
res.add("null")
|
||||
elif containsOrIncl(marker, n.id):
|
||||
res.addf "\"$1 @$2\"" % [$n.kind, strutils.toHex(cast[uint](n), sizeof(n) * 2)]
|
||||
else:
|
||||
let istr = spaces(indent * 4)
|
||||
if nl:
|
||||
res.addf("\n$1", istr)
|
||||
res.addf("kind: $2", [istr, makeYamlString($n.kind)])
|
||||
res.addf("\n$1sym: ", istr)
|
||||
res.symToYamlAux(conf, n.sym, marker, true, indent + 1, maxRecDepth - 1)
|
||||
res.addf("\n$1n: ", istr)
|
||||
res.treeToYamlAux(conf, n.n, marker, true, indent + 1, maxRecDepth - 1)
|
||||
res.addf("\n$1sym: ")
|
||||
res.symToYamlAux(conf, n.sym, marker, indent + 1, maxRecDepth - 1)
|
||||
res.addf("\n$1n: ")
|
||||
res.treeToYamlAux(conf, n.n, marker, indent + 1, maxRecDepth - 1)
|
||||
if card(n.flags) > 0:
|
||||
res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
|
||||
res.addf("\n$1callconv: $2", [istr, makeYamlString($n.callConv)])
|
||||
res.addf("\n$1size: $2", [istr, $(n.size)])
|
||||
res.addf("\n$1align: $2", [istr, $(n.align)])
|
||||
if n.hasElementType:
|
||||
res.addf("\n$1sons:", istr)
|
||||
res.addf("\n$1sons:")
|
||||
for a in n.kids:
|
||||
res.addf("\n$1 - ", istr)
|
||||
res.typeToYamlAux(conf, a, marker, false, indent + 1, maxRecDepth - 1)
|
||||
res.addf("\n - ")
|
||||
res.typeToYamlAux(conf, a, marker, indent + 1, maxRecDepth - 1)
|
||||
|
||||
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; nl: bool, indent: int;
|
||||
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; indent: int;
|
||||
maxRecDepth: int) =
|
||||
if n == nil:
|
||||
res.add("null")
|
||||
else:
|
||||
var istr = spaces(indent * 4)
|
||||
if nl:
|
||||
res.addf("\n$1", istr)
|
||||
res.addf("kind: $1" % [makeYamlString($n.kind)])
|
||||
|
||||
if maxRecDepth != 0:
|
||||
if conf != nil:
|
||||
res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
|
||||
case n.kind
|
||||
of nkCharLit .. nkUInt64Lit:
|
||||
of nkCharLit .. nkInt64Lit:
|
||||
res.addf("\n$1intVal: $2", [istr, $(n.intVal)])
|
||||
of nkFloatLit .. nkFloat128Lit:
|
||||
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
|
||||
res.addf("\n$1floatVal: $2", [istr, n.floatVal.toStrMaxPrecision])
|
||||
of nkStrLit .. nkTripleStrLit:
|
||||
res.addf("\n$1strVal: $2", [istr, makeYamlString(n.strVal)])
|
||||
of nkSym:
|
||||
res.addf("\n$1sym: ", [istr])
|
||||
res.symToYamlAux(conf, n.sym, marker, true, indent + 1, maxRecDepth)
|
||||
res.symToYamlAux(conf, n.sym, marker, indent + 1, maxRecDepth)
|
||||
of nkIdent:
|
||||
if n.ident != nil:
|
||||
res.addf("\n$1ident: $2", [istr, makeYamlString(n.ident.s)])
|
||||
@@ -139,22 +133,22 @@ proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSe
|
||||
res.addf("\n$1sons: ", [istr])
|
||||
for i in 0 ..< n.len:
|
||||
res.addf("\n$1 - ", [istr])
|
||||
res.treeToYamlAux(conf, n[i], marker, false, indent + 1, maxRecDepth - 1)
|
||||
res.treeToYamlAux(conf, n[i], marker, indent + 1, maxRecDepth - 1)
|
||||
if n.typ != nil:
|
||||
res.addf("\n$1typ: ", [istr])
|
||||
res.typeToYamlAux(conf, n.typ, marker, true, indent + 1, maxRecDepth)
|
||||
res.typeToYamlAux(conf, n.typ, marker, indent + 1, maxRecDepth)
|
||||
|
||||
proc treeToYaml*(conf: ConfigRef; n: PNode; indent: int = 0; maxRecDepth: int = -1): string =
|
||||
var marker = initIntSet()
|
||||
result = newStringOfCap(1024)
|
||||
result.treeToYamlAux(conf, n, marker, false, indent, maxRecDepth)
|
||||
result.treeToYamlAux(conf, n, marker, indent, maxRecDepth)
|
||||
|
||||
proc typeToYaml*(conf: ConfigRef; n: PType; indent: int = 0; maxRecDepth: int = -1): string =
|
||||
var marker = initIntSet()
|
||||
result = newStringOfCap(1024)
|
||||
result.typeToYamlAux(conf, n, marker, false, indent, maxRecDepth)
|
||||
result.typeToYamlAux(conf, n, marker, indent, maxRecDepth)
|
||||
|
||||
proc symToYaml*(conf: ConfigRef; n: PSym; indent: int = 0; maxRecDepth: int = -1): string =
|
||||
var marker = initIntSet()
|
||||
result = newStringOfCap(1024)
|
||||
result.symToYamlAux(conf, n, marker, false, indent, maxRecDepth)
|
||||
result.symToYamlAux(conf, n, marker, indent, maxRecDepth)
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
type
|
||||
Snippet = string
|
||||
Builder = string
|
||||
|
||||
template newBuilder(s: string): Builder =
|
||||
s
|
||||
|
||||
proc addField(obj: var Builder; name, typ: Snippet) =
|
||||
obj.add('\t')
|
||||
obj.add(typ)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool; initializer: Snippet) =
|
||||
obj.add('\t')
|
||||
if field.alignment > 0:
|
||||
obj.add("NIM_ALIGN(")
|
||||
obj.addInt(field.alignment)
|
||||
obj.add(") ")
|
||||
obj.add(typ)
|
||||
if sfNoalias in field.flags:
|
||||
obj.add(" NIM_NOALIAS")
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if field.bitsize != 0:
|
||||
obj.add(":")
|
||||
obj.addInt(field.bitsize)
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc structOrUnion(t: PType): Snippet =
|
||||
let t = t.skipTypes({tyAlias, tySink})
|
||||
if tfUnion in t.flags: "union"
|
||||
else: "struct"
|
||||
|
||||
proc ptrType(t: Snippet): Snippet =
|
||||
t & "*"
|
||||
|
||||
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: string; body: typed) =
|
||||
if tfPacked in typ.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add(structOrUnion(typ))
|
||||
obj.add(" __attribute__((__packed__))")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\n")
|
||||
obj.add(structOrUnion(typ))
|
||||
else:
|
||||
obj.add(structOrUnion(typ))
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
type BaseClassKind = enum
|
||||
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
|
||||
var baseKind = bcNone
|
||||
if typ.kind == tyObject:
|
||||
if typ.baseClass == nil:
|
||||
if lacksMTypeField(typ):
|
||||
baseKind = bcNone
|
||||
elif optTinyRtti in m.config.globalOptions:
|
||||
baseKind = bcNoneTinyRtti
|
||||
else:
|
||||
baseKind = bcNoneRtti
|
||||
elif m.compileToCpp:
|
||||
baseKind = bcCppInherit
|
||||
else:
|
||||
baseKind = bcSupField
|
||||
if baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
let currLen = obj.len
|
||||
case baseKind
|
||||
of bcNone:
|
||||
# rest of the options add a field or don't need it due to inheritance,
|
||||
# we need to add the dummy field for uncheckedarray ahead of time
|
||||
# so that it remains trailing
|
||||
if typ.itemId notin m.g.graph.memberProcsPerType and
|
||||
typ.n != nil and typ.n.len == 1 and typ.n[0].kind == nkSym and
|
||||
typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
|
||||
# only consists of flexible array field, add *initial* dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
of bcCppInherit: discard
|
||||
of bcNoneRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
|
||||
of bcNoneTinyRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
|
||||
of bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
body
|
||||
if baseKind == bcNone and currLen == obj.len and typ.itemId notin m.g.graph.memberProcsPerType:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
obj.add("};\n")
|
||||
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
|
||||
## adds a field with a `struct { ... }` type, building it according to `body`
|
||||
obj.add('\t')
|
||||
if tfPacked in parentTyp.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add("struct __attribute__((__packed__)) {\n")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\nstruct {")
|
||||
else:
|
||||
obj.add("struct {\n")
|
||||
body
|
||||
obj.add("} ")
|
||||
obj.add(fieldName)
|
||||
obj.add(";\n")
|
||||
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addAnonUnion(obj: var Builder; body: typed) =
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("};\n")
|
||||
@@ -11,11 +11,7 @@
|
||||
|
||||
proc canRaiseDisp(p: BProc; n: PNode): bool =
|
||||
# we assume things like sysFatal cannot raise themselves
|
||||
if n.kind == nkSym and n.sym.kind == skMethod:
|
||||
# A base method may be overridden by a branch with a wider exception set.
|
||||
# Its inferred effects describe only the base body, not every vtable target.
|
||||
result = true
|
||||
elif n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
|
||||
if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
|
||||
result = false
|
||||
elif optPanics in p.config.globalOptions or
|
||||
(n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
|
||||
@@ -139,7 +135,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
d.snippet = pl
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
|
||||
if d.k == locNone and p.splitDecls == 0:
|
||||
d = getTempCpp(p, typ.returnType, pl)
|
||||
else:
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
@@ -311,7 +307,7 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
|
||||
else:
|
||||
result = a
|
||||
|
||||
proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||
proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||
result = getTemp(p, a.lode.typ, needsInit=false)
|
||||
genAssignment(p, result, a, {})
|
||||
|
||||
@@ -330,7 +326,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
(optByRef notin param.options or not p.module.compileToCpp):
|
||||
a = initLocExpr(p, n)
|
||||
if n.kind in {nkCharLit..nkNilLit}:
|
||||
addAddrLoc(p.config, expressionsNeedsTmp(p, a), result)
|
||||
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
|
||||
else:
|
||||
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
|
||||
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
|
||||
@@ -340,7 +336,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
# 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 = n.typ.exactReplica
|
||||
n.typ.flags.incl tfVarIsPtr
|
||||
a = initLocExprSingleUse(p, n)
|
||||
a = withTmpIfNeeded(p, a, needsTmp)
|
||||
|
||||
@@ -237,8 +237,6 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
flags
|
||||
let t = skipTypes(dest.t, abstractInst).getUniqueType()
|
||||
for i, t in t.ikids:
|
||||
# Do not produce code for void types
|
||||
if isEmptyType(t): continue
|
||||
let field = "Field$1" % [i.rope]
|
||||
genAssignment(p, optAsgnLoc(dest, t, field),
|
||||
optAsgnLoc(src, t, field), newflags)
|
||||
@@ -662,9 +660,9 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mSubF64: applyFormat("(($4)($1) - ($4)($2))")
|
||||
of mMulF64: applyFormat("(($4)($1) * ($4)($2))")
|
||||
of mDivF64: applyFormat("(($4)($1) / ($4)($2))")
|
||||
of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2 & ($5 - 1)))")
|
||||
of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2 & ($5 - 1)))")
|
||||
of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2 & ($5 - 1)))")
|
||||
of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))")
|
||||
of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))")
|
||||
of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))")
|
||||
of mBitandI: applyFormat("($4)($1 & $2)")
|
||||
of mBitorI: applyFormat("($4)($1 | $2)")
|
||||
of mBitxorI: applyFormat("($4)($1 ^ $2)")
|
||||
@@ -760,18 +758,11 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
|
||||
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
|
||||
typ = typ.last
|
||||
typ = typ.skipTypes(abstractInstOwned)
|
||||
if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and
|
||||
p.module.compileToCpp and e[0].kind == nkHiddenAddr and
|
||||
# don't override existing location:
|
||||
d.k == locNone:
|
||||
if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
|
||||
d = initLocExprSingleUse(p, e[0][0])
|
||||
return
|
||||
else:
|
||||
a = initLocExprSingleUse(p, e[0])
|
||||
|
||||
# bug #23453 #25265
|
||||
if e.typ != nil and e.typ.skipTypes(abstractInst).kind == tyObject:
|
||||
discard getTypeDesc(p.module, e.typ)
|
||||
if d.k == locNone:
|
||||
# dest = *a; <-- We do not know that 'dest' is on the heap!
|
||||
# It is completely wrong to set 'd.storage' here, unless it's not yet
|
||||
@@ -814,11 +805,6 @@ proc cowBracket(p: BProc; n: PNode) =
|
||||
proc cow(p: BProc; n: PNode) {.inline.} =
|
||||
if n.kind == nkHiddenAddr: cowBracket(p, n[0])
|
||||
|
||||
template ignoreConv(e: PNode): bool =
|
||||
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
|
||||
let srcType = e[1].typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
|
||||
sameBackendTypePickyAliases(destType, srcType)
|
||||
|
||||
proc genAddr(p: BProc, e: PNode, d: var TLoc) =
|
||||
# careful 'addr(myptrToArray)' needs to get the ampersand:
|
||||
if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}:
|
||||
@@ -831,15 +817,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
|
||||
d.lode = e
|
||||
else:
|
||||
var a: TLoc = initLocExpr(p, e[0])
|
||||
if e[0].kind in {nkHiddenStdConv, nkHiddenSubConv, nkConv} and not ignoreConv(e[0]):
|
||||
# addr (conv x) introduces a temp because `conv x` is not a rvalue
|
||||
# transform addr ( conv ( x ) ) -> conv ( addr ( x ) )
|
||||
var exprLoc: TLoc = initLocExpr(p, e[0][1])
|
||||
var tmp = getTemp(p, e.typ, needsInit=false)
|
||||
putIntoDest(p, tmp, e, "(($1) $2)" % [getTypeDesc(p.module, e.typ), addrLoc(p.config, exprLoc)])
|
||||
putIntoDest(p, d, e, rdLoc(tmp))
|
||||
else:
|
||||
putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
|
||||
putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
|
||||
|
||||
template inheritLocation(d: var TLoc, a: TLoc) =
|
||||
if d.k == locNone: d.storage = a.storage
|
||||
@@ -854,7 +832,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
|
||||
var
|
||||
i: int = 0
|
||||
var a: TLoc = initLocExpr(p, e[0])
|
||||
let tupType = a.t.skipTypes(abstractInst+{tyVar}+tyUserTypeClasses) # ref #25227
|
||||
let tupType = a.t.skipTypes(abstractInst+{tyVar})
|
||||
assert tupType.kind == tyTuple
|
||||
d.inheritLocation(a)
|
||||
discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
|
||||
@@ -1563,24 +1541,15 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
var t = e.typ.skipTypes(abstractInstOwned)
|
||||
let isRef = t.kind == tyRef
|
||||
|
||||
# check if we need to construct the object in a temporary.
|
||||
# A temp is needed when:
|
||||
# - the constructor produces a ref (isRef)
|
||||
# - the destination is not a writable location (d.k == locNone)
|
||||
# - the constructed type differs from the destination type (subtype
|
||||
# assignments need the genAssignment path for ObjectAssignmentDefect)
|
||||
# - the constructor's field values may alias the destination (isPartOf)
|
||||
# check if we need to construct the object in a temporary
|
||||
var useTemp =
|
||||
isRef or
|
||||
d.k == locNone or
|
||||
(d.t != nil and not sameBackendType(t, d.t.skipTypes(abstractInstOwned))) or
|
||||
(d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or
|
||||
(isPartOf(d.lode, e) != arNo)
|
||||
|
||||
var tmp: TLoc = default(TLoc)
|
||||
var r: Rope
|
||||
let needsZeroMem =
|
||||
nfAllFieldsSet notin e.flags or
|
||||
(optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(t))
|
||||
let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc} or nfAllFieldsSet notin e.flags
|
||||
if useTemp:
|
||||
tmp = getTemp(p, t)
|
||||
r = rdLoc(tmp)
|
||||
@@ -1600,10 +1569,6 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
discard getTypeDesc(p.module, t)
|
||||
let ty = getUniqueType(t)
|
||||
for i in 1..<e.len:
|
||||
if nfPreventCg in e[i].flags:
|
||||
# this is an object constructor node generated by the VM and
|
||||
# this field is in an inactive case branch, don't generate assignment
|
||||
continue
|
||||
var check: PNode = nil
|
||||
if e[i].len == 3 and optFieldCheck in p.options:
|
||||
check = e[i][2]
|
||||
@@ -1658,7 +1623,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||
proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
|
||||
var elem, arr: TLoc
|
||||
if n[1].kind == nkBracket:
|
||||
n[1].typ() = n.typ
|
||||
n[1].typ = n.typ
|
||||
genSeqConstr(p, n[1], d)
|
||||
return
|
||||
if d.k == locNone:
|
||||
@@ -1936,14 +1901,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
|
||||
else: internalError(p.config, e.info, "genArrayLen()")
|
||||
|
||||
proc isTrivialTypesToSnippet(t: PType): Rope =
|
||||
if containsGarbageCollectedRef(t) or
|
||||
hasDestructor(t):
|
||||
result = rope"NIM_FALSE"
|
||||
else:
|
||||
result = rope"NIM_TRUE"
|
||||
|
||||
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc, noinit = false) =
|
||||
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
e[1] = makeAddr(e[1], p.module.idgen)
|
||||
genCall(p, e, d)
|
||||
@@ -1956,22 +1914,17 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc, noinit = false) =
|
||||
let t = skipTypes(e[1].typ, {tyVar})
|
||||
|
||||
var call = initLoc(locCall, e, OnHeap)
|
||||
let name = if noinit: "setLengthSeqUninit" else: "setLengthSeqV2"
|
||||
if not p.module.compileToCpp:
|
||||
const setLenPattern = "($3) #$6(($1)?&($1)->Sup:NIM_NIL, $4, $2, $5)"
|
||||
const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)"
|
||||
call.snippet = ropecg(p.module, setLenPattern, [
|
||||
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
|
||||
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info),
|
||||
isTrivialTypesToSnippet(t.skipTypes(abstractInst)[0]),
|
||||
name])
|
||||
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
|
||||
|
||||
else:
|
||||
const setLenPattern = "($3) #$6($1, $4, $2, $5)"
|
||||
const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)"
|
||||
call.snippet = ropecg(p.module, setLenPattern, [
|
||||
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
|
||||
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info),
|
||||
isTrivialTypesToSnippet(t.skipTypes(abstractInst)[0]),
|
||||
name])
|
||||
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
|
||||
|
||||
genAssignment(p, a, call, {})
|
||||
gcUsage(p.config, e)
|
||||
@@ -2088,7 +2041,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
|
||||
|
||||
proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
const
|
||||
lookupOpr: array[mLeSet..mXorSet, string] = [
|
||||
lookupOpr: array[mLeSet..mMinusSet, string] = [
|
||||
"for ($1 = 0; $1 < $2; $1++) { $n" &
|
||||
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
|
||||
" if (!$3) break;}$n",
|
||||
@@ -2098,8 +2051,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
"if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n",
|
||||
"&",
|
||||
"|",
|
||||
"& ~",
|
||||
"^"]
|
||||
"& ~"]
|
||||
var a, b: TLoc
|
||||
var i: TLoc
|
||||
var setType = skipTypes(e[1].typ, abstractVar)
|
||||
@@ -2130,7 +2082,6 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mMulSet: binaryExpr(p, e, d, "($1 & $2)")
|
||||
of mPlusSet: binaryExpr(p, e, d, "($1 | $2)")
|
||||
of mMinusSet: binaryExpr(p, e, d, "($1 & ~ $2)")
|
||||
of mXorSet: binaryExpr(p, e, d, "($1 ^ $2)")
|
||||
of mInSet:
|
||||
genInOp(p, e, d)
|
||||
else: internalError(p.config, e.info, "genSetOp()")
|
||||
@@ -2158,7 +2109,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
var a = initLocExpr(p, e[1])
|
||||
var b = initLocExpr(p, e[2])
|
||||
putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size]))
|
||||
of mMulSet, mPlusSet, mMinusSet, mXorSet:
|
||||
of mMulSet, mPlusSet, mMinusSet:
|
||||
# we inline the simple for loop for better code generation:
|
||||
i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
|
||||
a = initLocExpr(p, e[1])
|
||||
@@ -2199,8 +2150,6 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
|
||||
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
||||
elif etyp.kind == tyBool and srcTyp.kind in IntegralTypes:
|
||||
putIntoDest(p, d, e, "(($1) != 0)" % [rdCharLoc(a)], a.storage)
|
||||
elif etyp.kind == tyProc and srcTyp.kind == tyProc and sameBackendType(etyp, srcTyp):
|
||||
expr(p, e[1], d)
|
||||
else:
|
||||
if etyp.kind == tyPtr:
|
||||
# generates the definition of structs for casts like cast[ptr object](addr x)[]
|
||||
@@ -2293,7 +2242,8 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
|
||||
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
|
||||
|
||||
proc genConv(p: BProc, e: PNode, d: var TLoc) =
|
||||
if ignoreConv(e):
|
||||
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
|
||||
if sameBackendTypeIgnoreRange(destType, e[1].typ):
|
||||
expr(p, e[1], d)
|
||||
else:
|
||||
genSomeCast(p, e, d)
|
||||
@@ -2368,7 +2318,7 @@ proc genWasMoved(p: BProc; n: PNode) =
|
||||
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
|
||||
|
||||
proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref})
|
||||
var a: TLoc = initLocExpr(p, n[1].skipAddr)
|
||||
if n.len == 4:
|
||||
# generated by liftdestructors:
|
||||
var src: TLoc = initLocExpr(p, n[2])
|
||||
@@ -2477,13 +2427,6 @@ proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
|
||||
|
||||
proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
case op
|
||||
of mAsgn:
|
||||
let kind = if e[0].sym.name.s == "=sink": nkSinkAsgn else: nkAsgn
|
||||
let lhs = e[1].skipHiddenAddr
|
||||
let n = newTreeI(kind, e.info, lhs, e[2])
|
||||
n.typ = e.typ
|
||||
cow(p, e[2])
|
||||
genAsgn(p, n, fastAsgn = kind != nkAsgn)
|
||||
of mOr, mAnd: genAndOr(p, e, d, op)
|
||||
of mNot..mUnaryMinusF64: unaryArith(p, e, d, op)
|
||||
of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op)
|
||||
@@ -2601,9 +2544,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
|
||||
of mSetLengthStr: genSetLengthStr(p, e, d)
|
||||
of mSetLengthSeq: genSetLengthSeq(p, e, d)
|
||||
of mSetLengthSeqUninit: genSetLengthSeq(p, e, d, noinit = true)
|
||||
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mInSet, mXorSet:
|
||||
mInSet:
|
||||
genSetOp(p, e, d, op)
|
||||
of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat:
|
||||
var opr = e[0].sym
|
||||
@@ -2653,12 +2595,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
localError(p.config, e.info,
|
||||
"for --mm:arc|atomicArc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
|
||||
|
||||
let typ = e[1].typ.skipTypes({tyVar, tyRef, tyGenericInst, tyTypeDesc,
|
||||
tyAlias, tyInferred, tySink, tyLent, tyOwned})
|
||||
if hasDisabledAsgn(p.module.g.graph, typ):
|
||||
localError(p.config, e.info,
|
||||
"'deepCopy' is not available for type <" & typeToString(typ) & ">")
|
||||
|
||||
let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
|
||||
var a = initLocExpr(p, x)
|
||||
var b = initLocExpr(p, e[2])
|
||||
@@ -2759,8 +2695,6 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||
for i in 0..<n.len:
|
||||
var it = n[i]
|
||||
if it.kind == nkExprColonExpr: it = it[1]
|
||||
# Do not produce code for void types
|
||||
if it.typ != nil and isEmptyType(it.typ): continue
|
||||
rec = initLoc(locExpr, it, dest[].storage)
|
||||
rec.snippet = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
|
||||
rec.flags.incl(lfEnforceDeref)
|
||||
@@ -2879,11 +2813,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
raiseInstr(p, p.s(cpsStmts))
|
||||
linefmt p, cpsStmts, "}$n", []
|
||||
|
||||
# skip cast when types map to the same C type
|
||||
# this avoids invalid C code like `*(T*)&x` for types that can't have their address taken (e.g., WASM __externref_t)
|
||||
if getTypeDesc(p.module, n.typ) == getTypeDesc(p.module, n[0].typ):
|
||||
expr(p, n[0], d)
|
||||
elif n[0].typ.kind != tyObject:
|
||||
if n[0].typ.kind != tyObject:
|
||||
if n.isLValue:
|
||||
putIntoDest(p, d, n,
|
||||
"(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
||||
@@ -2911,7 +2841,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
var a: TLoc = initLocExpr(p, arg)
|
||||
putIntoDest(p, d, n,
|
||||
"(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
||||
elif p.module.compileToCpp or isImportedType(src):
|
||||
elif p.module.compileToCpp:
|
||||
# C++ implicitly downcasts for us
|
||||
expr(p, arg, d)
|
||||
else:
|
||||
@@ -3139,7 +3069,16 @@ 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 nkAddr, nkHiddenAddr: genAddr(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 nkBracketExpr: genBracketExpr(p, n, d)
|
||||
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
|
||||
of nkDotExpr: genRecordField(p, n, d)
|
||||
@@ -3170,12 +3109,6 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of nkConstSection:
|
||||
if useAliveDataFromDce in p.module.flags:
|
||||
genConstStmt(p, n)
|
||||
else: # enforce addressable consts for exportc
|
||||
let m = p.module
|
||||
for it in n:
|
||||
let symNode = skipPragmaExpr(it[0])
|
||||
if symNode.kind == nkSym and sfExportc in symNode.sym.flags:
|
||||
requestConstImpl(p, symNode.sym)
|
||||
# else: consts generated lazily on use
|
||||
of nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
|
||||
of nkCaseStmt: genCase(p, n, d)
|
||||
@@ -3286,8 +3219,6 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) =
|
||||
if p.vccAndC and t.isEmptyTupleType:
|
||||
result.add "0"
|
||||
for i, a in t.ikids:
|
||||
# Do not produce code for void types
|
||||
if isEmptyType(a): continue
|
||||
if i > 0: result.add ", "
|
||||
getDefaultValue(p, a, info, result)
|
||||
result.add "}"
|
||||
@@ -3419,8 +3350,6 @@ proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Ro
|
||||
if p.vccAndC and n.len == 0:
|
||||
result.add "0"
|
||||
for i in 0..<n.len:
|
||||
# Do not produce code for void types
|
||||
if isEmptyType(tup[i]): continue
|
||||
let it = n[i]
|
||||
if i > 0: result.add ",\n"
|
||||
if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, tup[i], result)
|
||||
|
||||
@@ -66,29 +66,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
var x = typ.baseClass
|
||||
if x != nil: x = x.skipTypes(skipPtrs)
|
||||
specializeResetT(p, accessor.parentObj(p.module), x)
|
||||
if typ.n != nil:
|
||||
if typ.sym != nil and sfImportc in typ.sym.flags:
|
||||
# imported C struct, nimZeroMem
|
||||
lineCg(p, cpsStmts, "#nimZeroMem((void**)&$1, sizeof($2));$n",
|
||||
[accessor, getTypeDesc(p.module, typ)])
|
||||
else:
|
||||
specializeResetN(p, accessor, typ.n, typ)
|
||||
if isCaseObj(typ.n):
|
||||
# The active branch was released above. Clear the complete object so
|
||||
# stale bytes from overlapping branches cannot be traced by the GC.
|
||||
# type
|
||||
# Foo = object
|
||||
# case kind: bool
|
||||
# of true:
|
||||
# a: ref Bar # 8 bytes (pointer)
|
||||
# of false:
|
||||
# b: int # 4 bytes
|
||||
# specializeResetT for b emits accessor.b = 0 — writes 4 bytes
|
||||
# But the union is 8 bytes wide (sized by the largest branch)
|
||||
# The remaining 4 bytes where a used to live are untouched
|
||||
# Those stale bytes could contain a heap pointer the GC traces → crash
|
||||
lineCg(p, cpsStmts, "#nimZeroMem((void**)&$1, sizeof($2));$n",
|
||||
[accessor, getTypeDesc(p.module, typ)])
|
||||
if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i, a in typ.ikids:
|
||||
|
||||
@@ -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
|
||||
containsManagedMemory(v.loc.t):
|
||||
containsGarbageCollectedRef(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)
|
||||
@@ -202,11 +202,11 @@ proc genState(p: BProc, n: PNode) =
|
||||
elif n0.kind == nkStrLit:
|
||||
linefmt(p, cpsStmts, "$1: ;$n", [n0.strVal])
|
||||
|
||||
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int, isReturnStmt = false) =
|
||||
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||
# Called by return and break stmts.
|
||||
# Deals with issues faced when jumping out of try/except/finally stmts.
|
||||
|
||||
var stack = newSeq[tuple[fin: PNode, inExcept: bool, isHidden: bool, label: Natural]](0)
|
||||
var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0)
|
||||
|
||||
inc p.withinBlockLeaveActions
|
||||
for i in 1..howManyTrys:
|
||||
@@ -234,7 +234,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int, isReturnStmt
|
||||
|
||||
# Pop exceptions that was handled by the
|
||||
# except-blocks we are in
|
||||
if noSafePoints notin p.flags and not (isReturnStmt and isClosureIterator(p.prc.typ)):
|
||||
if noSafePoints notin p.flags:
|
||||
for i in countdown(howManyExcepts-1, 0):
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||
|
||||
@@ -306,9 +306,9 @@ proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string =
|
||||
call[i][0]
|
||||
else:
|
||||
call[i]
|
||||
if not param.typ.isCompileTimeOnly and (param.kind != nkBracketExpr or param.typ.kind in
|
||||
if param.kind != nkBracketExpr or param.typ.kind in
|
||||
{tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray,
|
||||
tyVarargs, tySequence, tyString, tyCstring, tyTuple}):
|
||||
tyVarargs, tySequence, tyString, tyCstring, tyTuple}:
|
||||
let tempLoc = initLocExprSingleUse(p, param)
|
||||
didGenTemp = didGenTemp or tempLoc.k == locTemp
|
||||
genOtherArg(p, call, i, typ, result, argsCounter)
|
||||
@@ -509,8 +509,7 @@ proc genReturnStmt(p: BProc, t: PNode) =
|
||||
if (t[0].kind != nkEmpty): genStmts(p, t[0])
|
||||
blockLeaveActions(p,
|
||||
howManyTrys = p.nestedTryStmts.len,
|
||||
howManyExcepts = p.inExceptBlockLen,
|
||||
isReturnStmt = true)
|
||||
howManyExcepts = p.inExceptBlockLen)
|
||||
if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags:
|
||||
# If we're in a finally block, and we came here by exception
|
||||
# consume it before we return.
|
||||
@@ -769,26 +768,12 @@ proc raiseExitCleanup(p: BProc, destroy: string) =
|
||||
[p.nestedTryStmts[^1].label, destroy])
|
||||
|
||||
proc finallyActions(p: BProc) =
|
||||
if p.config.exc != excGoto:
|
||||
# Walk past compiler-injected `nkHiddenTryStmt` wrappers (e.g. ARC's
|
||||
# destructor try/finally that wraps `except T as e:` bodies) to reach
|
||||
# the user's actual try. We must NOT walk past a real user try whose
|
||||
# body we are currently in, because a raise from there will be caught
|
||||
# by that try's own except branches rather than escaping outward.
|
||||
#
|
||||
# If after skipping wrappers the next entry is a user try in its
|
||||
# except branch (inExcept=true), inline its finally body before the
|
||||
# raise propagates — without this, the C++ sibling-catch rule would
|
||||
# cause the user's catch(...)/finally pair to be bypassed and the
|
||||
# finally would be silently dropped.
|
||||
for i in countdown(p.nestedTryStmts.high, 0):
|
||||
if p.nestedTryStmts[i].isHidden:
|
||||
continue
|
||||
if p.nestedTryStmts[i].inExcept:
|
||||
let finallyBlock = p.nestedTryStmts[i].fin
|
||||
if finallyBlock != nil:
|
||||
genSimpleBlock(p, finallyBlock[0])
|
||||
return
|
||||
if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
|
||||
# if the current try stmt have a finally block,
|
||||
# we must execute it before reraising
|
||||
let finallyBlock = p.nestedTryStmts[^1].fin
|
||||
if finallyBlock != nil:
|
||||
genSimpleBlock(p, finallyBlock[0])
|
||||
|
||||
proc raiseInstr(p: BProc; result: var Rope) =
|
||||
if p.config.exc == excGoto:
|
||||
@@ -1074,7 +1059,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
throw;
|
||||
}
|
||||
} catch(...) {
|
||||
// C++ exception occurred, not under Nim's control.
|
||||
// C++ exception occured, not under Nim's control.
|
||||
}
|
||||
{
|
||||
/* finally: */
|
||||
@@ -1094,7 +1079,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp])
|
||||
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
|
||||
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||
|
||||
if t.kind == nkHiddenTryStmt:
|
||||
lineCg(p, cpsStmts, "try {$n", [])
|
||||
@@ -1125,7 +1110,6 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
if hasIf: lineF(p, cpsStmts, "else ", [])
|
||||
startBlock(p)
|
||||
# we handled the error:
|
||||
linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
|
||||
expr(p, t[i][0], d)
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||
endBlock(p)
|
||||
@@ -1255,7 +1239,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||
genLineDir(p, t)
|
||||
cgsym(p.module, "popCurrentExceptionEx")
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
|
||||
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||
startBlock(p, "try {$n")
|
||||
expr(p, t[0], d)
|
||||
endBlock(p)
|
||||
@@ -1324,7 +1308,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||
let lab = p.labels
|
||||
let hasExcept = t[1].kind == nkExceptBranch
|
||||
if hasExcept: inc p.withinTryWithExcept
|
||||
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, Natural lab))
|
||||
p.nestedTryStmts.add((fin, false, Natural lab))
|
||||
|
||||
p.flags.incl nimErrorFlagAccessed
|
||||
|
||||
@@ -1476,7 +1460,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||
lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint])
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, quirkyExceptions, t.kind == nkHiddenTryStmt, 0.Natural))
|
||||
p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural))
|
||||
expr(p, t[0], d)
|
||||
if not quirkyExceptions:
|
||||
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
||||
|
||||
@@ -57,15 +57,11 @@ proc mangleField(m: BModule; name: PIdent): string =
|
||||
|
||||
proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
|
||||
result = "_Z" # Common prefix in Itanium ABI
|
||||
var params = ""
|
||||
var staticLists = ""
|
||||
result.add encodeSym(m, s, makeUnique)
|
||||
if s.typ.len > 1: #we dont care about the return param
|
||||
for i in 1..<s.typ.len:
|
||||
if s.typ[i].isNil: continue
|
||||
params.add encodeType(m, s.typ[i], staticLists)
|
||||
|
||||
result.add encodeSym(m, s, makeUnique, staticLists)
|
||||
result.add params
|
||||
result.add encodeType(m, s.typ[i])
|
||||
|
||||
if result in m.g.mangledPrcs:
|
||||
result = mangleProc(m, s, true)
|
||||
@@ -75,7 +71,7 @@ proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
|
||||
proc fillBackendName(m: BModule; s: PSym) =
|
||||
if s.loc.snippet == "":
|
||||
var result: Rope
|
||||
if s.kind in routineKinds and {optCDebug, optItaniumMangle} * m.g.config.globalOptions == {optCDebug, optItaniumMangle} and
|
||||
if not m.compileToCpp and s.kind in routineKinds and optCDebug in m.g.config.globalOptions and
|
||||
m.g.config.symbolFiles == disabledSf:
|
||||
result = mangleProc(m, s, false).rope
|
||||
else:
|
||||
@@ -85,6 +81,7 @@ proc fillBackendName(m: BModule; s: PSym) =
|
||||
result.add '_'
|
||||
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
|
||||
s.loc.snippet = result
|
||||
writeMangledName(m.ndi, s, m.config)
|
||||
|
||||
proc fillParamName(m: BModule; s: PSym) =
|
||||
if s.loc.snippet == "":
|
||||
@@ -108,6 +105,7 @@ proc fillParamName(m: BModule; s: PSym) =
|
||||
# That would lead to either needing to reload `proxy` or to overwrite the
|
||||
# executable file for the main module, which is running (or both!) -> error.
|
||||
s.loc.snippet = res.rope
|
||||
writeMangledName(m.ndi, s, m.config)
|
||||
|
||||
proc fillLocalName(p: BProc; s: PSym) =
|
||||
assert s.kind in skLocalVars+{skTemp}
|
||||
@@ -119,10 +117,11 @@ proc fillLocalName(p: BProc; s: PSym) =
|
||||
if s.kind == skTemp:
|
||||
# speed up conflict search for temps (these are quite common):
|
||||
if counter != 0: result.add "_" & rope(counter+1)
|
||||
elif s.kind != skResult:
|
||||
elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key):
|
||||
result.add "_" & rope(counter+1)
|
||||
p.sigConflicts.inc(key)
|
||||
s.loc.snippet = result
|
||||
if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
|
||||
|
||||
proc scopeMangledParam(p: BProc; param: PSym) =
|
||||
## parameter generation only takes BModule, not a BProc, so we have to
|
||||
@@ -247,11 +246,14 @@ proc isOrHasImportedCppType(typ: PType): bool =
|
||||
searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType)
|
||||
|
||||
proc hasNoInit(t: PType): bool =
|
||||
let t = skipTypes(t, {tyGenericInst})
|
||||
result = t.sym != nil and sfNoInit in t.sym.flags
|
||||
|
||||
proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope
|
||||
|
||||
proc isObjLackingTypeField(typ: PType): bool {.inline.} =
|
||||
result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
|
||||
(typ.baseClass == nil) or isPureObject(typ))
|
||||
|
||||
proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
|
||||
# Arrays and sets cannot be returned by a C procedure, because C is
|
||||
# such a poor programming language.
|
||||
@@ -299,12 +301,7 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
|
||||
result = tab.getOrDefault(sig)
|
||||
|
||||
proc addAbiCheck(m: BModule; t: PType, name: Rope) =
|
||||
if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize) and
|
||||
not (t.kind == tyObject and searchTypeFor(t, proc (t: PType): bool {.nimcall.} = t.kind == tyUncheckedArray)):
|
||||
# `UncheckedArray`, not `ptr UncheckedArray` type field in object types is a flexible array.
|
||||
# `sizeof` in C and Nim doesn't always return the same value for object types containing it.
|
||||
# making `getSize` in Nim always returns the same value as `sizeof` in C from flexible arrays seems hard.
|
||||
# See `SEQ_DECL_SIZE` in lib/nimbase.h
|
||||
if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize):
|
||||
var msg = "backend & Nim disagree on size for: "
|
||||
msg.addTypeHeader(m.config, t)
|
||||
var msg2 = ""
|
||||
@@ -352,12 +349,7 @@ proc getSimpleTypeDesc(m: BModule; typ: PType): Rope =
|
||||
of tyNil: result = typeNameOrLiteral(m, typ, "void*")
|
||||
of tyInt..tyUInt64:
|
||||
result = typeNameOrLiteral(m, typ, NumericalTypeToStr[typ.kind])
|
||||
of tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.skipModifier)
|
||||
of tyDistinct:
|
||||
result = getSimpleTypeDesc(m, typ.skipModifier)
|
||||
if isImportedType(typ) and result != "":
|
||||
useHeader(m, typ.sym)
|
||||
result = typ.sym.loc.snippet
|
||||
of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.skipModifier)
|
||||
of tyStatic:
|
||||
if typ.n != nil: result = getSimpleTypeDesc(m, skipModifier typ)
|
||||
else:
|
||||
@@ -384,6 +376,11 @@ proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope =
|
||||
result = getSimpleTypeDesc(m, typ)
|
||||
if result == "": result = cacheGetType(m.typeCache, sig)
|
||||
|
||||
proc structOrUnion(t: PType): Rope =
|
||||
let t = t.skipTypes({tyAlias, tySink})
|
||||
if tfUnion in t.flags: "union"
|
||||
else: "struct"
|
||||
|
||||
proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) =
|
||||
if m.compileToCpp:
|
||||
m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
|
||||
@@ -603,7 +600,7 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
|
||||
if t.returnType == nil or isInvalidReturnType(m.config, t):
|
||||
rettype = "void"
|
||||
else:
|
||||
rettype = getTypeDescWeak(m, t.returnType, check, dkResult)
|
||||
rettype = getTypeDescAux(m, t.returnType, check, dkResult)
|
||||
for i in 1..<t.n.len:
|
||||
if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
|
||||
var param = t.n[i].sym
|
||||
@@ -701,7 +698,7 @@ proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool)
|
||||
|
||||
proc genRecordFieldsAux(m: BModule; n: PNode,
|
||||
rectype: PType,
|
||||
check: var IntSet; result: var Builder; unionPrefix = "") =
|
||||
check: var IntSet; result: var Rope; unionPrefix = "") =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
@@ -718,51 +715,64 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
|
||||
let k = lastSon(n[i])
|
||||
if k.kind != nkSym:
|
||||
let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i
|
||||
var a = newBuilder("")
|
||||
var a = newRopeAppender()
|
||||
genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".")
|
||||
if a.len != 0:
|
||||
unionBody.addFieldWithStructType(m, rectype, structName):
|
||||
unionBody.add(a)
|
||||
if a != "":
|
||||
if tfPacked notin rectype.flags:
|
||||
unionBody.add("struct {")
|
||||
else:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
unionBody.add("struct __attribute__((__packed__)){")
|
||||
else:
|
||||
unionBody.addf("#pragma pack(push, 1)$nstruct{", [])
|
||||
unionBody.add(a)
|
||||
unionBody.addf("} $1;$n", [structName])
|
||||
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
unionBody.addf("#pragma pack(pop)$n", [])
|
||||
else:
|
||||
genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix)
|
||||
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
|
||||
if unionBody.len != 0:
|
||||
result.addAnonUnion:
|
||||
result.add(unionBody)
|
||||
if unionBody != "":
|
||||
result.addf("union{\n$1};$n", [unionBody])
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
#assert(field.ast == nil)
|
||||
let sname = mangleRecFieldName(m, field)
|
||||
fillLoc(field.loc, locField, n, unionPrefix & sname, OnUnknown)
|
||||
if field.alignment > 0:
|
||||
result.addf "NIM_ALIGN($1) ", [rope(field.alignment)]
|
||||
# for importcpp'ed objects, we only need to set field.loc, but don't
|
||||
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
|
||||
# with heavily templatized C++ code:
|
||||
if not isImportedCppType(rectype):
|
||||
let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: ""
|
||||
|
||||
let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
|
||||
var typ: Rope = ""
|
||||
var isFlexArray = false
|
||||
var initializer = ""
|
||||
if fieldType.kind == tyUncheckedArray:
|
||||
typ = getTypeDescAux(m, fieldType.elemType, check, dkField)
|
||||
isFlexArray = true
|
||||
result.addf("\t$1 $2[SEQ_DECL_SIZE];$n",
|
||||
[getTypeDescAux(m, fieldType.elemType, check, dkField), sname])
|
||||
elif fieldType.kind == tySequence:
|
||||
# we need to use a weak dependency here for trecursive_table.
|
||||
typ = getTypeDescWeak(m, field.loc.t, check, dkField)
|
||||
result.addf("\t$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, dkField), sname, noAlias])
|
||||
elif field.bitsize != 0:
|
||||
result.addf("\t$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, rope($field.bitsize), noAlias])
|
||||
else:
|
||||
typ = getTypeDescAux(m, field.loc.t, check, dkField)
|
||||
# don't use fieldType here because we need the
|
||||
# tyGenericInst for C++ template support
|
||||
let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags)
|
||||
if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)):
|
||||
var didGenTemp = false
|
||||
initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
|
||||
result.addField(field, sname, typ, isFlexArray, initializer)
|
||||
var initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
|
||||
result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer])
|
||||
else:
|
||||
result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias])
|
||||
else: internalError(m.config, n.info, "genRecordFieldsAux()")
|
||||
|
||||
proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false)
|
||||
|
||||
proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) =
|
||||
proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope =
|
||||
result = newRopeAppender()
|
||||
genRecordFieldsAux(m, typ.n, typ, check, result)
|
||||
if typ.itemId in m.g.graph.memberProcsPerType:
|
||||
let procs = m.g.graph.memberProcsPerType[typ.itemId]
|
||||
@@ -784,38 +794,88 @@ proc fillObjectFields*(m: BModule; typ: PType) =
|
||||
# sometimes generic objects are not consistently merged. We patch over
|
||||
# this fact here.
|
||||
var check = initIntSet()
|
||||
var ignored = newBuilder("")
|
||||
addRecordFields(ignored, m, typ, check)
|
||||
if typ.baseClass != nil:
|
||||
fillObjectFields(m, typ.baseClass.skipTypes(skipPtrs))
|
||||
discard getRecordFields(m, typ, check)
|
||||
|
||||
proc mangleDynLibProc(sym: PSym): Rope
|
||||
|
||||
proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope,
|
||||
check: var IntSet, hasField:var bool): Rope =
|
||||
result = ""
|
||||
if typ.kind == tyObject:
|
||||
if typ.baseClass == nil:
|
||||
if lacksMTypeField(typ):
|
||||
appcg(m, result, " {$n", [])
|
||||
else:
|
||||
if optTinyRtti in m.config.globalOptions:
|
||||
appcg(m, result, " {$n#TNimTypeV2* m_type;$n", [])
|
||||
else:
|
||||
appcg(m, result, " {$n#TNimType* m_type;$n", [])
|
||||
hasField = true
|
||||
elif m.compileToCpp:
|
||||
appcg(m, result, " : public $1 {$n", [baseType])
|
||||
if typ.isException and m.config.exc == excCpp:
|
||||
when false:
|
||||
appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions
|
||||
if typ.sym.magic == mException:
|
||||
# Add cleanup destructor to Exception base class
|
||||
appcg(m, result, "~$1();$n", [name])
|
||||
# define it out of the class body and into the procs section so we don't have to
|
||||
# artificially forward-declare popCurrentExceptionEx (very VERY troublesome for HCR)
|
||||
appcg(m, cfsProcs, "inline $1::~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name])
|
||||
hasField = true
|
||||
else:
|
||||
appcg(m, result, " {$n $1 Sup;$n", [baseType])
|
||||
hasField = true
|
||||
else:
|
||||
result.addf(" {$n", [name])
|
||||
|
||||
proc getRecordDesc(m: BModule; typ: PType, name: Rope,
|
||||
check: var IntSet): Rope =
|
||||
# declare the record:
|
||||
var hasField = false
|
||||
var structOrUnion: string
|
||||
if tfPacked in typ.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
structOrUnion = structOrUnion(typ) & " __attribute__((__packed__))"
|
||||
else:
|
||||
structOrUnion = "#pragma pack(push, 1)\L" & structOrUnion(typ)
|
||||
else:
|
||||
structOrUnion = structOrUnion(typ)
|
||||
var baseType: string = ""
|
||||
if typ.baseClass != nil:
|
||||
baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField)
|
||||
if typ.sym == nil or sfCodegenDecl notin typ.sym.flags:
|
||||
result = newBuilder("")
|
||||
result.addStruct(m, typ, name, baseType):
|
||||
result.addRecordFields(m, typ, check)
|
||||
result = structOrUnion & " " & name
|
||||
result.add(getRecordDescAux(m, typ, name, baseType, check, hasField))
|
||||
let desc = getRecordFields(m, typ, check)
|
||||
if not hasField and typ.itemId notin m.g.graph.memberProcsPerType:
|
||||
if desc == "":
|
||||
result.add("\tchar dummy;\n")
|
||||
elif typ.n.len == 1 and typ.n[0].kind == nkSym:
|
||||
let field = typ.n[0].sym
|
||||
let fieldType = field.typ.skipTypes(abstractInst)
|
||||
if fieldType.kind == tyUncheckedArray:
|
||||
result.add("\tchar dummy;\n")
|
||||
result.add(desc)
|
||||
else:
|
||||
result.add(desc)
|
||||
result.add("};\L")
|
||||
else:
|
||||
var desc = newBuilder("")
|
||||
desc.addRecordFields(m, typ, check)
|
||||
let desc = getRecordFields(m, typ, check)
|
||||
result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType])
|
||||
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add "#pragma pack(pop)\L"
|
||||
|
||||
proc getTupleDesc(m: BModule; typ: PType, name: Rope,
|
||||
check: var IntSet): Rope =
|
||||
result = newBuilder("")
|
||||
result.addStruct(m, typ, name, ""):
|
||||
for i, a in typ.ikids:
|
||||
# Do not produce code for void types
|
||||
if isEmptyType(a): continue
|
||||
result.addField(
|
||||
name = "Field" & $i,
|
||||
typ = getTypeDescAux(m, a, check, dkField))
|
||||
result = "$1 $2 {$n" % [structOrUnion(typ), name]
|
||||
var desc: Rope = ""
|
||||
for i, a in typ.ikids:
|
||||
desc.addf("$1 Field$2;$n",
|
||||
[getTypeDescAux(m, a, check, dkField), rope(i)])
|
||||
if desc == "": result.add("char dummy;\L")
|
||||
else: result.add(desc)
|
||||
result.add("};\L")
|
||||
|
||||
proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
|
||||
# A helper proc for handling cppimport patterns, involving numeric
|
||||
@@ -872,8 +932,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
|
||||
let sig = hashType(origTyp, m.config)
|
||||
|
||||
# tyDistinct matters if it is an importc type
|
||||
result = getTypePre(m, origTyp.skipTypes(irrelevantForBackend-{tyOwned, tyDistinct}), sig)
|
||||
result = getTypePre(m, t, sig)
|
||||
defer: # defer is the simplest in this case
|
||||
if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
|
||||
addAbiCheck(m, t, result)
|
||||
@@ -1072,7 +1131,6 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
else: getTupleDesc(m, t, result, check)
|
||||
if not isImportedType(t):
|
||||
m.s[cfsTypes].add(recdesc)
|
||||
addAbiCheck(m, t, result)
|
||||
elif tfIncompleteStruct notin t.flags:
|
||||
discard # addAbiCheck(m, t, result) # already handled elsewhere
|
||||
of tySet:
|
||||
@@ -1425,36 +1483,25 @@ proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo
|
||||
t.flags.incl tfObjHasKids
|
||||
t = t.baseClass
|
||||
|
||||
proc validTupleTypeFields(t: PType): int =
|
||||
# we want to treat tuples with only void fields as empty, so we need to exclude void types here:
|
||||
result = 0
|
||||
for a in t.kids:
|
||||
if not isEmptyType(a): inc result
|
||||
|
||||
proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
|
||||
genTypeInfoAuxBase(m, typ, typ, name, rope("0"), info)
|
||||
var expr = getNimNode(m)
|
||||
let nonVoidKids = validTupleTypeFields(typ)
|
||||
if nonVoidKids > 0:
|
||||
var tmp = getTempName(m) & "_" & $nonVoidKids
|
||||
genTNimNodeArray(m, tmp, rope(nonVoidKids))
|
||||
var j = 0
|
||||
if not typ.isEmptyTupleType:
|
||||
var tmp = getTempName(m) & "_" & $typ.kidsLen
|
||||
genTNimNodeArray(m, tmp, rope(typ.kidsLen))
|
||||
for i, a in typ.ikids:
|
||||
# Do not produce code for void types
|
||||
if isEmptyType(a): continue
|
||||
var tmp2 = getNimNode(m)
|
||||
m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(j), tmp2])
|
||||
m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
|
||||
m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
|
||||
"$1.offset = offsetof($2, Field$3);$n" &
|
||||
"$1.typ = $4;$n" &
|
||||
"$1.name = \"Field$3\";$n",
|
||||
[tmp2, getTypeDesc(m, origType, dkVar), rope(i), genTypeInfoV1(m, a, info)])
|
||||
inc j
|
||||
m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
|
||||
[expr, rope(nonVoidKids), tmp])
|
||||
[expr, rope(typ.kidsLen), tmp])
|
||||
else:
|
||||
m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2;$n",
|
||||
[expr, rope("0")])
|
||||
[expr, rope(typ.kidsLen)])
|
||||
m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), expr])
|
||||
|
||||
proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
|
||||
@@ -1906,9 +1953,6 @@ proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope =
|
||||
of tyRef:
|
||||
genTypeInfoAux(m, t, t, result, info)
|
||||
if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}:
|
||||
# it may not be used in other places except in `genTraverseProc`,
|
||||
# we have to generate a typedesc for this case, not a weak one
|
||||
discard getTypeDesc(m, origType.last)
|
||||
let markerProc = genTraverseProc(m, origType, sig)
|
||||
m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
||||
of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, types, msgs, wordrecg,
|
||||
platform, trees, options, cgendata, mangleutils, renderer
|
||||
platform, trees, options, cgendata, mangleutils
|
||||
|
||||
import std/[hashes, strutils, formatfloat]
|
||||
|
||||
@@ -120,14 +120,14 @@ proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
|
||||
result.add "_u"
|
||||
result.add $s.itemId.item
|
||||
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string =
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
|
||||
#Module::Type
|
||||
var name = s.name.s & extra
|
||||
var name = s.name.s
|
||||
if makeUnique:
|
||||
name = makeUnique(m, s, name)
|
||||
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
|
||||
|
||||
proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
|
||||
proc encodeType*(m: BModule; t: PType): string =
|
||||
result = ""
|
||||
var kindName = ($t.kind)[2..^1]
|
||||
kindName[0] = toLower($kindName[0])[0]
|
||||
@@ -138,10 +138,10 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
|
||||
result = encodeName(t[0].sym.name.s)
|
||||
result.add "I"
|
||||
for i in 1..<t.len - 1:
|
||||
result.add encodeType(m, t[i], staticLists)
|
||||
result.add encodeType(m, t[i])
|
||||
result.add "E"
|
||||
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
result =
|
||||
case t.kind:
|
||||
of tySequence: encodeName("seq")
|
||||
@@ -150,13 +150,8 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
|
||||
for i in 0..<t.len:
|
||||
let s = t[i]
|
||||
if s.isNil: continue
|
||||
result.add encodeType(m, s, staticLists)
|
||||
result.add encodeType(m, s)
|
||||
result.add "E"
|
||||
of tyStatic:
|
||||
if t.n != nil:
|
||||
staticLists.add "_s" & renderTree(t.n)
|
||||
else:
|
||||
raiseAssert "unreachable"
|
||||
of tyRange:
|
||||
var val = "range_"
|
||||
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
|
||||
@@ -169,7 +164,7 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
|
||||
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
|
||||
result = encodeName(kindName)
|
||||
of tyAlias, tyInferred, tyOwned:
|
||||
result = encodeType(m, t.elementType, staticLists)
|
||||
result = encodeType(m, t.elementType)
|
||||
else:
|
||||
assert false, "encodeType " & $t.kind
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import
|
||||
nversion, nimsets, msgs, bitsets, idents, types,
|
||||
ccgutils, ropes, wordrecg, treetab, cgmeth,
|
||||
rodutils, renderer, cgendata, aliases,
|
||||
lowerings, lineinfos, pathutils, transf,
|
||||
lowerings, ndi, lineinfos, pathutils, transf,
|
||||
injectdestructors, astmsgs, modulepaths, pushpoppragmas,
|
||||
mangleutils
|
||||
|
||||
@@ -97,7 +97,7 @@ proc t(a: TLoc): PType {.inline.} =
|
||||
|
||||
proc lodeTyp(t: PType): PNode =
|
||||
result = newNode(nkEmpty)
|
||||
result.typ() = t
|
||||
result.typ = t
|
||||
|
||||
proc isSimpleConst(typ: PType): bool =
|
||||
let t = skipTypes(typ, abstractVar)
|
||||
@@ -315,10 +315,7 @@ proc genLineDir(p: BProc, t: PNode) =
|
||||
let line = t.info.safeLineNm
|
||||
|
||||
if optEmbedOrigSrc in p.config.globalOptions:
|
||||
var code = sourceLine(p.config, t.info)
|
||||
if code.endsWith('\\'):
|
||||
code.add "#"
|
||||
p.s(cpsStmts).add("// " & code & "\L")
|
||||
p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
|
||||
let lastFileIndex = p.lastLineInfo.fileIndex
|
||||
let freshLine = freshLineInfo(p, t.info)
|
||||
if freshLine:
|
||||
@@ -376,7 +373,6 @@ proc dataField(p: BProc): Rope =
|
||||
|
||||
proc genProcPrototype(m: BModule, sym: PSym)
|
||||
|
||||
include cbuilder
|
||||
include ccgliterals
|
||||
include ccgtypes
|
||||
|
||||
@@ -1153,14 +1149,7 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
|
||||
else:
|
||||
allPathsInBranch(n[i].lastSon)
|
||||
of nkCallKinds:
|
||||
if canRaiseDisp(p, n[0]) or
|
||||
(n[0].kind == nkSym and sfNoReturn in n[0].sym.flags):
|
||||
# requires initializations when encountering unreachable code
|
||||
result = InitRequired
|
||||
elif n[0].kind == nkSym and
|
||||
n[0].sym.magic in {mUnaryMinusI..mAbsI, mAddI..mPred} and
|
||||
optOverflowCheck in p.config.options:
|
||||
# arithmetic operations may raise exceptions
|
||||
if canRaiseDisp(p, n[0]):
|
||||
result = InitRequired
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
@@ -2095,6 +2084,9 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
|
||||
if sfSystemModule in module.flags:
|
||||
incl result.flags, preventStackTrace
|
||||
excl(result.preInitProc.options, optStackTrace)
|
||||
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCfilePath(g.config, filename), "ndi")
|
||||
else: AbsoluteFile""
|
||||
open(result.ndi, ndiName, g.config)
|
||||
|
||||
proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
|
||||
result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
|
||||
@@ -2178,23 +2170,6 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
|
||||
|
||||
genStmts(p, n)
|
||||
|
||||
proc handleProcGlobals(m: BModule) =
|
||||
var procGlobals: seq[PNode] = move m.g.graph.procGlobals
|
||||
|
||||
for i in 0..<procGlobals.len:
|
||||
var stmts = ""
|
||||
|
||||
# fixes recursive calls #24997
|
||||
swap stmts, m.preInitProc.s(cpsStmts)
|
||||
var transformedN = procGlobals[i]
|
||||
if sfInjectDestructors in m.module.flags:
|
||||
transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN)
|
||||
genStmts(m.preInitProc, transformedN)
|
||||
swap stmts, m.preInitProc.s(cpsStmts)
|
||||
|
||||
handleProcGlobals(m)
|
||||
m.preInitProc.s(cpsStmts).add stmts
|
||||
|
||||
proc genTopLevelStmt*(m: BModule; n: PNode) =
|
||||
## Also called from `ic/cbackend.nim`.
|
||||
if pipelineutils.skipCodegen(m.config, n): return
|
||||
@@ -2210,8 +2185,6 @@ proc genTopLevelStmt*(m: BModule; n: PNode) =
|
||||
else:
|
||||
genProcBody(m.initProc, transformedN)
|
||||
|
||||
handleProcGlobals(m)
|
||||
|
||||
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||
if optForceFullMake notin m.config.globalOptions:
|
||||
if not moduleHasChanged(m.g.graph, m.module):
|
||||
@@ -2243,6 +2216,7 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||
# it would generate multiple 'main' procs, for instance.
|
||||
|
||||
proc writeModule(m: BModule, pending: bool) =
|
||||
template onExit() = close(m.ndi, m.config)
|
||||
let cfile = getCFile(m)
|
||||
if moduleHasChanged(m.g.graph, m.module):
|
||||
genInitCode(m)
|
||||
@@ -2260,10 +2234,12 @@ proc writeModule(m: BModule, pending: bool) =
|
||||
when hasTinyCBackend:
|
||||
if m.config.cmd == cmdTcc:
|
||||
tccgen.compileCCode($code, m.config)
|
||||
onExit()
|
||||
return
|
||||
|
||||
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
onExit()
|
||||
|
||||
proc updateCachedModule(m: BModule) =
|
||||
let cfile = getCFile(m)
|
||||
@@ -2275,13 +2251,12 @@ proc updateCachedModule(m: BModule) =
|
||||
addFileToCompile(m.config, cf)
|
||||
|
||||
proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym =
|
||||
let prefixedName = m.config.nimMainPrefix & "NimDestroyGlobals"
|
||||
let procname = getIdent(graph.cache, prefixedName)
|
||||
let procname = getIdent(graph.cache, "NimDestroyGlobals")
|
||||
result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info)
|
||||
result.typ = newProcType(m.module.info, m.idgen, m.module.owner)
|
||||
result.typ.callConv = ccCDecl
|
||||
incl result.flags, sfExportc
|
||||
result.loc.snippet = prefixedName
|
||||
result.loc.snippet = "NimDestroyGlobals"
|
||||
if isDynlib:
|
||||
incl(result.loc.flags, lfExportLib)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, ropes, options,
|
||||
lineinfos, pathutils, modulegraphs
|
||||
ndi, lineinfos, pathutils, modulegraphs
|
||||
|
||||
import std/[intsets, tables, sets]
|
||||
|
||||
@@ -75,13 +75,10 @@ type
|
||||
flags*: set[TCProcFlag]
|
||||
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
||||
currLineInfo*: TLineInfo # AST codegen will make this superfluous
|
||||
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, isHidden: bool, label: Natural]]
|
||||
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
|
||||
# in how many nested try statements we are
|
||||
# (the vars must be volatile then)
|
||||
# `inExcept` is true when we are in the except part of a try block.
|
||||
# `isHidden` is true for compiler-injected `nkHiddenTryStmt` wrappers
|
||||
# (e.g. ARC's destructor try/finally around `except T as e:` bodies);
|
||||
# finallyActions walks past such wrappers to reach the user's try.
|
||||
# bool is true when are in the except part of a try block
|
||||
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
|
||||
# using return in finally statements
|
||||
labels*: Natural # for generating unique labels in the C proc
|
||||
@@ -175,6 +172,7 @@ type
|
||||
# OpenGL wrapper
|
||||
sigConflicts*: CountTable[SigHash]
|
||||
g*: BModuleList
|
||||
ndi*: NdiFile
|
||||
|
||||
template config*(m: BModule): ConfigRef = m.g.config
|
||||
template config*(p: BProc): ConfigRef = p.module.g.config
|
||||
|
||||
@@ -55,7 +55,7 @@ proc methodCall*(n: PNode; conf: ConfigRef): PNode =
|
||||
# replace ordinary method by dispatcher method:
|
||||
let disp = getDispatcher(result[0].sym)
|
||||
if disp != nil:
|
||||
result[0].typ() = disp.typ
|
||||
result[0].typ = disp.typ
|
||||
result[0].sym = disp
|
||||
# change the arguments to up/downcasts to fit the dispatcher's parameters:
|
||||
for i in 1..<result.len:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -118,7 +118,7 @@ const
|
||||
errInvalidCmdLineOption = "invalid command line option: '$1'"
|
||||
errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
|
||||
errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
|
||||
errOffHintsError = "'off', 'hint', 'warning', 'error' or 'usages' expected, but '$1' found"
|
||||
errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found"
|
||||
|
||||
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
|
||||
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
|
||||
@@ -203,14 +203,13 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
else: invalidCmdLineOption(conf, pass, orig, info)
|
||||
|
||||
let isSomeHint = state in {wHint, wHintAsError}
|
||||
let isSomeWarning = state in {wWarning, wWarningAsError}
|
||||
template findNote(noteMin, noteMax, name) =
|
||||
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
|
||||
let x = findStr(noteMin, noteMax, id, errUnknown)
|
||||
if x != errUnknown: notes = {TNoteKind(x)}
|
||||
else:
|
||||
if isSomeHint or isSomeWarning:
|
||||
message(conf, info, warnUnknownNotes, "unknown $#: $#" % [name, id])
|
||||
if isSomeHint:
|
||||
message(conf, info, hintUnknownHint, id)
|
||||
else:
|
||||
localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
case id.normalize
|
||||
@@ -364,7 +363,6 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
result = false
|
||||
of "panics": result = contains(conf.globalOptions, optPanics)
|
||||
of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64)
|
||||
of "mangle": result = contains(conf.globalOptions, optItaniumMangle)
|
||||
else:
|
||||
result = false
|
||||
invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
@@ -460,7 +458,7 @@ template handleStdinOrCmdInput =
|
||||
conf.outDir = getNimcacheDir(conf)
|
||||
|
||||
proc handleStdinInput*(conf: ConfigRef) =
|
||||
conf.projectName = conf.stdinFile.string
|
||||
conf.projectName = "stdinfile"
|
||||
conf.projectIsStdin = true
|
||||
handleStdinOrCmdInput()
|
||||
|
||||
@@ -761,14 +759,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
conf.globalOptions.excl optCDebug
|
||||
else:
|
||||
localError(conf, info, "expected native|gdb|on|off but found " & arg)
|
||||
of "mangle":
|
||||
case arg.normalize
|
||||
of "nim":
|
||||
conf.globalOptions.excl optItaniumMangle
|
||||
of "cpp":
|
||||
conf.globalOptions.incl optItaniumMangle
|
||||
else:
|
||||
localError(conf, info, "expected nim|cpp but found " & arg)
|
||||
of "g": # alias for --debugger:native
|
||||
conf.globalOptions.incl optCDebug
|
||||
conf.options.incl optLineDir
|
||||
@@ -784,7 +774,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "hotcodereloading":
|
||||
processOnOffSwitchG(conf, {optHotCodeReloading}, arg, pass, info)
|
||||
if conf.hcrOn:
|
||||
warningDeprecated(conf, info, "hotCodeReloading is deprecated, see https://github.com/nim-lang/RFCs/issues/573 for further information")
|
||||
defineSymbol(conf.symbols, "hotcodereloading")
|
||||
defineSymbol(conf.symbols, "useNimRtl")
|
||||
# hardcoded linking with dynamic runtime for MSVC for smaller binaries
|
||||
@@ -892,19 +881,11 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "import":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
let m = findModule(conf, arg, toFullPath(conf, info)).string
|
||||
if m.len == 0:
|
||||
localError(conf, info, "Cannot resolve filename: " & arg)
|
||||
else:
|
||||
conf.implicitImports.add(if arg.startsWith(stdPrefix): arg else: m)
|
||||
conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||
of "include":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
let m = findModule(conf, arg, toFullPath(conf, info)).string
|
||||
if m.len == 0:
|
||||
localError(conf, info, "Cannot resolve filename: " & arg)
|
||||
else:
|
||||
conf.implicitIncludes.add m
|
||||
conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||
of "listcmd":
|
||||
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
|
||||
of "asm":
|
||||
@@ -936,14 +917,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
var value: int = 10_000_000
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
if value <= 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
|
||||
if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
|
||||
conf.maxLoopIterationsVM = value
|
||||
of "maxcalldepthvm":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
var value: int = 2_000
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
if value <= 0: localError(conf, info, "maxCallDepthVM must be a positive integer greater than zero")
|
||||
conf.maxCallDepthVM = value
|
||||
of "errormax":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
# Note: `nim check` (etc) can overwrite this.
|
||||
@@ -953,10 +928,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
var value: int = 0
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
conf.errorMax = if value == 0: high(int) else: value
|
||||
of "stdinfile":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.stdinFile = if os.isAbsolute(arg): AbsoluteFile(arg)
|
||||
else: AbsoluteFile(getCurrentDir() / arg)
|
||||
of "verbosity":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
let verbosity = parseInt(arg)
|
||||
@@ -1098,9 +1069,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "shownonexports":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
showNonExportedFields(conf)
|
||||
of "raw":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
docRawOutput(conf)
|
||||
of "exceptions":
|
||||
case arg.normalize
|
||||
of "cpp": conf.exc = excCpp
|
||||
@@ -1132,10 +1100,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
of "stylecheck":
|
||||
case arg.normalize
|
||||
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError, optStyleWarning}
|
||||
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError, optStyleWarning}
|
||||
of "warning": conf.globalOptions = conf.globalOptions + {optStyleWarning} - {optStyleHint, optStyleError}
|
||||
of "error": conf.globalOptions = conf.globalOptions + {optStyleError} - {optStyleHint, optStyleWarning}
|
||||
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
|
||||
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError}
|
||||
of "error": conf.globalOptions = conf.globalOptions + {optStyleError}
|
||||
of "usages": conf.globalOptions.incl optStyleUsages
|
||||
else: localError(conf, info, errOffHintsError % arg)
|
||||
of "showallmismatches":
|
||||
|
||||
@@ -11,16 +11,15 @@
|
||||
## for details. Note this is a first implementation and only the "Concept matching"
|
||||
## section has been implemented.
|
||||
|
||||
import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types,
|
||||
layeredtable, semtypinst
|
||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types
|
||||
|
||||
import std/sets
|
||||
import std/intsets
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
const
|
||||
logBindings = when defined(debugConcepts): true else: false
|
||||
logBindings = false
|
||||
|
||||
## Code dealing with Concept declarations
|
||||
## --------------------------------------
|
||||
@@ -71,282 +70,88 @@ proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
|
||||
## ----------------
|
||||
|
||||
type
|
||||
MatchFlags* = enum
|
||||
mfDontBind # Do not export bindings from the concept match
|
||||
mfBindGenericParam # Export inferred invocation parameters despite mfDontBind
|
||||
mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand
|
||||
|
||||
ConceptTypePair = tuple[conceptId, typeId: ItemId]
|
||||
## Pair of (concept type id, implementation type id) used for cycle detection
|
||||
|
||||
MatchCon = object ## Context we pass around during concept matching.
|
||||
bindings: LayeredIdTable
|
||||
marker: HashSet[ConceptTypePair] ## Tracks (concept, type) pairs being checked to detect cycles.
|
||||
inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
|
||||
## that turned out to be wrong.
|
||||
marker: IntSet ## Some protection against wild runaway recursions.
|
||||
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
|
||||
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
|
||||
## cannot be fixed that easily.
|
||||
## Thus we special case it here.
|
||||
concpt: PType ## current concept being evaluated
|
||||
flags: set[MatchFlags]
|
||||
|
||||
MatchKind = enum
|
||||
mkNoMatch, mkSubset, mkSame
|
||||
|
||||
const
|
||||
asymmetricConceptParamMods = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred} # param modifiers that to not have to match implementation -> concept
|
||||
bindableTypes = {tyGenericParam, tyOr, tyTypeDesc}
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||
|
||||
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool
|
||||
|
||||
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool
|
||||
|
||||
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool
|
||||
|
||||
proc existingBinding(m: MatchCon; key: PType): PType =
|
||||
## checks if we bound the type variable 'key' already to some
|
||||
## concrete type.
|
||||
result = m.bindings.lookup(key)
|
||||
if result == nil:
|
||||
result = key
|
||||
for i in 0..<m.inferred.len:
|
||||
if m.inferred[i][0] == key: return m.inferred[i][1]
|
||||
return nil
|
||||
|
||||
const
|
||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred}
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||
|
||||
proc unrollGenericParam(param: PType): PType =
|
||||
result = param.skipTypes(ignorableForArgType)
|
||||
while result.kind in {tyGenericParam, tyTypeDesc} and result.hasElementType and result.elementType.kind != tyNone:
|
||||
result = result.elementType
|
||||
|
||||
proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable .} =
|
||||
if v.kind == tyTypeDesc:
|
||||
return false
|
||||
var value = unrollGenericParam(v)
|
||||
if value.kind == tyGenericParam:
|
||||
value = existingBinding(m, value)
|
||||
if value.kind == tyGenericParam:
|
||||
if value.hasElementType:
|
||||
value = value.elementType
|
||||
else:
|
||||
return true
|
||||
if value.kind == tyStatic:
|
||||
return false
|
||||
|
||||
if m.magic in {mArrPut, mArrGet} and value.kind in arrPutGetMagicApplies:
|
||||
value = value.last
|
||||
|
||||
let old = existingBinding(m, key)
|
||||
if old != key:
|
||||
# check previously bound value
|
||||
if not matchType(c, old, value, m):
|
||||
return false
|
||||
elif key.hasElementType and not key.elementType.isNil and key.elementType.kind != tyNone:
|
||||
# check constaint
|
||||
if matchType(c, unrollGenericParam(key), value, m) == false:
|
||||
return false
|
||||
|
||||
when logBindings: echo "bind table adding '", key, "', ", value
|
||||
assert value != nil
|
||||
assert value.kind != tyVoid
|
||||
m.bindings.put(key, value)
|
||||
return true
|
||||
|
||||
proc defSignatureType(n: PNode): PType = n[0].sym.typ
|
||||
|
||||
proc conceptBody*(n: PType): PNode = n.n.lastSon
|
||||
|
||||
proc acceptsAllTypes(t: PType): bool=
|
||||
result = false
|
||||
if t.kind == tyAnything:
|
||||
result = true
|
||||
elif t.kind == tyGenericParam:
|
||||
if tfImplicitTypeParam in t.flags:
|
||||
result = true
|
||||
if not t.hasElementType or t.elementType.kind == tyNone:
|
||||
result = true
|
||||
|
||||
proc procDefSignature(s: PSym): PNode {. deprecated .} =
|
||||
var nc = s.ast.copyNode()
|
||||
for i in 0 .. 5:
|
||||
nc.add s.ast[i]
|
||||
nc
|
||||
|
||||
proc matchKids(c: PContext; f, a: PType; m: var MatchCon, start=0): bool=
|
||||
result = true
|
||||
for i in start ..< f.kidsLen - ord(f.kind in {tyGenericInst, tyGenericInvocation}):
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
|
||||
iterator traverseTyOr(t: PType): PType {. closure .}=
|
||||
for i in t.kids:
|
||||
case i.kind:
|
||||
of tyGenericParam:
|
||||
if i.hasElementType:
|
||||
for s in traverseTyOr(i.elementType):
|
||||
yield s
|
||||
else:
|
||||
yield i
|
||||
else:
|
||||
yield i
|
||||
|
||||
proc matchConceptToImpl(c: PContext, f, potentialImpl: PType; m: var MatchCon): bool =
|
||||
assert not(potentialImpl.reduceToBase.kind == tyConcept)
|
||||
let concpt = f.reduceToBase
|
||||
|
||||
# Handle self-referential concepts: when a concept references itself in its body
|
||||
# (e.g., `A = concept; proc test(x: Self, y: A)`), the inner type A has n=nil.
|
||||
# We detect this by checking if the concept has the same symbol name as the
|
||||
# one we're currently matching and has no body (n=nil).
|
||||
if concpt.n.isNil:
|
||||
if concpt.sym != nil and m.concpt.sym != nil and
|
||||
concpt.sym == m.concpt.sym:
|
||||
# Self-reference: check if potentialImpl matches what we're already checking
|
||||
return potentialImpl.id == m.potentialImplementation.id
|
||||
# Concept without body that's not a self-reference - cannot match
|
||||
return false
|
||||
|
||||
# Cycle detection: track (concept, type) pairs to prevent infinite recursion.
|
||||
# Returns true on cycle (coinductive semantics) to support co-dependent concepts.
|
||||
let pair: ConceptTypePair = (concpt.itemId, potentialImpl.itemId)
|
||||
if pair in m.marker:
|
||||
return true
|
||||
m.marker.incl pair
|
||||
|
||||
var efPot = potentialImpl
|
||||
if potentialImpl.isSelf:
|
||||
if m.concpt.n == concpt.n:
|
||||
m.marker.excl pair
|
||||
return true
|
||||
efPot = m.potentialImplementation
|
||||
|
||||
var oldBindings = m.bindings
|
||||
m.bindings = newTypeMapLayer(m.bindings)
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = efPot
|
||||
let oldConcept = m.concpt
|
||||
m.concpt = concpt
|
||||
|
||||
var invocation: PType = nil
|
||||
if f.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
invocation = f
|
||||
result = processConcept(c, concpt, invocation, oldBindings, m)
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
m.concpt = oldConcept
|
||||
m.bindings = oldBindings
|
||||
m.marker.excl pair
|
||||
|
||||
proc cmpConceptDefs(c: PContext, fn, an: PNode, m: var MatchCon): bool=
|
||||
if fn.kind != an.kind:
|
||||
return false
|
||||
if fn[namePos].sym.name != an[namePos].sym.name:
|
||||
return false
|
||||
let
|
||||
ft = fn.defSignatureType
|
||||
at = an.defSignatureType
|
||||
if ft.len != at.len:
|
||||
return false
|
||||
|
||||
for i in 1 ..< ft.n.len:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
|
||||
let aType = at.n[i].typ
|
||||
let fType = ft.n[i].typ
|
||||
|
||||
if aType.isSelf and fType.isSelf:
|
||||
continue
|
||||
|
||||
if not matchType(c, fType, aType, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
return false
|
||||
result = true
|
||||
if not matchReturnType(c, ft.returnType, at.returnType, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
result = false
|
||||
|
||||
proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind =
|
||||
# XXX: In the future this may need extra parameters to carry info for container types
|
||||
if fc.n == ac.n:
|
||||
# This will have to take generic parameters into account at some point
|
||||
return mkSame
|
||||
let
|
||||
fn = fc.conceptBody
|
||||
an = ac.conceptBody
|
||||
sameLen = fc.len == ac.len
|
||||
var match = false
|
||||
for fdef in fn:
|
||||
var cmpResult = false
|
||||
for ia, ndef in an:
|
||||
match = cmpConceptDefs(c, fdef, ndef, m)
|
||||
if match:
|
||||
break
|
||||
if not match:
|
||||
return mkNoMatch
|
||||
return mkSubset
|
||||
|
||||
proc isObjectSubtype(f, a: PType): bool =
|
||||
var t = a
|
||||
result = false
|
||||
while t != nil:
|
||||
t = t.baseClass
|
||||
if t == nil:
|
||||
break
|
||||
t = t.skipTypes({tyPtr,tyRef})
|
||||
if t == nil:
|
||||
break
|
||||
if t.kind != tyObject:
|
||||
break
|
||||
if sameObjectTypes(f, t):
|
||||
result = true
|
||||
break
|
||||
|
||||
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
|
||||
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
## The heart of the concept matching process. 'f' is the formal parameter of some
|
||||
## routine inside the concept that we're looking for. 'a' is the formal parameter
|
||||
## of a routine that might match.
|
||||
|
||||
var
|
||||
a = ao
|
||||
f = fo
|
||||
if a.isSelf:
|
||||
if m.magic in {mArrPut, mArrGet}:
|
||||
return false
|
||||
a = m.potentialImplementation
|
||||
if a.kind in bindableTypes:
|
||||
a = existingBinding(m, ao)
|
||||
if a == ao and a.kind == tyGenericParam and a.hasElementType and a.elementType.kind != tyNone:
|
||||
a = a.elementType
|
||||
|
||||
if f.isConcept:
|
||||
if a.acceptsAllTypes:
|
||||
return false
|
||||
if a.skipTypes(ignorableForArgType).isConcept:
|
||||
# if f is a subset of a then any match to a will also match f. Not the other way around
|
||||
return conceptsMatch(c, a.reduceToBase, f.reduceToBase, m) >= mkSubset
|
||||
else:
|
||||
return matchConceptToImpl(c, f, a, m)
|
||||
|
||||
result = false
|
||||
|
||||
const
|
||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
|
||||
case f.kind
|
||||
of tyAlias:
|
||||
result = matchType(c, f.skipModifier, a, m)
|
||||
of tyTypeDesc:
|
||||
if isSelf(f):
|
||||
let ua = a.skipTypes(asymmetricConceptParamMods)
|
||||
if m.magic in {mArrPut, mArrGet}:
|
||||
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
|
||||
bindParam(c, m, a, last m.potentialImplementation)
|
||||
result = true
|
||||
#elif ua.isConcept:
|
||||
# result = matchType(c, m.concpt, ua, m)
|
||||
else:
|
||||
result = matchType(c, a.skipTypes(ignorableForArgType), m.potentialImplementation, m)
|
||||
#let oldLen = m.inferred.len
|
||||
result = matchType(c, a, m.potentialImplementation, m)
|
||||
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
|
||||
#m.inferred.setLen oldLen
|
||||
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
else:
|
||||
if a.kind == tyTypeDesc:
|
||||
if not(a.hasElementType) or a.elementType.kind == tyNone:
|
||||
result = true
|
||||
elif f.hasElementType:
|
||||
if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
|
||||
if f.hasElementType:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
result = true # both lack it
|
||||
else:
|
||||
result = false
|
||||
|
||||
of tyGenericInvocation:
|
||||
result = false
|
||||
if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
|
||||
if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
|
||||
for i in FirstGenericParamAt ..< f.kidsLen:
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
return true
|
||||
of tyGenericParam:
|
||||
let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
|
||||
if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
|
||||
result = false
|
||||
else:
|
||||
let old = existingBinding(m, f)
|
||||
if old == nil:
|
||||
if f.hasElementType and f.elementType.kind != tyNone:
|
||||
# also check the generic's constraints:
|
||||
let oldLen = m.inferred.len
|
||||
result = matchType(c, f.elementType, a, m)
|
||||
m.inferred.setLen oldLen
|
||||
if result:
|
||||
when logBindings: echo "A adding ", f, " ", ak
|
||||
m.inferred.add((f, ak))
|
||||
elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}:
|
||||
when logBindings: echo "B adding ", f, " ", lastSon ak
|
||||
m.inferred.add((f, last ak))
|
||||
result = true
|
||||
else:
|
||||
when logBindings: echo "C adding ", f, " ", ak
|
||||
m.inferred.add((f, ak))
|
||||
#echo "binding ", typeToString(ak), " to ", typeToString(f)
|
||||
result = true
|
||||
elif not m.marker.containsOrIncl(old.id):
|
||||
result = matchType(c, old, ak, m)
|
||||
if m.magic == mArrPut and ak.kind == tyGenericParam:
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
|
||||
of tyVar, tySink, tyLent, tyOwned:
|
||||
# modifiers in the concept must be there in the actual implementation
|
||||
# too but not vice versa.
|
||||
@@ -354,136 +159,70 @@ proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
elif m.magic == mArrPut:
|
||||
result = matchType(c, f.elementType, a, m)
|
||||
of tyEnum, tyObject, tyDistinct:
|
||||
if a.kind in ignorableForArgType:
|
||||
result = matchType(c, f, a.skipTypes(ignorableForArgType), m)
|
||||
else:
|
||||
if a.kind == tyGenericInst:
|
||||
# tyOr does this to generic typeclasses
|
||||
result = a.base.sym == f.sym
|
||||
else:
|
||||
result = sameType(f, a)
|
||||
if not result and f.kind == tyObject and a.kind == tyObject:
|
||||
result = isObjectSubtype(f, a)
|
||||
result = false
|
||||
of tyEnum, tyObject, tyDistinct:
|
||||
result = sameType(f, a)
|
||||
of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
|
||||
result = a.skipTypes(ignorableForArgType).kind == f.kind
|
||||
of tyBool, tyChar, tyInt..tyUInt64:
|
||||
let ak = a.skipTypes(ignorableForArgType)
|
||||
result = ak.kind == f.kind or ak.kind == tyOrdinal or
|
||||
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
|
||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr:
|
||||
if f.kind == tyArray and f.kidsLen == 3 and a.kind == tyArray:
|
||||
# XXX: this is a work-around!
|
||||
# system.nim creates these for the magic array typeclass
|
||||
result = true
|
||||
else:
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind:
|
||||
if f.base.kind == tyNone:
|
||||
result = true
|
||||
elif f.kidsLen == ak.kidsLen:
|
||||
result = matchKids(c, f, ak, m)
|
||||
of tyGenericInvocation, tyGenericInst:
|
||||
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
|
||||
of tyConcept:
|
||||
let oldLen = m.inferred.len
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = a
|
||||
result = conceptMatchNode(c, f.n.lastSon, m)
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
|
||||
tyGenericInst:
|
||||
# ^ XXX Rewrite this logic, it's more complex than it needs to be.
|
||||
result = false
|
||||
let ea = a.skipTypes(ignorableForArgType)
|
||||
if ea.kind in {tyGenericInst, tyGenericInvocation}:
|
||||
var
|
||||
k1 = f.kidsLen - ord(f.kind == tyGenericInst)
|
||||
k2 = ea.kidsLen - ord(ea.kind == tyGenericInst)
|
||||
if sameType(f.genericHead, ea.genericHead) and k1 == k2:
|
||||
result = true
|
||||
for i in 1 ..< k2:
|
||||
if not matchType(c, f[i], ea[i], m):
|
||||
result = false
|
||||
break
|
||||
elif f.kind == tyGenericInvocation:
|
||||
# bind potential generic constraints into body
|
||||
let body = f.base
|
||||
for i in 1 ..< len(f):
|
||||
bindParam(c,m,body[i-1], f[i])
|
||||
result = matchType(c, body, a, m)
|
||||
else: # tyGenericInst
|
||||
result = matchType(c, f.last, a, m)
|
||||
of tyOrdinal:
|
||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
||||
of tyStatic:
|
||||
var scomp = f.base
|
||||
if scomp.kind == tyGenericParam:
|
||||
if f.base.kidsLen > 0:
|
||||
scomp = scomp.base
|
||||
if a.kind == tyStatic:
|
||||
result = matchType(c, scomp, a.base, m)
|
||||
else:
|
||||
result = matchType(c, scomp, a, m)
|
||||
of tyGenericParam:
|
||||
if a.acceptsAllTypes:
|
||||
discard bindParam(c, m, f, a)
|
||||
result = f.acceptsAllTypes
|
||||
else:
|
||||
result = bindParam(c, m, f, a)
|
||||
of tyAnything:
|
||||
result = true
|
||||
of tyNot:
|
||||
if a.kind == tyNot:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
result = not matchType(c, f.elementType, a, m)
|
||||
m.bindings.setToPreviousLayer()
|
||||
of tyAnd:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
result = true
|
||||
for ff in traverseTyOr(f):
|
||||
let r = matchType(c, ff, a, m)
|
||||
if not r:
|
||||
m.bindings.setToPreviousLayer()
|
||||
result = false
|
||||
break
|
||||
of tyGenericBody:
|
||||
var ak = a
|
||||
if a.kind == tyGenericBody:
|
||||
ak = last(a)
|
||||
result = matchType(c, last(f), ak, m)
|
||||
of tyCompositeTypeClass:
|
||||
if a.kind == tyCompositeTypeClass:
|
||||
result = matchKids(c, f, a, m)
|
||||
else:
|
||||
result = matchType(c, last(f), a, m)
|
||||
of tyBuiltInTypeClass:
|
||||
let target = f.genericHead.kind
|
||||
result = a.skipTypes(ignorableForArgType).reduceToBase.kind == target
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
|
||||
for i in 0..<ak.kidsLen:
|
||||
if not matchType(c, f[i], ak[i], m): return false
|
||||
return true
|
||||
of tyOr:
|
||||
let oldLen = m.inferred.len
|
||||
if a.kind == tyOr:
|
||||
# say the concept requires 'int|float|string' if the potentialImplementation
|
||||
# says 'int|string' that is good enough.
|
||||
var covered = 0
|
||||
for ff in traverseTyOr(f):
|
||||
for aa in traverseTyOr(a):
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
for ff in f.kids:
|
||||
for aa in a.kids:
|
||||
let oldLenB = m.inferred.len
|
||||
let r = matchType(c, ff, aa, m)
|
||||
if r:
|
||||
inc covered
|
||||
break
|
||||
m.bindings.setToPreviousLayer()
|
||||
m.inferred.setLen oldLenB
|
||||
|
||||
result = covered >= a.kidsLen
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
else:
|
||||
result = false
|
||||
for ff in f.kids:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
result = matchType(c, ff, a, m)
|
||||
if result: break # and remember the binding!
|
||||
m.bindings.setToPreviousLayer()
|
||||
of tySet:
|
||||
result = false
|
||||
if a.kind == tySet:
|
||||
m.inferred.setLen oldLen
|
||||
of tyNot:
|
||||
if a.kind == tyNot:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
let oldLen = m.inferred.len
|
||||
result = not matchType(c, f.elementType, a, m)
|
||||
m.inferred.setLen oldLen
|
||||
of tyAnything:
|
||||
result = true
|
||||
of tyOrdinal:
|
||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
||||
else:
|
||||
result = false
|
||||
if result and ao.kind == tyGenericParam:
|
||||
let bf = if f.isSelf: m.potentialImplementation else: f
|
||||
if bindParam(c, m, ao, bf):
|
||||
when logBindings: echo " ^ reverse binding"
|
||||
|
||||
proc checkConstraint(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
result = matchType(c, f, a, m) or matchType(c, a, f, m)
|
||||
|
||||
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
## Like 'matchType' but with extra logic dealing with proc return types
|
||||
@@ -493,38 +232,30 @@ proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
elif a == nil:
|
||||
result = false
|
||||
else:
|
||||
result = checkConstraint(c, f, a, m)
|
||||
result = matchType(c, f, a, m)
|
||||
|
||||
proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
|
||||
## or similar.
|
||||
|
||||
# watch out: only add bindings after a completely successful match.
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
let oldLen = m.inferred.len
|
||||
|
||||
let can = candidate.typ.n
|
||||
let con = defSignatureType(n).n
|
||||
let con = n[0].sym.typ.n
|
||||
|
||||
if can.len < con.len:
|
||||
# too few arguments, cannot be a match:
|
||||
return false
|
||||
|
||||
if can.len > con.len:
|
||||
# too many arguments (not optional)
|
||||
for i in con.len ..< can.len:
|
||||
if can[i].sym.ast == nil:
|
||||
return false
|
||||
|
||||
when defined(debugConcepts):
|
||||
echo "considering: ", renderTree(candidate.procDefSignature), " ", candidate.magic
|
||||
|
||||
|
||||
let common = min(can.len, con.len)
|
||||
for i in 1 ..< common:
|
||||
if not checkConstraint(c, con[i].typ, can[i].typ, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
if not matchType(c, con[i].typ, can[i].typ, m):
|
||||
m.inferred.setLen oldLen
|
||||
return false
|
||||
|
||||
if not matchReturnType(c, n.defSignatureType.returnType, candidate.typ.returnType, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
|
||||
if not matchReturnType(c, n[0].sym.typ.returnType, candidate.typ.returnType, m):
|
||||
m.inferred.setLen oldLen
|
||||
return false
|
||||
|
||||
# all other parameters have to be optional parameters:
|
||||
@@ -532,7 +263,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
assert can[i].kind == nkSym
|
||||
if can[i].sym.ast == nil:
|
||||
# has too many arguments one of which is not optional:
|
||||
m.bindings.setToPreviousLayer()
|
||||
m.inferred.setLen oldLen
|
||||
return false
|
||||
|
||||
return true
|
||||
@@ -540,14 +271,12 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
|
||||
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
|
||||
## 'n' is the nkProcDef or similar from the concept that we try to match.
|
||||
result = false
|
||||
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
|
||||
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
|
||||
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
|
||||
for candidate in candidates:
|
||||
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
||||
m.magic = candidate.magic
|
||||
if matchSym(c, candidate, n, m):
|
||||
result = true
|
||||
break
|
||||
if matchSym(c, candidate, n, m): return true
|
||||
result = false
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
## Traverse the concept's AST ('n') and see if every declaration inside 'n'
|
||||
@@ -580,76 +309,7 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
# error was reported earlier.
|
||||
result = false
|
||||
|
||||
proc resolvedBinding(c: PContext; t: PType; m: MatchCon): PType =
|
||||
## An inferred concept parameter can refer to an implementation-local
|
||||
## generic parameter, for example `Elem[Impl.T]`. Resolve it while the
|
||||
## matcher's private bindings (`Impl.T -> int`) are still available.
|
||||
if t.containsUnresolvedType:
|
||||
prepareMetatypeForSigmatch(c, m.bindings, m.concpt.sym.info, t)
|
||||
else:
|
||||
t
|
||||
|
||||
proc fixBindings(c: PContext; bindings: var LayeredIdTable; concpt: PType;
|
||||
invocation: PType; m: var MatchCon) =
|
||||
# invocation != nil means we have a non-atomic concept:
|
||||
if invocation != nil and invocation.kind == tyGenericInvocation:
|
||||
assert concpt.sym.typ.kind == tyGenericBody
|
||||
|
||||
for i in 0 .. concpt.sym.typ.len - 1:
|
||||
let thisSym = concpt.sym.typ[i]
|
||||
if lookup(bindings, thisSym) != nil:
|
||||
# dont trust the bindings over existing ones
|
||||
continue
|
||||
let found = m.bindings.lookup(thisSym)
|
||||
if found != nil:
|
||||
let resolved = resolvedBinding(c, found, m)
|
||||
when logBindings: echo "Invocation bind: ", thisSym, " ", resolved
|
||||
bindings.put(thisSym, resolved)
|
||||
|
||||
# bind even more generic parameters
|
||||
let genBody = invocation.base
|
||||
assert genBody.kind == tyGenericBody
|
||||
for i in FirstGenericParamAt ..< invocation.kidsLen:
|
||||
let bpram = genBody[i - 1]
|
||||
if lookup(bindings, invocation[i]) != nil:
|
||||
# dont trust the bindings over existing ones
|
||||
continue
|
||||
let boundV = lookup(bindings, bpram)
|
||||
when logBindings: echo "generic body bind: '", invocation[i], "' '", boundV, "'"
|
||||
if boundV != nil:
|
||||
bindings.put(invocation[i], boundV)
|
||||
bindings.put(concpt, m.potentialImplementation)
|
||||
|
||||
proc fixConstraintBindings(c: PContext; bindings: var LayeredIdTable;
|
||||
invocation: PType; m: MatchCon) =
|
||||
## Propagates only the dependent parameters of a concept constraint. The
|
||||
## concept itself and its private matcher bindings must remain unbound so
|
||||
## that independent constraints using the same concept don't get coupled.
|
||||
if invocation != nil and invocation.kind == tyGenericInvocation:
|
||||
let genBody = invocation.base
|
||||
assert genBody.kind == tyGenericBody
|
||||
for i in FirstGenericParamAt ..< invocation.kidsLen:
|
||||
if lookup(bindings, invocation[i]) == nil:
|
||||
let boundValue = m.bindings.lookup(genBody[i - 1])
|
||||
if boundValue != nil:
|
||||
bindings.put(invocation[i], resolvedBinding(c, boundValue, m))
|
||||
|
||||
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool =
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
if invocation != nil and invocation.kind == tyGenericInst:
|
||||
let genericBody = invocation.base
|
||||
for i in 1..<invocation.kidsLen-1:
|
||||
# instGenericContainer can bind `tyVoid`
|
||||
if invocation[i].kind != tyVoid:
|
||||
bindParam(c, m, genericBody[i-1], invocation[i])
|
||||
result = conceptMatchNode(c, concpt.conceptBody, m)
|
||||
if result:
|
||||
if mfDontBind notin m.flags:
|
||||
fixBindings(c, bindings, concpt, invocation, m)
|
||||
elif mfBindGenericParam in m.flags:
|
||||
fixConstraintBindings(c, bindings, invocation, m)
|
||||
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType, flags: set[MatchFlags] = {}): bool =
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool =
|
||||
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
|
||||
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
|
||||
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
|
||||
@@ -658,16 +318,26 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable
|
||||
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
|
||||
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
|
||||
## we do not add any bindings at all on an unsuccessful match!
|
||||
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags, marker: initHashSet[ConceptTypePair]())
|
||||
if arg.isConcept:
|
||||
result = conceptsMatch(c, concpt.reduceToBase, arg.reduceToBase, m) >= mkSubset
|
||||
elif arg.acceptsAllTypes:
|
||||
# XXX: I think this is wrong, or at least partially wrong. Can still test ambiguous types
|
||||
result = false
|
||||
elif mfCheckGeneric in m.flags:
|
||||
# prioritize concepts the least. Specifically if the arg is not a catch all as per above
|
||||
result = true
|
||||
else:
|
||||
result = processConcept(c, concpt, invocation, bindings, m)
|
||||
|
||||
|
||||
var m = MatchCon(inferred: @[], potentialImplementation: arg)
|
||||
result = conceptMatchNode(c, concpt.n.lastSon, m)
|
||||
if result:
|
||||
for (a, b) in m.inferred:
|
||||
if b.kind == tyGenericParam:
|
||||
var dest = b
|
||||
while true:
|
||||
dest = existingBinding(m, dest)
|
||||
if dest == nil or dest.kind != tyGenericParam: break
|
||||
if dest != nil:
|
||||
bindings.idTablePut(a, dest)
|
||||
when logBindings: echo "A bind ", a, " ", dest
|
||||
else:
|
||||
bindings.idTablePut(a, b)
|
||||
when logBindings: echo "B bind ", a, " ", b
|
||||
# we have a match, so bind 'arg' itself to 'concpt':
|
||||
bindings.idTablePut(concpt, arg)
|
||||
# invocation != nil means we have a non-atomic concept:
|
||||
if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1:
|
||||
# bind even more generic parameters
|
||||
assert invocation.kind == tyGenericInvocation
|
||||
for i in FirstGenericParamAt ..< invocation.kidsLen:
|
||||
bindings.idTablePut(invocation[i], arg[i])
|
||||
|
||||
@@ -169,10 +169,3 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasGenericsOpenSym2")
|
||||
defineSymbol("nimHasGenericsOpenSym3")
|
||||
defineSymbol("nimHasJsNoLambdaLifting")
|
||||
defineSymbol("nimHasDefaultFloatRoundtrip")
|
||||
defineSymbol("nimHasXorSet")
|
||||
|
||||
defineSymbol("nimHasPreviewDuplicateModuleError")
|
||||
defineSymbol("nimHasSetLengthSeqUninitMagic")
|
||||
|
||||
defineSymbol("nimHasImplicitRangeConversion")
|
||||
|
||||
@@ -1,340 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2025 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Generate a .build.nif file for nifmake from a Nim project.
|
||||
## This enables incremental and parallel compilation using the `m` switch.
|
||||
|
||||
import std / [os, tables, sets, times, osproc, strutils]
|
||||
import options, msgs, lineinfos
|
||||
|
||||
import "../dist/nimony/src/lib" / [nifstreams, nifcursors, bitabs, nifreader, nifbuilder]
|
||||
import "../dist/nimony/src/gear2" / modnames
|
||||
|
||||
type
|
||||
FilePair = object
|
||||
nimFile: string
|
||||
modname: string
|
||||
|
||||
Node = ref object
|
||||
files: seq[FilePair] # main file + includes
|
||||
deps: seq[int] # indices into DepContext.nodes
|
||||
id: int
|
||||
|
||||
DepContext = object
|
||||
config: ConfigRef
|
||||
nifler: string
|
||||
nodes: seq[Node]
|
||||
processedModules: Table[string, int] # modname -> node index
|
||||
includeStack: seq[string]
|
||||
|
||||
proc toPair(c: DepContext; f: string): FilePair =
|
||||
FilePair(nimFile: f, modname: moduleSuffix(f, cast[seq[string]](c.config.searchPaths)))
|
||||
|
||||
proc depsFile(c: DepContext; f: FilePair): string =
|
||||
getNimcacheDir(c.config).string / f.modname & ".deps.nif"
|
||||
|
||||
proc parsedFile(c: DepContext; f: FilePair): string =
|
||||
getNimcacheDir(c.config).string / f.modname & ".p.nif"
|
||||
|
||||
proc semmedFile(c: DepContext; f: FilePair): string =
|
||||
getNimcacheDir(c.config).string / f.modname & ".nif"
|
||||
|
||||
proc findNifler(): string =
|
||||
# Look for nifler in common locations
|
||||
result = findExe("nifler")
|
||||
if result.len == 0:
|
||||
# Try relative to nim executable
|
||||
let nimDir = getAppDir()
|
||||
result = nimDir / "nifler"
|
||||
if not fileExists(result):
|
||||
result = nimDir / ".." / "nimony" / "bin" / "nifler"
|
||||
if not fileExists(result):
|
||||
result = ""
|
||||
|
||||
proc runNifler(c: DepContext; nimFile: string): bool =
|
||||
## Run nifler deps on a file if needed. Returns true on success.
|
||||
let pair = c.toPair(nimFile)
|
||||
let depsPath = c.depsFile(pair)
|
||||
|
||||
# Check if deps file is up-to-date
|
||||
if fileExists(depsPath) and fileExists(nimFile):
|
||||
if getLastModificationTime(depsPath) > getLastModificationTime(nimFile):
|
||||
return true # Already up-to-date
|
||||
|
||||
# Create output directory if needed
|
||||
createDir(parentDir(depsPath))
|
||||
|
||||
# Run nifler deps
|
||||
let cmd = quoteShell(c.nifler) & " deps " & quoteShell(nimFile) & " " & quoteShell(depsPath)
|
||||
let exitCode = execShellCmd(cmd)
|
||||
result = exitCode == 0
|
||||
|
||||
proc resolveFile(c: DepContext; origin, toResolve: string): string =
|
||||
## Resolve an import path relative to origin file
|
||||
# Handle std/ prefix
|
||||
var path = toResolve
|
||||
if path.startsWith("std/"):
|
||||
path = path.substr(4)
|
||||
|
||||
# Try relative to origin first
|
||||
let originDir = parentDir(origin)
|
||||
result = originDir / path.addFileExt("nim")
|
||||
if fileExists(result):
|
||||
return result
|
||||
|
||||
# Try search paths
|
||||
for searchPath in c.config.searchPaths:
|
||||
result = searchPath.string / path.addFileExt("nim")
|
||||
if fileExists(result):
|
||||
return result
|
||||
|
||||
result = ""
|
||||
|
||||
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node)
|
||||
|
||||
proc processInclude(c: var DepContext; includePath: string; current: Node) =
|
||||
let resolved = resolveFile(c, current.files[current.files.len - 1].nimFile, includePath)
|
||||
if resolved.len == 0 or not fileExists(resolved):
|
||||
return
|
||||
|
||||
# Check for recursive includes
|
||||
for s in c.includeStack:
|
||||
if s == resolved:
|
||||
return # Skip recursive include
|
||||
|
||||
c.includeStack.add resolved
|
||||
current.files.add c.toPair(resolved)
|
||||
traverseDeps(c, c.toPair(resolved), current)
|
||||
discard c.includeStack.pop()
|
||||
|
||||
proc processImport(c: var DepContext; importPath: string; current: Node) =
|
||||
let resolved = resolveFile(c, current.files[0].nimFile, importPath)
|
||||
if resolved.len == 0 or not fileExists(resolved):
|
||||
return
|
||||
|
||||
let pair = c.toPair(resolved)
|
||||
let existingIdx = c.processedModules.getOrDefault(pair.modname, -1)
|
||||
|
||||
if existingIdx == -1:
|
||||
# New module - create node and process it
|
||||
let newNode = Node(files: @[pair], id: c.nodes.len)
|
||||
current.deps.add newNode.id
|
||||
c.processedModules[pair.modname] = newNode.id
|
||||
c.nodes.add newNode
|
||||
traverseDeps(c, pair, newNode)
|
||||
else:
|
||||
# Already processed - just add dependency
|
||||
if existingIdx notin current.deps:
|
||||
current.deps.add existingIdx
|
||||
|
||||
proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
|
||||
## Read a .deps.nif file and process imports/includes
|
||||
let depsPath = c.depsFile(pair)
|
||||
if not fileExists(depsPath):
|
||||
return
|
||||
|
||||
var s = nifstreams.open(depsPath)
|
||||
defer: nifstreams.close(s)
|
||||
discard processDirectives(s.r)
|
||||
|
||||
var t = next(s)
|
||||
if t.kind != ParLe:
|
||||
return
|
||||
|
||||
# Skip to content (past stmts tag)
|
||||
t = next(s)
|
||||
|
||||
while t.kind != EofToken:
|
||||
if t.kind == ParLe:
|
||||
let tag = pool.tags[t.tagId]
|
||||
case tag
|
||||
of "import", "fromimport":
|
||||
# Read import path
|
||||
t = next(s)
|
||||
# Check for "when" marker (conditional import)
|
||||
if t.kind == Ident and pool.strings[t.litId] == "when":
|
||||
t = next(s) # skip it, still process the import
|
||||
# Handle path expression (could be ident, string, or infix like std/foo)
|
||||
var importPath = ""
|
||||
if t.kind == Ident:
|
||||
importPath = pool.strings[t.litId]
|
||||
elif t.kind == StringLit:
|
||||
importPath = pool.strings[t.litId]
|
||||
elif t.kind == ParLe and pool.tags[t.tagId] == "infix":
|
||||
# Handle std / foo style imports
|
||||
t = next(s) # skip infix tag
|
||||
if t.kind == Ident: # operator (/)
|
||||
t = next(s)
|
||||
if t.kind == Ident: # first part (std)
|
||||
importPath = pool.strings[t.litId]
|
||||
t = next(s)
|
||||
if t.kind == Ident: # second part (foo)
|
||||
importPath = importPath & "/" & pool.strings[t.litId]
|
||||
if importPath.len > 0:
|
||||
processImport(c, importPath, current)
|
||||
# Skip to end of import node
|
||||
var depth = 1
|
||||
while depth > 0:
|
||||
t = next(s)
|
||||
if t.kind == ParLe: inc depth
|
||||
elif t.kind == ParRi: dec depth
|
||||
of "include":
|
||||
# Read include path
|
||||
t = next(s)
|
||||
if t.kind == Ident and pool.strings[t.litId] == "when":
|
||||
t = next(s) # skip conditional marker
|
||||
var includePath = ""
|
||||
if t.kind == Ident:
|
||||
includePath = pool.strings[t.litId]
|
||||
elif t.kind == StringLit:
|
||||
includePath = pool.strings[t.litId]
|
||||
if includePath.len > 0:
|
||||
processInclude(c, includePath, current)
|
||||
# Skip to end
|
||||
var depth = 1
|
||||
while depth > 0:
|
||||
t = next(s)
|
||||
if t.kind == ParLe: inc depth
|
||||
elif t.kind == ParRi: dec depth
|
||||
else:
|
||||
# Skip unknown node
|
||||
var depth = 1
|
||||
while depth > 0:
|
||||
t = next(s)
|
||||
if t.kind == ParLe: inc depth
|
||||
elif t.kind == ParRi: dec depth
|
||||
t = next(s)
|
||||
|
||||
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node) =
|
||||
## Process a module: run nifler and read deps
|
||||
if not runNifler(c, pair.nimFile):
|
||||
rawMessage(c.config, errGenerated, "nifler failed for: " & pair.nimFile)
|
||||
return
|
||||
readDepsFile(c, pair, current)
|
||||
|
||||
proc generateBuildFile(c: DepContext): string =
|
||||
## Generate the .build.nif file for nifmake
|
||||
result = getNimcacheDir(c.config).string / c.nodes[0].files[0].modname & ".build.nif"
|
||||
|
||||
var b = nifbuilder.open(result)
|
||||
defer: b.close()
|
||||
|
||||
b.addHeader("nim deps", "nifmake")
|
||||
b.addTree "stmts"
|
||||
|
||||
# Define nifler command
|
||||
b.addTree "cmd"
|
||||
b.addSymbolDef "nifler"
|
||||
b.addStrLit c.nifler
|
||||
b.addStrLit "parse"
|
||||
b.addStrLit "--deps"
|
||||
b.addTree "input"
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
# Define nim m command
|
||||
b.addTree "cmd"
|
||||
b.addSymbolDef "nim_m"
|
||||
b.addStrLit getAppFilename()
|
||||
b.addStrLit "m"
|
||||
# Add search paths
|
||||
for p in c.config.searchPaths:
|
||||
b.addStrLit "--path:" & p.string
|
||||
b.addTree "input"
|
||||
b.addIntLit 0
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
# Build rules for parsing (nifler)
|
||||
var seenFiles = initHashSet[string]()
|
||||
for node in c.nodes:
|
||||
for pair in node.files:
|
||||
let parsed = c.parsedFile(pair)
|
||||
if not seenFiles.containsOrIncl(parsed):
|
||||
b.addTree "do"
|
||||
b.addIdent "nifler"
|
||||
b.addTree "input"
|
||||
b.addStrLit pair.nimFile
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.addStrLit parsed
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.addStrLit c.depsFile(pair)
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
# Build rules for semantic checking (nim m)
|
||||
for i in countdown(c.nodes.len - 1, 0):
|
||||
let node = c.nodes[i]
|
||||
let pair = node.files[0]
|
||||
b.addTree "do"
|
||||
b.addIdent "nim_m"
|
||||
# Input: all parsed files for this module
|
||||
for f in node.files:
|
||||
b.addTree "input"
|
||||
b.addStrLit c.parsedFile(f)
|
||||
b.endTree()
|
||||
# Also depend on semmed files of dependencies
|
||||
for depIdx in node.deps:
|
||||
b.addTree "input"
|
||||
b.addStrLit c.semmedFile(c.nodes[depIdx].files[0])
|
||||
b.endTree()
|
||||
# Output: semmed file
|
||||
b.addTree "output"
|
||||
b.addStrLit c.semmedFile(pair)
|
||||
b.endTree()
|
||||
b.addTree "args"
|
||||
b.addStrLit pair.nimFile
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
b.endTree() # stmts
|
||||
|
||||
proc commandDeps*(conf: ConfigRef) =
|
||||
## Main entry point for `nim deps`
|
||||
when not defined(nimKochBootstrap):
|
||||
let nifler = findNifler()
|
||||
if nifler.len == 0:
|
||||
rawMessage(conf, errGenerated, "nifler tool not found. Install nimony or add nifler to PATH.")
|
||||
return
|
||||
|
||||
let projectFile = conf.projectFull.string
|
||||
if not fileExists(projectFile):
|
||||
rawMessage(conf, errGenerated, "project file not found: " & projectFile)
|
||||
return
|
||||
|
||||
# Create nimcache directory
|
||||
createDir(getNimcacheDir(conf).string)
|
||||
|
||||
var c = DepContext(
|
||||
config: conf,
|
||||
nifler: nifler,
|
||||
nodes: @[],
|
||||
processedModules: initTable[string, int](),
|
||||
includeStack: @[]
|
||||
)
|
||||
|
||||
# Create root node for main project file
|
||||
let rootPair = c.toPair(projectFile)
|
||||
let rootNode = Node(files: @[rootPair], id: 0)
|
||||
c.nodes.add rootNode
|
||||
c.processedModules[rootPair.modname] = 0
|
||||
|
||||
# Process dependencies
|
||||
traverseDeps(c, rootPair, rootNode)
|
||||
|
||||
# Generate build file
|
||||
let buildFile = generateBuildFile(c)
|
||||
rawMessage(conf, hintSuccess, "generated: " & buildFile)
|
||||
rawMessage(conf, hintSuccess, "run: nifmake run " & buildFile)
|
||||
else:
|
||||
rawMessage(conf, errGenerated, "nim deps not available in bootstrap build")
|
||||
@@ -439,8 +439,8 @@ proc gen(c: var Con; n: PNode) =
|
||||
genUse(c, n)
|
||||
of nkIfStmt, nkIfExpr: genIf(c, n)
|
||||
of nkWhenStmt:
|
||||
# This is "when nimvm" node. Chose the second branch.
|
||||
gen(c, n[1][0])
|
||||
# This is "when nimvm" node. Chose the first branch.
|
||||
gen(c, n[0][1])
|
||||
of nkCaseStmt: genCase(c, n)
|
||||
of nkWhileStmt: genWhile(c, n)
|
||||
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
|
||||
@@ -454,7 +454,7 @@ proc gen(c: var Con; n: PNode) =
|
||||
of nkPragmaBlock: gen(c, n.lastSon)
|
||||
of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
|
||||
gen(c, n[0])
|
||||
of nkConv, nkExprColonExpr, nkExprEqExpr, PathKinds1:
|
||||
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
|
||||
gen(c, n[1])
|
||||
of nkVarSection, nkLetSection: genVarSection(c, n)
|
||||
of nkDefer: raiseAssert "dfa construction pass requires the elimination of 'defer'"
|
||||
|
||||
@@ -115,7 +115,7 @@ proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, s
|
||||
|
||||
proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex =
|
||||
let invalid = rstast.FileIndex(-1)
|
||||
result = d.nimToRstFid.getOrDefault(fileIndex, invalid)
|
||||
result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid)
|
||||
if result == invalid:
|
||||
let fname = toFullPath(d.conf, fileIndex)
|
||||
result = addFilename(d.sharedState, fname)
|
||||
@@ -148,7 +148,7 @@ proc cmpDecimalsIgnoreCase(a, b: string): int =
|
||||
limitB = iB
|
||||
while limitA < aLen and isDigit(a[limitA]): inc limitA
|
||||
while limitB < bLen and isDigit(b[limitB]): inc limitB
|
||||
var pos = max(limitA-iA, limitB-iB)
|
||||
var pos = max(limitA-iA, limitB-iA)
|
||||
while pos > 0:
|
||||
if limitA-pos < iA: # digit in `a` is 0 effectively
|
||||
result = ord('0') - ord(b[limitB-pos])
|
||||
@@ -433,9 +433,6 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
|
||||
|
||||
proc genComment(d: PDoc, n: PNode): PRstNode =
|
||||
if n.comment.len > 0:
|
||||
if optDocRaw in d.conf.globalOptions:
|
||||
return newRstLeaf(n.comment)
|
||||
|
||||
d.sharedState.currFileIdx = addRstFileIndex(d, n.info)
|
||||
try:
|
||||
result = parseRst(n.comment,
|
||||
@@ -540,11 +537,10 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
|
||||
elif s != nil and s.kind in {skType, skVar, skLet, skConst} and
|
||||
sfExported in s.flags and s.owner != nil and
|
||||
belongsToProjectPackage(d.conf, s.owner) and d.target == outHtml:
|
||||
let href = (if d.module == s.owner: ""
|
||||
else: externalDep(d, s.owner).changeFileExt("html")
|
||||
) & "#" & literal
|
||||
result.addf "<a href=\"$1\"><span class=\"Identifier\">$2</span></a>",
|
||||
[href, escLit]
|
||||
let external = externalDep(d, s.owner)
|
||||
result.addf "<a href=\"$1#$2\"><span class=\"Identifier\">$3</span></a>",
|
||||
[changeFileExt(external, "html"), literal,
|
||||
escLit]
|
||||
else:
|
||||
dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
|
||||
"\\spanIdentifier{$1}", [escLit])
|
||||
@@ -834,7 +830,7 @@ proc getName(n: PNode): string =
|
||||
of nkAccQuoted:
|
||||
result = "`"
|
||||
for i in 0..<n.len: result.add(getName(n[i]))
|
||||
result.add('`')
|
||||
result = "`"
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getName(n[0])
|
||||
else:
|
||||
@@ -1180,12 +1176,8 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
|
||||
"col": %n.info.col}
|
||||
)
|
||||
if comm != nil:
|
||||
if optDocRaw in d.conf.globalOptions:
|
||||
result.json["description"] = %comm.text
|
||||
else:
|
||||
result.rst = comm
|
||||
result.rstField = "description"
|
||||
|
||||
result.rst = comm
|
||||
result.rstField = "description"
|
||||
if r.buf.len > 0:
|
||||
result.json["code"] = %r.buf
|
||||
if k in routineKinds:
|
||||
@@ -1328,7 +1320,7 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id
|
||||
if t.startsWith("ref "): t = substr(t, 4)
|
||||
effects[i] = newIdentNode(getIdent(cache, t), n.info)
|
||||
# set the type so that the following analysis doesn't screw up:
|
||||
effects[i].typ() = real[i].typ
|
||||
effects[i].typ = real[i].typ
|
||||
|
||||
result = newTreeI(nkExprColonExpr, n.info,
|
||||
newIdentNode(getIdent(cache, $effectType), n.info), effects)
|
||||
@@ -1414,19 +1406,16 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
|
||||
for it in n: traceDeps(d, it)
|
||||
of nkExportStmt:
|
||||
for it in n:
|
||||
if it.kind == nkSym:
|
||||
if d.module != nil and d.module == it.sym.owner: # in current module
|
||||
# bug #23051; don't generate documentation for exported symbols again
|
||||
if sfExported notin it.sym.flags:
|
||||
generateDoc(d, it.sym.ast, orig, config, kForceExport)
|
||||
# else it's to be handled in `of XxxSection` branch
|
||||
# bug #23051; don't generate documentation for exported symbols again
|
||||
if it.kind == nkSym and sfExported notin it.sym.flags:
|
||||
if d.module != nil and d.module == it.sym.owner:
|
||||
generateDoc(d, it.sym.ast, orig, config, kForceExport)
|
||||
elif it.sym.ast != nil:
|
||||
# only export symbols in imported modules, not in current module
|
||||
exportSym(d, it.sym)
|
||||
of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
|
||||
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
|
||||
of nkCallKinds:
|
||||
var comm = default(ItemPre)
|
||||
var comm: ItemPre = default(ItemPre)
|
||||
getAllRunnableExamples(d, n, comm)
|
||||
if comm.len != 0: d.modDescPre.add(comm)
|
||||
else: discard
|
||||
@@ -1900,9 +1889,6 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
|
||||
else:
|
||||
#echo getOutFile(gProjectFull, JsonExt)
|
||||
let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
|
||||
conf.outFile = filename.relativeTo(conf.outDir)
|
||||
let dir = filename.splitFile.dir
|
||||
createDir(dir)
|
||||
try:
|
||||
writeFile(filename, content)
|
||||
except IOError:
|
||||
@@ -1923,10 +1909,8 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
|
||||
if optStdout in d.conf.globalOptions:
|
||||
write(stdout, content)
|
||||
else:
|
||||
#echo getOutFile(gProjectFull, TagsExt)
|
||||
let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
|
||||
conf.outFile = filename.relativeTo(conf.outDir)
|
||||
let dir = filename.splitFile.dir
|
||||
createDir(dir)
|
||||
try:
|
||||
writeFile(filename, content)
|
||||
except IOError:
|
||||
|
||||
@@ -33,10 +33,6 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
|
||||
caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
|
||||
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
|
||||
#newIntTypeNode(nkIntLit, field.position, t)
|
||||
# safety branch for invalid data:
|
||||
caseStmt.add newTree(nkElse,
|
||||
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res),
|
||||
newStrNode("", info))))
|
||||
|
||||
body.add(caseStmt)
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
# the nkPar node:
|
||||
if n.isNil:
|
||||
result = newNode(nkTupleConstr)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
if typ.n.isNil:
|
||||
internalError(conf, "cannot unpack unnamed tuple")
|
||||
unpackObjectAdd(conf, x, typ.n, result)
|
||||
@@ -298,7 +298,7 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
if n.isNil:
|
||||
result = newNode(nkBracket)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
newSeq(result.sons, lengthOrd(conf, typ).toInt)
|
||||
else:
|
||||
result = n
|
||||
@@ -319,7 +319,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
template aw(k, v, field: untyped): untyped =
|
||||
if n.isNil:
|
||||
result = newNode(k)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
else:
|
||||
# check we have the right field:
|
||||
result = n
|
||||
@@ -333,12 +333,12 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
template setNil() =
|
||||
if n.isNil:
|
||||
result = newNode(nkNilLit)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
else:
|
||||
reset n[]
|
||||
result = n
|
||||
result[] = TNode(kind: nkNilLit)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
|
||||
template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
|
||||
template awf(kind, v: untyped): untyped = aw(kind, v, floatVal)
|
||||
@@ -427,7 +427,7 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
# cast through a pointer needs a new inner object:
|
||||
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
|
||||
else: x.copyTree
|
||||
y.typ() = x.typ
|
||||
y.typ = x.typ
|
||||
result = unpack(conf, a, destTyp, y)
|
||||
dealloc a
|
||||
|
||||
@@ -481,7 +481,7 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||
if aTyp.isNil:
|
||||
internalAssert conf, i+1 < fntyp.len
|
||||
aTyp = fntyp[i+1]
|
||||
args[i+start].typ() = aTyp
|
||||
args[i+start].typ = aTyp
|
||||
sig[i] = mapType(conf, aTyp)
|
||||
if sig[i].isNil: globalError(conf, info, "cannot map FFI type")
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
if x == nil:
|
||||
x = copySym(s, c.idgen)
|
||||
# sem'check needs to set the owner properly later, see bug #9476
|
||||
setOwner(x, nil) # c.genSymOwner
|
||||
x.owner = nil # c.genSymOwner
|
||||
#if x.kind == skParam and x.owner.kind == skModule:
|
||||
# internalAssert c.config, false
|
||||
idTablePut(c.mapping, s, x)
|
||||
@@ -182,7 +182,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
|
||||
d.add newSymNode(sym, info)
|
||||
result.add d
|
||||
result.add res
|
||||
result.typ() = res.typ
|
||||
result.typ = res.typ
|
||||
|
||||
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
conf: ConfigRef;
|
||||
|
||||
@@ -63,7 +63,7 @@ type
|
||||
|
||||
# Configuration settings for various compilers.
|
||||
# When adding new compilers, the cmake sources could be a good reference:
|
||||
# https://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
||||
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
||||
|
||||
template compiler(name, settings: untyped): untyped =
|
||||
proc name: TInfoCC {.compileTime.} = settings
|
||||
@@ -162,11 +162,7 @@ compiler vcc:
|
||||
linkerExe: "cl",
|
||||
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
|
||||
includeCmd: " /I",
|
||||
# HACK: we call `cl` so we have to pass `/link` for linker options,
|
||||
# but users may still want to pass arguments to `cl` (see #14221)
|
||||
# to deal with this, we add `/link` before each `/LIBPATH`,
|
||||
# the linker ignores extra `/link`s since it's an unrecognized argument
|
||||
linkDirCmd: " /link /LIBPATH:",
|
||||
linkDirCmd: " /LIBPATH:",
|
||||
linkLibCmd: " $1.lib",
|
||||
debug: " /RTC1 /Z7 ",
|
||||
pic: "",
|
||||
@@ -257,8 +253,8 @@ compiler tcc:
|
||||
linkerExe: "tcc",
|
||||
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: " -L",
|
||||
linkLibCmd: " -l$1",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
debug: " -g ",
|
||||
pic: "",
|
||||
asmStmtFrmt: "asm($1);$n",
|
||||
@@ -474,11 +470,6 @@ 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)
|
||||
@@ -786,7 +777,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||
# https://blog.molecular-matters.com/2017/05/09/deleting-pdb-files-locked-by-visual-studio/
|
||||
# and a bit about the .pdb format in case that is ever needed:
|
||||
# https://github.com/crosire/blink
|
||||
# https://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
|
||||
# http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
|
||||
if conf.hcrOn and isVSCompatible(conf):
|
||||
let t = now()
|
||||
let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
|
||||
@@ -810,59 +801,6 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body
|
||||
(ose.msg & " " & $ose.errorCode))
|
||||
raise
|
||||
|
||||
proc createMacAppBundle(conf: ConfigRef; exefile: AbsoluteFile) =
|
||||
let (dir, name, _) = splitFile(exefile.string)
|
||||
let appBundleName = name & ".app"
|
||||
let appBundlePath = dir / appBundleName
|
||||
let contentsPath = appBundlePath / "Contents"
|
||||
let macosPath = contentsPath / "MacOS"
|
||||
|
||||
createDir(macosPath)
|
||||
|
||||
let bundleExePath = macosPath / name
|
||||
copyFileWithPermissions(exefile.string, bundleExePath)
|
||||
|
||||
let infoPlistPath = contentsPath / "Info.plist"
|
||||
|
||||
proc xmlEscape(s: string): string =
|
||||
result = newStringOfCap(s.len)
|
||||
for c in items(s):
|
||||
case c:
|
||||
of '<': result.add("<")
|
||||
of '>': result.add(">")
|
||||
of '&': result.add("&")
|
||||
of '"': result.add(""")
|
||||
of '\'': result.add("'")
|
||||
else:
|
||||
if ord(c) < 32:
|
||||
result.add("&#" & $ord(c) & ';')
|
||||
else:
|
||||
result.add(c)
|
||||
|
||||
let escapedName = xmlEscape(name)
|
||||
let infoPlistContent = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$1</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.nim.$1</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$1</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>""" % [escapedName]
|
||||
|
||||
writeFile(infoPlistPath, infoPlistContent)
|
||||
|
||||
removeFile(exefile.string)
|
||||
|
||||
rawMessage(conf, hintUserRaw, "Created Mac app bundle: " & appBundlePath)
|
||||
|
||||
proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] =
|
||||
result = @[]
|
||||
when defined(macosx):
|
||||
@@ -1047,10 +985,6 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||
preventLinkCmdMaxCmdLen(conf, linkCmd)
|
||||
for cmd in extraCmds:
|
||||
execExternalProgram(conf, cmd, hintExecuting)
|
||||
# create Mac app bundle for GUI apps on macOS
|
||||
when defined(macosx):
|
||||
if conf.globalOptions * {optGenGuiApp, optGenDynLib, optGenStaticLib} == {optGenGuiApp}:
|
||||
createMacAppBundle(conf, mainOutput)
|
||||
else:
|
||||
linkCmd = ""
|
||||
if optGenScript in conf.globalOptions:
|
||||
@@ -1066,11 +1000,10 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
|
||||
# works out of the box with `hashMainCompilationParams`.
|
||||
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
|
||||
|
||||
const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
|
||||
const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
|
||||
type BuildCache = object
|
||||
cacheVersion: string
|
||||
outputFile: string
|
||||
outputLastModificationTime: string
|
||||
compile: seq[(string, string)]
|
||||
link: seq[string]
|
||||
linkcmd: string
|
||||
@@ -1114,8 +1047,6 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
|
||||
bcache.depfiles.add (path, $secureHashFile(path))
|
||||
|
||||
bcache.nimexe = hashNimExe()
|
||||
if fileExists(bcache.outputFile):
|
||||
bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
|
||||
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
|
||||
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
|
||||
|
||||
@@ -1136,8 +1067,6 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
|
||||
# xxx optimize by returning false if stdin input was the same
|
||||
for (file, hash) in bcache.depfiles:
|
||||
if $secureHashFile(file) != hash: return true
|
||||
if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
|
||||
return true
|
||||
|
||||
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
var bcache: BuildCache = default(BuildCache)
|
||||
@@ -1154,7 +1083,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
"jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
|
||||
[outputCurrent, output, jsonFile.string])
|
||||
var cmds: TStringSeq = default(TStringSeq)
|
||||
var prettyCmds: TStringSeq = default(TStringSeq)
|
||||
var prettyCmds: TStringSeq= default(TStringSeq)
|
||||
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
|
||||
for (name, cmd) in bcache.compile:
|
||||
cmds.add cmd
|
||||
|
||||
@@ -46,7 +46,7 @@ proc isLocation(n: PNode): bool = not n.isValue
|
||||
|
||||
proc isLet(n: PNode): bool =
|
||||
if n.kind == nkSym:
|
||||
if n.sym.kind in {skLet, skConst, skTemp, skForVar}: # guard immutable variables
|
||||
if n.sym.kind in {skLet, skTemp, skForVar}:
|
||||
result = true
|
||||
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
|
||||
abstractInst).kind notin {tyVar}:
|
||||
@@ -1104,7 +1104,7 @@ proc settype(n: PNode): PType =
|
||||
|
||||
proc buildOf(it, loc: PNode; o: Operators): PNode =
|
||||
var s = newNodeI(nkCurly, it.info, it.len-1)
|
||||
s.typ() = settype(loc)
|
||||
s.typ = settype(loc)
|
||||
for i in 0..<it.len-1: s[i] = it[i]
|
||||
result = newNodeI(nkCall, it.info, 3)
|
||||
result[0] = newSymNode(o.opContains)
|
||||
@@ -1165,12 +1165,8 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||
if check[1].kind == nkCurly:
|
||||
result = copyTree(check)
|
||||
if access.kind == nkDotExpr:
|
||||
# change the access to the discriminator field access
|
||||
var a = copyTree(access)
|
||||
# set field name to discriminator field name
|
||||
a[1] = check[2]
|
||||
# set discriminator field type: important for `neg`
|
||||
a.typ() = check[2].typ
|
||||
result[2] = a
|
||||
# 'access.kind != nkDotExpr' can happen for object constructors
|
||||
# which we don't check yet
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# This include implements the high level optimization pass.
|
||||
# included from sem.nim
|
||||
|
||||
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode
|
||||
proc hlo(c: PContext, n: PNode): PNode
|
||||
|
||||
proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
||||
internalAssert c.config, n.kind == nkCall and n[0].kind == nkSym
|
||||
@@ -61,11 +61,10 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||
# activate this pattern again:
|
||||
c.patterns[i] = pattern
|
||||
|
||||
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
|
||||
proc hlo(c: PContext, n: PNode): PNode =
|
||||
inc(c.hloLoopDetector)
|
||||
# simply stop and do not perform any further transformations:
|
||||
if loopDetector > 300:
|
||||
message(c.config, n.info, warnUser, "term rewrite macro instantiation too nested")
|
||||
return n
|
||||
if c.hloLoopDetector > 300: return n
|
||||
case n.kind
|
||||
of nkMacroDef, nkTemplateDef, procDefs:
|
||||
# already processed (special cases in semstmts.nim)
|
||||
@@ -81,7 +80,7 @@ proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
|
||||
# no optimization applied, try subtrees:
|
||||
for i in 0..<result.safeLen:
|
||||
let a = result[i]
|
||||
let h = hlo(c, a, loopDetector)
|
||||
let h = hlo(c, a)
|
||||
if h != a: result[i] = h
|
||||
else:
|
||||
# perform type checking, so that the replacement still fits:
|
||||
@@ -91,15 +90,17 @@ proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
|
||||
result = fitNode(c, n.typ, result, n.info)
|
||||
# optimization has been applied so check again:
|
||||
result = commonOptimizations(c.graph, c.idgen, c.module, result)
|
||||
result = hlo(c, result, loopDetector + 1)
|
||||
result = hlo(c, result)
|
||||
result = commonOptimizations(c.graph, c.idgen, c.module, result)
|
||||
|
||||
proc hloBody(c: PContext, n: PNode): PNode =
|
||||
# fast exit:
|
||||
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
|
||||
result = hlo(c, n, 0)
|
||||
c.hloLoopDetector = 0
|
||||
result = hlo(c, n)
|
||||
|
||||
proc hloStmt(c: PContext, n: PNode): PNode =
|
||||
# fast exit:
|
||||
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
|
||||
result = hlo(c, n, 0)
|
||||
c.hloLoopDetector = 0
|
||||
result = hlo(c, n)
|
||||
|
||||
@@ -838,7 +838,7 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
of nkSym:
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].soperand))
|
||||
if result.typ == nil:
|
||||
result.typ() = result.sym.typ
|
||||
result.typ = result.sym.typ
|
||||
of externIntLit:
|
||||
result.intVal = g[thisModule].fromDisk.numbers[n.litId]
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
@@ -852,7 +852,7 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
transitionNoneToSym(result)
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand))
|
||||
if result.typ == nil:
|
||||
result.typ() = result.sym.typ
|
||||
result.typ = result.sym.typ
|
||||
else:
|
||||
for n0 in sonsReadonly(tree, n):
|
||||
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
|
||||
@@ -942,7 +942,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
result.guard = loadSym(c, g, si, s.guard)
|
||||
result.bitsize = s.bitsize
|
||||
result.alignment = s.alignment
|
||||
setOwner(result, loadSym(c, g, si, s.owner))
|
||||
result.owner = loadSym(c, g, si, s.owner)
|
||||
let externalName = g[si].fromDisk.strings[s.externalName]
|
||||
if externalName != "":
|
||||
result.loc.snippet = externalName
|
||||
@@ -998,7 +998,7 @@ proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
t: PackedType; si, item: int32; result: PType) =
|
||||
result.sym = loadSym(c, g, si, t.sym)
|
||||
setOwner(result, loadSym(c, g, si, t.owner))
|
||||
result.owner = loadSym(c, g, si, t.owner)
|
||||
when false:
|
||||
for op, item in pairs t.attachedOps:
|
||||
result.attachedOps[op] = loadSym(c, g, si, item)
|
||||
@@ -1062,7 +1062,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
|
||||
name: getIdent(cache, splitFile(filename).name),
|
||||
info: newLineInfo(fileIdx, 1, 1),
|
||||
position: int(fileIdx))
|
||||
setOwner(m.module, getPackage(conf, cache, fileIdx))
|
||||
m.module.owner = getPackage(conf, cache, fileIdx)
|
||||
m.module.flags = m.fromDisk.moduleFlags
|
||||
|
||||
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||
|
||||
@@ -13,7 +13,7 @@ import
|
||||
ast, astalgo, msgs, options, idents, lookups,
|
||||
semdata, modulepaths, sigmatch, lineinfos,
|
||||
modulegraphs, wordrecg
|
||||
from std/strutils import `%`, startsWith, replace
|
||||
from std/strutils import `%`, startsWith
|
||||
from std/sequtils import addUnique
|
||||
import std/[sets, tables, intsets]
|
||||
|
||||
@@ -231,7 +231,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
|
||||
for i in 0..n.safeLen-1:
|
||||
importForwarded(c, n[i], exceptSet, fromMod, importSet)
|
||||
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, trackUnusedImport: bool): PSym =
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
||||
result = realModule
|
||||
template createModuleAliasImpl(ident): untyped =
|
||||
createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
|
||||
@@ -246,10 +246,8 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, track
|
||||
result = createModuleAliasImpl(realModule.name)
|
||||
if importHidden:
|
||||
result.options.incl optImportHidden
|
||||
let moduleIdent = if n.kind in {nkInfix, nkImportAs}: n[^1] else: n
|
||||
result.info = moduleIdent.info
|
||||
if trackUnusedImport:
|
||||
c.unusedImports.add((result, result.info))
|
||||
let moduleIdent = if n.kind == nkInfix: n[^1] else: n
|
||||
c.unusedImports.add((result, moduleIdent.info))
|
||||
c.importModuleMap[result.id] = realModule.id
|
||||
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
|
||||
|
||||
@@ -290,11 +288,10 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
toFullPath(c.config, c.graph.importStack[i+1])
|
||||
c.recursiveDep = err
|
||||
|
||||
let trackUnusedImport = warnUnusedImportX in c.config.notes
|
||||
var realModule: PSym
|
||||
discard pushOptionEntry(c)
|
||||
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
|
||||
result = importModuleAs(c, n, realModule, transf.importHidden, trackUnusedImport)
|
||||
result = importModuleAs(c, n, realModule, transf.importHidden)
|
||||
popOptionEntry(c)
|
||||
|
||||
#echo "set back to ", L
|
||||
@@ -307,9 +304,9 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
var prefix = ""
|
||||
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
|
||||
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
|
||||
let moduleNameNorm = getModuleName(c.config, n).replace("\\", "/")
|
||||
if belongsToStdlib(c.graph, result) and not startsWith(moduleNameNorm, stdPrefix) and
|
||||
not startsWith(moduleNameNorm, "system/") and not startsWith(moduleNameNorm, "packages/"):
|
||||
let moduleName = getModuleName(c.config, n)
|
||||
if belongsToStdlib(c.graph, result) and not startsWith(moduleName, stdPrefix) and
|
||||
not startsWith(moduleName, "system/") and not startsWith(moduleName, "packages/"):
|
||||
message(c.config, n.info, warnStdPrefix, realModule.name.s)
|
||||
|
||||
proc suggestMod(n: PNode; s: PSym) =
|
||||
@@ -338,7 +335,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
|
||||
let m = myImportModule(c, it, importStmtResult)
|
||||
if m != nil:
|
||||
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
addDecl(c, m, it.info) # add symbol to symbol table of module
|
||||
importAllSymbols(c, m)
|
||||
#importForwarded(c, m.ast, emptySet, m)
|
||||
afterImport(c, m)
|
||||
@@ -375,7 +372,7 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
|
||||
var m = myImportModule(c, n[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
||||
|
||||
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
|
||||
for i in 1..<n.len:
|
||||
@@ -390,7 +387,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
||||
var m = myImportModule(c, n[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
||||
importAllSymbolsExcept(c, m, readExceptSet(c, n))
|
||||
#importForwarded(c, m.ast, exceptSet, m)
|
||||
afterImport(c, m)
|
||||
|
||||
@@ -24,7 +24,7 @@ import std/[strtabs, tables, strutils, intsets]
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
from trees import exprStructuralEquivalent, getRoot, isCursor, whichPragma
|
||||
from trees import exprStructuralEquivalent, getRoot, whichPragma
|
||||
|
||||
type
|
||||
Con = object
|
||||
@@ -72,11 +72,9 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} =
|
||||
if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
assert(not containsGarbageCollectedRef(t))
|
||||
|
||||
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo; needsInit: bool): PNode =
|
||||
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
|
||||
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.idgen, c.owner, info)
|
||||
sym.typ = typ
|
||||
if not needsInit:
|
||||
sym.flags.incl sfNoInit
|
||||
s.vars.add(sym)
|
||||
result = newSymNode(sym)
|
||||
|
||||
@@ -102,7 +100,6 @@ when false:
|
||||
proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
||||
let root = parampatterns.exprRoot(n, allowCalls=false)
|
||||
if root == nil: return false
|
||||
elif sfSingleUsedTemp in root.flags: return true
|
||||
|
||||
var s = addr(scope)
|
||||
while s != nil:
|
||||
@@ -165,26 +162,33 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
||||
else:
|
||||
result = false
|
||||
|
||||
template hasDestructorOrAsgn(c: var Con, typ: PType): bool =
|
||||
# bug #23354; an object type could have a non-trivial assignements when it is passed to a sink parameter
|
||||
hasDestructor(c, typ) or (c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
typ.kind == tyObject and not isTrivial(getAttachedOp(c.graph, typ, attachedAsgn)))
|
||||
|
||||
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
|
||||
if not hasDestructorOrAsgn(c, n.typ): return true
|
||||
# bug #23354; an object type could have a non-trival assignements when it is passed to a sink parameter
|
||||
if not hasDestructor(c, n.typ) and (n.typ.kind != tyObject or isTrival(getAttachedOp(c.graph, n.typ, attachedAsgn))): return true
|
||||
|
||||
let m = skipConvDfa(n)
|
||||
result = isLastReadImpl(n, c, s)
|
||||
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
|
||||
isLastReadImpl(n, c, s)
|
||||
|
||||
proc isFirstWrite(n: PNode; c: var Con): bool =
|
||||
let m = skipConvDfa(n)
|
||||
result = nfFirstWrite in m.flags
|
||||
|
||||
template isFullyUnpackedTuple(n: PNode): bool =
|
||||
proc isCursor(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
sfCursor in n.sym.flags
|
||||
of nkDotExpr:
|
||||
isCursor(n[1])
|
||||
of nkCheckedFieldExpr:
|
||||
isCursor(n[0])
|
||||
else:
|
||||
false
|
||||
|
||||
template isUnpackedTuple(n: PNode): bool =
|
||||
## we move out all elements of unpacked tuples,
|
||||
## hence unpacked tuples themselves don't need to be destroyed
|
||||
## except it's already a cursor
|
||||
## restricted to `skTemp`, tuple temps where not every field is unpacked should not use `skTemp`
|
||||
(n.kind == nkSym and n.sym.kind == skTemp and
|
||||
n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
|
||||
|
||||
@@ -193,8 +197,7 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFr
|
||||
if inferredFromCopy:
|
||||
m.add ", which is inferred from unavailable '=copy'"
|
||||
|
||||
if (opname == "=" or opname == "=copy" or opname == "=dup") and
|
||||
ri != nil:
|
||||
if (opname == "=" or opname == "=copy" or opname == "=dup") and ri != nil:
|
||||
m.add "; requires a copy because it's not the last read of '"
|
||||
m.add renderTree(ri)
|
||||
m.add '\''
|
||||
@@ -244,12 +247,7 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode
|
||||
dbg:
|
||||
if kind == attachedDestructor:
|
||||
echo "destructor is ", op.id, " ", op.ast
|
||||
if sfError in op.flags:
|
||||
if ri != nil:
|
||||
checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
|
||||
else:
|
||||
# uses the lineinfos of `dest` is `ri` is not available
|
||||
checkForErrorPragma(c, t, dest, AttachedOpToStr[kind])
|
||||
if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
|
||||
c.genOp(op, dest)
|
||||
|
||||
proc genDestroy(c: var Con; dest: PNode): PNode =
|
||||
@@ -277,7 +275,7 @@ proc deepAliases(dest, ri: PNode): bool =
|
||||
return aliases(dest, ri) != no
|
||||
|
||||
proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
if (c.inLoopCond == 0 and (isFullyUnpackedTuple(dest) or IsDecl in flags or
|
||||
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
|
||||
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
|
||||
isNoInit(dest) or IsReturn in flags:
|
||||
# optimize sink call into a bitwise memcopy
|
||||
@@ -293,7 +291,7 @@ proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFla
|
||||
if deepAliases(dest, ri):
|
||||
# consider: x = x + y, it is wrong to destroy the destination first!
|
||||
# tmp to support self assignments
|
||||
let tmp = c.getTemp(s, dest.typ, dest.info, needsInit = false)
|
||||
let tmp = c.getTemp(s, dest.typ, dest.info)
|
||||
result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri),
|
||||
c.genDestroy(tmp))
|
||||
else:
|
||||
@@ -319,9 +317,8 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
|
||||
]#
|
||||
result = dest.kind != nkSym
|
||||
|
||||
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
|
||||
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc:
|
||||
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))
|
||||
@@ -334,7 +331,7 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) =
|
||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
|
||||
else:
|
||||
let xenv = genBuiltin(c.graph, c.idgen, mAccessEnv, "accessEnv", dest)
|
||||
xenv.typ() = getSysType(c.graph, dest.info, tyPointer)
|
||||
xenv.typ = getSysType(c.graph, dest.info, tyPointer)
|
||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
|
||||
|
||||
proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
|
||||
@@ -362,7 +359,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
|
||||
# but fields within active case branch might need destruction
|
||||
|
||||
# tmp to support self assignments
|
||||
let tmp = c.getTemp(s, n[1].typ, n.info, needsInit = false)
|
||||
let tmp = c.getTemp(s, n[1].typ, n.info)
|
||||
|
||||
result = newTree(nkStmtList)
|
||||
result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed))
|
||||
@@ -402,7 +399,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode =
|
||||
result = genOp(c, op, n)
|
||||
else:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "`=wasMoved`", mWasMoved)))
|
||||
result.add copyTree(n) #mWasMoved does not take the address
|
||||
#if n.kind != nkSym:
|
||||
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
|
||||
@@ -410,21 +407,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode =
|
||||
proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
||||
result = newNodeI(nkCall, info)
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault)))
|
||||
result.typ() = t
|
||||
|
||||
proc stabilizeBracketIndex(n: PNode; c: var Con; body: var PNode): PNode =
|
||||
## Evaluate a side-effecting index once and return the stable access.
|
||||
doAssert n.kind == nkBracketExpr and not isAtom(n[1])
|
||||
let temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen,
|
||||
c.owner, n[1].info)
|
||||
temp.typ = n[1].typ
|
||||
let tempAsNode = newSymNode(temp)
|
||||
body.add newTree(nkLetSection, n[1].info,
|
||||
newTree(nkIdentDefs, tempAsNode,
|
||||
newNodeI(nkEmpty, tempAsNode.info), n[1]))
|
||||
result = copyNode(n)
|
||||
result.add n[0]
|
||||
result.add tempAsNode
|
||||
result.typ = t
|
||||
|
||||
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
# generate: (let tmp = v; reset(v); tmp)
|
||||
@@ -437,10 +420,6 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
else:
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
var n = n
|
||||
if n.kind == nkBracketExpr and not isAtom(n[1]):
|
||||
n = stabilizeBracketIndex(n, c, result)
|
||||
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.idgen, c.owner, n.info)
|
||||
temp.typ = n.typ
|
||||
var v = newNodeI(nkLetSection, n.info)
|
||||
@@ -466,50 +445,49 @@ proc isCapturedVar(n: PNode): bool =
|
||||
else: result = false
|
||||
|
||||
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
|
||||
if not hasDestructorOrAsgn(c, nTyp):
|
||||
# Non-managed (plain-old-data) type: no ownership transfer is needed.
|
||||
# Return the expression directly — no temp required.
|
||||
let tmp = c.getTemp(s, nTyp, n.info)
|
||||
if hasDestructor(c, nTyp):
|
||||
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let op = getAttachedOp(c.graph, typ, attachedDup)
|
||||
if op != nil and tfHasOwned notin typ.flags:
|
||||
if sfError in op.flags:
|
||||
c.checkForErrorPragma(nTyp, n, "=dup")
|
||||
else:
|
||||
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
|
||||
if copyOp != nil and sfError in copyOp.flags and
|
||||
sfOverridden notin op.flags:
|
||||
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
|
||||
|
||||
let src = p(n, c, s, normal)
|
||||
var newCall = newTreeIT(nkCall, src.info, src.typ,
|
||||
newSymNode(op),
|
||||
src)
|
||||
c.finishCopy(newCall, n, isFromSink = true)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
newCall
|
||||
)
|
||||
else:
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
message(c.graph.config, n.info, hintPerformance,
|
||||
("passing '$1' to a sink parameter introduces an implicit copy; " &
|
||||
"if possible, rearrange your program's control flow to prevent it") % $n)
|
||||
if c.inEnsureMove > 0:
|
||||
localError(c.graph.config, n.info, errFailedMove,
|
||||
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
|
||||
else:
|
||||
if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
assert(not containsManagedMemory(nTyp))
|
||||
if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
|
||||
return p(n, c, s, normal)
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
let tmp = c.getTemp(s, nTyp, n.info, needsInit = false)
|
||||
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let op = getAttachedOp(c.graph, typ, attachedDup)
|
||||
if op != nil and tfHasOwned notin typ.flags:
|
||||
if sfError in op.flags:
|
||||
c.checkForErrorPragma(nTyp, n, "=dup")
|
||||
else:
|
||||
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
|
||||
if copyOp != nil and sfError in copyOp.flags and
|
||||
sfOverridden notin op.flags:
|
||||
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
|
||||
|
||||
let src = p(n, c, s, normal)
|
||||
var newCall = newTreeIT(nkCall, src.info, src.typ,
|
||||
newSymNode(op),
|
||||
src)
|
||||
c.finishCopy(newCall, n, {}, isFromSink = true)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
newCall
|
||||
)
|
||||
else:
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, {}, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
message(c.graph.config, n.info, hintPerformance,
|
||||
("passing '$1' to a sink parameter introduces an implicit copy; " &
|
||||
"if possible, rearrange your program's control flow to prevent it") % $n)
|
||||
if c.inEnsureMove > 0:
|
||||
localError(c.graph.config, n.info, errFailedMove,
|
||||
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
|
||||
result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
|
||||
# Since we know somebody will take over the produced copy, there is
|
||||
# no need to destroy it.
|
||||
result.add tmp
|
||||
@@ -540,7 +518,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
|
||||
# produce temp creation for (fn, env). But we need to move 'env'?
|
||||
# This was already done in the sink parameter handling logic.
|
||||
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
||||
let tmp = c.getTemp(s, arg.typ, arg.info, true)
|
||||
let tmp = c.getTemp(s, arg.typ, arg.info)
|
||||
result.add c.genSink(s, tmp, arg, {IsDecl})
|
||||
result.add tmp
|
||||
s.final.add c.genDestroy(tmp)
|
||||
@@ -580,7 +558,7 @@ proc cycleCheck(n: PNode; c: var Con) =
|
||||
proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
|
||||
# move the variable declaration to the top of the frame:
|
||||
s.vars.add v.sym
|
||||
if isFullyUnpackedTuple(v):
|
||||
if isUnpackedTuple(v):
|
||||
if c.inLoop > 0:
|
||||
# unpacked tuple needs reset at every loop iteration
|
||||
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
|
||||
@@ -619,7 +597,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
|
||||
# There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
|
||||
# later and use it to eliminate the temporary when theres no need for it, but its
|
||||
# tricky because you would have to intercept moveOrCopy at a certain point
|
||||
let tmp = c.getTemp(s.parent[], ret.typ, ret.info, needsInit = true)
|
||||
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
|
||||
tmp.sym.flags = tmpFlags
|
||||
let cpy = if hasDestructor(c, ret.typ) and
|
||||
ret.typ.kind notin {tyOpenArray, tyVarargs}:
|
||||
@@ -780,10 +758,10 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||
result = copyNode(n)
|
||||
result.add call
|
||||
else:
|
||||
let tmp = c.getTemp(s, n[0].typ, n.info, needsInit = true)
|
||||
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
|
||||
@@ -810,23 +788,6 @@ proc hasCustomDestructor(c: Con, t: PType): bool =
|
||||
obj = skipTypes(obj.baseClass, abstractPtrs)
|
||||
result = result or isCustomDestructor(c, obj)
|
||||
|
||||
const
|
||||
exprBranchKinds = {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt,
|
||||
nkTryStmt, nkPragmaBlock}
|
||||
|
||||
proc distributeAsgn(asgnKind: TNodeKind; dest, ri: PNode; c: var Con; s: var Scope): PNode =
|
||||
## Distributes an assignment ``dest = ri`` into the leaf expressions of
|
||||
## ``ri`` when ``ri`` is an expression-based control flow construct. This
|
||||
## avoids creating pointless intermediate temporaries (bug #25850). The
|
||||
## descent is recursive so that nestings like ``block: ...; if c: a else: b``
|
||||
## assign directly to ``dest`` instead of going through a temp per branch.
|
||||
if ri.kind in exprBranchKinds:
|
||||
template process(child, s): untyped =
|
||||
distributeAsgn(asgnKind, dest, child, c, s)
|
||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||
else:
|
||||
result = newTree(asgnKind, dest, p(ri, c, s, consumed))
|
||||
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode =
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
|
||||
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}:
|
||||
@@ -840,7 +801,15 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result = passCopyToSink(n, c, s)
|
||||
elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
|
||||
nkCallKinds + nkLiterals:
|
||||
result = p(n, c, s, consumed)
|
||||
if n.kind in nkCallKinds and n[0].kind == nkSym:
|
||||
if n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
||||
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
@@ -852,9 +821,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
|
||||
# allow conversions from owned to unowned via this little hack:
|
||||
let nTyp = n[1].typ
|
||||
n[1].typ() = n.typ
|
||||
n[1].typ = n.typ
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
result[1].typ() = nTyp
|
||||
result[1].typ = nTyp
|
||||
else:
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
elif n.kind in {nkObjDownConv, nkObjUpConv}:
|
||||
@@ -916,6 +885,12 @@ 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
|
||||
@@ -933,19 +908,13 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
isDangerous = true
|
||||
|
||||
result = shallowCopy(n)
|
||||
|
||||
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)
|
||||
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:
|
||||
@@ -973,9 +942,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
of nkVarSection, nkLetSection:
|
||||
# transform; var x = y to var x; x op y where op is a move or copy
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
let isInProc = c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}
|
||||
|
||||
for it in n:
|
||||
var ri = it[^1]
|
||||
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
|
||||
@@ -991,15 +957,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
s.locals.add v.sym
|
||||
pVarTopLevel(v, c, s, result)
|
||||
if ri.kind != nkEmpty:
|
||||
let isGlobalPragma = v.kind == nkSym and
|
||||
{sfPure, sfGlobal} <= v.sym.flags and
|
||||
isInProc
|
||||
|
||||
if isGlobalPragma:
|
||||
c.graph.procGlobals.add newTree(nkFastAsgn, v, ri)
|
||||
else:
|
||||
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
||||
result.add value
|
||||
result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
||||
elif ri.kind == nkEmpty and c.inLoop > 0:
|
||||
let skipInit = v.kind == nkDotExpr and # Closure var
|
||||
sfNoInit in v[1].sym.flags
|
||||
@@ -1028,11 +986,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
|
||||
elif isDiscriminantField(n[0]):
|
||||
result = c.genDiscriminantAsgn(s, n)
|
||||
elif n[1].kind in exprBranchKinds:
|
||||
# Distribute the assignment into each branch to avoid
|
||||
# creating pointless temporaries for expression-based control flow.
|
||||
let dest = p(n[0], c, s, mode)
|
||||
result = distributeAsgn(n.kind, dest, n[1], c, s)
|
||||
else:
|
||||
result = copyNode(n)
|
||||
result.add p(n[0], c, s, mode)
|
||||
@@ -1068,9 +1021,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
|
||||
# allow conversions from owned to unowned via this little hack:
|
||||
let nTyp = n[1].typ
|
||||
n[1].typ() = n.typ
|
||||
n[1].typ = n.typ
|
||||
result[1] = p(n[1], c, s, mode)
|
||||
result[1].typ() = nTyp
|
||||
result[1].typ = nTyp
|
||||
else:
|
||||
result[1] = p(n[1], c, s, mode)
|
||||
|
||||
@@ -1157,30 +1110,24 @@ proc sameLocation*(a, b: PNode): bool =
|
||||
else: false
|
||||
|
||||
proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
result = newNodeI(nkStmtList, ri.info)
|
||||
let newAccess = stabilizeBracketIndex(ri, c, result)
|
||||
let snk = c.genSink(s, dest, newAccess, flags)
|
||||
result.add snk
|
||||
result.add c.genWasMoved(newAccess)
|
||||
# with side effects
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen, c.owner, ri[1].info)
|
||||
temp.typ = ri[1].typ
|
||||
var v = newNodeI(nkLetSection, ri[1].info)
|
||||
let tempAsNode = newSymNode(temp)
|
||||
|
||||
proc ownsData(c: var Con; s: var Scope; orig: PNode; flags: set[MoveOrCopyFlag]): PNode =
|
||||
var n = orig
|
||||
while true:
|
||||
case n.kind
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
|
||||
n = n[0]
|
||||
else:
|
||||
break
|
||||
if n.kind in nkCallKinds and n.typ != nil and hasDestructor(c, n.typ):
|
||||
result = newNodeIT(nkStmtListExpr, orig.info, orig.typ)
|
||||
let tmp = c.getTemp(s, n.typ, n.info, needsInit = true)
|
||||
tmp.sym.flags.incl sfSingleUsedTemp
|
||||
result.add newTree(nkFastAsgn, tmp, copyTree(n))
|
||||
s.final.add c.genDestroy(tmp)
|
||||
n[] = tmp[]
|
||||
result.add copyTree(orig)
|
||||
else:
|
||||
result = nil
|
||||
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
||||
vpart[0] = tempAsNode
|
||||
vpart[1] = newNodeI(nkEmpty, tempAsNode.info)
|
||||
vpart[2] = ri[1]
|
||||
v.add(vpart)
|
||||
|
||||
var newAccess = copyNode(ri)
|
||||
newAccess.add ri[0]
|
||||
newAccess.add tempAsNode
|
||||
|
||||
var snk = c.genSink(s, dest, newAccess, flags)
|
||||
result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
var ri = ri
|
||||
@@ -1208,7 +1155,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
of nkCallKinds:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkBracketExpr:
|
||||
if isFullyUnpackedTuple(ri[0]):
|
||||
if isUnpackedTuple(ri[0]):
|
||||
# unpacking of tuple: take over the elements
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
|
||||
@@ -1226,7 +1173,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, flags, isFromSink = false)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
@@ -1234,7 +1181,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, flags, isFromSink = false)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
else:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
@@ -1255,24 +1202,17 @@ 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, flags, isFromSink = false)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
|
||||
if IsExplicitSink in flags:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
else:
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock:
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
|
||||
# We know the result will be a stmt so we use that fact to optimize
|
||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||
of nkRaiseStmt:
|
||||
result = pRaiseStmt(ri, c, s)
|
||||
else:
|
||||
let isOwnsData = ownsData(c, s, ri2, flags)
|
||||
|
||||
if isOwnsData != nil:
|
||||
result = moveOrCopy(dest, isOwnsData, c, s, flags)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
|
||||
canBeMoved(c, dest.typ):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(s, dest, ri, flags)
|
||||
@@ -1282,7 +1222,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, flags, isFromSink = false)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
|
||||
when false:
|
||||
proc computeUninit(c: var Con) =
|
||||
|
||||
@@ -6,7 +6,7 @@ Name: "Nim"
|
||||
Version: "$version"
|
||||
Platforms: """
|
||||
windows: i386;amd64
|
||||
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;s390x;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
|
||||
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
|
||||
macosx: i386;amd64;powerpc64;arm64
|
||||
solaris: i386;amd64;sparc;sparc64
|
||||
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
|
||||
|
||||
@@ -166,7 +166,7 @@ proc containsVariable(n: PNode): bool =
|
||||
proc checkIsolate*(n: PNode): bool =
|
||||
if types.containsTyRef(n.typ):
|
||||
# XXX Maybe require that 'n.typ' is acyclic. This is not much
|
||||
# worse than the already existing inheritance and closure restrictions.
|
||||
# worse than the already exisiting inheritance and closure restrictions.
|
||||
case n.kind
|
||||
of nkCharLit..nkNilLit:
|
||||
result = true
|
||||
|
||||
@@ -34,7 +34,7 @@ import
|
||||
ropes, wordrecg, renderer,
|
||||
cgmeth, lowerings, sighashes, modulegraphs, lineinfos,
|
||||
transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas,
|
||||
mangleutils, varpartitions
|
||||
mangleutils
|
||||
|
||||
import pipelineutils
|
||||
|
||||
@@ -728,47 +728,44 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
of mShrI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> (BigInt($2) & 63n))")
|
||||
applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))")
|
||||
elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("($1 >> (BigInt($2) & 63n))")
|
||||
applyFormat("($1 >> BigInt($2))")
|
||||
else:
|
||||
let bitmask = typ.size * 8 - 1
|
||||
if typ.kind in {tyInt..tyInt32}:
|
||||
let trimmerU = unsignedTrimmer(typ.size)
|
||||
let trimmerS = signedTrimmer(typ.size)
|
||||
r.res = "((($1 $2) >>> ($3 & $5)) $4)" % [xLoc, trimmerU, yLoc, trimmerS, $bitmask]
|
||||
r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS]
|
||||
else:
|
||||
r.res = "($1 >>> ($2 & $3))" % [xLoc, yLoc, $bitmask]
|
||||
applyFormat("($1 >>> $2)")
|
||||
of mShlI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.size == 8:
|
||||
if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asIntN(64, $1 << (BigInt($2) & 63n))")
|
||||
applyFormat("BigInt.asIntN(64, $1 << BigInt($2))")
|
||||
elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asUintN(64, $1 << (BigInt($2) & 63n))")
|
||||
applyFormat("BigInt.asUintN(64, $1 << BigInt($2))")
|
||||
else:
|
||||
applyFormat("($1 * Math.pow(2, ($2 & 63)))")
|
||||
applyFormat("($1 * Math.pow(2, $2))")
|
||||
else:
|
||||
let bitmask = typ.size * 8 - 1
|
||||
if typ.kind in {tyUInt..tyUInt32}:
|
||||
let trimmer = unsignedTrimmer(typ.size)
|
||||
r.res = "(($1 << ($2 & $4)) $3)" % [xLoc, yLoc, trimmer, $bitmask]
|
||||
r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
|
||||
else:
|
||||
let trimmer = signedTrimmer(typ.size)
|
||||
r.res = "(($1 << ($2 & $4)) $3)" % [xLoc, yLoc, trimmer, $bitmask]
|
||||
r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
|
||||
of mAshrI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.size == 8:
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("($1 >> (BigInt($2) & 63n))")
|
||||
applyFormat("($1 >> BigInt($2))")
|
||||
else:
|
||||
applyFormat("Math.floor($1 / Math.pow(2, ($2 & 63)))")
|
||||
applyFormat("Math.floor($1 / Math.pow(2, $2))")
|
||||
else:
|
||||
let bitmask = typ.size * 8 - 1
|
||||
if typ.kind in {tyUInt..tyUInt32}:
|
||||
r.res = "($1 >>> ($2 & $3)))" % [xLoc, yLoc, $bitmask]
|
||||
applyFormat("($1 >>> $2)")
|
||||
else:
|
||||
r.res = "($1 >> ($2 & $3))" % [xLoc, yLoc, $bitmask]
|
||||
applyFormat("($1 >> $2)")
|
||||
of mBitandI: bitwiseExpr("&")
|
||||
of mBitorI: bitwiseExpr("|")
|
||||
of mBitxorI: bitwiseExpr("^")
|
||||
@@ -1294,16 +1291,14 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||
xtyp = etySeq
|
||||
case xtyp
|
||||
of etySeq:
|
||||
if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded or
|
||||
(x.kind == nkSym and sfCursor in x.sym.flags):
|
||||
if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
|
||||
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
|
||||
else:
|
||||
useMagic(p, "nimCopy")
|
||||
lineF(p, "$1 = nimCopy(null, $2, $3);$n",
|
||||
[a.rdLoc, b.res, genTypeInfo(p, y.typ)])
|
||||
of etyObject:
|
||||
if x.typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded or
|
||||
(x.kind == nkSym and sfCursor in x.sym.flags):
|
||||
if x.typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
|
||||
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
|
||||
else:
|
||||
useMagic(p, "nimCopy")
|
||||
@@ -1376,8 +1371,7 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.typ = etyBaseIndex
|
||||
let b = if n.kind == nkHiddenAddr: n[0] else: n
|
||||
gen(p, b[0], a)
|
||||
if skipTypes(b[0].typ, abstractVarRange + tyTypeClasses).kind == tyTuple:
|
||||
# ref #25227 about `+ tyTypeClasses`
|
||||
if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple:
|
||||
r.res = makeJSString("Field" & $getFieldPosition(p, b[1]))
|
||||
else:
|
||||
if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
|
||||
@@ -1464,20 +1458,6 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
|
||||
r.res = "$1.$2" % [tmp, field.loc.snippet]
|
||||
r.kind = resExpr
|
||||
|
||||
proc isVarOpenArrayParam(n: PNode): bool =
|
||||
## True if `n` resolves to a `var openArray` parameter. The JS backend
|
||||
## represents such parameters as a `{base, off, len}` slice view so that
|
||||
## writes through a `toOpenArray` view reach the caller's storage (bug #15952).
|
||||
var it = n
|
||||
while true:
|
||||
case it.kind
|
||||
of nkHiddenDeref, nkDerefExpr, nkHiddenAddr, nkAddr: it = it[0]
|
||||
of nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: it = it[1]
|
||||
else: break
|
||||
result = it.kind == nkSym and it.sym.kind == skParam and
|
||||
it.sym.typ != nil and it.sym.typ.kind == tyVar and
|
||||
it.sym.typ.len > 0 and it.sym.typ[0].kind == tyOpenArray
|
||||
|
||||
proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var
|
||||
a, b: TCompRes = default(TCompRes)
|
||||
@@ -1486,19 +1466,6 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
let m = if n.kind == nkHiddenAddr: n[0] else: n
|
||||
gen(p, m[0], a)
|
||||
gen(p, m[1], b)
|
||||
if isVarOpenArrayParam(m[0]):
|
||||
# `var openArray` param is a `{base, off, len}` view; index the base with
|
||||
# the offset applied. `m[0]` is a plain param name, safe to reference
|
||||
# repeatedly (no side effects, so no temp needed).
|
||||
let pn = a.rdLoc
|
||||
r.address = "($1).base" % [pn]
|
||||
if optBoundsCheck in p.options:
|
||||
useMagic(p, "chckIndx")
|
||||
r.res = "($1).off + chckIndx($2, 0, ($1).len - 1)" % [pn, b.rdLoc]
|
||||
else:
|
||||
r.res = "($1).off + ($2)" % [pn, b.rdLoc]
|
||||
r.kind = resExpr
|
||||
return
|
||||
#internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex
|
||||
let (x, tmp) = maybeMakeTemp(p, m[0], a)
|
||||
r.address = x
|
||||
@@ -1561,7 +1528,7 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
|
||||
r.res = s.loc.snippet
|
||||
r.address = ""
|
||||
r.typ = etyNone
|
||||
of skVar, skLet, skResult, skTemp, skForVar:
|
||||
of skVar, skLet, skResult:
|
||||
r.kind = resExpr
|
||||
let jsType = mapType(p):
|
||||
if typ.isNil:
|
||||
@@ -1618,12 +1585,15 @@ 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, nkDerefExpr:
|
||||
if n.kind in {nkAddr, nkHiddenAddr}:
|
||||
# addr ( deref ( x )) --> x
|
||||
gen(p, n[0][0], r)
|
||||
else:
|
||||
gen(p, n[0], r)
|
||||
of nkHiddenDeref:
|
||||
gen(p, n[0], r)
|
||||
of nkDerefExpr:
|
||||
var x = n[0]
|
||||
if n.kind == nkHiddenAddr:
|
||||
x = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
x.typ = n.typ
|
||||
gen(p, x, r)
|
||||
of nkHiddenAddr:
|
||||
gen(p, n[0], r)
|
||||
of nkConv:
|
||||
@@ -1767,47 +1737,8 @@ proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else:
|
||||
r.res.add(a.res)
|
||||
|
||||
proc genVarOpenArrayArg(p: PProc, n: PNode, r: var TCompRes) =
|
||||
## Emit a `{base, off, len}` slice view for an argument to a `var openArray`
|
||||
## parameter (bug #15952). The view always aliases the base storage, so writes
|
||||
## through the callee's `openArray` reach the caller's array/seq/typed array.
|
||||
var b, lo, hi, v: TCompRes = default(TCompRes)
|
||||
# the argument reaches codegen as `addr(toOpenArray(x, lo, hi))` (possibly
|
||||
# under conversions); unwrap to the actual `toOpenArray` call.
|
||||
var sl = n
|
||||
while true:
|
||||
case sl.kind
|
||||
of nkHiddenAddr, nkAddr, nkHiddenDeref, nkDerefExpr: sl = sl[0]
|
||||
of nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: sl = sl[1]
|
||||
else: break
|
||||
if sl.kind in nkCallKinds and getMagic(sl) == mSlice:
|
||||
gen(p, sl[1], b)
|
||||
gen(p, sl[2], lo)
|
||||
gen(p, sl[3], hi)
|
||||
if isVarOpenArrayParam(sl[1]):
|
||||
# slicing a `var openArray` view: rebase onto the same underlying storage
|
||||
r.res = "{base: ($1).base, off: ($1).off + $2, len: $3 - $2 + 1}" % [
|
||||
b.rdLoc, lo.rdLoc, hi.rdLoc]
|
||||
else:
|
||||
r.res = "{base: $1, off: $2, len: $3 - $2 + 1}" % [
|
||||
b.rdLoc, lo.rdLoc, hi.rdLoc]
|
||||
elif isVarOpenArrayParam(sl):
|
||||
# already a view from another `var openArray` param: forward it unchanged
|
||||
gen(p, sl, b)
|
||||
r.res = b.rdLoc
|
||||
else:
|
||||
# a whole array/seq/typed-array value: wrap with a zero offset
|
||||
gen(p, n, v)
|
||||
r.res = "{base: $1, off: 0, len: ($1).length}" % [v.rdLoc]
|
||||
r.kind = resExpr
|
||||
|
||||
proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) =
|
||||
var a: TCompRes = default(TCompRes)
|
||||
if param.typ != nil and param.typ.kind == tyVar and param.typ[0].kind == tyOpenArray:
|
||||
# `var openArray` params are passed as a `{base, off, len}` slice view.
|
||||
genVarOpenArrayArg(p, n, a)
|
||||
r.res.add(a.rdLoc)
|
||||
return
|
||||
gen(p, n, a)
|
||||
if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
|
||||
a.typ == etyBaseIndex:
|
||||
@@ -1817,13 +1748,6 @@ proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int =
|
||||
r.res.add(", ")
|
||||
r.res.add(a.res)
|
||||
if emitted != nil: inc emitted[]
|
||||
elif skipTypes(param.typ, abstractVar).kind == tyOpenArray and
|
||||
isVarOpenArrayParam(n):
|
||||
# a `var openArray` view passed to a read-only `openArray` param: materialize
|
||||
# a snapshot so the callee sees a plain array.
|
||||
var w: TCompRes = default(TCompRes)
|
||||
gen(p, n, w)
|
||||
r.res.add("(($1).base).slice(($1).off, ($1).off + ($1).len)" % [w.rdLoc])
|
||||
elif n.typ.kind in {tyVar, tyPtr, tyRef, tyLent, tyOwned} and
|
||||
n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex:
|
||||
# this fixes bug #5608:
|
||||
@@ -2079,12 +2003,8 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
if indirect: result = "[$1]" % [result]
|
||||
of tyTuple:
|
||||
result = rope("{")
|
||||
var first = true
|
||||
for i in 0..<t.len:
|
||||
# Do not produce code for void types
|
||||
if isEmptyType(t[i]): continue
|
||||
if not first: result.add(", ")
|
||||
first = false
|
||||
if i > 0: result.add(", ")
|
||||
result.addf("Field$1: $2", [i.rope,
|
||||
createVar(p, t[i], false)])
|
||||
result.add("}")
|
||||
@@ -2153,8 +2073,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||
gen(p, n, a)
|
||||
case mapType(p, v.typ)
|
||||
of etyObject, etySeq:
|
||||
if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n) or
|
||||
sfCursor in v.flags:
|
||||
if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n):
|
||||
s = a.res
|
||||
else:
|
||||
useMagic(p, "nimCopy")
|
||||
@@ -2417,8 +2336,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.res = "if (null != $1) { if (null == $2) $2 = $3; else $2 += $3; }" %
|
||||
[b, lhs.rdLoc, tmp]
|
||||
else:
|
||||
useMagic(p, "nimAddStrStr")
|
||||
r.res = "nimAddStrStr($1, $2);" % [lhs.rdLoc, rhs.rdLoc]
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], lhs)
|
||||
r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp]
|
||||
r.kind = resExpr
|
||||
of mAppendSeqElem:
|
||||
var x, y: TCompRes = default(TCompRes)
|
||||
@@ -2465,21 +2384,13 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
useMagic(p, "nimCopy")
|
||||
r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)]
|
||||
of mOpenArrayToSeq:
|
||||
if isVarOpenArrayParam(n[1]):
|
||||
var x: TCompRes = default(TCompRes)
|
||||
gen(p, n[1], x)
|
||||
r.res = "(($1).base).slice(($1).off, ($1).off + ($1).len)" % [x.rdLoc]
|
||||
r.kind = resExpr
|
||||
else:
|
||||
genCall(p, n, r)
|
||||
genCall(p, n, r)
|
||||
of mDestroy, mTrace: discard "ignore calls to the default destructor"
|
||||
of mOrd: genOrd(p, n, r)
|
||||
of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray:
|
||||
var x: TCompRes = default(TCompRes)
|
||||
gen(p, n[1], x)
|
||||
if isVarOpenArrayParam(n[1]):
|
||||
r.res = "($1).len" % [x.rdLoc]
|
||||
elif skipTypes(n[1].typ, abstractInst).kind == tyCstring:
|
||||
if skipTypes(n[1].typ, abstractInst).kind == tyCstring:
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
r.res = "(($1) == null ? 0 : ($2).length)" % [a, tmp]
|
||||
else:
|
||||
@@ -2488,9 +2399,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mHigh:
|
||||
var x: TCompRes = default(TCompRes)
|
||||
gen(p, n[1], x)
|
||||
if isVarOpenArrayParam(n[1]):
|
||||
r.res = "($1).len - 1" % [x.rdLoc]
|
||||
elif skipTypes(n[1].typ, abstractInst).kind == tyCstring:
|
||||
if skipTypes(n[1].typ, abstractInst).kind == tyCstring:
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
r.res = "(($1) == null ? -1 : ($2).length - 1)" % [a, tmp]
|
||||
else:
|
||||
@@ -2532,7 +2441,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
binaryExpr(p, n, r, "mnewString",
|
||||
"""if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); }
|
||||
else {$3.length = $4; }""")
|
||||
of mSetLengthSeq, mSetLengthSeqUninit:
|
||||
of mSetLengthSeq:
|
||||
var x, y: TCompRes = default(TCompRes)
|
||||
gen(p, n[1], x)
|
||||
gen(p, n[2], y)
|
||||
@@ -2549,7 +2458,6 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)")
|
||||
of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
|
||||
of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
|
||||
of mXorSet: binaryExpr(p, n, r, "SetXor", "SetXor($1, $2)")
|
||||
of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
|
||||
of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
|
||||
of mInSet:
|
||||
@@ -2573,24 +2481,11 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
genCall(p, n, r)
|
||||
of mSlice:
|
||||
# arr.slice([begin[, end]]): 'end' is exclusive
|
||||
# Fixed homogeneous numeric arrays lower to JS typed arrays; `slice`
|
||||
# copies, which silently breaks `var openArray` write-through (bug #15952).
|
||||
# `subarray` returns a live shared-buffer view with the same
|
||||
# exclusive-end signature, so use it there; keep `slice` for seqs/strings.
|
||||
var x, y, z: TCompRes = default(TCompRes)
|
||||
gen(p, n[1], x)
|
||||
gen(p, n[2], y)
|
||||
gen(p, n[3], z)
|
||||
if isVarOpenArrayParam(n[1]):
|
||||
# re-slicing a `var openArray` view: materialize from the view's base/offset
|
||||
r.res = "(($1).base).slice(($1).off + $2, ($1).off + $3 + 1)" % [
|
||||
x.rdLoc, y.rdLoc, z.rdLoc]
|
||||
else:
|
||||
let baseTy = skipTypes(n[1].typ, abstractVarRange + {tyLent})
|
||||
if baseTy.kind == tyArray and arrayTypeForElemType(p.config, elemType(baseTy)).len > 0:
|
||||
r.res = "($1.subarray($2, $3 + 1))" % [x.rdLoc, y.rdLoc, z.rdLoc]
|
||||
else:
|
||||
r.res = "($1.slice($2, $3 + 1))" % [x.rdLoc, y.rdLoc, z.rdLoc]
|
||||
r.res = "($1.slice($2, $3 + 1))" % [x.rdLoc, y.rdLoc, z.rdLoc]
|
||||
r.kind = resExpr
|
||||
of mMove:
|
||||
genMove(p, n, r)
|
||||
@@ -2881,11 +2776,6 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
|
||||
var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, {})
|
||||
if sfInjectDestructors in prc.flags:
|
||||
transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody)
|
||||
else:
|
||||
# JS has a GC, so the destructor pass is off; but the cursor (alias) analysis
|
||||
# is independent of ownership and always memory-safe on a traced target.
|
||||
# Running it lets last-use `var b = a` aliases skip the deep `nimCopy`.
|
||||
computeCursors(prc, transformedBody, p.module.graph)
|
||||
|
||||
p.nested: genStmt(p, transformedBody)
|
||||
|
||||
@@ -2991,8 +2881,6 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
|
||||
elif dest.kind in tyFloat..tyFloat64:
|
||||
if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "Number($1)" % [r.res]
|
||||
elif dest.kind == tyChar and (fromInt or fromUint):
|
||||
r.res = "($1 & 255)" % [r.res]
|
||||
elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
|
||||
r.address = r.res
|
||||
r.res = "null"
|
||||
|
||||
@@ -122,7 +122,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
|
||||
[name, genTypeInfo(p, typ.baseClass)])
|
||||
|
||||
proc genTypeInfo(p: PProc, typ: PType): Rope =
|
||||
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned} + tyUserTypeClasses)
|
||||
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
|
||||
result = "NTI$1" % [rope(t.id)]
|
||||
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
|
||||
case t.kind
|
||||
|
||||
@@ -150,8 +150,7 @@ template isIterator*(owner: PSym): bool =
|
||||
|
||||
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
|
||||
result = createObj(g, idgen, owner, info, final=false)
|
||||
result.flags.incl tfFinal
|
||||
if owner.isIterator:
|
||||
if owner.isIterator or not isDefined(g.config, "nimOptIters"):
|
||||
rawAddField(result, createStateField(g, owner, idgen))
|
||||
|
||||
proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
|
||||
@@ -176,7 +175,6 @@ proc addHiddenParam(routine: PSym, param: PSym) =
|
||||
#echo "produced environment: ", param.id, " for ", routine.id
|
||||
|
||||
proc getEnvParam*(routine: PSym): PSym =
|
||||
if routine.ast.isNil: return nil
|
||||
let params = routine.ast[paramsPos]
|
||||
let hidden = lastSon(params)
|
||||
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
|
||||
@@ -200,7 +198,7 @@ proc interestingVar(s: PSym): bool {.inline.} =
|
||||
proc illegalCapture(s: PSym): bool {.inline.} =
|
||||
result = classifyViewType(s.typ) != noView or s.kind == skResult
|
||||
|
||||
proc isInnerProc*(s: PSym): bool =
|
||||
proc isInnerProc(s: PSym): bool =
|
||||
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
|
||||
result = s.skipGenericOwner.kind in routineKinds
|
||||
else:
|
||||
@@ -230,6 +228,13 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf
|
||||
if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
|
||||
prc.flags.incl sfInjectDestructors
|
||||
|
||||
proc interestingIterVar(s: PSym): bool {.inline.} =
|
||||
# unused with -d:nimOptIters
|
||||
# XXX optimization: Only lift the variable if it lives across
|
||||
# yield/return boundaries! This can potentially speed up
|
||||
# closure iterators quite a bit.
|
||||
result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
|
||||
|
||||
template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
|
||||
## lambda lifting can be harmful for JS-like code generators.
|
||||
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
|
||||
@@ -276,6 +281,16 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
|
||||
result.add makeClosure(g, idgen, iter, env, n.info)
|
||||
|
||||
proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
|
||||
# unused with -d:nimOptIters
|
||||
let envParam = getHiddenParam(g, owner)
|
||||
let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
let field = addField(obj, s, g.cache, idgen)
|
||||
|
||||
var access = newSymNode(envParam)
|
||||
assert obj.kind == tyObject
|
||||
result = rawIndirectAccess(access, field, s.info)
|
||||
|
||||
# ------------------ new stuff -------------------------------------------
|
||||
|
||||
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
@@ -285,8 +300,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
localError(g.config, n.info,
|
||||
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
|
||||
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
|
||||
" Consider using a <ref T> which can be captured.") %
|
||||
[s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
|
||||
" Consider using a <ref $2> which can be captured.") %
|
||||
[s.name.s, typeToString(s.typ), g.config$s.info])
|
||||
elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
|
||||
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
|
||||
[s.name.s, owner.name.s, $owner.typ.callConv])
|
||||
@@ -324,7 +339,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
|
||||
result = c.ownerToType.getOrDefault(owner.id)
|
||||
if result.isNil:
|
||||
let env = getEnvParam(owner)
|
||||
if env.isNil or not owner.isIterator:
|
||||
if env.isNil or not owner.isIterator or not isDefined(c.graph.config, "nimOptIters"):
|
||||
result = newType(tyRef, c.idgen, owner)
|
||||
let obj = createEnvObj(c.graph, c.idgen, owner, info)
|
||||
rawAddSon(result, obj)
|
||||
@@ -408,12 +423,6 @@ Consider:
|
||||
proc isTypeOf(n: PNode): bool =
|
||||
n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
|
||||
|
||||
proc isEnvTypeForRoutine(envTyp: PType; routine: PSym): bool =
|
||||
## True if `envTyp` is (maybe wrapped) env object type owned by `routine`, as
|
||||
## created by `getEnvTypeForOwner` / `createEnvObj`.
|
||||
let obj = envTyp.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
result = obj.kind == tyObject and obj.owner.id == routine.id
|
||||
|
||||
proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
|
||||
var cp = getEnvParam(fn)
|
||||
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
|
||||
@@ -424,21 +433,9 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
|
||||
cp.typ = t
|
||||
addHiddenParam(fn, cp)
|
||||
elif cp.typ != t and fn.kind != skIterator:
|
||||
# Nested `liftLambdas` uses a fresh `DetectionPass`, so `getEnvTypeForOwner`
|
||||
# can allocate another PType for the same logical env; the hidden param from
|
||||
# the inner pass is authoritative (bug #21242).
|
||||
if isEnvTypeForRoutine(cp.typ, owner) and isEnvTypeForRoutine(t, owner):
|
||||
c.ownerToType[owner.id] = cp.typ
|
||||
else:
|
||||
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
|
||||
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
|
||||
#echo "adding closure to ", fn.name.s
|
||||
|
||||
proc iterEnvHasUpField(g: ModuleGraph, iter: PSym): bool =
|
||||
let cp = getEnvParam(iter)
|
||||
doAssert(cp != nil, "Env param not present in iter")
|
||||
let upField = lookupInRecord(cp.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
|
||||
upField != nil
|
||||
|
||||
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
@@ -457,12 +454,23 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
let body = transformBody(c.graph, c.idgen, s, {useCache})
|
||||
detectCapturedVars(body, s, c)
|
||||
let ow = s.skipGenericOwner
|
||||
let innerClosure = innerProc and s.typ.callConv == ccClosure and (not s.isIterator or iterEnvHasUpField(c.graph, s))
|
||||
let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
|
||||
let interested = interestingVar(s)
|
||||
if ow == owner:
|
||||
if owner.isIterator:
|
||||
c.somethingToDo = true
|
||||
addClosureParam(c, owner, n.info)
|
||||
if not isDefined(c.graph.config, "nimOptIters") and interestingIterVar(s):
|
||||
if not c.capturedVars.contains(s.id):
|
||||
if not c.inTypeOf: c.capturedVars.incl(s.id)
|
||||
let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
|
||||
if s.name.id == getIdent(c.graph.cache, ":state").id:
|
||||
obj.n[0].sym.flags.incl sfNoInit
|
||||
obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
else:
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
# direct or indirect dependency:
|
||||
elif innerClosure or interested:
|
||||
discard """
|
||||
@@ -622,7 +630,7 @@ proc rawClosureCreation(owner: PSym;
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
let env2 = copyTree(env)
|
||||
env2.typ() = unowned.typ
|
||||
env2.typ = unowned.typ
|
||||
result.add newAsgnStmt(unowned, env2, env.info)
|
||||
createTypeBoundOpsLL(d.graph, unowned.typ, env.info, d.idgen, owner)
|
||||
|
||||
@@ -662,27 +670,16 @@ proc finishClosureCreation(owner: PSym; d: var DetectionPass; c: LiftingPass;
|
||||
res.add newAsgnStmt(unowned, nilLit, info)
|
||||
createTypeBoundOpsLL(d.graph, unowned.typ, info, d.idgen, owner)
|
||||
|
||||
proc getUpForIter(g: ModuleGraph; owner, iterOwner: PSym, expectedUpTyp: PType): PNode =
|
||||
var p = getHiddenParam(g, owner)
|
||||
var res = p.newSymNode
|
||||
while res.typ.skipTypes({tyOwned, tyRef, tyPtr}) != expectedUpTyp:
|
||||
let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
|
||||
if upField == nil:
|
||||
return nil
|
||||
p = upField
|
||||
res = rawIndirectAccess(res, upField, p.info)
|
||||
res
|
||||
|
||||
proc closureCreationForIter(owner: PSym, iter: PNode;
|
||||
proc closureCreationForIter(iter: PNode;
|
||||
d: var DetectionPass; c: var LiftingPass): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
|
||||
let iterOwner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, iterOwner, iter.info)
|
||||
let owner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
|
||||
incl(v.flags, sfShadowed)
|
||||
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
|
||||
var vnode: PNode
|
||||
if iterOwner.isIterator:
|
||||
let it = getHiddenParam(d.graph, iterOwner)
|
||||
if owner.isIterator:
|
||||
let it = getHiddenParam(d.graph, owner)
|
||||
addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
|
||||
vnode = indirectAccess(newSymNode(it), v, v.info)
|
||||
else:
|
||||
@@ -691,14 +688,12 @@ proc closureCreationForIter(owner: PSym, iter: PNode;
|
||||
addVar(vs, vnode)
|
||||
result.add(vs)
|
||||
result.add genCreateEnv(vnode)
|
||||
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, iterOwner)
|
||||
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
|
||||
|
||||
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
|
||||
if upField != nil:
|
||||
let expectedUpTyp = upField.typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
let u = if iterOwner == owner: setupEnvVar(iterOwner, d, c, iter.info)
|
||||
else: getUpForIter(d.graph, owner, iterOwner, expectedUpTyp)
|
||||
if u != nil and u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == expectedUpTyp:
|
||||
let u = setupEnvVar(owner, d, c, iter.info)
|
||||
if u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == upField.typ.skipTypes({tyOwned, tyRef, tyPtr}):
|
||||
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
|
||||
u, iter.info))
|
||||
else:
|
||||
@@ -732,7 +727,7 @@ proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
let available = getHiddenParam(d.graph, owner)
|
||||
result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
|
||||
elif s.isIterator:
|
||||
result = closureCreationForIter(owner, n, d, c)
|
||||
result = closureCreationForIter(n, d, c)
|
||||
elif s.skipGenericOwner == owner:
|
||||
# direct dependency, so use the outer's env variable:
|
||||
result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
|
||||
@@ -779,6 +774,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
elif s.id in d.capturedVars:
|
||||
if s.owner != owner:
|
||||
result = accessViaEnvParam(d.graph, n, owner)
|
||||
elif owner.isIterator and not isDefined(d.graph.config, "nimOptIters") and interestingIterVar(s):
|
||||
result = accessViaEnvParam(d.graph, n, owner)
|
||||
else:
|
||||
result = accessViaEnvVar(n, owner, d, c)
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
|
||||
@@ -799,7 +796,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
let oldInContainer = c.inContainer
|
||||
c.inContainer = 0
|
||||
let m = newSymNode(n[namePos].sym)
|
||||
m.typ() = n.typ
|
||||
m.typ = n.typ
|
||||
result = liftCapturedVars(m, owner, d, c)
|
||||
c.inContainer = oldInContainer
|
||||
of nkHiddenStdConv:
|
||||
@@ -896,7 +893,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
|
||||
# ignore forward declaration:
|
||||
result = body
|
||||
tooEarly = true
|
||||
if fn.isIterator:
|
||||
if fn.isIterator and isDefined(g.config, "nimOptIters"):
|
||||
var d = initDetectionPass(g, fn, idgen)
|
||||
addClosureParam(d, fn, body.info)
|
||||
else:
|
||||
@@ -987,20 +984,6 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
for i in 0..<op.len-1:
|
||||
result.add op[i]
|
||||
|
||||
elif op.kind != nkSym: # might have side effects
|
||||
# bug #25046
|
||||
# create a temp for the closure
|
||||
# var :closureTemp
|
||||
# :closureTemp = ...
|
||||
let tempSym = newSym(skLet, getIdent(g.cache, ":closureTemp"), idgen, owner, body.info)
|
||||
tempSym.typ = call[0].typ
|
||||
let temp = newSymNode(tempSym)
|
||||
var v = newNodeI(nkVarSection, body.info)
|
||||
addVar(v, temp)
|
||||
result.add(v)
|
||||
result.add newAsgnStmt(temp, call[0], body.info)
|
||||
call[0] = temp
|
||||
|
||||
var loopBody = newNodeI(nkStmtList, body.info, 3)
|
||||
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
|
||||
whileLoop[0] = newIntTypeNode(1, getSysType(g, body.info, tyBool))
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
import ast, astalgo
|
||||
|
||||
type
|
||||
LayeredIdTableObj* {.acyclic.} = object
|
||||
## stack of type binding contexts implemented as a linked list
|
||||
topLayer*: TypeMapping
|
||||
## the mappings on the current layer
|
||||
nextLayer*: ref LayeredIdTableObj
|
||||
## the parent type binding context, possibly `nil`
|
||||
previousLen*: int
|
||||
## total length of the bindings up to the parent layer,
|
||||
## used to track if new bindings were added
|
||||
|
||||
const useRef = not defined(gcDestructors)
|
||||
# implementation detail, only arc/orc doesn't cause issues when
|
||||
# using LayeredIdTable as an object and not a ref
|
||||
|
||||
when useRef:
|
||||
type LayeredIdTable* = ref LayeredIdTableObj
|
||||
else:
|
||||
type LayeredIdTable* = LayeredIdTableObj
|
||||
|
||||
proc initLayeredTypeMap*(pt: sink TypeMapping = initTypeMapping()): LayeredIdTable =
|
||||
result = LayeredIdTable(topLayer: pt, nextLayer: nil)
|
||||
|
||||
proc shallowCopy*(pt: LayeredIdTable): LayeredIdTable {.inline.} =
|
||||
## copies only the type bindings of the current layer, but not any parent layers,
|
||||
## useful for write-only bindings
|
||||
result = LayeredIdTable(topLayer: pt.topLayer, nextLayer: pt.nextLayer, previousLen: pt.previousLen)
|
||||
#copyIdTable(result.topLayer, pt.topLayer)
|
||||
|
||||
proc currentLen*(pt: LayeredIdTable): int =
|
||||
## the sum of the cached total binding count of the parents and
|
||||
## the current binding count, just used to track if bindings were added
|
||||
pt.previousLen + pt.topLayer.counter
|
||||
|
||||
proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable =
|
||||
result = LayeredIdTable(topLayer: initTypeMapping(), previousLen: pt.currentLen)
|
||||
when useRef:
|
||||
result.nextLayer = pt
|
||||
else:
|
||||
new(result.nextLayer)
|
||||
result.nextLayer[] = pt
|
||||
|
||||
proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
|
||||
when useRef:
|
||||
pt = pt.nextLayer
|
||||
else:
|
||||
when defined(gcDestructors):
|
||||
pt = pt.nextLayer[]
|
||||
else:
|
||||
# workaround refc
|
||||
let tmp = pt.nextLayer[]
|
||||
pt = tmp
|
||||
|
||||
iterator pairs*(pt: LayeredIdTable): (ItemId, PType) =
|
||||
var tm = pt
|
||||
while true:
|
||||
for (k, v) in idTablePairs(tm.topLayer):
|
||||
yield (k, v)
|
||||
if tm.nextLayer == nil:
|
||||
break
|
||||
tm.setToPreviousLayer
|
||||
|
||||
proc lookup(typeMap: ref LayeredIdTableObj, key: ItemId): PType =
|
||||
result = nil
|
||||
var tm = typeMap
|
||||
while tm != nil:
|
||||
result = getOrDefault(tm.topLayer, key)
|
||||
if result != nil: return
|
||||
tm = tm.nextLayer
|
||||
|
||||
template lookup*(typeMap: ref LayeredIdTableObj, key: PType): PType =
|
||||
## recursively looks up binding of `key` in all parent layers
|
||||
lookup(typeMap, key.itemId)
|
||||
|
||||
when not useRef:
|
||||
proc lookup(typeMap: LayeredIdTableObj, key: ItemId): PType {.inline.} =
|
||||
result = getOrDefault(typeMap.topLayer, key)
|
||||
if result == nil and typeMap.nextLayer != nil:
|
||||
result = lookup(typeMap.nextLayer, key)
|
||||
|
||||
template lookup*(typeMap: LayeredIdTableObj, key: PType): PType =
|
||||
lookup(typeMap, key.itemId)
|
||||
|
||||
proc put(typeMap: var LayeredIdTable, key: ItemId, value: PType) {.inline.} =
|
||||
typeMap.topLayer[key] = value
|
||||
|
||||
template put*(typeMap: var LayeredIdTable, key, value: PType) =
|
||||
## binds `key` to `value` only in current layer
|
||||
put(typeMap, key.itemId, value)
|
||||
@@ -451,13 +451,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||
elif tok.indent >= 0:
|
||||
var newlineKind = ltCrucialNewline
|
||||
if em.keepIndents > 0:
|
||||
# Apply the requested --indent width to "don't touch" regions (if/block
|
||||
# expressions) too: keep the relative offset from the enclosing block
|
||||
# baseline, but rebase it onto indWidth. Otherwise a non-default
|
||||
# --indent would leave these lines at the original column and inject
|
||||
# invalid indentation (see #20078).
|
||||
em.indentLevel = em.indentStack.high * em.indWidth +
|
||||
(tok.indent - em.indentStack[^1])
|
||||
em.indentLevel = tok.indent
|
||||
elif (em.lastTok in (splitters + oprSet) and
|
||||
tok.tokType notin (closedPars - {tkBracketDotRi})):
|
||||
if tok.tokType in openPars and tok.indent > em.indentStack[^1]:
|
||||
|
||||
@@ -316,28 +316,6 @@ proc getNumber(L: var Lexer, result: var Token) =
|
||||
L.bufpos = msgPos
|
||||
lexMessage(L, msgKind, msg % t.literal)
|
||||
|
||||
proc checkBitWidth(L: var Lexer, base: NumericalBase, tokType: TokType,
|
||||
numDigits: int, startpos: int) =
|
||||
# Check bit width for non-base-10 literals
|
||||
# Warn if the digit count exceeds what can fit in the target type
|
||||
let bitsPerDigit = case base
|
||||
of base2: 1
|
||||
of base8: 3
|
||||
of base16: 4
|
||||
else: raiseAssert "unreachable"
|
||||
let bitWidth = case tokType
|
||||
of tkInt8Lit, tkUInt8Lit: 8
|
||||
of tkInt16Lit, tkUInt16Lit: 16
|
||||
of tkInt32Lit, tkUInt32Lit: 32
|
||||
of tkInt64Lit, tkUIntLit, tkIntLit, tkUInt64Lit: 64
|
||||
else: raiseAssert "unreachable"
|
||||
# Maximum digits = ceil(bitWidth / bitsPerDigit) = (bitWidth + bitsPerDigit - 1) div bitsPerDigit
|
||||
let maxDigits = (bitWidth + bitsPerDigit - 1) div bitsPerDigit
|
||||
if numDigits > maxDigits:
|
||||
lexMessageLitNum(L,
|
||||
"number has " & $numDigits & " digits but type only supports " &
|
||||
$maxDigits & " digits: '$1'", startpos, warnLongLiterals)
|
||||
|
||||
var
|
||||
xi: BiggestInt
|
||||
isBase10 = true
|
||||
@@ -513,11 +491,6 @@ proc getNumber(L: var Lexer, result: var Token) =
|
||||
setNumber result.fNumber, (cast[ptr float64](addr(xi)))[]
|
||||
else: internalError(L.config, getLineInfo(L), "getNumber")
|
||||
|
||||
# Check bit width for non-base-10 literals
|
||||
# Warn if the digit count exceeds what can fit in the target type
|
||||
if result.base != base10 and result.tokType in {tkIntLit..tkUInt64Lit} and numDigits > 0:
|
||||
checkBitWidth(L, result.base, result.tokType, numDigits, startpos)
|
||||
|
||||
# Bounds checks. Non decimal literals are allowed to overflow the range of
|
||||
# the datatype as long as their pattern don't overflow _bitwise_, hence
|
||||
# below checks of signed sizes against uint*.high is deliberate:
|
||||
@@ -923,7 +896,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
|
||||
tok.tokType = tkSymbol
|
||||
else:
|
||||
tok.tokType = TokType(tok.ident.id + ord(tkSymbol))
|
||||
if suspicious and {optStyleHint, optStyleError, optStyleWarning} * L.config.globalOptions != {}:
|
||||
if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}:
|
||||
lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s)
|
||||
L.bufpos = pos
|
||||
|
||||
@@ -1349,7 +1322,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
|
||||
of '-':
|
||||
if L.buf[L.bufpos+1] in {'0'..'9'} and
|
||||
(L.bufpos == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
|
||||
(L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
|
||||
# x)-23 # binary minus
|
||||
# ,-23 # unary minus
|
||||
# \n-78 # unary minus? Yes.
|
||||
|
||||
@@ -40,7 +40,7 @@ template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator): PSym
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
|
||||
|
||||
proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
idgen: IdGenerator)
|
||||
@@ -49,7 +49,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
|
||||
result = newNodeI(nkBracketExpr, a.info, 2)
|
||||
result[0] = a
|
||||
result[1] = i
|
||||
result.typ() = elemType
|
||||
result.typ = elemType
|
||||
|
||||
proc destructorOverridden(g: ModuleGraph; t: PType): bool =
|
||||
let op = getAttachedOp(g, t, attachedDestructor)
|
||||
@@ -68,7 +68,7 @@ proc dotField(x: PNode, f: PSym): PNode =
|
||||
else:
|
||||
result[0] = x
|
||||
result[1] = newSymNode(f, x.info)
|
||||
result.typ() = f.typ
|
||||
result.typ = f.typ
|
||||
|
||||
proc newAsgnStmt(le, ri: PNode): PNode =
|
||||
result = newNodeI(nkAsgn, le.info, 2)
|
||||
@@ -88,10 +88,10 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
elif c.kind == attachedDestructor and c.addMemReset:
|
||||
let call = genBuiltin(c, mDefault, "default", x)
|
||||
call.typ() = t
|
||||
call.typ = t
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif c.kind == attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc genAddr(c: var TLiftCtx; x: PNode): PNode =
|
||||
if x.kind == nkHiddenDeref:
|
||||
@@ -105,7 +105,7 @@ proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
|
||||
result = newNodeI(nkWhileStmt, c.info, 2)
|
||||
let cmp = genBuiltin(c, mLtI, "<", i)
|
||||
cmp.add genLen(c.g, dest)
|
||||
cmp.typ() = getSysType(c.g, c.info, tyBool)
|
||||
cmp.typ = getSysType(c.g, c.info, tyBool)
|
||||
result[0] = cmp
|
||||
result[1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
@@ -127,10 +127,10 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
|
||||
dotExpr.add newSymNode(field)
|
||||
|
||||
let offsetOf = genBuiltin(c, mOffsetOf, "offsetof", dotExpr)
|
||||
offsetOf.typ() = intType
|
||||
offsetOf.typ = intType
|
||||
|
||||
let minusExpr = genBuiltin(c, mSubI, "-", castExpr1)
|
||||
minusExpr.typ() = intType
|
||||
minusExpr.typ = intType
|
||||
minusExpr.add offsetOf
|
||||
|
||||
let objPtr = makePtrType(objType.owner, objType, c.idgen)
|
||||
@@ -148,7 +148,7 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
if sfNeverRaises notin op.flags:
|
||||
c.canRaise = true
|
||||
if c.addMemReset:
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "wasMoved", x))
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "`=wasMoved`", x))
|
||||
else:
|
||||
result = destroy
|
||||
|
||||
@@ -168,7 +168,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
defaultOp(c, f.typ, body, x.dotField(f), b)
|
||||
else:
|
||||
if enforceWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x.dotField(f))
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
|
||||
fillBody(c, f.typ, body, x.dotField(f), b)
|
||||
of nkNilLit: discard
|
||||
of nkRecCase:
|
||||
@@ -218,38 +218,23 @@ 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:
|
||||
# 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)
|
||||
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) =
|
||||
template fillBase =
|
||||
if t.baseClass != nil:
|
||||
let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
dest.add newNodeI(nkEmpty, c.info)
|
||||
dest.add x
|
||||
var src = y
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
src.add newNodeI(nkEmpty, c.info)
|
||||
src.add y
|
||||
if t.baseClass != nil:
|
||||
let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
dest.add newNodeI(nkEmpty, c.info)
|
||||
dest.add x
|
||||
var src = y
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
src.add newNodeI(nkEmpty, c.info)
|
||||
src.add y
|
||||
|
||||
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
|
||||
template fillFields =
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
if c.kind == attachedDestructor:
|
||||
# destroys in reverse order #24719
|
||||
fillFields()
|
||||
fillBase()
|
||||
else:
|
||||
fillBase()
|
||||
fillFields()
|
||||
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
var hasCase = isCaseObj(t.n)
|
||||
@@ -280,7 +265,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
# because the wasMoved(dest) call would zero out src, if dest aliases src.
|
||||
var cond = newTree(nkCall, newSymNode(c.g.getSysMagic(c.info, "==", mEqRef)),
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
|
||||
cond.typ() = getSysType(c.g, x.info, tyBool)
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
temp.typ = x.typ
|
||||
@@ -292,8 +277,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
#body.add newAsgnStmt(blob, x)
|
||||
|
||||
var wasMovedCall = newNodeI(nkCall, c.info)
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
|
||||
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "`=wasMoved`", mWasMoved)))
|
||||
wasMovedCall.add x # mWasMoved does not take the address
|
||||
body.add wasMovedCall
|
||||
|
||||
@@ -312,7 +296,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
|
||||
proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
|
||||
result = newIntLit(g, info, ord value)
|
||||
result.typ() = getSysType(g, info, tyBool)
|
||||
result.typ = getSysType(g, info, tyBool)
|
||||
|
||||
proc getCycleParam(c: TLiftCtx): PNode =
|
||||
assert c.kind in {attachedAsgn, attachedDup}
|
||||
@@ -380,10 +364,6 @@ proc requiresDestructor(c: TLiftCtx; t: PType): bool {.inline.} =
|
||||
proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
|
||||
if c.c != nil and typeInst != nil:
|
||||
result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
|
||||
elif typeInst != nil and getAttachedOp(c.g, typeInst, c.kind) != nil:
|
||||
# c.c == nil in lambdalifting
|
||||
# hooks are already insted
|
||||
result = getAttachedOp(c.g, typeInst, c.kind)
|
||||
else:
|
||||
localError(c.g.config, c.info,
|
||||
"cannot generate destructor for generic type: " & typeToString(t))
|
||||
@@ -565,18 +545,18 @@ proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
# don't call genAddr(c, x) here:
|
||||
result = genBuiltin(c, mNewSeq, "newSeq", x)
|
||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||
lenCall.typ() = getSysType(c.g, x.info, tyInt)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
result.add lenCall
|
||||
|
||||
proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c, mLengthStr, "len", y)
|
||||
lenCall.typ() = getSysType(c.g, x.info, tyInt)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
|
||||
result.add lenCall
|
||||
|
||||
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||
lenCall.typ() = getSysType(c.g, x.info, tyInt)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
|
||||
op = instantiateGeneric(c, op, t, t)
|
||||
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
|
||||
@@ -599,37 +579,14 @@ proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
|
||||
)
|
||||
cond.typ() = getSysType(c.g, c.info, tyBool)
|
||||
cond.typ = getSysType(c.g, c.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
|
||||
proc genBulkCopySeq(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
## Generates a call to nimCopySeqPayload for bulk memcpy of seq data.
|
||||
let elemType = t.elementType
|
||||
let sym = magicsys.getCompilerProc(c.g, "nimCopySeqPayload")
|
||||
if sym == nil:
|
||||
localError(c.g.config, c.info, "system module needs: nimCopySeqPayload")
|
||||
return
|
||||
var sizeOf = genBuiltin(c, mSizeOf, "sizeof", newNodeIT(nkType, c.info, elemType))
|
||||
sizeOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
let call = newNodeI(nkCall, c.info)
|
||||
call.add newSymNode(sym)
|
||||
call.add newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x)
|
||||
call.add newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
|
||||
call.add sizeOf
|
||||
call.add alignOf
|
||||
call.typ = sym.typ.returnType
|
||||
body.add call
|
||||
|
||||
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
of attachedDup:
|
||||
body.add setLenSeqCall(c, t, x, y)
|
||||
if supportsCopyMem(t.elementType):
|
||||
genBulkCopySeq(c, t, body, x, y)
|
||||
else:
|
||||
forallElements(c, t, body, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedAsgn, attachedDeepCopy:
|
||||
# we generate:
|
||||
# if x.p == y.p:
|
||||
@@ -638,13 +595,9 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# var i = 0
|
||||
# while i < y.len: dest[i] = y[i]; inc(i)
|
||||
# This is usually more efficient than a destroy/create pair.
|
||||
# For trivially copyable types, use bulk copyMem instead of element loop.
|
||||
checkSelfAssignment(c, t, body, x, y)
|
||||
body.add setLenSeqCall(c, t, x, y)
|
||||
if supportsCopyMem(t.elementType):
|
||||
genBulkCopySeq(c, t, body, x, y)
|
||||
else:
|
||||
forallElements(c, t, body, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedSink:
|
||||
let moveCall = genBuiltin(c, mMove, "move", x)
|
||||
moveCall.add y
|
||||
@@ -659,7 +612,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if canFormAcycle(c.g, t.elemType):
|
||||
# follow all elements:
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
|
||||
@@ -697,7 +650,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if op == nil:
|
||||
return # protect from recursion
|
||||
body.add newHookCall(c, op, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedDup:
|
||||
# XXX: replace these with assertions.
|
||||
let op = getAttachedOp(c.g, t, c.kind)
|
||||
@@ -719,7 +672,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
of attachedTrace:
|
||||
discard "strings are atomic and have no inner elements that are to trace"
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc cyclicType*(g: ModuleGraph, t: PType): bool =
|
||||
case t.kind
|
||||
@@ -767,7 +720,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isFinal(elemType):
|
||||
addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
|
||||
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
alignOf.typ() = getSysType(c.g, c.info, tyInt)
|
||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf)
|
||||
else:
|
||||
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
|
||||
@@ -777,7 +730,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isCyclic:
|
||||
if isFinal(elemType):
|
||||
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
typInfo.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
|
||||
else:
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
|
||||
@@ -785,7 +738,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x)
|
||||
else:
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
||||
cond.typ() = getSysType(c.g, x.info, tyBool)
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
@@ -812,13 +765,13 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isCyclic:
|
||||
if isFinal(elemType):
|
||||
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
typInfo.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||
body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y)
|
||||
else:
|
||||
# If the ref is polymorphic we have to account for this
|
||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
|
||||
#echo "can follow ", elemType, " static ", isFinal(elemType)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedDup:
|
||||
if isCyclic:
|
||||
body.add newAsgnStmt(x, y)
|
||||
@@ -833,7 +786,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
## Closures are really like refs except they always use a virtual destructor
|
||||
## and we need to do the refcounting only on the ref field which we call 'xenv':
|
||||
let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xenv.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
xenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc
|
||||
let tmp =
|
||||
@@ -849,7 +802,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isCyclic: "nimDecRefIsLastCyclicDyn"
|
||||
else: "nimDecRefIsLast"
|
||||
let cond = callCodegenProc(c.g, decRefProc, c.info, tmp)
|
||||
cond.typ() = getSysType(c.g, x.info, tyBool)
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
@@ -861,7 +814,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yenv.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
if isCyclic:
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
|
||||
body.add newAsgnStmt(x, y)
|
||||
@@ -873,7 +826,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yenv.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
if isCyclic:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
|
||||
@@ -885,7 +838,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace:
|
||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
@@ -913,7 +866,7 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.sons.insert(des, 0)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
var actions = newNodeI(nkStmtList, c.info)
|
||||
@@ -925,7 +878,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isFinal(elemType):
|
||||
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
|
||||
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
alignOf.typ() = getSysType(c.g, c.info, tyInt)
|
||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf)
|
||||
else:
|
||||
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
|
||||
@@ -941,21 +894,21 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, x, actions)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if c.kind == attachedDeepCopy:
|
||||
# a big problem is that we don't know the environment's type here, so we
|
||||
# have to go through some indirection; we delegate this to the codegen:
|
||||
let call = newNodeI(nkCall, c.info, 2)
|
||||
call.typ() = t
|
||||
call.typ = t
|
||||
call[0] = newSymNode(createMagic(c.g, c.idgen, "deepCopy", mDeepCopy))
|
||||
call[1] = y
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xx.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
# we 'nil' y out afterwards so we *need* to take over its reference
|
||||
@@ -964,13 +917,13 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yy.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yy.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
of attachedDestructor:
|
||||
@@ -981,11 +934,11 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.sons.insert(des, 0)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xx.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
var actions = newNodeI(nkStmtList, c.info)
|
||||
#discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
|
||||
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
|
||||
@@ -999,7 +952,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, xx, actions)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case t.kind
|
||||
@@ -1049,13 +1002,9 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# 'selectedGC' here to determine if we have the new runtime.
|
||||
discard considerUserDefinedOp(c, t, body, x, y)
|
||||
elif tfHasAsgn in t.flags:
|
||||
# seqs with elements using custom hooks in refc
|
||||
if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
|
||||
body.add newSeqCall(c, x, y)
|
||||
if c.kind == attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
else:
|
||||
forallElements(c, t, body, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
else:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyString:
|
||||
@@ -1072,11 +1021,9 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of {attachedAsgn, attachedSink, attachedDup}:
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
elif tfUnion in t.flags: # bug #25236
|
||||
defaultOp(c, t, body, x, y)
|
||||
else:
|
||||
if c.kind == attachedDup:
|
||||
var op2 = getAttachedOp(c.g, t, attachedAsgn)
|
||||
@@ -1116,7 +1063,9 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ.elementType
|
||||
if getAttachedOp(g, baseType, kind) == nil:
|
||||
discard produceSym(g, c, baseType, kind, info, idgen)
|
||||
# TODO: fixme `isDistinct` is a fix for #23552; remove it after
|
||||
# `-d:nimPreviewNonVarDestructor` becomes the default
|
||||
discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
|
||||
result = getAttachedOp(g, baseType, kind)
|
||||
setAttachedOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
@@ -1155,7 +1104,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache
|
||||
incl result.flags, sfGeneratedOp
|
||||
|
||||
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
|
||||
if kind == attachedDup:
|
||||
return symDupPrototype(g, typ, owner, kind, info, idgen)
|
||||
|
||||
@@ -1166,7 +1115,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
idgen, result, info)
|
||||
|
||||
if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence})):
|
||||
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)):
|
||||
dest.typ = typ
|
||||
else:
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
@@ -1203,18 +1152,18 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
|
||||
let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y)
|
||||
xx.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ() = xx.typ
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ = xx.typ
|
||||
body.add newAsgnStmt(xx, yy)
|
||||
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
|
||||
if typ.kind == tyDistinct:
|
||||
return produceSymDistinctType(g, c, typ, kind, info, idgen)
|
||||
|
||||
result = getAttachedOp(g, typ, kind)
|
||||
if result == nil:
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen)
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
|
||||
|
||||
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
@@ -1237,10 +1186,8 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
result.ast[bodyPos].add newAsgnStmt(d, src)
|
||||
else:
|
||||
var tk: TTypeKind
|
||||
var skipped: PType = nil
|
||||
if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
|
||||
skipped = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink})
|
||||
tk = skipped.kind
|
||||
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
|
||||
else:
|
||||
tk = tyNone # no special casing for strings and seqs
|
||||
case tk
|
||||
@@ -1250,7 +1197,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
fillStrOp(a, typ, result.ast[bodyPos], d, src)
|
||||
else:
|
||||
fillBody(a, typ, result.ast[bodyPos], d, src)
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(skipped):
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not lacksMTypeField(typ):
|
||||
# bug #19205: Do not forget to also copy the hidden type field:
|
||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
||||
|
||||
@@ -1260,8 +1207,6 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
result.ast[pragmasPos].add newTree(nkExprColonExpr,
|
||||
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))
|
||||
|
||||
if kind == attachedDestructor:
|
||||
incl result.options, optQuirky
|
||||
completePartialOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
|
||||
@@ -1321,7 +1266,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
|
||||
else:
|
||||
localError(g.config, info, "unresolved generic parameter")
|
||||
|
||||
proc isTrivial*(s: PSym): bool {.inline.} =
|
||||
proc isTrival*(s: PSym): bool {.inline.} =
|
||||
s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
|
||||
|
||||
proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
@@ -1331,10 +1276,6 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
## The later 'injectdestructors' pass depends on it.
|
||||
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
|
||||
incl orig.flags, tfCheckedForDestructor
|
||||
# for user defined generic destructors:
|
||||
let origRoot = genericRoot(orig)
|
||||
if origRoot != nil:
|
||||
incl origRoot.flags, tfGenericHasDestructor
|
||||
|
||||
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
|
||||
@@ -1376,8 +1317,8 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
if canon != orig:
|
||||
setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
|
||||
|
||||
if not isTrivial(getAttachedOp(g, orig, attachedDestructor)):
|
||||
#or not isTrivial(orig.assignment) or
|
||||
# not isTrivial(orig.sink):
|
||||
if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
|
||||
#or not isTrival(orig.assignment) or
|
||||
# not isTrival(orig.sink):
|
||||
orig.flags.incl tfHasAsgn
|
||||
# ^ XXX Breaks IC!
|
||||
|
||||
@@ -32,12 +32,12 @@ proc interestingVar(s: PSym): bool {.inline.} =
|
||||
proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode =
|
||||
let field = addUniqueField(c.objType, s, c.cache, c.idgen)
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ() = c.objType
|
||||
deref.typ = c.objType
|
||||
deref.add(newSymNode(c.partialParam, info))
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add(deref)
|
||||
result.add(newSymNode(field))
|
||||
result.typ() = field.typ
|
||||
result.typ = field.typ
|
||||
|
||||
proc liftLocals(n: PNode; i: int; c: var Ctx) =
|
||||
let it = n[i]
|
||||
|
||||
@@ -93,14 +93,9 @@ type
|
||||
warnBareExcept = "BareExcept",
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
|
||||
warnStdPrefix = "StdPrefix",
|
||||
warnUnknownNotes = "UnknownNotes",
|
||||
warnLongLiterals = "LongLiterals",
|
||||
warnStdPrefix = "StdPrefix"
|
||||
warnUser = "User",
|
||||
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
|
||||
warnImplicitRangeConversion = "ImplicitRangeConversion",
|
||||
warnSystemRangeConversion = "SystemRangeConversion",
|
||||
warnInvalidCmpOp = "InvalidCmpOp",
|
||||
# hints
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||
hintCC = "CC",
|
||||
@@ -114,9 +109,9 @@ type
|
||||
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
|
||||
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintUnknownRaises = "UnknownRaises",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
hintUnknownHint = "UnknownHint"
|
||||
|
||||
const
|
||||
MsgKindToStr*: array[TMsgKind, string] = [
|
||||
@@ -205,13 +200,8 @@ const
|
||||
warnImplicitDefaultValue: "$1",
|
||||
warnIgnoredSymbolInjection: "$1",
|
||||
warnStdPrefix: "$1 needs the 'std' prefix",
|
||||
warnUnknownNotes: "$1",
|
||||
warnLongLiterals: "$1",
|
||||
warnUser: "$1",
|
||||
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
|
||||
warnImplicitRangeConversion: "implicit range conversion $1",
|
||||
warnSystemRangeConversion: "implicit range conversion $1",
|
||||
warnInvalidCmpOp: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
|
||||
@@ -245,9 +235,9 @@ const
|
||||
hintUser: "$1",
|
||||
hintUserRaw: "$1",
|
||||
hintExtendedContext: "$1",
|
||||
hintUnknownRaises: "$1 is a forward declaration without explicit .raises, assuming it can raise anything",
|
||||
hintMsgOrigin: "$1",
|
||||
hintDeclaredLoc: "$1"
|
||||
hintDeclaredLoc: "$1",
|
||||
hintUnknownHint: "unknown hint: $1"
|
||||
]
|
||||
|
||||
const
|
||||
@@ -266,9 +256,9 @@ type
|
||||
|
||||
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
||||
result = default(array[0..3, TNoteKinds])
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix, warnSystemRangeConversion}
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix}
|
||||
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
|
||||
result[1] = result[2] - {warnImplicitRangeConversion, warnProveField, warnProveIndex,
|
||||
result[1] = result[2] - {warnProveField, warnProveIndex,
|
||||
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
||||
hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
|
||||
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
|
||||
@@ -278,7 +268,6 @@ const
|
||||
NotesVerbosity* = computeNotesVerbosity()
|
||||
errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
|
||||
errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
|
||||
errFloatToString* = "cannot convert '$1' to '$2'"
|
||||
|
||||
type
|
||||
TFileInfo* = object
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import std/strutils
|
||||
from std/sugar import dup
|
||||
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages, modulegraphs
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages
|
||||
export packages
|
||||
|
||||
const
|
||||
@@ -95,9 +95,9 @@ proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) =
|
||||
## Check symbol definitions adhere to NEP1 style rules.
|
||||
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
|
||||
{optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # check only if hint/error/warning is enabled
|
||||
{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.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
|
||||
sym.kind != skResult and # ignore `result`
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
@@ -136,9 +136,9 @@ proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
|
||||
template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
|
||||
## Check symbol uses match their definition's style.
|
||||
if {optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
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.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
|
||||
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
|
||||
@@ -152,7 +152,7 @@ proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragm
|
||||
template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
|
||||
## Check builtin pragma uses match their definition's style.
|
||||
## Note: This only applies to builtin pragmas, not user pragmas.
|
||||
if {optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
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.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)): # ignore foreign packages
|
||||
(sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
|
||||
checkPragmaUseImpl(ctx.config, info, w, pragmaName)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
pathutils
|
||||
import std/strutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
|
||||
@@ -86,47 +86,6 @@ const
|
||||
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
|
||||
'|', '%', '&', '$', '@', '~', ','}
|
||||
AdditionalLineContinuationOprs = {'#', ':', '='}
|
||||
LineContinuationTokens = [
|
||||
"let", "var", "const", "type", # section
|
||||
"object", "tuple",
|
||||
# from ./layouter.oprSet
|
||||
"div", "mod", "shl", "shr", "in", "notin", "is",
|
||||
"isnot", "not", "of", "as", "from", "..", "and", "or", "xor",
|
||||
] # must be all `nimIdentNormalized`-ed
|
||||
|
||||
proc eqIdent(a, bNormalized: string): bool =
|
||||
a.nimIdentNormalize == bNormalized
|
||||
|
||||
proc endsWithIdent(s, subs: string): bool =
|
||||
let le = subs.len
|
||||
if le > s.len: return false
|
||||
s[^le .. ^1].eqIdent subs
|
||||
|
||||
proc continuesWithIdent(s, subs: string, start: int): bool =
|
||||
s.substr(start, start+subs.high).eqIdent subs
|
||||
|
||||
proc endsWithIdent(s, subs: string, endIdx: var int): bool =
|
||||
endIdx.dec subs.len
|
||||
result = s.continuesWithIdent(subs, endIdx+1)
|
||||
|
||||
proc containsObjectOf(x: string): bool =
|
||||
const sep = ' '
|
||||
var idx = x.rfind(sep)
|
||||
if idx == -1: return
|
||||
template eatWord(word) =
|
||||
while x[idx] == sep: idx.dec
|
||||
result = x.endsWithIdent(word, idx)
|
||||
if not result: return
|
||||
eatWord "of"
|
||||
eatWord "object"
|
||||
result = true
|
||||
|
||||
proc endsWithLineContinuationToken(x: string): bool =
|
||||
result = false
|
||||
for tok in LineContinuationTokens:
|
||||
if x.endsWithIdent(tok):
|
||||
return true
|
||||
result = x.containsObjectOf
|
||||
|
||||
proc endsWithOpr*(x: string): bool =
|
||||
result = x.endsWith(LineContinuationOprs)
|
||||
@@ -134,9 +93,7 @@ proc endsWithOpr*(x: string): bool =
|
||||
proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
|
||||
result = inTripleString or line.len > 0 and (
|
||||
line[0] == ' ' or
|
||||
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) or
|
||||
line.endsWithLineContinuationToken()
|
||||
)
|
||||
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
|
||||
|
||||
proc countTriples(s: string): int =
|
||||
result = 0
|
||||
@@ -152,10 +109,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
|
||||
s.rd = 0
|
||||
var line = newStringOfCap(120)
|
||||
var triples = 0
|
||||
while true:
|
||||
if not readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
|
||||
# now readLineFromStdin meets EOF (ctrl-D/Z) or ctrl-C
|
||||
quit()
|
||||
while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
|
||||
s.s.add(line)
|
||||
s.s.add("\n")
|
||||
inc triples, countTriples(line)
|
||||
|
||||
@@ -58,11 +58,13 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
|
||||
else: handleError(n, origin)
|
||||
result = getIdent(c.cache, id)
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
if n[0].kind == nkSym:
|
||||
result = n[0].sym.name
|
||||
else:
|
||||
handleError(n, origin)
|
||||
of nkOpenSym:
|
||||
result = considerQuotedIdent(c, n[0], origin)
|
||||
else:
|
||||
handleError(n, origin)
|
||||
|
||||
@@ -75,13 +77,10 @@ proc addUniqueSym*(scope: PScope, s: PSym): PSym =
|
||||
proc openScope*(c: PContext): PScope {.discardable.} =
|
||||
result = PScope(parent: c.currentScope,
|
||||
symbols: initStrTable(),
|
||||
depthLevel: c.scopeDepth + 1,
|
||||
optionStackLen: c.optionStack.len)
|
||||
depthLevel: c.scopeDepth + 1)
|
||||
c.currentScope = result
|
||||
|
||||
proc rawCloseScope*(c: PContext) =
|
||||
if c.currentScope.optionStackLen >= 1:
|
||||
c.optionStack.setLen(c.currentScope.optionStackLen)
|
||||
c.currentScope = c.currentScope.parent
|
||||
|
||||
proc closeScope*(c: PContext) =
|
||||
@@ -222,14 +221,7 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
|
||||
if i == limit: return
|
||||
inc i
|
||||
|
||||
proc searchImportsAll*(c: PContext, s: PIdent, filter: TSymKinds, holding: var seq[PSym]) =
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
holding.add s
|
||||
|
||||
proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
@@ -239,12 +231,14 @@ proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result.add candidate
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
|
||||
proc searchScopesAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = searchScopes(c,s,filter)
|
||||
if result.len == 0:
|
||||
searchImportsAll(c, s, filter, result)
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
@@ -258,7 +252,11 @@ proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PS
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
|
||||
if result.len == 0:
|
||||
searchImportsAll(c, s, filter, result)
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc cmpScopes*(ctx: PContext, s: PSym): int =
|
||||
# Do not return a negative number
|
||||
@@ -388,7 +386,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
|
||||
if sym.name.id == ord(wUnderscore): return
|
||||
let conflict = scope.addUniqueSym(sym)
|
||||
if conflict != nil:
|
||||
if sym.kind == skModule and conflict.kind == skModule and not c.config.isDefined("nimPreviewDuplicateModuleError"):
|
||||
if sym.kind == skModule and conflict.kind == skModule:
|
||||
# e.g.: import foo; import foo
|
||||
# xxx we could refine this by issuing a different hint for the case
|
||||
# where a duplicate import happens inside an include.
|
||||
@@ -463,15 +461,6 @@ proc openShadowScope*(c: PContext) =
|
||||
symbols: initStrTable(),
|
||||
depthLevel: c.scopeDepth)
|
||||
|
||||
proc rememberShadowDefs*(c: PContext) =
|
||||
## bug #25693: a template/macro operand's local definitions are sem-checked in
|
||||
## a shadow scope that is then discarded. Record those definitions so that a
|
||||
## later re-emission (e.g. a captured `typed` fragment expanded more than once)
|
||||
## can be detected as a redefinition rather than silently miscompiled.
|
||||
for s in c.currentScope.symbols:
|
||||
if s.kind in {skVar, skLet, skForVar} and {sfGenSym, sfWasGenSym} * s.flags == {}:
|
||||
c.shadowDiscardedDefs.incl s.id
|
||||
|
||||
proc closeShadowScope*(c: PContext) =
|
||||
## closes the shadow scope, but doesn't merge any of the symbols
|
||||
## Does not check for unused symbols or missing forward decls since a macro
|
||||
@@ -655,7 +644,7 @@ const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
|
||||
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
|
||||
includePureEnum = false): seq[PSym] =
|
||||
result = selectFromScopesElseAll(c, ident, filter)
|
||||
result = searchInScopesFilterBy(c, ident, filter)
|
||||
if skEnumField in filter and (result.len == 0 or includePureEnum):
|
||||
result.add allPureEnumFields(c, ident)
|
||||
|
||||
@@ -696,12 +685,10 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
|
||||
if m != nil and m.kind == skModule:
|
||||
var ident: PIdent = nil
|
||||
if n[1].kind == nkAccQuoted:
|
||||
if n[1].kind == nkIdent:
|
||||
ident = n[1].ident
|
||||
elif n[1].kind == nkAccQuoted:
|
||||
ident = considerQuotedIdent(c, n[1])
|
||||
else:
|
||||
# this includes sym and symchoice nodes, but since we are looking in
|
||||
# a module, it shouldn't matter what was captured
|
||||
ident = n[1].getPIdent
|
||||
if ident != nil:
|
||||
if m == c.module:
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
@@ -736,7 +723,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
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`
|
||||
# for now it only seems this is called for `pickSym` in `getTypeIdent`
|
||||
return initOverloadIter(o, c, n[0])
|
||||
o.importIdx = -1
|
||||
o.marked = initIntSet()
|
||||
|
||||
@@ -174,12 +174,12 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
|
||||
# returns a[].field as a node
|
||||
assert field.kind == skField
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ() = a.typ.skipTypes(abstractInst)[0]
|
||||
deref.typ = a.typ.skipTypes(abstractInst)[0]
|
||||
deref.add a
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add deref
|
||||
result.add newSymNode(field)
|
||||
result.typ() = field.typ
|
||||
result.typ = field.typ
|
||||
|
||||
proc rawDirectAccess*(obj, field: PSym): PNode =
|
||||
# returns a.field as a node
|
||||
@@ -187,7 +187,7 @@ proc rawDirectAccess*(obj, field: PSym): PNode =
|
||||
result = newNodeI(nkDotExpr, field.info)
|
||||
result.add newSymNode(obj)
|
||||
result.add newSymNode(field)
|
||||
result.typ() = field.typ
|
||||
result.typ = field.typ
|
||||
|
||||
proc lookupInRecord(n: PNode, id: ItemId): PSym =
|
||||
result = nil
|
||||
@@ -250,12 +250,12 @@ proc newDotExpr*(obj, b: PSym): PNode =
|
||||
assert field != nil, b.name.s
|
||||
result.add newSymNode(obj)
|
||||
result.add newSymNode(field)
|
||||
result.typ() = field.typ
|
||||
result.typ = field.typ
|
||||
|
||||
proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
|
||||
# returns a[].b as a node
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ() = a.typ.skipTypes(abstractInst).elementType
|
||||
deref.typ = a.typ.skipTypes(abstractInst).elementType
|
||||
var t = deref.typ.skipTypes(abstractInst)
|
||||
var field: PSym
|
||||
while true:
|
||||
@@ -273,12 +273,12 @@ proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add deref
|
||||
result.add newSymNode(field)
|
||||
result.typ() = field.typ
|
||||
result.typ = field.typ
|
||||
|
||||
proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
|
||||
# returns a[].b as a node
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ() = a.typ.skipTypes(abstractInst).elementType
|
||||
deref.typ = a.typ.skipTypes(abstractInst).elementType
|
||||
var t = deref.typ.skipTypes(abstractInst)
|
||||
var field: PSym
|
||||
let bb = getIdent(cache, b)
|
||||
@@ -297,7 +297,7 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): P
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add deref
|
||||
result.add newSymNode(field)
|
||||
result.typ() = field.typ
|
||||
result.typ = field.typ
|
||||
|
||||
proc getFieldFromObj*(t: PType; v: PSym): PSym =
|
||||
assert v.kind != skField
|
||||
@@ -320,7 +320,7 @@ proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
|
||||
proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
|
||||
result = newNodeI(nkAddr, n.info, 1)
|
||||
result[0] = n
|
||||
result.typ() = newType(typeKind, idgen, n.typ.owner)
|
||||
result.typ = newType(typeKind, idgen, n.typ.owner)
|
||||
result.typ.rawAddSon(n.typ)
|
||||
|
||||
proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
|
||||
@@ -344,18 +344,18 @@ proc callCodegenProc*(g: ModuleGraph; name: string;
|
||||
if optionalArgs != nil:
|
||||
for i in 1..<optionalArgs.len-2:
|
||||
result.add optionalArgs[i]
|
||||
result.typ() = sym.typ.returnType
|
||||
result.typ = sym.typ.returnType
|
||||
|
||||
proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
|
||||
result = nkIntLit.newIntNode(value)
|
||||
result.typ() = getSysType(g, info, tyInt)
|
||||
result.typ = getSysType(g, info, tyInt)
|
||||
|
||||
proc genHigh*(g: ModuleGraph; n: PNode): PNode =
|
||||
if skipTypes(n.typ, abstractVar).kind == tyArray:
|
||||
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar))))
|
||||
else:
|
||||
result = newNodeI(nkCall, n.info, 2)
|
||||
result.typ() = getSysType(g, n.info, tyInt)
|
||||
result.typ = getSysType(g, n.info, tyInt)
|
||||
result[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
|
||||
result[1] = n
|
||||
|
||||
@@ -364,7 +364,7 @@ proc genLen*(g: ModuleGraph; n: PNode): PNode =
|
||||
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1))
|
||||
else:
|
||||
result = newNodeI(nkCall, n.info, 2)
|
||||
result.typ() = getSysType(g, n.info, tyInt)
|
||||
result.typ = getSysType(g, n.info, tyInt)
|
||||
result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
|
||||
result[1] = n
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, astalgo, msgs, platform, idents,
|
||||
modulegraphs, lineinfos, types
|
||||
modulegraphs, lineinfos
|
||||
|
||||
export createMagic
|
||||
|
||||
@@ -134,7 +134,7 @@ proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
|
||||
proc resetNimScriptSymbols*(g: ModuleGraph) = g.exposed = initStrTable()
|
||||
|
||||
proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
|
||||
case t.skipTypes(abstractRange).kind
|
||||
case t.kind
|
||||
of tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
|
||||
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64:
|
||||
result = getSysMagic(g, info, "==", mEqI)
|
||||
@@ -166,4 +166,4 @@ proc makeAddr*(n: PNode; idgen: IdGenerator): PNode =
|
||||
result = n
|
||||
else:
|
||||
result = newTree(nkHiddenAddr, n)
|
||||
result.typ() = makePtrType(n.typ.skipTypes({tySink}), idgen)
|
||||
result.typ = makePtrType(n.typ, idgen)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## represents a complete Nim project. Single modules can either be kept in RAM
|
||||
## or stored in a rod-file.
|
||||
|
||||
import std/[intsets, tables, hashes, strtabs, os, strutils, parseutils]
|
||||
import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
|
||||
import ../dist/checksums/src/checksums/md5
|
||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
|
||||
import ic / [packed_ast, ic]
|
||||
@@ -88,7 +88,6 @@ type
|
||||
suggestMode*: bool # whether we are in nimsuggest mode or not.
|
||||
invalidTransitiveClosure: bool
|
||||
interactive*: bool
|
||||
withinSystem*: bool # in system.nim or a module imported by system.nim
|
||||
inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
|
||||
# first module that included it
|
||||
importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
|
||||
@@ -136,8 +135,6 @@ type
|
||||
|
||||
cachedFiles*: StringTableRef
|
||||
|
||||
procGlobals*: seq[PNode]
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
idgen*: IdGenerator
|
||||
PPassContext* = ref TPassContext
|
||||
@@ -410,15 +407,9 @@ iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) =
|
||||
proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) =
|
||||
g.methodsPerGenericType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
|
||||
|
||||
proc hasDisabledOp(g: ModuleGraph; t: PType; kind: TTypeAttachedOp): bool =
|
||||
let op = getAttachedOp(g, t, kind)
|
||||
result = op != nil and sfError in op.flags
|
||||
|
||||
proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool =
|
||||
result = hasDisabledOp(g, t, attachedAsgn)
|
||||
|
||||
proc hasDisabledDup*(g: ModuleGraph; t: PType): bool =
|
||||
result = hasDisabledOp(g, t, attachedDup)
|
||||
let op = getAttachedOp(g, t, attachedAsgn)
|
||||
result = op != nil and sfError in op.flags
|
||||
|
||||
proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
|
||||
for k in low(TTypeAttachedOp)..high(TTypeAttachedOp):
|
||||
@@ -462,10 +453,10 @@ template getPContext(): untyped =
|
||||
else: c.c
|
||||
|
||||
when defined(nimsuggest):
|
||||
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
|
||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
||||
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
|
||||
else:
|
||||
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
|
||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
||||
template onDef*(info: TLineInfo; s: PSym) = discard
|
||||
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
|
||||
|
||||
|
||||
@@ -79,10 +79,6 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
|
||||
else:
|
||||
result = fileInfoIdx(conf, fullPath)
|
||||
|
||||
type
|
||||
SelectedBase = enum
|
||||
FromProject, FromSearchPath, FromNimblePath
|
||||
|
||||
proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
|
||||
## Mangle a relative module path to avoid path and symbol collisions.
|
||||
##
|
||||
@@ -91,27 +87,9 @@ proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
|
||||
##
|
||||
## Example:
|
||||
## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar`
|
||||
var best = relativeTo(path, conf.projectPath).string
|
||||
var selectedBase = FromProject
|
||||
for x in conf.searchPaths:
|
||||
let other = relativeTo(path, x).string
|
||||
if other.len < best.len:
|
||||
best = other
|
||||
selectedBase = FromSearchPath
|
||||
for x in conf.nimblePaths:
|
||||
let other = relativeTo(path, x).string
|
||||
if other.len < best.len:
|
||||
best = other
|
||||
selectedBase = FromNimblePath
|
||||
let prefix =
|
||||
case selectedBase
|
||||
of FromProject: "@m"
|
||||
of FromSearchPath: "@p"
|
||||
of FromNimblePath: "@n"
|
||||
|
||||
prefix & best.multiReplace(
|
||||
"@m" & relativeTo(path, conf.projectPath).string.multiReplace(
|
||||
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
|
||||
|
||||
proc demangleModuleName*(path: string): string =
|
||||
## Demangle a relative module path.
|
||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@p": "", "@n": "", "@c": ":"})
|
||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})
|
||||
|
||||
@@ -25,7 +25,7 @@ template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
|
||||
|
||||
proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
|
||||
let packSym = getPackage(graph, fileIdx)
|
||||
setOwner(result, packSym)
|
||||
result.owner = packSym
|
||||
result.position = int fileIdx
|
||||
|
||||
proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
|
||||
@@ -648,9 +648,7 @@ template internalAssert*(conf: ConfigRef, e: bool) =
|
||||
|
||||
template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") =
|
||||
let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg]
|
||||
let msg = if optStyleError in conf.globalOptions: errGenerated
|
||||
elif optStyleWarning in conf.globalOptions: warnUser
|
||||
else: hintName
|
||||
let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
|
||||
liMessage(conf, info, msg, m, doNothing, instLoc())
|
||||
|
||||
proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope =
|
||||
|
||||
52
compiler/ndi.nim
Normal file
52
compiler/ndi.nim
Normal file
@@ -0,0 +1,52 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2017 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the generation of ``.ndi`` files for better debugging
|
||||
## support of Nim code. "ndi" stands for "Nim debug info".
|
||||
|
||||
import ast, msgs, ropes, options, pathutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
type
|
||||
NdiFile* = object
|
||||
enabled: bool
|
||||
f: File
|
||||
buf: string
|
||||
filename: AbsoluteFile
|
||||
syms: seq[PSym]
|
||||
|
||||
proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
|
||||
f.buf.setLen 0
|
||||
f.buf.addInt s.info.line.int
|
||||
f.buf.add "\t"
|
||||
f.buf.addInt s.info.col.int
|
||||
f.f.write(s.name.s, "\t")
|
||||
f.f.writeRope(s.loc.snippet)
|
||||
f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
|
||||
|
||||
template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
|
||||
if f.enabled: f.syms.add s
|
||||
|
||||
proc open*(f: var NdiFile; filename: AbsoluteFile; conf: ConfigRef) =
|
||||
f.enabled = not filename.isEmpty
|
||||
if f.enabled:
|
||||
f.filename = filename
|
||||
f.buf = newStringOfCap(20)
|
||||
|
||||
proc close*(f: var NdiFile, conf: ConfigRef) =
|
||||
if f.enabled:
|
||||
f.f = open(f.filename.string, fmWrite, 8000)
|
||||
doAssert f.f != nil, f.filename.string
|
||||
for s in f.syms:
|
||||
doWrite(f, s, conf)
|
||||
close(f.f)
|
||||
f.syms.reset
|
||||
f.filename.reset
|
||||
@@ -183,6 +183,12 @@ func `<`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
func `<=`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
a.int16 <= b.int16
|
||||
|
||||
func `>`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
a.int16 > b.int16
|
||||
|
||||
func `>=`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
a.int16 >= b.int16
|
||||
|
||||
func `==`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
a.int16 == b.int16
|
||||
|
||||
@@ -913,7 +919,7 @@ proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
|
||||
newSymNode(op, r.info),
|
||||
l,
|
||||
r)
|
||||
result.typ() = newType(tyBool, ctx.idgen, nil)
|
||||
result.typ = newType(tyBool, ctx.idgen, nil)
|
||||
|
||||
proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
|
||||
var cache = newIdentCache()
|
||||
@@ -923,7 +929,7 @@ proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
|
||||
result = nkPrefix.newTree(
|
||||
newSymNode(op, node.info),
|
||||
node)
|
||||
result.typ() = newType(tyBool, ctx.idgen, nil)
|
||||
result.typ = newType(tyBool, ctx.idgen, nil)
|
||||
|
||||
proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
|
||||
infix(ctx, l, r, mEqRef)
|
||||
|
||||
@@ -4,16 +4,12 @@ hint[XDeclaredButNotUsed]:off
|
||||
|
||||
define:booting
|
||||
define:nimcore
|
||||
define:nimPreviewFloatRoundtrip
|
||||
define:nimPreviewSlimSystem
|
||||
define:nimPreviewCstringConversion
|
||||
define:nimPreviewProcConversion
|
||||
define:nimPreviewRangeDefault
|
||||
define:nimPreviewNonVarDestructor
|
||||
define:nimPreviewCheckedClose
|
||||
define:nimPreviewAsmSemSymbol
|
||||
define:nimPreviewCStringComparisons
|
||||
define:nimPreviewDuplicateModuleError
|
||||
|
||||
threads:off
|
||||
|
||||
#import:"$projectpath/testability"
|
||||
@@ -65,7 +61,3 @@ define:useStdoutAsStdmsg
|
||||
@if nimHasVtables:
|
||||
experimental:vtables
|
||||
@end
|
||||
|
||||
@if nimHasImplicitRangeConversion:
|
||||
warning[ImplicitRangeConversion]:off
|
||||
@end
|
||||
|
||||
@@ -207,6 +207,7 @@ proc parseAssignment(L: var Lexer, tok: var Token;
|
||||
checkSymbol(L, tok)
|
||||
val.add($tok)
|
||||
confTok(L, tok, config, condStack)
|
||||
config.currentConfigDir = parentDir(filename.string)
|
||||
if percent:
|
||||
processSwitch(s, strtabs.`%`(val, config.configVars,
|
||||
{useEnvironment, useEmpty}), passPP, info, config)
|
||||
@@ -248,8 +249,6 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
|
||||
setDefaultLibpath(conf)
|
||||
template readConfigFile(path) =
|
||||
let configPath = path
|
||||
conf.currentConfigDir = configPath.splitFile.dir.string
|
||||
setConfigVar(conf, "selfDir", conf.currentConfigDir)
|
||||
if readConfigFile(configPath, cache, conf):
|
||||
conf.configFiles.add(configPath)
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
|
||||
elemType = settype[0]
|
||||
first = firstOrd(conf, elemType).toInt64
|
||||
result = newNodeI(nkCurly, info)
|
||||
result.typ() = settype
|
||||
result.typ = settype
|
||||
result.info = info
|
||||
e = 0
|
||||
while e < s.len * ElemSize:
|
||||
@@ -101,7 +101,7 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
|
||||
result.add aa
|
||||
else:
|
||||
n = newNodeI(nkRange, info)
|
||||
n.typ() = elemType
|
||||
n.typ = elemType
|
||||
n.add aa
|
||||
let bb = newIntTypeNode(b + first, elemType)
|
||||
bb.info = info
|
||||
|
||||
@@ -28,14 +28,11 @@ type
|
||||
hasReturn, hasBreak: bool
|
||||
label: PSym # can be nil
|
||||
parent: ptr BasicBlock
|
||||
symToDel: seq[PNode]
|
||||
|
||||
Con = object
|
||||
somethingTodo: bool
|
||||
inFinally: int
|
||||
|
||||
proc invalidateWasMoved(c: var BasicBlock; x: PNode)
|
||||
|
||||
proc nestedBlock(parent: var BasicBlock; kind: TNodeKind): BasicBlock =
|
||||
BasicBlock(wasMovedLocs: @[], kind: kind, hasReturn: false, hasBreak: false,
|
||||
label: nil, parent: addr(parent))
|
||||
@@ -65,10 +62,6 @@ proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} =
|
||||
if this.hasReturn:
|
||||
parent.wasMovedLocs.setLen 0
|
||||
parent.hasReturn = true
|
||||
elif this.symToDel.len > 0:
|
||||
parent.symToDel = this.symToDel
|
||||
for i in this.symToDel:
|
||||
invalidateWasMoved(parent, i)
|
||||
|
||||
proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool =
|
||||
result = false
|
||||
@@ -156,7 +149,6 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
|
||||
# any usage of the location before destruction implies we
|
||||
# cannot elide the 'wasMoved(x)':
|
||||
b.invalidateWasMoved n
|
||||
b.symToDel.add n
|
||||
|
||||
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
|
||||
@@ -25,7 +25,7 @@ const
|
||||
useEffectSystem* = true
|
||||
useWriteTracking* = false
|
||||
hasFFI* = defined(nimHasLibFFI)
|
||||
copyrightYear* = "2026"
|
||||
copyrightYear* = "2024"
|
||||
|
||||
nimEnableCovariance* = defined(nimEnableCovariance)
|
||||
|
||||
@@ -68,7 +68,6 @@ type # please make sure we have under 32 options
|
||||
optUseNimcache, # save artifacts (including binary) in $nimcache
|
||||
optStyleHint, # check that the names adhere to NEP-1
|
||||
optStyleError, # enforce that the names adhere to NEP-1
|
||||
optStyleWarning, # emit style checks as warnings
|
||||
optStyleUsages, # only enforce consistent **usages** of the symbol
|
||||
optSkipSystemConfigFile, # skip the system's cfg/nims config file
|
||||
optSkipProjConfigFile, # skip the project's cfg/nims config file
|
||||
@@ -111,8 +110,6 @@ type # please make sure we have under 32 options
|
||||
optEnableDeepCopy # ORC specific: enable 'deepcopy' for all types.
|
||||
optShowNonExportedFields # for documentation: show fields that are not exported
|
||||
optJsBigInt64 # use bigints for 64-bit integers in JS
|
||||
optDocRaw # for documentation: Don't render markdown for JSON output
|
||||
optItaniumMangle # mangling follows the Itanium spec
|
||||
|
||||
TGlobalOptions* = set[TGlobalOption]
|
||||
|
||||
@@ -232,7 +229,6 @@ type
|
||||
# alternative to above:
|
||||
genericsOpenSym
|
||||
vtables
|
||||
typeBoundOps
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
@@ -251,16 +247,6 @@ type
|
||||
## Useful for libraries that rely on local passC
|
||||
jsNoLambdaLifting
|
||||
## Old transformation for closures in JS backend
|
||||
noPanicOnExcept
|
||||
## don't panic on bare except
|
||||
procParamTypeBackendAliases
|
||||
## Keep the old proc type compatibility rules that ignore backend
|
||||
## c type aliases.
|
||||
injectedSymbolRedefinition
|
||||
## Allow a template to inject a symbol *definition* that is then emitted
|
||||
## more than once (e.g. a `typed` argument captured by a `{.dirty.}`
|
||||
## template and re-emitted). This is a redefinition and rejected by
|
||||
## default; enabling this restores the old, unsound behavior. See #25693.
|
||||
|
||||
SymbolFilesOption* = enum
|
||||
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
|
||||
@@ -396,7 +382,6 @@ type
|
||||
warnCounter*: int
|
||||
errorMax*: int
|
||||
maxLoopIterationsVM*: int ## VM: max iterations of all loops
|
||||
maxCallDepthVM*: int ## VM: max call depth
|
||||
isVmTrace*: bool
|
||||
configVars*: StringTableRef
|
||||
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
|
||||
@@ -415,7 +400,6 @@ type
|
||||
projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
|
||||
projectFull*: AbsoluteFile # projectPath/projectName
|
||||
projectIsStdin*: bool # whether we're compiling from stdin
|
||||
stdinFile*: AbsoluteFile # Filename to use in messages for stdin
|
||||
lastMsgWasDot*: set[StdOrrKind] # the last compiler message was a single '.'
|
||||
projectMainIdx*: FileIndex # the canonical path id of the main module
|
||||
projectMainIdx2*: FileIndex # consider merging with projectMainIdx
|
||||
@@ -517,7 +501,7 @@ const
|
||||
optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs`
|
||||
optTrMacros, optStyleCheck, optCursorInference}
|
||||
DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace,
|
||||
optJsBigInt64, optItaniumMangle}
|
||||
optJsBigInt64}
|
||||
|
||||
proc getSrcTimestamp(): DateTime =
|
||||
try:
|
||||
@@ -592,7 +576,6 @@ proc newConfigRef*(): ConfigRef =
|
||||
projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
|
||||
projectFull: AbsoluteFile"", # projectPath/projectName
|
||||
projectIsStdin: false, # whether we're compiling from stdin
|
||||
stdinFile: AbsoluteFile"stdinfile",
|
||||
projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
|
||||
command: "", # the main command (e.g. cc, check, scan, etc)
|
||||
commandArgs: @[], # any arguments after the main command
|
||||
@@ -614,7 +597,6 @@ proc newConfigRef*(): ConfigRef =
|
||||
arguments: "",
|
||||
suggestMaxResults: 10_000,
|
||||
maxLoopIterationsVM: 10_000_000,
|
||||
maxCallDepthVM: 2_000,
|
||||
vmProfileData: newProfileData(),
|
||||
spellSuggestMax: spellSuggestSecretSauce,
|
||||
currentConfigDir: ""
|
||||
@@ -650,7 +632,6 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
|
||||
of "x86": result = conf.target.targetCPU == cpuI386
|
||||
of "itanium": result = conf.target.targetCPU == cpuIa64
|
||||
of "x8664": result = conf.target.targetCPU == cpuAmd64
|
||||
of "wasm": result = conf.target.targetCPU in {cpuWasm32, cpuWasm64}
|
||||
of "posix", "unix":
|
||||
result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
|
||||
osQnx, osAtari, osAix,
|
||||
@@ -1050,9 +1031,6 @@ proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
|
||||
proc showNonExportedFields*(conf: ConfigRef) =
|
||||
incl(conf.globalOptions, optShowNonExportedFields)
|
||||
|
||||
proc docRawOutput*(conf: ConfigRef) =
|
||||
incl(conf.globalOptions, optDocRaw)
|
||||
|
||||
proc expandDone*(conf: ConfigRef): bool =
|
||||
result = conf.ideCmd == ideExpand and conf.expandLevels == 0 and conf.expandProgress
|
||||
|
||||
|
||||
@@ -51,11 +51,3 @@ func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
conf.mainPackageId == sym.getPackageId
|
||||
|
||||
func belongsToProjectPackageMaybeNil*(conf: ConfigRef, sym: PSym): bool =
|
||||
## Return whether the symbol belongs to the project's package.
|
||||
## Returns `false` if `sym` is nil.
|
||||
##
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
sym != nil and conf.mainPackageId == sym.getPackageId
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import ast, types, msgs, idents, renderer, wordrecg, trees,
|
||||
options
|
||||
|
||||
import std/[strutils, assertions]
|
||||
import std/strutils
|
||||
|
||||
# we precompile the pattern here for efficiency into some internal
|
||||
# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
|
||||
@@ -216,11 +216,6 @@ proc exprRoot*(n: PNode; allowCalls = true): PSym =
|
||||
else:
|
||||
break
|
||||
|
||||
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult
|
||||
|
||||
proc isLentableBranch(owner: PSym, n: PNode): bool =
|
||||
result = isAssignable(owner, n) in {arLentValue, arAddressableConst, arLentValue}
|
||||
|
||||
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
## 'owner' can be nil!
|
||||
result = arNone
|
||||
@@ -313,35 +308,6 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
# nkVarTy denotes an lvalue, but the example above is the only
|
||||
# possible code which will get us here
|
||||
result = arLValue
|
||||
of nkIfExpr, nkIfStmt:
|
||||
# allow 'if' expressions to be lent if all branches are lentable
|
||||
for branch in n:
|
||||
if branch.len == 2:
|
||||
if not isLentableBranch(owner, branch[1]):
|
||||
return
|
||||
elif branch.len == 1:
|
||||
if not isLentableBranch(owner, branch[0]):
|
||||
return
|
||||
else:
|
||||
raiseAssert "Malformed `if` statement in isAssignable"
|
||||
result = arLentValue
|
||||
of nkCaseStmt:
|
||||
# allow 'case' expressions to be lent if all branches are lentable
|
||||
for i in 1 ..< n.len:
|
||||
let branch = n[i]
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
if not isLentableBranch(owner, branch[^1]):
|
||||
return
|
||||
of nkElifBranch:
|
||||
if not isLentableBranch(owner, branch[1]):
|
||||
return
|
||||
of nkElse:
|
||||
if not isLentableBranch(owner, branch[0]):
|
||||
return
|
||||
else:
|
||||
raiseAssert "Malformed `case` statement in isAssignable"
|
||||
result = arLentValue
|
||||
else:
|
||||
discard
|
||||
|
||||
|
||||
@@ -638,9 +638,8 @@ proc semiStmtList(p: var Parser, result: PNode) =
|
||||
getTok(p)
|
||||
if p.tok.tokType == tkParRi:
|
||||
break
|
||||
# ignore indent:
|
||||
#elif not (sameOrNoInd(p) or realInd(p)):
|
||||
# parMessage(p, errInvalidIndentation)
|
||||
elif not (sameInd(p) or realInd(p)):
|
||||
parMessage(p, errInvalidIndentation)
|
||||
let a = complexOrSimpleStmt(p)
|
||||
if a.kind == nkEmpty:
|
||||
parMessage(p, errExprExpected, p.tok)
|
||||
@@ -700,12 +699,10 @@ proc parsePar(p: var Parser): PNode =
|
||||
asgn.add b
|
||||
result.add(asgn)
|
||||
if p.tok.tokType == tkSemiColon:
|
||||
getTok(p)
|
||||
semiStmtList(p, result)
|
||||
elif p.tok.tokType == tkSemiColon:
|
||||
# stmt context:
|
||||
result.add(a)
|
||||
getTok(p)
|
||||
semiStmtList(p, result)
|
||||
else:
|
||||
a = colonOrEquals(p, a)
|
||||
@@ -1156,10 +1153,7 @@ proc parseParamList(p: var Parser, retColon = true): PNode =
|
||||
parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'")
|
||||
break
|
||||
else:
|
||||
if p.tok.tokType in tokKeywordLow..tokKeywordHigh:
|
||||
parMessage(p, errGenerated, "'" & $p.tok.ident.s & "' is a keyword and cannot be used as a parameter name")
|
||||
else:
|
||||
parMessage(p, "expected closing ')'")
|
||||
parMessage(p, "expected closing ')'")
|
||||
break
|
||||
result.add(a)
|
||||
if p.tok.tokType notin {tkComma, tkSemiColon}: break
|
||||
@@ -2113,28 +2107,12 @@ proc parseObjectCase(p: var Parser): PNode =
|
||||
#| objectBranches = objectBranch (IND{=} objectBranch)*
|
||||
#| (IND{=} 'elif' expr colcom objectPart)*
|
||||
#| (IND{=} 'else' colcom objectPart)?
|
||||
#| objectCase = 'case' (declColonEquals / pragma)? ':'? COMMENT?
|
||||
#| objectCase = 'case' declColonEquals ':'? COMMENT?
|
||||
#| (IND{>} objectBranches DED
|
||||
#| | IND{=} objectBranches)
|
||||
result = newNodeP(nkRecCase, p)
|
||||
getTok(p)
|
||||
if p.tok.tokType != tkOf:
|
||||
# of case will be handled later
|
||||
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
|
||||
var a: PNode
|
||||
if p.tok.tokType in {tkSymbol, tkAccent}:
|
||||
a = parseIdentColonEquals(p, {withPragma})
|
||||
else:
|
||||
a = newNodeP(nkIdentDefs, p)
|
||||
if p.tok.tokType == tkCurlyDotLe:
|
||||
var prag = newNodeP(nkPragmaExpr, p)
|
||||
prag.add(p.emptyNode)
|
||||
prag.add(parsePragma(p))
|
||||
a.add(prag)
|
||||
else:
|
||||
a.add(p.emptyNode)
|
||||
a.add(p.emptyNode)
|
||||
a.add(p.emptyNode)
|
||||
getTokNoInd(p)
|
||||
var a = parseIdentColonEquals(p, {withPragma})
|
||||
result.add(a)
|
||||
if p.tok.tokType == tkColon: getTok(p)
|
||||
flexComment(p, result)
|
||||
|
||||
@@ -42,7 +42,10 @@ proc makePass*(open: TPassOpen = nil,
|
||||
process: TPassProcess = nil,
|
||||
close: TPassClose = nil,
|
||||
isFrontend = false): TPass =
|
||||
result = (open, process, close, isFrontend)
|
||||
result.open = open
|
||||
result.close = close
|
||||
result.process = process
|
||||
result.isFrontend = isFrontend
|
||||
|
||||
const
|
||||
maxPasses = 10
|
||||
@@ -97,8 +100,8 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
||||
stream: PLLStream): bool {.discardable.} =
|
||||
if graph.stopCompile(): return true
|
||||
var
|
||||
p: Parser = default(Parser)
|
||||
a: TPassContextArray = default(TPassContextArray)
|
||||
p: Parser
|
||||
a: TPassContextArray
|
||||
s: PLLStream
|
||||
fileIdx = module.fileIdx
|
||||
prepareConfigNotes(graph, module)
|
||||
|
||||
@@ -77,7 +77,7 @@ proc inSymChoice(sc, x: PNode): bool =
|
||||
result = false
|
||||
for i in 0..<sc.len:
|
||||
if sc[i].sym == x.sym: return true
|
||||
elif sc.kind in {nkOpenSymChoice, nkOpenSym}:
|
||||
elif sc.kind == nkOpenSymChoice:
|
||||
# same name suffices for open sym choices!
|
||||
result = sc[0].sym.name.id == x.sym.name.id
|
||||
else:
|
||||
|
||||
@@ -47,8 +47,9 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
|
||||
of NonePass:
|
||||
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
|
||||
|
||||
proc processImplicitImports*(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
|
||||
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator) =
|
||||
proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
|
||||
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
|
||||
) =
|
||||
# XXX fixme this should actually be relative to the config file!
|
||||
let relativeTo = toFullPath(graph.config, m.info)
|
||||
for module in items(implicits):
|
||||
@@ -63,7 +64,7 @@ proc processImplicitImports*(graph: ModuleGraph; implicits: seq[string], nodeKin
|
||||
if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
|
||||
break
|
||||
|
||||
proc prePass*(c: PContext; n: PNode) =
|
||||
proc prePass(c: PContext; n: PNode) =
|
||||
for son in n:
|
||||
if son.kind == nkPragma:
|
||||
for s in son:
|
||||
@@ -234,8 +235,7 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
|
||||
result = moduleFromRodFile(graph, fileIdx, cachedModules)
|
||||
let path = toFullPath(graph.config, fileIdx)
|
||||
let filename = AbsoluteFile path
|
||||
# it could be a stdinfile/cmdfile
|
||||
if fileExists(filename) and not graph.config.projectIsStdin:
|
||||
if fileExists(filename): # it could be a stdinfile
|
||||
graph.cachedFiles[path] = $secureHashFile(path)
|
||||
if result == nil:
|
||||
result = newModule(graph, fileIdx)
|
||||
|
||||
@@ -210,8 +210,8 @@ type
|
||||
cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
|
||||
cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips,
|
||||
cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430,
|
||||
cpuSparc64, cpuS390x, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64,
|
||||
cpuEsp, cpuWasm32, cpuE2k, cpuLoongArch64, cpuWasm64
|
||||
cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32,
|
||||
cpuE2k, cpuLoongArch64
|
||||
|
||||
type
|
||||
TInfoCPU* = tuple[name: string, intSize: int, endian: Endianness,
|
||||
@@ -241,7 +241,6 @@ const
|
||||
(name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
|
||||
(name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
|
||||
(name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
|
||||
(name: "s390x", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
|
||||
(name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
|
||||
(name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
|
||||
(name: "riscv32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
@@ -249,8 +248,7 @@ const
|
||||
(name: "esp", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
(name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
(name: "e2k", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
|
||||
(name: "loongarch64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
|
||||
(name: "wasm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)]
|
||||
(name: "loongarch64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)]
|
||||
|
||||
type
|
||||
Target* = object
|
||||
|
||||
@@ -107,7 +107,7 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
|
||||
return it[1]
|
||||
|
||||
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
|
||||
isStatement: bool = false; comesFromPush = false)
|
||||
isStatement: bool = false)
|
||||
|
||||
proc recordPragma(c: PContext; n: PNode; args: varargs[string]) =
|
||||
var recorded = newNodeI(nkReplayAction, n.info)
|
||||
@@ -590,7 +590,7 @@ proc processCompile(c: PContext, n: PNode) =
|
||||
var customArgs = ""
|
||||
if n.kind in nkCallKinds:
|
||||
s = getStrLit(c, n, 1)
|
||||
if n.len == 3:
|
||||
if n.len <= 3:
|
||||
customArgs = getStrLit(c, n, 2)
|
||||
else:
|
||||
localError(c.config, n.info, "'.compile' pragma takes up 2 arguments")
|
||||
@@ -637,10 +637,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
||||
# XXX what to do here if 'amb' is true?
|
||||
if e != nil:
|
||||
incl(e.flags, sfUsed)
|
||||
if isDefined(con.config, "nimPreviewAsmSemSymbol"):
|
||||
result.add con.semExprWithType(con, newSymNode(e), {efTypeAllowed})
|
||||
else:
|
||||
result.add newSymNode(e)
|
||||
result.add newSymNode(e)
|
||||
else:
|
||||
result.add newStrNode(nkStrLit, sub)
|
||||
else:
|
||||
@@ -725,12 +722,12 @@ proc processPragma(c: PContext, n: PNode, i: int) =
|
||||
proc pragmaRaisesOrTags(c: PContext, n: PNode) =
|
||||
proc processExc(c: PContext, x: PNode) =
|
||||
if c.hasUnresolvedArgs(c, x):
|
||||
x.typ() = makeTypeFromExpr(c, x)
|
||||
x.typ = makeTypeFromExpr(c, x)
|
||||
else:
|
||||
var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
|
||||
if t.kind notin {tyObject, tyOr}:
|
||||
localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
|
||||
x.typ() = t
|
||||
x.typ = t
|
||||
|
||||
if n.kind in nkPragmaCallKinds and n.len == 2:
|
||||
let it = n[1]
|
||||
@@ -893,7 +890,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
if keyDeep:
|
||||
localError(c.config, it.info, "user pragma cannot have arguments")
|
||||
|
||||
pragma(c, sym, userPragma.ast, validPragmas, isStatement, comesFromPush)
|
||||
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
|
||||
else:
|
||||
@@ -947,19 +944,15 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
of wSize:
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
var size = expectIntLit(c, it)
|
||||
if sfImportc in sym.flags:
|
||||
# no restrictions on size for imported types
|
||||
setImportedTypeSize(c.config, sym.typ, size)
|
||||
case size
|
||||
of 1, 2, 4:
|
||||
sym.typ.size = size
|
||||
sym.typ.align = int16 size
|
||||
of 8:
|
||||
sym.typ.size = 8
|
||||
sym.typ.align = floatInt64Align(c.config)
|
||||
else:
|
||||
case size
|
||||
of 1, 2, 4:
|
||||
sym.typ.size = size
|
||||
sym.typ.align = int16 size
|
||||
of 8:
|
||||
sym.typ.size = 8
|
||||
sym.typ.align = floatInt64Align(c.config)
|
||||
else:
|
||||
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
|
||||
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
|
||||
of wAlign:
|
||||
let alignment = expectIntLit(c, it)
|
||||
if isPowerOfTwo(alignment) and alignment > 0:
|
||||
@@ -1322,12 +1315,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
pragmaProposition(c, it)
|
||||
of wEnsures:
|
||||
pragmaEnsures(c, it)
|
||||
of wEnforceNoRaises:
|
||||
of wEnforceNoRaises, wQuirky:
|
||||
sym.flags.incl sfNeverRaises
|
||||
of 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:
|
||||
@@ -1409,12 +1398,11 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
|
||||
inc i
|
||||
|
||||
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
|
||||
isStatement: bool; comesFromPush = false) =
|
||||
isStatement: bool) =
|
||||
if n == nil: return
|
||||
pragmaRec(c, sym, n, validPragmas, isStatement)
|
||||
# XXX: in the case of a callable def, this should use its info
|
||||
if not comesFromPush:
|
||||
implicitPragmas(c, sym, n.info, validPragmas)
|
||||
implicitPragmas(c, sym, n.info, validPragmas)
|
||||
|
||||
proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords,
|
||||
isStatement: bool = false) =
|
||||
|
||||
@@ -30,7 +30,7 @@ type
|
||||
TRenderFlags* = set[TRenderFlag]
|
||||
TRenderTok* = object
|
||||
kind*: TokType
|
||||
length*: int32
|
||||
length*: int16
|
||||
sym*: PSym
|
||||
|
||||
Section = enum
|
||||
@@ -154,7 +154,7 @@ proc initSrcGen(renderFlags: TRenderFlags; config: ConfigRef): TSrcGen =
|
||||
)
|
||||
|
||||
proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
|
||||
g.tokens.add TRenderTok(kind: kind, length: int32(s.len), sym: sym)
|
||||
g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym)
|
||||
g.buf.add(s)
|
||||
if kind != tkSpaces:
|
||||
inc g.col, s.len
|
||||
@@ -229,7 +229,6 @@ proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
|
||||
inc(g.lineLen, s.len)
|
||||
|
||||
proc putComment(g: var TSrcGen, s: string) =
|
||||
const SpecialWhitespace = {' ', '\t', '\r', '\n', '\0'}
|
||||
if s.len == 0: return
|
||||
var i = 0
|
||||
let hi = s.len - 1
|
||||
@@ -259,12 +258,12 @@ proc putComment(g: var TSrcGen, s: string) =
|
||||
# gets too long:
|
||||
# compute length of the following word:
|
||||
var j = i
|
||||
while j <= hi and s[j] notin SpecialWhitespace: inc(j)
|
||||
while j <= hi and s[j] > ' ': inc(j)
|
||||
if not isCode and (g.col + (j - i) > MaxLineLen):
|
||||
put(g, tkComment, com)
|
||||
optNL(g, ind)
|
||||
com = "## "
|
||||
while i <= hi and s[i] notin SpecialWhitespace:
|
||||
while i <= hi and s[i] > ' ':
|
||||
com.add(s[i])
|
||||
inc(i)
|
||||
put(g, tkComment, com)
|
||||
@@ -328,10 +327,6 @@ proc pushCom(g: var TSrcGen, n: PNode) =
|
||||
setLen(g.comStack, g.comStack.len + 1)
|
||||
g.comStack[^1] = n
|
||||
|
||||
proc popCom(g: var TSrcGen): PNode =
|
||||
result = g.comStack[^1]
|
||||
setLen(g.comStack, g.comStack.len - 1)
|
||||
|
||||
proc popAllComs(g: var TSrcGen) =
|
||||
setLen(g.comStack, 0)
|
||||
|
||||
@@ -415,7 +410,7 @@ proc atom(g: TSrcGen; n: PNode): string =
|
||||
of nkEmpty: result = ""
|
||||
of nkIdent: result = n.ident.s
|
||||
of nkSym: result = n.sym.name.s
|
||||
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym: result = n[0].sym.name.s
|
||||
of nkClosedSymChoice, nkOpenSymChoice: result = n[0].sym.name.s
|
||||
of nkStrLit: result = ""; result.addQuoted(n.strVal)
|
||||
of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
|
||||
of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
|
||||
@@ -447,11 +442,6 @@ proc atom(g: TSrcGen; n: PNode): string =
|
||||
result = $n.floatVal & "\'f64"
|
||||
else:
|
||||
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
|
||||
of nkFloat128Lit:
|
||||
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
|
||||
result = $n.floatVal & "\'f128"
|
||||
else:
|
||||
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128"
|
||||
of nkNilLit: result = "nil"
|
||||
of nkType:
|
||||
if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
|
||||
@@ -570,16 +560,8 @@ 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, 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 nkElifExpr: result = lsons(g, n) + len("_elif_:_")
|
||||
of nkElseExpr: 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")
|
||||
@@ -622,6 +604,8 @@ 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:
|
||||
@@ -1013,7 +997,7 @@ type
|
||||
proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
|
||||
if renderIds notin g.flags:
|
||||
case n.kind
|
||||
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym:
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
if n.len > 0: result = bracketKind(g, n[0])
|
||||
else: result = bkNone
|
||||
of nkSym:
|
||||
@@ -1322,11 +1306,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
put(g, tkCustomLit, n[0].strVal)
|
||||
gsub(g, n, 1)
|
||||
else:
|
||||
for i in 0..<n.len-1:
|
||||
gsub(g, n, i)
|
||||
gsub(g, n, 0)
|
||||
put(g, tkDot, ".")
|
||||
if n.len > 1:
|
||||
accentedName(g, n[^1])
|
||||
assert n.len == 2, $n.len
|
||||
accentedName(g, n[1])
|
||||
of nkBind:
|
||||
putWithSpace(g, tkBind, "bind")
|
||||
gsub(g, n, 0)
|
||||
@@ -1358,10 +1341,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
if not n[0].isExported() and renderNonExportedFields notin g.flags:
|
||||
# Skip if this is a property in a type and its not exported
|
||||
# (While also not allowing rendering of non exported fields)
|
||||
if shouldRenderComment(g, n):
|
||||
# `shouldRenderComment` indicts that we have comments to render
|
||||
# but it's a non-exported field, so we just pop without rendering any comment
|
||||
discard popCom(g)
|
||||
return
|
||||
# render postfix for object fields:
|
||||
exclFlags = g.flags * {renderNoPostfix}
|
||||
@@ -1430,7 +1409,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
of nkPrefix:
|
||||
gsub(g, n, 0)
|
||||
if n.len > 1:
|
||||
let opr = getPIdent(n[0])
|
||||
let opr = if n[0].kind == nkIdent: n[0].ident
|
||||
elif n[0].kind == nkSym: n[0].sym.name
|
||||
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
|
||||
else: nil
|
||||
let nNext = skipHiddenNodes(n[1])
|
||||
if nNext.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
|
||||
put(g, tkSpaces, Space)
|
||||
@@ -1481,30 +1463,15 @@ 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, 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 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 nkTypeOfExpr:
|
||||
put(g, tkType, "typeof")
|
||||
put(g, tkParLe, "(")
|
||||
@@ -1766,6 +1733,19 @@ 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:
|
||||
|
||||
@@ -93,7 +93,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
|
||||
of nkIdent: uses.incl n.ident.id
|
||||
of nkSym: uses.incl n.sym.name.id
|
||||
of nkAccQuoted: uses.incl accQuoted(cache, n).id
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
uses.incl n[0].sym.name.id
|
||||
of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
|
||||
for i in 0..<n.len: computeDeps(cache, n[i], declares, uses, topLevel)
|
||||
|
||||
132
compiler/sem.nim
132
compiler/sem.nim
@@ -18,7 +18,7 @@ import
|
||||
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
|
||||
lowerings, plugins/active, lineinfos, int128,
|
||||
isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs,
|
||||
extccomp, layeredtable
|
||||
extccomp
|
||||
|
||||
import vtables
|
||||
import std/[strtabs, math, tables, intsets, strutils, packedsets]
|
||||
@@ -89,23 +89,6 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
|
||||
changeType(c, x, formal, check=true)
|
||||
result = arg
|
||||
result = skipHiddenSubConv(result, c.graph, c.idgen)
|
||||
# Walk through nested statement-list/block expressions to find the innermost
|
||||
# value node. Empty containers (e.g. `@[]`) inside `nkStmtListExpr` wrappers
|
||||
# need their type resolved to match the formal type, otherwise the C codegen
|
||||
# cannot map `tyEmpty` to a concrete type (fixes #25945).
|
||||
var tail = result
|
||||
while tail.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkPragmaBlock} and tail.len > 0:
|
||||
tail = tail.lastSon
|
||||
|
||||
if tail.typ != nil and tail.typ.isEmptyContainer and
|
||||
formal.kind notin {tyUntyped, tyBuiltInTypeClass, tyAnything}:
|
||||
changeType(c, tail, formal, check=true)
|
||||
|
||||
# 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 =
|
||||
@@ -114,15 +97,12 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
renderTree(arg, {renderNoComments}))
|
||||
# error correction:
|
||||
result = copyTree(arg)
|
||||
result.typ() = formal
|
||||
result.typ = formal
|
||||
elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum:
|
||||
# Pick the right 'sym' from the sym choice by looking at 'formal' type:
|
||||
# The choice candidates may be wrapped in `var`/`lent` when they come from
|
||||
# a loop-local view, but for enum disambiguation only the underlying enum
|
||||
# type matters.
|
||||
result = nil
|
||||
for ch in arg:
|
||||
if sameType(ch.typ.skipTypes({tyVar, tyLent}), formal):
|
||||
if sameType(ch.typ, formal):
|
||||
return ch
|
||||
typeMismatch(c.config, info, formal, arg.typ, arg)
|
||||
else:
|
||||
@@ -131,7 +111,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
typeMismatch(c.config, info, formal, arg.typ, arg)
|
||||
# error correction:
|
||||
result = copyTree(arg)
|
||||
result.typ() = formal
|
||||
result.typ = formal
|
||||
else:
|
||||
result = fitNodePostMatch(c, formal, result)
|
||||
|
||||
@@ -262,26 +242,6 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
if result.kind notin {kind, skTemp}:
|
||||
localError(c.config, n.info, "cannot use symbol of kind '$1' as a '$2'" %
|
||||
[result.kind.toHumanStr, kind.toHumanStr])
|
||||
# bug #25693: a local declared inside a template/macro operand (recorded in
|
||||
# `shadowDiscardedDefs`) can be captured by a `{.dirty.}` template and
|
||||
# re-emitted as a definition more than once. The first emission keeps the
|
||||
# original symbol (so a leaked dirty-template name still resolves); every
|
||||
# later emission gets a fresh copy, so distinct emissions don't share one
|
||||
# symbol - which the destructor/liveness analysis would otherwise miscompile.
|
||||
# Unlike a plain redefinition check this is control-flow agnostic, so the
|
||||
# common "emit a `typed` body in several mutually-exclusive branches" pattern
|
||||
# keeps working. gensym'ed locals (and ones derived from a gensym name) are
|
||||
# excluded: the gensym machinery already keeps their names unique, and a
|
||||
# fresh copy would reuse the unique name and clash in the same scope.
|
||||
if kind in {skVar, skLet, skForVar} and
|
||||
{sfGenSym, sfWasGenSym} * result.flags == {} and
|
||||
result.id in c.shadowDiscardedDefs:
|
||||
if containsOrIncl(c.realizedDefs, result.id):
|
||||
let fresh = copySym(result, c.idgen)
|
||||
fresh.ast = result.ast
|
||||
put(c.p, result, fresh)
|
||||
c.hasSymRedefs = true
|
||||
result = fresh
|
||||
when false:
|
||||
if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
|
||||
# declarative context, so produce a fresh gensym:
|
||||
@@ -291,7 +251,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
# when there is a nested proc inside a template, semtmpl
|
||||
# will assign a wrong owner during the first pass over the
|
||||
# template; we must fix it here: see #909
|
||||
setOwner(result, getCurrOwner(c))
|
||||
result.owner = getCurrOwner(c)
|
||||
else:
|
||||
result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
|
||||
if find(result.name.s, '`') >= 0:
|
||||
@@ -356,7 +316,7 @@ proc hasCycle(n: PNode): bool =
|
||||
break
|
||||
excl n.flags, nfNone
|
||||
|
||||
proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode; producedClosure: var bool): PNode =
|
||||
proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
|
||||
# recompute the types as 'eval' isn't guaranteed to construct types nor
|
||||
# that the types are sound:
|
||||
when true:
|
||||
@@ -368,7 +328,7 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode; producedClosure: v
|
||||
if hasCycle(result):
|
||||
result = localErrorNode(c, eOrig, "the resulting AST is cyclic and cannot be processed further")
|
||||
else:
|
||||
semmacrosanity.annotateType(result, expectedType, c.config, producedClosure)
|
||||
semmacrosanity.annotateType(result, expectedType, c.config)
|
||||
else:
|
||||
result = semExprWithType(c, evaluated)
|
||||
#result = fitNode(c, e.typ, result) inlined with special case:
|
||||
@@ -381,19 +341,6 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode; producedClosure: v
|
||||
isArrayConstr(arg):
|
||||
arg.typ = eOrig.typ
|
||||
|
||||
proc resetEvalPosition(n: PNode) =
|
||||
# resets the eval position of variables because `tryConstExpr` may be
|
||||
# called multiple times on the same node
|
||||
case n.kind
|
||||
of {nkNone..nkNilLit}-{nkSym}:
|
||||
discard
|
||||
of nkSym:
|
||||
if n.sym.kind in {skVar, skLet} and sfGlobal notin n.sym.flags:
|
||||
n.sym.position = 0
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
resetEvalPosition(n[i])
|
||||
|
||||
proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
var e = semExprWithType(c, n, expectedType = expectedType)
|
||||
if e == nil: return
|
||||
@@ -418,10 +365,7 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
if result == nil or result.kind == nkEmpty:
|
||||
result = nil
|
||||
else:
|
||||
var producedClosure = false
|
||||
result = fixupTypeAfterEval(c, result, e, producedClosure)
|
||||
if producedClosure:
|
||||
result = nil
|
||||
result = fixupTypeAfterEval(c, result, e)
|
||||
|
||||
except ERecoverableError:
|
||||
result = nil
|
||||
@@ -430,8 +374,6 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
# Restore the error hook
|
||||
c.graph.config.structuredErrorHook = tempHook
|
||||
|
||||
resetEvalPosition(n)
|
||||
|
||||
c.config.errorCounter = oldErrorCount
|
||||
c.config.errorMax = oldErrorMax
|
||||
c.config.m.errorOutputs = oldErrorOutputs
|
||||
@@ -460,10 +402,7 @@ proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
# error correction:
|
||||
result = e
|
||||
else:
|
||||
var producedClosure = false
|
||||
result = fixupTypeAfterEval(c, result, e, producedClosure)
|
||||
if producedClosure:
|
||||
result = nil
|
||||
result = fixupTypeAfterEval(c, result, e)
|
||||
|
||||
proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
if efNeedStatic in flags:
|
||||
@@ -526,7 +465,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
renderTree(result, {renderNoComments}))
|
||||
result = newSymNode(errorSym(c, result))
|
||||
else:
|
||||
result.typ() = makeTypeDesc(c, typ)
|
||||
result.typ = makeTypeDesc(c, typ)
|
||||
#result = symNodeFromType(c, typ, n.info)
|
||||
else:
|
||||
if s.ast[genericParamsPos] != nil and retType.isMetaType:
|
||||
@@ -534,7 +473,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
# e.g. template foo(T: typedesc): seq[T]
|
||||
# We will instantiate the return type here, because
|
||||
# we now know the supplied arguments
|
||||
var paramTypes = initLayeredTypeMap()
|
||||
var paramTypes = initTypeMapping()
|
||||
for param, value in genericParamsInMacroCall(s, call):
|
||||
var givenType = value.typ
|
||||
# the sym nodes used for the supplied generic arguments for
|
||||
@@ -542,7 +481,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
# in this case, get the type directly from the sym
|
||||
if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
|
||||
givenType = value.sym.typ
|
||||
put(paramTypes, param.typ, givenType)
|
||||
idTablePut(paramTypes, param.typ, givenType)
|
||||
|
||||
retType = generateTypeInstance(c, paramTypes,
|
||||
macroResult.info, retType)
|
||||
@@ -550,29 +489,15 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
if retType.kind == tyVoid:
|
||||
result = semStmt(c, result, flags)
|
||||
else:
|
||||
result = semExpr(c, result, flags)
|
||||
result = semExpr(c, result, flags, expectedType)
|
||||
result = fitNode(c, retType, result, result.info)
|
||||
#globalError(s.info, errInvalidParamKindX, typeToString(s.typ.returnType))
|
||||
dec(c.config.evalTemplateCounter)
|
||||
discard c.friendModules.pop()
|
||||
|
||||
proc getLineInfo(n: PNode): TLineInfo =
|
||||
case n.kind
|
||||
of nkPostfix:
|
||||
if len(n) > 1:
|
||||
result = getLineInfo(n[1])
|
||||
else:
|
||||
result = n.info
|
||||
of nkAccQuoted, nkPragmaExpr:
|
||||
if len(n) > 0:
|
||||
result = getLineInfo(n[0])
|
||||
else:
|
||||
result = n.info
|
||||
else:
|
||||
result = n.info
|
||||
|
||||
const
|
||||
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
|
||||
errFloatToString = "cannot convert '$1' to '$2'"
|
||||
|
||||
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
|
||||
@@ -597,9 +522,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
if efNoSemCheck notin flags:
|
||||
result = semAfterMacroCall(c, n, result, sym, flags, expectedType)
|
||||
if c.config.macrosToExpand.hasKey(sym.name.s):
|
||||
message(c.config, nOrig.info, hintExpandMacro, renderTree(result, {
|
||||
renderNonExportedFields, renderDocComments, renderNoComments
|
||||
}))
|
||||
message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
|
||||
result = wrapInComesFrom(nOrig.info, sym, result)
|
||||
popInfoContext(c.config)
|
||||
|
||||
@@ -685,7 +608,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, ch
|
||||
newNodeIT(nkType, recNode.info, asgnType)
|
||||
)
|
||||
asgnExpr.flags.incl nfSkipFieldChecking
|
||||
asgnExpr.typ() = recNode.typ
|
||||
asgnExpr.typ = recNode.typ
|
||||
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
|
||||
else:
|
||||
raiseAssert "unreachable"
|
||||
@@ -707,7 +630,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
|
||||
if checkDefault: # don't add defaults when checking whether a case branch has default fields
|
||||
return
|
||||
defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
|
||||
defaultValue.typ() = discriminator.typ
|
||||
defaultValue.typ = discriminator.typ
|
||||
selectedBranch = recNode.pickCaseBranchIndex defaultValue
|
||||
defaultValue.flags.incl nfSkipFieldChecking
|
||||
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
|
||||
@@ -720,7 +643,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
|
||||
elif recType.kind in {tyObject, tyArray, tyTuple}:
|
||||
let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
|
||||
if asgnExpr != nil:
|
||||
asgnExpr.typ() = recNode.typ
|
||||
asgnExpr.typ = recNode.typ
|
||||
asgnExpr.flags.incl nfSkipFieldChecking
|
||||
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
|
||||
else:
|
||||
@@ -733,7 +656,7 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
|
||||
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
|
||||
if child.len > 0:
|
||||
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
|
||||
asgnExpr.typ() = aTyp
|
||||
asgnExpr.typ = aTyp
|
||||
asgnExpr.sons.add child
|
||||
result = semExpr(c, asgnExpr)
|
||||
else:
|
||||
@@ -744,12 +667,11 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
|
||||
if child != nil:
|
||||
let node = newNode(nkIntLit)
|
||||
node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
|
||||
let typeNode = newNode(nkType)
|
||||
typeNode.typ() = makeTypeDesc(c, aTypSkip[1])
|
||||
result = semExpr(c, newTree(nkCall, newTree(nkBracketExpr, newSymNode(getSysSym(c.graph, a.info, "arrayWithDefault"), a.info), typeNode),
|
||||
result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
|
||||
semExprWithType(c, child),
|
||||
node
|
||||
))
|
||||
result.typ() = aTyp
|
||||
result.typ = aTyp
|
||||
else:
|
||||
result = nil
|
||||
of tyTuple:
|
||||
@@ -758,7 +680,7 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
|
||||
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
|
||||
if hasDefault and children.len > 0:
|
||||
result = newNodeI(nkTupleConstr, a.info)
|
||||
result.typ() = aTyp
|
||||
result.typ = aTyp
|
||||
result.sons.add children
|
||||
result = semExpr(c, result)
|
||||
else:
|
||||
@@ -807,11 +729,9 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
|
||||
result.semInferredLambda = semInferredLambda
|
||||
result.semGenerateInstance = generateInstance
|
||||
result.instantiateOnlyProcType = instantiateOnlyProcType
|
||||
result.fitDefaultNode = fitDefaultNode
|
||||
result.semTypeNode = semTypeNode
|
||||
result.instTypeBoundOp = sigmatch.instTypeBoundOp
|
||||
result.hasUnresolvedArgs = hasUnresolvedArgs
|
||||
result.semAsgnOpr = semAsgnOpr
|
||||
result.templInstCounter = new int
|
||||
|
||||
pushProcCon(result, module)
|
||||
@@ -929,9 +849,9 @@ proc semWithPContext*(c: PContext, n: PNode): PNode =
|
||||
|
||||
proc reportUnusedModules(c: PContext) =
|
||||
if c.config.cmd == cmdM: return
|
||||
for (s, info) in c.unusedImports:
|
||||
if sfUsed notin s.flags:
|
||||
message(c.config, info, warnUnusedImportX, s.name.s)
|
||||
for i in 0..high(c.unusedImports):
|
||||
if sfUsed notin c.unusedImports[i][0].flags:
|
||||
message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
|
||||
|
||||
proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
|
||||
if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
|
||||
|
||||
@@ -56,7 +56,7 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
|
||||
proc name[T: static proc()]() = T()
|
||||
name[proc() = echo"hello"]()
|
||||
]#
|
||||
for paramSym in searchScopesAll(c, symx.name, {skConst}):
|
||||
for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}):
|
||||
let paramTyp = paramSym.typ
|
||||
if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter:
|
||||
result.add((paramTyp.n.sym, o.lastOverloadScope))
|
||||
@@ -69,39 +69,6 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
|
||||
result[0].scope, diagnostics)
|
||||
best.state = csNoMatch
|
||||
|
||||
proc isAttachableRoutineTo(prc: PSym, arg: PType): bool =
|
||||
result = false
|
||||
if arg.owner != prc.owner: return false
|
||||
for i in 1 ..< prc.typ.len:
|
||||
if prc.typ.n[i].kind == nkSym and prc.typ.n[i].sym.ast != nil:
|
||||
# has default value, parameter is not considered in type attachment
|
||||
continue
|
||||
let t = nominalRoot(prc.typ[i])
|
||||
if t != nil and t.itemId == arg.itemId:
|
||||
# parameter `i` is a nominal type in this module
|
||||
# attachable if the nominal root `t` has the same id as `arg`
|
||||
return true
|
||||
|
||||
proc addTypeBoundSymbols(graph: ModuleGraph, arg: PType, name: PIdent,
|
||||
filter: TSymKinds, marker: var IntSet,
|
||||
syms: var seq[tuple[s: PSym, scope: int]]) =
|
||||
# add type bound ops for `name` based on the argument type `arg`
|
||||
if arg != nil:
|
||||
# argument must be typed first, meaning arguments always
|
||||
# matching `untyped` are ignored
|
||||
let t = nominalRoot(arg)
|
||||
if t != nil and t.owner.kind == skModule:
|
||||
# search module for routines attachable to `t`
|
||||
let module = t.owner
|
||||
var iter = default(ModuleIter)
|
||||
var s = initModuleIter(iter, graph, module, name)
|
||||
while s != nil:
|
||||
if s.kind in filter and s.isAttachableRoutineTo(t) and
|
||||
not containsOrIncl(marker, s.id):
|
||||
# least priority scope, less than explicit imports:
|
||||
syms.add((s, -2))
|
||||
s = nextModuleIter(iter, graph)
|
||||
|
||||
proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
n, orig: PNode,
|
||||
initialBinding: PNode,
|
||||
@@ -121,22 +88,9 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
best, alt, o, diagnosticsFlag)
|
||||
if len(syms) == 0:
|
||||
return
|
||||
let allowTypeBoundOps = typeBoundOps in c.features and
|
||||
# qualified or bound symbols cannot refer to type bound ops
|
||||
headSymbol.kind in {nkIdent, nkAccQuoted, nkOpenSymChoice, nkOpenSym}
|
||||
var symMarker = initIntSet()
|
||||
for s in syms:
|
||||
symMarker.incl(s.s.id)
|
||||
# current overload being considered
|
||||
var sym = syms[0].s
|
||||
let name = sym.name
|
||||
var scope = syms[0].scope
|
||||
c.openShadowScope
|
||||
if allowTypeBoundOps:
|
||||
for a in 1 ..< n.len:
|
||||
# for every already typed argument, add type bound ops
|
||||
let arg = n[a]
|
||||
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
|
||||
|
||||
# starts at 1 because 0 is already done with setup, only needs checking
|
||||
var nextSymIndex = 1
|
||||
@@ -144,6 +98,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
while true:
|
||||
determineType(c, sym)
|
||||
z = initCandidate(c, sym, initialBinding, scope, diagnosticsFlag)
|
||||
|
||||
# this is kinda backwards as without a check here the described
|
||||
# problems in recalc would not happen, but instead it 100%
|
||||
# does check forever in some cases
|
||||
@@ -151,22 +106,10 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# may introduce new symbols with caveats described in recalc branch
|
||||
matches(c, n, orig, z)
|
||||
|
||||
if allowTypeBoundOps:
|
||||
# this match may have given some arguments new types,
|
||||
# in which case add their type bound ops as well
|
||||
# type bound ops of arguments always matching `untyped` are not considered
|
||||
for x in z.newlyTypedOperands:
|
||||
let arg = n[x]
|
||||
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
|
||||
|
||||
if z.state == csMatch:
|
||||
# Iterator preference is heuristic in iterator-admitting contexts.
|
||||
# The dedicated iterable path uses `iteratorPreference`, other
|
||||
# context use exact-match bump
|
||||
# little hack so that iterators are preferred over everything else:
|
||||
if sym.kind == skIterator:
|
||||
if efPreferIteratorForIterable in flags:
|
||||
inc(z.iteratorPreference)
|
||||
elif not (efWantIterator notin flags and efWantIterable in flags):
|
||||
if not (efWantIterator notin flags and efWantIterable in flags):
|
||||
inc(z.exactMatches, 200)
|
||||
else:
|
||||
dec(z.exactMatches, 200)
|
||||
@@ -187,20 +130,13 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# 1) new symbols are discovered but the loop ends before we recalc
|
||||
# 2) new symbols are discovered and resemmed forever
|
||||
# not 100% sure if these are possible though as they would rely
|
||||
# on somehow introducing a new overload during overload resolution
|
||||
# on somehow introducing a new overload during overload resolution
|
||||
|
||||
# Symbol table has been modified. Restart and pre-calculate all syms
|
||||
# before any further candidate init and compare. SLOW, but rare case.
|
||||
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
|
||||
best, alt, o, diagnosticsFlag)
|
||||
symMarker = initIntSet()
|
||||
for s in syms:
|
||||
symMarker.incl(s.s.id)
|
||||
if allowTypeBoundOps:
|
||||
for a in 1 ..< n.len:
|
||||
# for every already typed argument, add type bound ops
|
||||
let arg = n[a]
|
||||
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
|
||||
|
||||
# reset counter because syms may be in a new order
|
||||
symCount = c.currentScope.symbols.counter
|
||||
nextSymIndex = 0
|
||||
@@ -218,10 +154,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
scope = syms[nextSymIndex].scope
|
||||
inc(nextSymIndex)
|
||||
|
||||
if best.state == csMatch and best.calleeSym != nil and best.calleeSym.kind in {skTemplate, skMacro}:
|
||||
c.closeShadowScope
|
||||
else:
|
||||
c.mergeShadowScope
|
||||
|
||||
proc effectProblem(f, a: PType; result: var string; c: PContext) =
|
||||
if f.kind == tyProc and a.kind == tyProc:
|
||||
@@ -252,6 +184,14 @@ proc effectProblem(f, a: PType; result: var string; c: PContext) =
|
||||
if not c.graph.compatibleProps(c.graph, f, a):
|
||||
result.add "\n The `.requires` or `.ensures` properties are incompatible."
|
||||
|
||||
proc renderNotLValue(n: PNode): string =
|
||||
result = $n
|
||||
let n = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
if n.kind == nkHiddenCallConv and n.len > 1:
|
||||
result = $n[0] & "(" & result & ")"
|
||||
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
|
||||
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
|
||||
|
||||
proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
(TPreferedDesc, string) =
|
||||
var prefer = preferName
|
||||
@@ -589,7 +529,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
let overloadsState = result.state
|
||||
if overloadsState != csMatch:
|
||||
if nfDotField in n.flags:
|
||||
internalAssert c.config, f.kind in nkIdentKinds and n.len >= 2
|
||||
internalAssert c.config, f.kind == nkIdent and n.len >= 2
|
||||
|
||||
# leave the op head symbol empty,
|
||||
# we are going to try multiple variants
|
||||
@@ -679,7 +619,7 @@ proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
|
||||
# copied from semOverloadedCallAnalyzeEffects, might be overkill:
|
||||
const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}
|
||||
let filter =
|
||||
if flags*{efInTypeof, efWantIterator, efWantIterable, efPreferIteratorForIterable} != {}:
|
||||
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
|
||||
baseFilter + {skIterator}
|
||||
else: baseFilter
|
||||
# this will add the errors:
|
||||
@@ -694,16 +634,9 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
let s = a[0].sym
|
||||
if s.isGenericRoutineStrict:
|
||||
var src = s.typ.firstParamType
|
||||
var convMatch = newCandidate(c, src)
|
||||
var arg = a[1]
|
||||
if arg.kind in {nkHiddenAddr, nkHiddenSubConv}: arg = arg[^1]
|
||||
let srca = typeRel(convMatch, src, arg.typ)
|
||||
if srca notin {isEqual, isGeneric, isSubtype}:
|
||||
internalError(c.config, a.info, "generic converter failed rematch")
|
||||
let finalCallee = generateInstance(c, s, convMatch.bindings, a.info)
|
||||
let finalCallee = generateInstance(c, s, x.bindings, a.info)
|
||||
a[0].sym = finalCallee
|
||||
a[0].typ() = finalCallee.typ
|
||||
a[0].typ = finalCallee.typ
|
||||
#a.typ = finalCallee.typ.returnType
|
||||
|
||||
proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
|
||||
@@ -712,15 +645,6 @@ 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)
|
||||
@@ -738,13 +662,13 @@ proc inferWithMetatype(c: PContext, formal: PType,
|
||||
# This almost exactly replicates the steps taken by the compiler during
|
||||
# param matching. It performs an embarrassing amount of back-and-forth
|
||||
# type jugling, but it's the price to pay for consistency and correctness
|
||||
result.typ() = generateTypeInstance(c, m.bindings, arg.info,
|
||||
result.typ = generateTypeInstance(c, m.bindings, arg.info,
|
||||
formal.skipTypes({tyCompositeTypeClass}))
|
||||
else:
|
||||
typeMismatch(c.config, arg.info, formal, arg.typ, arg)
|
||||
# error correction:
|
||||
result = copyTree(arg)
|
||||
result.typ() = formal
|
||||
result.typ = formal
|
||||
|
||||
proc updateDefaultParams(c: PContext, call: PNode) =
|
||||
# In generic procs, the default parameter may be unique for each
|
||||
@@ -767,7 +691,7 @@ proc updateDefaultParams(c: PContext, call: PNode) =
|
||||
pushInfoContext(c.config, call.info, call[0].sym.detailedInfo)
|
||||
typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast)
|
||||
popInfoContext(c.config)
|
||||
def.typ() = errorType(c)
|
||||
def.typ = errorType(c)
|
||||
call[i] = def
|
||||
|
||||
proc getCallLineInfo(n: PNode): TLineInfo =
|
||||
@@ -824,7 +748,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
|
||||
if t[i] == nil or u[i] == nil: return
|
||||
stackPut(t[i], u[i])
|
||||
of tyGenericParam:
|
||||
let prebound = x.bindings.lookup(t)
|
||||
let prebound = x.bindings.idTableGet(t)
|
||||
if prebound != nil:
|
||||
continue # Skip param, already bound
|
||||
|
||||
@@ -836,22 +760,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
|
||||
discard
|
||||
# update bindings
|
||||
for i in 0 ..< flatUnbound.len():
|
||||
x.bindings.put(flatUnbound[i], flatBound[i])
|
||||
|
||||
proc compactVoidArgs(n: PNode): PNode =
|
||||
# deletes void args from the argument list, which are created by `setSon`
|
||||
var hasNil = false
|
||||
for i in 0..<n.len:
|
||||
if n[i] == nil:
|
||||
hasNil = true
|
||||
break
|
||||
if not hasNil:
|
||||
result = n
|
||||
else:
|
||||
result = copyNode(n)
|
||||
for i in 0..<n.len:
|
||||
if n[i] != nil:
|
||||
result.add n[i]
|
||||
x.bindings.idTablePut(flatUnbound[i], flatBound[i])
|
||||
|
||||
proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
n: PNode, flags: TExprFlags;
|
||||
@@ -859,17 +768,14 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
assert x.state == csMatch
|
||||
var finalCallee = x.calleeSym
|
||||
let info = getCallLineInfo(n)
|
||||
markUsed(c, info, finalCallee, isGenericInstance = false)
|
||||
onUse(info, finalCallee, isGenericInstance = false)
|
||||
markUsed(c, info, finalCallee)
|
||||
onUse(info, finalCallee)
|
||||
assert finalCallee.ast != nil
|
||||
if x.matchedErrorType:
|
||||
markUsed(c, info, finalCallee, isGenericInstance = true)
|
||||
onUse(info, finalCallee, isGenericInstance = true)
|
||||
if x.matchedErrorType:
|
||||
result = x.call
|
||||
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
|
||||
if containsGenericType(result.typ):
|
||||
result.typ() = newTypeS(tyError, c)
|
||||
result.typ = newTypeS(tyError, c)
|
||||
incl result.typ.flags, tfCheckedForDestructor
|
||||
return
|
||||
let gp = finalCallee.ast[genericParamsPos]
|
||||
@@ -896,19 +802,16 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
# this node will be used in template substitution,
|
||||
# pretend this is an untyped node and let regular sem handle the type
|
||||
# to prevent problems where a generic parameter is treated as a value
|
||||
tn.typ() = nil
|
||||
tn.typ = nil
|
||||
x.call.add tn
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
markUsed(c, info, finalCallee, isGenericInstance = true)
|
||||
onUse(info, finalCallee, isGenericInstance = true)
|
||||
|
||||
result = compactVoidArgs(x.call)
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
markConvertersUsed(c, result)
|
||||
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
|
||||
if finalCallee.magic notin {mArrGet, mArrPut}:
|
||||
result.typ() = finalCallee.typ.returnType
|
||||
result.typ = finalCallee.typ.returnType
|
||||
updateDefaultParams(c, result)
|
||||
|
||||
proc canDeref(n: PNode): bool {.inline.} =
|
||||
@@ -917,7 +820,7 @@ proc canDeref(n: PNode): bool {.inline.} =
|
||||
|
||||
proc tryDeref(n: PNode): PNode =
|
||||
result = newNodeI(nkHiddenDeref, n.info)
|
||||
result.typ() = n.typ.skipTypes(abstractInst)[0]
|
||||
result.typ = n.typ.skipTypes(abstractInst)[0]
|
||||
result.add n
|
||||
|
||||
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
@@ -936,22 +839,22 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
else:
|
||||
if c.inGenericContext > 0 and c.matchedConcept == nil:
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
elif efNoUndeclared in flags:
|
||||
result = nil
|
||||
result.typ = makeTypeFromExpr(c, result.copyTree)
|
||||
elif efExplain notin flags:
|
||||
# repeat the overload resolution,
|
||||
# this time enabling all the diagnostic output (this should fail again)
|
||||
result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
|
||||
else:
|
||||
elif efNoUndeclared notin flags:
|
||||
result = nil
|
||||
notFoundError(c, n, errors)
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc explicitGenericInstError(c: PContext; n: PNode): PNode =
|
||||
localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n))
|
||||
result = n
|
||||
|
||||
proc explicitGenericSym(c: PContext, n: PNode, s: PSym, errors: var CandidateErrors, doError: bool): PNode =
|
||||
proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
|
||||
if s.kind in {skTemplate, skMacro}:
|
||||
internalError c.config, n.info, "cannot get explicitly instantiated symbol of " &
|
||||
(if s.kind == skTemplate: "template" else: "macro")
|
||||
@@ -961,19 +864,12 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym, errors: var CandidateErr
|
||||
if m.state != csMatch:
|
||||
# state is csMatch only if *all* generic params were matched,
|
||||
# including implicit parameters
|
||||
if doError:
|
||||
errors.add(CandidateError(
|
||||
sym: s,
|
||||
firstMismatch: m.firstMismatch,
|
||||
diagnostics: m.diagnostics))
|
||||
return nil
|
||||
var newInst = generateInstance(c, s, m.bindings, n.info)
|
||||
newInst.typ.flags.excl tfUnresolved
|
||||
let info = getCallLineInfo(n)
|
||||
markUsed(c, info, s, isGenericInstance = false)
|
||||
onUse(info, s, isGenericInstance = false)
|
||||
markUsed(c, info, newInst, isGenericInstance = true)
|
||||
onUse(info, newInst, isGenericInstance = true)
|
||||
markUsed(c, info, s)
|
||||
onUse(info, s)
|
||||
result = newSymNode(newInst, info)
|
||||
|
||||
proc setGenericParams(c: PContext, n, expectedParams: PNode) =
|
||||
@@ -987,57 +883,46 @@ proc setGenericParams(c: PContext, n, expectedParams: PNode) =
|
||||
nil
|
||||
e = semExprWithType(c, n[i], expectedType = constraint)
|
||||
if e.typ == nil:
|
||||
n[i].typ() = errorType(c)
|
||||
n[i].typ = errorType(c)
|
||||
else:
|
||||
n[i].typ() = e.typ
|
||||
n[i].typ = e.typ.skipTypes({tyTypeDesc})
|
||||
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool): PNode =
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
assert n.kind == nkBracketExpr
|
||||
setGenericParams(c, n, s.ast[genericParamsPos])
|
||||
var s = s
|
||||
var a = n[0]
|
||||
var errors: CandidateErrors = @[]
|
||||
if a.kind == nkSym:
|
||||
# common case; check the only candidate has the right
|
||||
# number of generic type parameters:
|
||||
result = explicitGenericSym(c, n, s, errors, doError)
|
||||
if result == nil:
|
||||
if c.inGenericContext > 0:
|
||||
# same as in semOverloadedCall, make expression untyped,
|
||||
# may have failed match due to unresolved types
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
elif doError:
|
||||
notFoundError(c, n, errors)
|
||||
if s.ast[genericParamsPos].safeLen != n.len-1:
|
||||
let expected = s.ast[genericParamsPos].safeLen
|
||||
localError(c.config, getCallLineInfo(n), errGenerated, "cannot instantiate: '" & renderTree(n) &
|
||||
"'; got " & $(n.len-1) & " typeof(s) but expected " & $expected)
|
||||
return n
|
||||
result = explicitGenericSym(c, n, s)
|
||||
if result == nil: result = explicitGenericInstError(c, n)
|
||||
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
|
||||
# choose the generic proc with the proper number of type parameters.
|
||||
# XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
|
||||
# It's good enough for now.
|
||||
result = newNodeI(a.kind, getCallLineInfo(n))
|
||||
for i in 0..<a.len:
|
||||
var candidate = a[i].sym
|
||||
if candidate.kind in {skProc, skMethod, skConverter,
|
||||
skFunc, skIterator}:
|
||||
let x = explicitGenericSym(c, n, candidate, errors, doError)
|
||||
if x != nil: result.add(x)
|
||||
elif c.inGenericContext > 0:
|
||||
# same as in semOverloadedCall, make expression untyped,
|
||||
# may have failed match due to unresolved types
|
||||
# any failing match stops building the symchoice for correctness,
|
||||
# can also make it untyped from the start
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
return
|
||||
# it suffices that the candidate has the proper number of generic
|
||||
# type parameters:
|
||||
if candidate.ast[genericParamsPos].safeLen == n.len-1:
|
||||
let x = explicitGenericSym(c, n, candidate)
|
||||
if x != nil: result.add(x)
|
||||
# get rid of nkClosedSymChoice if not ambiguous:
|
||||
if result.len == 0:
|
||||
result = nil
|
||||
if doError:
|
||||
notFoundError(c, n, errors)
|
||||
if result.len == 1 and a.kind == nkClosedSymChoice:
|
||||
result = result[0]
|
||||
elif result.len == 0: result = explicitGenericInstError(c, n)
|
||||
# candidateCount != 1: return explicitGenericInstError(c, n)
|
||||
else:
|
||||
# probably unreachable: we are trying to instantiate `a` which is not
|
||||
# a sym/symchoice
|
||||
if doError:
|
||||
result = explicitGenericInstError(c, n)
|
||||
else:
|
||||
result = nil
|
||||
result = explicitGenericInstError(c, n)
|
||||
|
||||
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] =
|
||||
# Searches for the fn in the symbol table. If the parameter lists are suitable
|
||||
|
||||
@@ -9,15 +9,14 @@
|
||||
|
||||
## This module contains the data structures for the semantic checking phase.
|
||||
|
||||
import std/[tables, intsets, sets, strutils]
|
||||
import std/[tables, intsets, sets]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import
|
||||
options, ast, msgs, idents, renderer,
|
||||
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable,
|
||||
types, lowerings, trees, parampatterns, astalgo
|
||||
options, ast, astalgo, msgs, idents, renderer,
|
||||
magicsys, vmdef, modulegraphs, lineinfos, pathutils
|
||||
|
||||
import ic / ic
|
||||
|
||||
@@ -42,7 +41,7 @@ type
|
||||
breakInLoop*: bool # whether we are in a loop without block
|
||||
next*: PProcCon # used for stacking procedure contexts
|
||||
mappingExists*: bool
|
||||
mapping*: SymMapping
|
||||
mapping*: Table[ItemId, PSym]
|
||||
caseContext*: seq[tuple[n: PNode, idx: int]]
|
||||
localBindStmts*: seq[PNode]
|
||||
|
||||
@@ -56,18 +55,7 @@ type
|
||||
inst*: PInstantiation
|
||||
|
||||
TExprFlag* = enum
|
||||
efLValue,
|
||||
# The expression is used as an assignable location.
|
||||
efWantIterator,
|
||||
# Admit iterator candidates and prefer them during overload resolution.
|
||||
efWantIterable,
|
||||
# Admit iterator candidates for expressions that may feed iterable-style
|
||||
# chaining.
|
||||
efPreferIteratorForIterable,
|
||||
# Prefer iterator candidates for `iterable[T]` matching and wrap a
|
||||
# successful iterator call as `tyIterable`.
|
||||
efInTypeof,
|
||||
# The expression is being semchecked under `typeof`.
|
||||
efLValue, efWantIterator, efWantIterable, efInTypeof,
|
||||
efNeedStatic,
|
||||
# Use this in contexts where a static value is mandatory
|
||||
efPreferStatic,
|
||||
@@ -148,13 +136,12 @@ type
|
||||
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
||||
filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.}
|
||||
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
|
||||
semInferredLambda*: proc(c: PContext, pt: LayeredIdTable, n: PNode): PNode
|
||||
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: LayeredIdTable,
|
||||
semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
|
||||
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
|
||||
info: TLineInfo): PSym
|
||||
instantiateOnlyProcType*: proc (c: PContext, pt: LayeredIdTable,
|
||||
instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping,
|
||||
prc: PSym, info: TLineInfo): PType
|
||||
# used by sigmatch for explicit generic instantiations
|
||||
fitDefaultNode*: proc (c: PContext, n: var PNode, expectedType: PType)
|
||||
includedFiles*: IntSet # used to detect recursive include files
|
||||
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
|
||||
userPragmas*: TStrTable
|
||||
@@ -164,6 +151,7 @@ type
|
||||
generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile
|
||||
topStmts*: int # counts the number of encountered top level statements
|
||||
lastGenericIdx*: int # used for the generics stack
|
||||
hloLoopDetector*: int # used to prevent endless loops in the HLO
|
||||
inParallelStmt*: int
|
||||
instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
|
||||
op: TTypeAttachedOp; col: int): PSym {.nimcall.}
|
||||
@@ -183,28 +171,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.
|
||||
forwardFlagUpdates*: seq[(PType, PType)]
|
||||
# (owner, son) pairs whose `propagateToOwner` ran on a not yet reified
|
||||
# forward type and has to be redone in the final pass
|
||||
staleTypeFlags*: IntSet
|
||||
# ids of the owners in `forwardFlagUpdates`; their flags are provisional
|
||||
# too, so reading them makes the reader provisional in turn
|
||||
inTypeofContext*: int
|
||||
|
||||
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
|
||||
shadowDiscardedDefs*: IntSet
|
||||
# ids of local symbols that were declared inside a template/macro operand's
|
||||
# shadow scope and then discarded; re-emitting such a symbol as a
|
||||
# definition gives a fresh copy so distinct emissions don't share a symbol.
|
||||
# See bug #25693 and `rememberShadowDefs`.
|
||||
realizedDefs*: IntSet
|
||||
# ids from `shadowDiscardedDefs` already realized once; the first emission
|
||||
# keeps the original symbol (so leaked dirty-template names still resolve),
|
||||
# later emissions get a fresh copy.
|
||||
hasSymRedefs*: bool
|
||||
# set once a redefinition mapping has been installed; makes `getGenSym`
|
||||
# consult the proc-con mapping for non-gensym symbols too.
|
||||
|
||||
TBorrowState* = enum
|
||||
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
|
||||
|
||||
@@ -228,29 +195,29 @@ proc getIntLitType*(c: PContext; literal: PNode): PType =
|
||||
proc setIntLitType*(c: PContext; result: PNode) =
|
||||
let i = result.intVal
|
||||
case c.config.target.intSize
|
||||
of 8: result.typ() = getIntLitType(c, result)
|
||||
of 8: result.typ = getIntLitType(c, result)
|
||||
of 4:
|
||||
if i >= low(int32) and i <= high(int32):
|
||||
result.typ() = getIntLitType(c, result)
|
||||
result.typ = getIntLitType(c, result)
|
||||
else:
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt64)
|
||||
result.typ = getSysType(c.graph, result.info, tyInt64)
|
||||
of 2:
|
||||
if i >= low(int16) and i <= high(int16):
|
||||
result.typ() = getIntLitType(c, result)
|
||||
result.typ = getIntLitType(c, result)
|
||||
elif i >= low(int32) and i <= high(int32):
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt32)
|
||||
result.typ = getSysType(c.graph, result.info, tyInt32)
|
||||
else:
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt64)
|
||||
result.typ = getSysType(c.graph, result.info, tyInt64)
|
||||
of 1:
|
||||
# 8 bit CPUs are insane ...
|
||||
if i >= low(int8) and i <= high(int8):
|
||||
result.typ() = getIntLitType(c, result)
|
||||
result.typ = getIntLitType(c, result)
|
||||
elif i >= low(int16) and i <= high(int16):
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt16)
|
||||
result.typ = getSysType(c.graph, result.info, tyInt16)
|
||||
elif i >= low(int32) and i <= high(int32):
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt32)
|
||||
result.typ = getSysType(c.graph, result.info, tyInt32)
|
||||
else:
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt64)
|
||||
result.typ = getSysType(c.graph, result.info, tyInt64)
|
||||
else:
|
||||
internalError(c.config, result.info, "invalid int size")
|
||||
|
||||
@@ -286,7 +253,7 @@ proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
|
||||
|
||||
proc put*(p: PProcCon; key, val: PSym) =
|
||||
if not p.mappingExists:
|
||||
p.mapping = initSymMapping()
|
||||
p.mapping = initTable[ItemId, PSym]()
|
||||
p.mappingExists = true
|
||||
#echo "put into table ", key.info
|
||||
p.mapping[key.itemId] = val
|
||||
@@ -296,10 +263,7 @@ proc get*(p: PProcCon; key: PSym): PSym =
|
||||
result = p.mapping.getOrDefault(key.itemId)
|
||||
|
||||
proc getGenSym*(c: PContext; s: PSym): PSym =
|
||||
# `c.hasSymRedefs` additionally routes ordinary (non-gensym) symbols through
|
||||
# the mapping so a re-emitted definition can redirect them to its fresh copy,
|
||||
# see bug #25693 and `newSymG`.
|
||||
if sfGenSym notin s.flags and not c.hasSymRedefs: return s
|
||||
if sfGenSym notin s.flags: return s
|
||||
var it = c.p
|
||||
while it != nil:
|
||||
result = get(it, s)
|
||||
@@ -321,24 +285,22 @@ proc considerGenSyms*(c: PContext; n: PNode) =
|
||||
considerGenSyms(c, n[i])
|
||||
|
||||
proc newOptionEntry*(conf: ConfigRef): POptionEntry =
|
||||
result = POptionEntry(
|
||||
options: conf.options,
|
||||
defaultCC: ccNimCall,
|
||||
dynlib: nil,
|
||||
notes: conf.notes,
|
||||
warningAsErrors: conf.warningAsErrors
|
||||
)
|
||||
new(result)
|
||||
result.options = conf.options
|
||||
result.defaultCC = ccNimCall
|
||||
result.dynlib = nil
|
||||
result.notes = conf.notes
|
||||
result.warningAsErrors = conf.warningAsErrors
|
||||
|
||||
proc pushOptionEntry*(c: PContext): POptionEntry =
|
||||
let prev = c.optionStack[^1]
|
||||
result = POptionEntry(
|
||||
options: c.config.options,
|
||||
defaultCC: prev.defaultCC,
|
||||
dynlib: prev.dynlib,
|
||||
notes: c.config.notes,
|
||||
warningAsErrors: c.config.warningAsErrors,
|
||||
features: c.features
|
||||
)
|
||||
new(result)
|
||||
var prev = c.optionStack[^1]
|
||||
result.options = c.config.options
|
||||
result.defaultCC = prev.defaultCC
|
||||
result.dynlib = prev.dynlib
|
||||
result.notes = c.config.notes
|
||||
result.warningAsErrors = c.config.warningAsErrors
|
||||
result.features = c.features
|
||||
c.optionStack.add(result)
|
||||
|
||||
proc popOptionEntry*(c: PContext) =
|
||||
@@ -349,26 +311,22 @@ proc popOptionEntry*(c: PContext) =
|
||||
c.optionStack.setLen(c.optionStack.len - 1)
|
||||
|
||||
proc newContext*(graph: ModuleGraph; module: PSym): PContext =
|
||||
result = PContext(
|
||||
optionStack: @[newOptionEntry(graph.config)],
|
||||
libs: @[],
|
||||
module: module,
|
||||
friendModules: @[module],
|
||||
converters: @[],
|
||||
patterns: @[],
|
||||
includedFiles: initIntSet(),
|
||||
pureEnumFields: initStrTable(),
|
||||
userPragmas: initStrTable(),
|
||||
generics: @[],
|
||||
unknownIdents: initIntSet(),
|
||||
shadowDiscardedDefs: initIntSet(),
|
||||
realizedDefs: initIntSet(),
|
||||
staleTypeFlags: initIntSet(),
|
||||
cache: graph.cache,
|
||||
graph: graph,
|
||||
signatures: initStrTable(),
|
||||
features: graph.config.features
|
||||
)
|
||||
new(result)
|
||||
result.optionStack = @[newOptionEntry(graph.config)]
|
||||
result.libs = @[]
|
||||
result.module = module
|
||||
result.friendModules = @[module]
|
||||
result.converters = @[]
|
||||
result.patterns = @[]
|
||||
result.includedFiles = initIntSet()
|
||||
result.pureEnumFields = initStrTable()
|
||||
result.userPragmas = initStrTable()
|
||||
result.generics = @[]
|
||||
result.unknownIdents = initIntSet()
|
||||
result.cache = graph.cache
|
||||
result.graph = graph
|
||||
result.signatures = initStrTable()
|
||||
result.features = graph.config.features
|
||||
if graph.config.symbolFiles != disabledSf:
|
||||
let id = module.position
|
||||
if graph.config.cmd != cmdM:
|
||||
@@ -432,7 +390,8 @@ proc reexportSym*(c: PContext; s: PSym) =
|
||||
addReexport(c.encoder, c.packedRepr, s)
|
||||
|
||||
proc newLib*(kind: TLibKind): PLib =
|
||||
result = PLib(kind: kind) #result.syms = initObjectSet()
|
||||
new(result)
|
||||
result.kind = kind #result.syms = initObjectSet()
|
||||
|
||||
proc addToLib*(lib: PLib, sym: PSym) =
|
||||
#if sym.annex != nil and not isGenericRoutine(sym):
|
||||
@@ -490,7 +449,7 @@ when false:
|
||||
proc makeStaticExpr*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkStaticExpr, n.info)
|
||||
result.sons = @[n]
|
||||
result.typ() = if n.typ != nil and n.typ.kind == tyStatic: n.typ
|
||||
result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ
|
||||
else: newTypeS(tyStatic, c, n.typ)
|
||||
|
||||
proc makeAndType*(c: PContext, t1, t2: PType): PType =
|
||||
@@ -549,7 +508,7 @@ proc errorType*(c: PContext): PType =
|
||||
|
||||
proc errorNode*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
result.typ() = errorType(c)
|
||||
result.typ = errorType(c)
|
||||
|
||||
# These mimic localError
|
||||
template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, msg: TMsgKind, arg: string): PNode =
|
||||
@@ -570,11 +529,10 @@ template localErrorNode*(c: PContext, n: PNode, arg: string): PNode =
|
||||
liMessage(c.config, n2.info, errGenerated, arg, doNothing, instLoc())
|
||||
errorNode(c, n2)
|
||||
|
||||
when false:
|
||||
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
|
||||
dest.kind = kind
|
||||
dest.owner = getCurrOwner(c)
|
||||
dest.size = - 1
|
||||
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
|
||||
dest.kind = kind
|
||||
dest.owner = getCurrOwner(c)
|
||||
dest.size = - 1
|
||||
|
||||
proc makeRangeType*(c: PContext; first, last: BiggestInt;
|
||||
info: TLineInfo; intType: PType = nil): PType =
|
||||
@@ -605,7 +563,7 @@ proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym =
|
||||
|
||||
proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode =
|
||||
result = newSymNode(symFromType(c, t, info), info)
|
||||
result.typ() = makeTypeDesc(c, t)
|
||||
result.typ = makeTypeDesc(c, t)
|
||||
|
||||
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
||||
if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
|
||||
@@ -675,166 +633,3 @@ proc rememberExpansion*(c: PContext; info: TLineInfo; expandedSym: PSym) =
|
||||
## delegated to the "rod" file mechanism.
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
storeExpansion(c.encoder, c.packedRepr, info, expandedSym)
|
||||
|
||||
const
|
||||
errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
|
||||
errXStackEscape = "address of '$1' may not escape its stack frame"
|
||||
|
||||
proc renderNotLValue*(n: PNode): string =
|
||||
result = $n
|
||||
let n = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
if n.kind == nkHiddenCallConv and n.len > 1:
|
||||
result = $n[0] & "(" & result & ")"
|
||||
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
|
||||
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
|
||||
|
||||
proc isAssignable(c: PContext, n: PNode): TAssignableResult =
|
||||
result = parampatterns.isAssignable(c.p.owner, n)
|
||||
|
||||
proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
|
||||
if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or
|
||||
sfCompileToCpp in c.module.flags):
|
||||
checkSonsLen(n, 1, c.config)
|
||||
result = n[0]
|
||||
else:
|
||||
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
|
||||
result.add n
|
||||
let aa = isAssignable(c, n)
|
||||
let sym = getRoot(n)
|
||||
if aa notin {arLValue, arLocalLValue}:
|
||||
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
|
||||
discard "allow access within a cast(unsafeAssign) section"
|
||||
elif strictDefs in c.features and aa == arAddressableConst and
|
||||
sym != nil and sym.kind == skLet and isOutParam:
|
||||
discard "allow let varaibles to be passed to out parameters"
|
||||
else:
|
||||
localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
|
||||
|
||||
proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
|
||||
result = n
|
||||
case n.kind
|
||||
of nkSym:
|
||||
# n.sym.typ can be nil in 'check' mode ...
|
||||
if n.sym.typ != nil and
|
||||
skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
|
||||
incl(n.sym.flags, sfAddrTaken)
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
of nkDotExpr:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
if n[1].kind != nkSym:
|
||||
internalError(c.config, n.info, "analyseIfAddressTaken")
|
||||
return
|
||||
if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
|
||||
incl(n[1].sym.flags, sfAddrTaken)
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
of nkBracketExpr:
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
|
||||
if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken)
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
else:
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
|
||||
proc analyseIfAddressTakenInCall*(c: PContext, n: PNode, isConverter = false) =
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
if n[0].typ == nil:
|
||||
# n[0] might be erroring node in nimsuggest
|
||||
return
|
||||
const
|
||||
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
|
||||
mSetLengthStr, mSetLengthSeq, mSetLengthSeqUninit, mAppendStrCh, mAppendStrStr, mSwap,
|
||||
mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, mWasMoved}
|
||||
|
||||
template checkIfConverterCalled(c: PContext, n: PNode) =
|
||||
## Checks if there is a converter call which wouldn't be checked otherwise
|
||||
# Call can sometimes be wrapped in a deref
|
||||
let node = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
if node.kind == nkHiddenCallConv:
|
||||
analyseIfAddressTakenInCall(c, node, true)
|
||||
# get the real type of the callee
|
||||
# it may be a proc var with a generic alias type, so we skip over them
|
||||
var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams:
|
||||
# BUGFIX: check for L-Value still needs to be done for the arguments!
|
||||
# note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
|
||||
for i in 1..<n.len:
|
||||
if i < t.len and t[i] != nil and
|
||||
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
|
||||
let it = n[i]
|
||||
let aa = isAssignable(c, it)
|
||||
if aa notin {arLValue, arLocalLValue}:
|
||||
if it.kind != nkHiddenAddr:
|
||||
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
|
||||
discard "allow access within a cast(unsafeAssign) section"
|
||||
else:
|
||||
localError(c.config, it.info, errVarForOutParamNeededX % $it)
|
||||
# Make sure to still check arguments for converters
|
||||
c.checkIfConverterCalled(n[i])
|
||||
# bug #5113: disallow newSeq(result) where result is a 'var T':
|
||||
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||
var arg = n[1] #.skipAddr
|
||||
if arg.kind == nkHiddenDeref: arg = arg[0]
|
||||
if arg.kind == nkSym and arg.sym.kind == skResult and
|
||||
arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
|
||||
localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments}))
|
||||
|
||||
return
|
||||
for i in 1..<n.len:
|
||||
let n = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
c.checkIfConverterCalled(n[i])
|
||||
if i < t.len and
|
||||
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
|
||||
# Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet.
|
||||
# So we need to make sure we are checking them still when in a converter call
|
||||
if n[i].kind != nkHiddenAddr or isConverter:
|
||||
n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc})))
|
||||
|
||||
|
||||
proc replaceHookMagic*(c: PContext, n: PNode, kind: TTypeAttachedOp): PNode =
|
||||
## Replaces builtin generic hooks with lifted hooks.
|
||||
case kind
|
||||
of attachedDestructor:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
||||
let op = getAttachedOp(c.graph, t, attachedDestructor)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
|
||||
if n[1].kind == nkSym and n[1].sym.kind == skParam and
|
||||
n[1].typ.kind == tyVar:
|
||||
result[1] = genDeref(n[1])
|
||||
else:
|
||||
result[1] = skipAddr(n[1])
|
||||
of attachedTrace:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
||||
let op = getAttachedOp(c.graph, t, attachedTrace)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
of attachedDup:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
||||
let op = getAttachedOp(c.graph, t, attachedDup)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
if op.typ.len == 3:
|
||||
let boolLit = newIntLit(c.graph, n.info, 1)
|
||||
boolLit.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
result.add boolLit
|
||||
of attachedWasMoved:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
||||
let op = getAttachedOp(c.graph, t, attachedWasMoved)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
analyseIfAddressTakenInCall(c, result, false)
|
||||
of attachedSink:
|
||||
result = c.semAsgnOpr(c, n, nkSinkAsgn)
|
||||
of attachedAsgn:
|
||||
result = c.semAsgnOpr(c, n, nkAsgn)
|
||||
of attachedDeepCopy:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
||||
let op = getAttachedOp(c.graph, t, kind)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,15 +18,6 @@ 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)
|
||||
@@ -36,8 +27,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
of nkIdent, nkSym:
|
||||
result = n
|
||||
let ident = considerQuotedIdent(c.c, n)
|
||||
if c.replaceByFieldName and
|
||||
ident.id != ord(wUnderscore):
|
||||
if c.replaceByFieldName:
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
|
||||
let fieldName = if c.tupleType.isNil: c.field.name.s
|
||||
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
|
||||
@@ -46,8 +36,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
return
|
||||
# other fields:
|
||||
for i in ord(c.replaceByFieldName)..<forLoop.len-2:
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id and
|
||||
ident.id != ord(wUnderscore):
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
|
||||
var call = forLoop[^2]
|
||||
var tupl = call[i+1-ord(c.replaceByFieldName)]
|
||||
if c.field.isNil:
|
||||
@@ -83,9 +72,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
|
||||
)
|
||||
openScope(c.c)
|
||||
inc c.c.inUnrolledContext
|
||||
var body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
# new scope for each field that codegen should know about:
|
||||
body = wrapNewScope(c.c, body)
|
||||
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
father.add(semStmt(c.c, body, {}))
|
||||
dec c.c.inUnrolledContext
|
||||
closeScope(c.c)
|
||||
@@ -161,8 +148,6 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
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
|
||||
|
||||
@@ -16,7 +16,7 @@ import
|
||||
commands, magicsys, modulegraphs, lineinfos, wordrecg
|
||||
|
||||
import std/[strutils, math, strtabs]
|
||||
#from system/memory import nimCStrLen
|
||||
from system/memory import nimCStrLen
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[assertions, formatfloat]
|
||||
@@ -38,7 +38,7 @@ proc newIntNodeT*(intVal: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph):
|
||||
# original type was 'int', not a distinct int etc.
|
||||
if n.typ.kind == tyInt:
|
||||
# access cache for the int lit type
|
||||
result.typ() = getIntLitTypeG(g, result, idgen)
|
||||
result.typ = getIntLitTypeG(g, result, idgen)
|
||||
result.info = n.info
|
||||
|
||||
proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
|
||||
@@ -46,12 +46,12 @@ proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
|
||||
result = newFloatNode(nkFloat32Lit, floatVal)
|
||||
else:
|
||||
result = newFloatNode(nkFloatLit, floatVal)
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
result.info = n.info
|
||||
|
||||
proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
|
||||
result = newStrNode(nkStrLit, strVal)
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
result.info = n.info
|
||||
|
||||
proc getConstExpr*(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
@@ -179,30 +179,29 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
let argB = getInt(b)
|
||||
result = newIntNodeT(if argA > argB: argA else: argB, n, idgen, g)
|
||||
of mShlI:
|
||||
let valueB = toInt64(getInt(b)) and (n.typ.size * 8 - 1)
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt:
|
||||
if g.config.target.intSize == 4:
|
||||
result = newIntNodeT(toInt128(toInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(toInt128(toInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt:
|
||||
if g.config.target.intSize == 4:
|
||||
result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
else: internalError(g.config, n.info, "constant folding for shl")
|
||||
of mShrI:
|
||||
var a = cast[uint64](getInt(a))
|
||||
let b = cast[uint64](getInt(b)) and cast[uint64](n.typ.size * 8 - 1)
|
||||
let b = cast[uint64](getInt(b))
|
||||
# To support the ``-d:nimOldShiftRight`` flag, we need to mask the
|
||||
# signed integers to cut off the extended sign bit in the internal
|
||||
# representation.
|
||||
@@ -221,13 +220,12 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
let c = cast[BiggestInt](a shr b)
|
||||
result = newIntNodeT(toInt128(c), n, idgen, g)
|
||||
of mAshrI:
|
||||
let valueB = toInt64(getInt(b)) and (n.typ.size * 8 - 1)
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), valueB)), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), valueB)), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), valueB)), n, idgen, g)
|
||||
of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), toInt8(getInt(b)))), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), toInt16(getInt(b)))), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), toInt32(getInt(b)))), n, idgen, g)
|
||||
of tyInt64, tyInt:
|
||||
result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), valueB)), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), toInt64(getInt(b)))), n, idgen, g)
|
||||
else: internalError(g.config, n.info, "constant folding for ashr")
|
||||
of mDivI:
|
||||
let argA = getInt(a)
|
||||
@@ -304,9 +302,6 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
of mMinusSet:
|
||||
result = nimsets.diffSets(g.config, a, b)
|
||||
result.info = n.info
|
||||
of mXorSet:
|
||||
result = nimsets.symdiffSets(g.config, a, b)
|
||||
result.info = n.info
|
||||
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
|
||||
of mInSet: result = newIntNodeT(toInt128(ord(inSet(a, b))), n, idgen, g)
|
||||
of mRepr:
|
||||
@@ -321,7 +316,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g)
|
||||
of mArrToSeq:
|
||||
result = copyTree(a)
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
of mCompileOption:
|
||||
result = newIntNodeT(toInt128(ord(commands.testCompileOption(g.config, a.getStr, n.info))), n, idgen, g)
|
||||
of mCompileOptionArg:
|
||||
@@ -416,7 +411,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
|
||||
result = newIntNodeT(toInt128(a.getOrdValue != 0), n, idgen, g)
|
||||
of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`?
|
||||
result = a
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
else:
|
||||
raiseAssert $srcTyp.kind
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
@@ -433,7 +428,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
else:
|
||||
result = a
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
if check and result.kind in {nkCharLit..nkUInt64Lit} and
|
||||
dstTyp.kind notin {tyUInt..tyUInt64}:
|
||||
rangeCheck(n, getInt(result), g)
|
||||
@@ -443,12 +438,12 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
|
||||
result = newFloatNodeT(toFloat64(getOrdValue(a)), n, g)
|
||||
else:
|
||||
result = a
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
of tyOpenArray, tyVarargs, tyProc, tyPointer:
|
||||
result = nil
|
||||
else:
|
||||
result = a
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
|
||||
proc getArrayConstr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
if n.kind == nkBracket:
|
||||
@@ -473,20 +468,19 @@ proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNo
|
||||
if result.kind == nkExprColonExpr: result = result[1]
|
||||
else:
|
||||
result = nil
|
||||
#localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
|
||||
localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
|
||||
of nkBracket:
|
||||
idx -= toInt64(firstOrd(g.config, x.typ))
|
||||
if idx >= 0 and idx < x.len: result = x[int(idx)]
|
||||
else:
|
||||
result = nil
|
||||
#localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
|
||||
localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
result = newNodeIT(nkCharLit, x.info, n.typ)
|
||||
if idx >= 0 and idx < x.strVal.len:
|
||||
result.intVal = ord(x.strVal[int(idx)])
|
||||
else:
|
||||
result = nil
|
||||
#localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
|
||||
localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
|
||||
else: result = nil
|
||||
|
||||
proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
@@ -520,10 +514,10 @@ proc foldConStrStr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode =
|
||||
result = newSymNode(s, info)
|
||||
if s.typ.kind != tyTypeDesc:
|
||||
result.typ() = newType(tyTypeDesc, idgen, s.owner)
|
||||
result.typ = newType(tyTypeDesc, idgen, s.owner)
|
||||
result.typ.addSonSkipIntLit(s.typ, idgen)
|
||||
else:
|
||||
result.typ() = s.typ
|
||||
result.typ = s.typ
|
||||
|
||||
proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
result = nil
|
||||
@@ -592,7 +586,7 @@ proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
raise newException(ValueError, "invalid enum value: " & str)
|
||||
else:
|
||||
localError(g.config, s.info, "unsupported type $1 for define '$2'" %
|
||||
[typeToString(rawTyp), name])
|
||||
[name, typeToString(rawTyp)])
|
||||
except ValueError as e:
|
||||
localError(g.config, s.info,
|
||||
"could not process define '$1' of type $2; $3" %
|
||||
@@ -642,7 +636,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
if s.typ.kind == tyStatic:
|
||||
if s.typ.n != nil and tfUnresolved notin s.typ.flags:
|
||||
result = s.typ.n
|
||||
result.typ() = s.typ.base
|
||||
result.typ = s.typ.base
|
||||
elif s.typ.isIntLit:
|
||||
result = s.typ.n
|
||||
else:
|
||||
@@ -755,7 +749,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
if a == nil: return
|
||||
if leValueConv(n[1], a) and leValueConv(a, n[2]):
|
||||
result = a # a <= x and x <= b
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
elif n.typ.kind in {tyUInt..tyUInt64}:
|
||||
discard "don't check uints"
|
||||
else:
|
||||
@@ -766,7 +760,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
var a = getConstExpr(m, n[0], idgen, g)
|
||||
if a == nil: return
|
||||
result = a
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
var a = getConstExpr(m, n[1], idgen, g)
|
||||
if a == nil: return
|
||||
@@ -783,7 +777,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
not (n.typ.kind == tyProc and a.typ.kind == tyProc):
|
||||
# we allow compile-time 'cast' for pointer types:
|
||||
result = a
|
||||
result.typ() = n.typ
|
||||
result.typ = n.typ
|
||||
of nkBracketExpr: result = foldArrayAccess(m, n, idgen, g)
|
||||
of nkDotExpr: result = foldFieldAccess(m, n, idgen, g)
|
||||
of nkCheckedFieldExpr:
|
||||
|
||||
@@ -78,10 +78,10 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
@@ -116,7 +116,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
else:
|
||||
result = n
|
||||
else:
|
||||
@@ -126,22 +126,10 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
of skParam:
|
||||
if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil:
|
||||
# The enclosing routine gives this static parameter a concrete value.
|
||||
# Keep that value so the nested generic can fold it as a compile-time
|
||||
# expression instead of generating a runtime parameter reference.
|
||||
result = s.typ.n
|
||||
elif s.owner == c.p.owner:
|
||||
# Parameters of the routine currently being semchecked stay as local
|
||||
# identifiers
|
||||
result = n
|
||||
else:
|
||||
# Preserve captured outer parameters so nested generic procs can still
|
||||
# see them after the generic pre-pass.
|
||||
result = newSymNode(s, n.info)
|
||||
result = n
|
||||
onUse(n.info, s)
|
||||
of skType:
|
||||
if (s.typ != nil) and
|
||||
@@ -157,7 +145,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
elif c.inGenericContext > 0 and withinConcept notin flags:
|
||||
# don't leave generic param as identifier node in generic type,
|
||||
# sigmatch will try to instantiate generic type AST without all params
|
||||
@@ -169,7 +157,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
else:
|
||||
result = n
|
||||
onUse(n.info, s)
|
||||
@@ -180,7 +168,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
|
||||
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
@@ -230,7 +218,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
let ident = considerQuotedIdent(c, n)
|
||||
# could be type conversion if like a.T and not a.T()
|
||||
let symKinds = if inCall: routineKinds else: routineKinds+{skType}
|
||||
var candidates = selectFromScopesElseAll(c, ident, symKinds)
|
||||
var candidates = searchInScopesFilterBy(c, ident, symKinds)
|
||||
if candidates.len > 0:
|
||||
let s = candidates[0] # XXX take into account the other candidates!
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
@@ -286,8 +274,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
result = lookup(c, n, flags, ctx)
|
||||
if result != nil and result.kind == nkSym:
|
||||
assert result.sym != nil
|
||||
incl result.sym.flags, sfUsed
|
||||
markOwnerModuleAsUsed(c, result.sym)
|
||||
markUsed(c, n.info, result.sym)
|
||||
of nkDotExpr:
|
||||
#let luf = if withinMixin notin flags: {checkUndeclared} else: {}
|
||||
#var s = qualifiedLookUp(c, n, luf)
|
||||
@@ -629,24 +616,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
let bodyFlags = if n.kind == nkTemplateDef: flags + {withinMixin} else: flags
|
||||
n[bodyPos] = semGenericStmtScope(c, body, bodyFlags, ctx)
|
||||
closeScope(c)
|
||||
of nkPragmaExpr:
|
||||
result[1] = semGenericStmt(c, n[1], flags, ctx)
|
||||
of nkPragma:
|
||||
for i in 0 ..< n.len:
|
||||
let x = n[i]
|
||||
let prag = whichPragma(x)
|
||||
if x.kind in nkPragmaCallKinds:
|
||||
# process each child individually to prevent untyped macros/templates
|
||||
# from instantiating
|
||||
# if pragma is language-level pragma, skip name node:
|
||||
let start = ord(prag != wInvalid)
|
||||
for j in start ..< x.len:
|
||||
# treat as mixin context for user pragmas & macro args
|
||||
x[j] = semGenericStmt(c, x[j], flags+{withinMixin}, ctx)
|
||||
elif prag == wInvalid:
|
||||
# only sem if not a language-level pragma
|
||||
# treat as mixin context for user pragmas & macro args
|
||||
result[i] = semGenericStmt(c, x, flags+{withinMixin}, ctx)
|
||||
of nkPragma, nkPragmaExpr: discard
|
||||
of nkExprColonExpr, nkExprEqExpr:
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
result[1] = semGenericStmt(c, n[1], flags, ctx)
|
||||
@@ -686,3 +656,4 @@ proc semConceptBody(c: PContext, n: PNode): PNode =
|
||||
)
|
||||
result = semGenericStmt(c, n, {withinConcept}, ctx)
|
||||
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ proc pushProcCon*(c: PContext; owner: PSym) =
|
||||
const
|
||||
errCannotInstantiateX = "cannot instantiate: '$1'"
|
||||
|
||||
iterator instantiateGenericParamList(c: PContext, n: PNode, pt: LayeredIdTable): PSym =
|
||||
iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PSym =
|
||||
internalAssert c.config, n.kind == nkGenericParams
|
||||
for a in n.items:
|
||||
internalAssert c.config, a.kind == nkSym
|
||||
@@ -43,7 +43,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: LayeredIdTable):
|
||||
let symKind = if q.typ.kind == tyStatic: skConst else: skType
|
||||
var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info)
|
||||
s.flags.incl {sfUsed, sfFromGeneric}
|
||||
var t = lookup(pt, q.typ)
|
||||
var t = idTableGet(pt, q.typ)
|
||||
if t == nil:
|
||||
if tfRetType in q.typ.flags:
|
||||
# keep the generic type and allow the return type to be bound
|
||||
@@ -53,9 +53,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: LayeredIdTable):
|
||||
if q.typ.kind != tyCompositeTypeClass:
|
||||
localError(c.config, a.info, errCannotInstantiateX % s.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind in {tyGenericParam, tyConcept, tyFromExpr} or
|
||||
# generic body types are accepted as typedesc arguments
|
||||
(t.kind == tyGenericBody and q.typ.kind != tyTypeDesc):
|
||||
elif t.kind in {tyGenericParam, tyConcept, tyFromExpr}:
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or
|
||||
@@ -111,7 +109,7 @@ proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var SymMappi
|
||||
elif s.owner == nil or s.owner.kind == skPackage:
|
||||
#echo "copied this ", s.name.s
|
||||
x = copySym(s, c.idgen)
|
||||
setOwner(x, owner)
|
||||
x.owner = owner
|
||||
idTablePut(symMap, s, x)
|
||||
n.sym = x
|
||||
else:
|
||||
@@ -222,7 +220,7 @@ proc referencesAnotherParam(n: PNode, p: PSym): bool =
|
||||
if referencesAnotherParam(n[i], p): return true
|
||||
return false
|
||||
|
||||
proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
proc instantiateProcType(c: PContext, pt: TypeMapping,
|
||||
prc: PSym, info: TLineInfo) =
|
||||
# XXX: Instantiates a generic proc signature, while at the same
|
||||
# time adding the instantiated proc params into the current scope.
|
||||
@@ -239,7 +237,7 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
# will need to use openScope, addDecl, etc.
|
||||
#addDecl(c, prc)
|
||||
pushInfoContext(c.config, info)
|
||||
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
|
||||
var typeMap = initLayeredTypeMap(pt)
|
||||
var cl = initTypeVars(c, typeMap, info, nil)
|
||||
var result = instCopyType(cl, prc.typ)
|
||||
let originalParams = result.n
|
||||
@@ -273,7 +271,7 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam, c.idgen)
|
||||
setOwner(param, prc)
|
||||
param.owner = prc
|
||||
param.typ = result[i]
|
||||
|
||||
# The default value is instantiated and fitted against the final
|
||||
@@ -302,14 +300,12 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
# the only way the default value might be inserted).
|
||||
param.ast = errorNode(c, def)
|
||||
# we know the node is empty, we need the actual type for error message
|
||||
param.ast.typ() = def.typ
|
||||
param.ast.typ = def.typ
|
||||
else:
|
||||
param.ast = fitNodePostMatch(c, typeToFit, converted)
|
||||
param.typ = result[i]
|
||||
|
||||
result.n[i] = newSymNode(param)
|
||||
if isRecursiveStructuralType(result[i]):
|
||||
localError(c.config, originalParams[i].sym.info, "illegal recursion in type '" & typeToString(result[i]) & "'")
|
||||
propagateToOwner(result, result[i])
|
||||
addDecl(c, param)
|
||||
|
||||
@@ -320,8 +316,6 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
cl.isReturnType = false
|
||||
result.n[0] = originalParams[0].copyTree
|
||||
if result[0] != nil:
|
||||
if isRecursiveStructuralType(result[0]):
|
||||
localError(c.config, originalParams[0].info, "illegal recursion in type '" & typeToString(result[0]) & "'")
|
||||
propagateToOwner(result, result[0])
|
||||
|
||||
eraseVoidParams(result)
|
||||
@@ -330,7 +324,7 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
prc.typ = result
|
||||
popInfoContext(c.config)
|
||||
|
||||
proc instantiateOnlyProcType(c: PContext, pt: LayeredIdTable, prc: PSym, info: TLineInfo): PType =
|
||||
proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType =
|
||||
# instantiates only the type of a given proc symbol
|
||||
# used by sigmatch for explicit generics
|
||||
# wouldn't be needed if sigmatch could handle complex cases,
|
||||
@@ -366,7 +360,7 @@ proc getLocalPassC(c: PContext, s: PSym): string =
|
||||
for p in n:
|
||||
extractPassc(p)
|
||||
|
||||
proc generateInstance(c: PContext, fn: PSym, pt: LayeredIdTable,
|
||||
proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
|
||||
info: TLineInfo): PSym =
|
||||
## Generates a new instance of a generic procedure.
|
||||
## The `pt` parameter is a type-unsafe mapping table used to link generic
|
||||
@@ -377,11 +371,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: LayeredIdTable,
|
||||
if c.instCounter > 50:
|
||||
globalError(c.config, info, "generic instantiation too nested")
|
||||
inc c.instCounter
|
||||
let currentTypeofContext = c.inTypeofContext
|
||||
c.inTypeofContext = 0
|
||||
defer:
|
||||
dec c.instCounter
|
||||
c.inTypeofContext = currentTypeofContext
|
||||
defer: dec c.instCounter
|
||||
# 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
|
||||
@@ -399,9 +389,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: LayeredIdTable,
|
||||
let passc = getLocalPassC(c, producer)
|
||||
if passc != "": #pass the local compiler options to the consumer module too
|
||||
extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex))
|
||||
setOwner(result, c.module)
|
||||
result.owner = c.module
|
||||
else:
|
||||
setOwner(result, fn)
|
||||
result.owner = fn
|
||||
result.ast = n
|
||||
pushOwner(c, result)
|
||||
|
||||
|
||||
@@ -10,28 +10,9 @@
|
||||
## Implements type sanity checking for ASTs resulting from macros. Lots of
|
||||
## room for improvement here.
|
||||
|
||||
import ast, msgs, types, options, trees, nimsets
|
||||
import ast, msgs, types, options
|
||||
|
||||
type
|
||||
FieldTracker = object
|
||||
index: int
|
||||
remaining: int
|
||||
constr: PNode
|
||||
delete: bool # to delete fields from inactive case branches
|
||||
FieldInfo = ref object
|
||||
sym: PSym
|
||||
delete: bool
|
||||
|
||||
proc caseBranchMatchesExpr(branch, matched: PNode): bool =
|
||||
# copied from sem
|
||||
result = false
|
||||
for i in 0 ..< branch.len-1:
|
||||
if branch[i].kind == nkRange:
|
||||
if overlap(branch[i], matched): return true
|
||||
elif exprStructuralEquivalent(branch[i], matched):
|
||||
return true
|
||||
|
||||
proc ithField(n: PNode, field: var FieldTracker): FieldInfo =
|
||||
proc ithField(n: PNode, field: var int): PSym =
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
@@ -42,42 +23,18 @@ proc ithField(n: PNode, field: var FieldTracker): FieldInfo =
|
||||
if n[0].kind != nkSym: return
|
||||
result = ithField(n[0], field)
|
||||
if result != nil: return
|
||||
# value of the discriminator field, from (index - remaining - 1 + 1):
|
||||
# - 1 because the `ithField` call above decreased it by 1,
|
||||
# + 1 because the constructor node has an initial type child
|
||||
let val = field.constr[field.index - field.remaining][1]
|
||||
var branchFound = false
|
||||
for i in 1..<n.len:
|
||||
let previousDelete = field.delete
|
||||
case n[i].kind
|
||||
of nkOfBranch:
|
||||
if branchFound or previousDelete or
|
||||
not caseBranchMatchesExpr(n[i], val):
|
||||
# if this is not the active case branch,
|
||||
# mark all fields inside as deleted
|
||||
field.delete = true
|
||||
else:
|
||||
branchFound = true
|
||||
of nkOfBranch, nkElse:
|
||||
result = ithField(lastSon(n[i]), field)
|
||||
if result != nil: return
|
||||
field.delete = previousDelete
|
||||
of nkElse:
|
||||
if branchFound:
|
||||
# if this is not the active case branch,
|
||||
# mark all fields inside as deleted
|
||||
field.delete = true
|
||||
result = ithField(lastSon(n[i]), field)
|
||||
if result != nil: return
|
||||
field.delete = previousDelete
|
||||
else: discard
|
||||
of nkSym:
|
||||
if field.remaining == 0:
|
||||
result = FieldInfo(sym: n.sym, delete: field.delete)
|
||||
else:
|
||||
dec(field.remaining)
|
||||
if field == 0: result = n.sym
|
||||
else: dec(field)
|
||||
else: discard
|
||||
|
||||
proc ithField(t: PType, field: var FieldTracker): FieldInfo =
|
||||
proc ithField(t: PType, field: var int): PSym =
|
||||
var base = t.baseClass
|
||||
while base != nil:
|
||||
let b = skipTypes(base, skipPtrs)
|
||||
@@ -86,36 +43,31 @@ proc ithField(t: PType, field: var FieldTracker): FieldInfo =
|
||||
base = b.baseClass
|
||||
result = ithField(t.n, field)
|
||||
|
||||
proc annotateType*(n: PNode, t: PType; conf: ConfigRef; producedClosure: var bool) =
|
||||
proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
let x = t.skipTypes(abstractInst+{tyRange})
|
||||
# Note: x can be unequal to t and we need to be careful to use 't'
|
||||
# to not to skip tyGenericInst
|
||||
case n.kind
|
||||
of nkObjConstr:
|
||||
let x = t.skipTypes(abstractPtrs)
|
||||
n.typ() = t
|
||||
n[0].typ() = t
|
||||
n.typ = t
|
||||
n[0].typ = t
|
||||
for i in 1..<n.len:
|
||||
var tracker = FieldTracker(index: i-1, remaining: i-1, constr: n, delete: false)
|
||||
let field = x.ithField(tracker)
|
||||
var j = i-1
|
||||
let field = x.ithField(j)
|
||||
if field.isNil:
|
||||
globalError conf, n.info, "invalid field at index " & $i
|
||||
else:
|
||||
internalAssert(conf, n[i].kind == nkExprColonExpr)
|
||||
annotateType(n[i][1], field.sym.typ, conf, producedClosure)
|
||||
if field.delete:
|
||||
# only codegen fields from active case branches
|
||||
incl(n[i].flags, nfPreventCg)
|
||||
annotateType(n[i][1], field.typ, conf)
|
||||
of nkPar, nkTupleConstr:
|
||||
if x.kind == tyTuple:
|
||||
n.typ() = t
|
||||
n.typ = t
|
||||
for i in 0..<n.len:
|
||||
if i >= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i
|
||||
else: annotateType(n[i], x[i], conf, producedClosure)
|
||||
else: annotateType(n[i], x[i], conf)
|
||||
elif x.kind == tyProc and x.callConv == ccClosure:
|
||||
n.typ() = t
|
||||
if n.len > 1 and n[1].kind notin {nkEmpty, nkNilLit}:
|
||||
producedClosure = true
|
||||
n.typ = t
|
||||
elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples
|
||||
if n.kind == nkTupleConstr:
|
||||
let
|
||||
@@ -127,53 +79,48 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef; producedClosure: var boo
|
||||
of nkStrKinds:
|
||||
for i in left..right:
|
||||
bracketExpr.add newIntNode(nkCharLit, BiggestInt n[0].strVal[i])
|
||||
annotateType(bracketExpr[^1], x.elementType, conf, producedClosure)
|
||||
annotateType(bracketExpr[^1], x.elementType, conf)
|
||||
of nkBracket:
|
||||
for i in left..right:
|
||||
bracketExpr.add n[0][i]
|
||||
annotateType(bracketExpr[^1], x.elementType, conf, producedClosure)
|
||||
annotateType(bracketExpr[^1], x.elementType, conf)
|
||||
else:
|
||||
globalError(conf, n.info, "Incorrectly generated tuple constr")
|
||||
n[] = bracketExpr[]
|
||||
|
||||
n.typ() = t
|
||||
n.typ = t
|
||||
else:
|
||||
globalError(conf, n.info, "() must have a tuple type")
|
||||
of nkBracket:
|
||||
if x.kind in {tyArray, tySequence, tyOpenArray}:
|
||||
n.typ() = t
|
||||
for m in n: annotateType(m, x.elemType, conf, producedClosure)
|
||||
n.typ = t
|
||||
for m in n: annotateType(m, x.elemType, conf)
|
||||
else:
|
||||
globalError(conf, n.info, "[] must have some form of array type")
|
||||
of nkCurly:
|
||||
if x.kind in {tySet}:
|
||||
n.typ() = t
|
||||
for m in n:
|
||||
if m.kind == nkRange:
|
||||
annotateType(m[0], x.elemType, conf, producedClosure)
|
||||
annotateType(m[1], x.elemType, conf, producedClosure)
|
||||
else:
|
||||
annotateType(m, x.elemType, conf, producedClosure)
|
||||
n.typ = t
|
||||
for m in n: annotateType(m, x.elemType, conf)
|
||||
else:
|
||||
globalError(conf, n.info, "{} must have the set type")
|
||||
of nkFloatLit..nkFloat128Lit:
|
||||
if x.kind in {tyFloat..tyFloat128}:
|
||||
n.typ() = t
|
||||
n.typ = t
|
||||
else:
|
||||
globalError(conf, n.info, "float literal must have some float type")
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if x.kind in {tyInt..tyUInt64, tyBool, tyChar, tyEnum}:
|
||||
n.typ() = t
|
||||
n.typ = t
|
||||
else:
|
||||
globalError(conf, n.info, "integer literal must have some int type")
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if x.kind in {tyString, tyCstring}:
|
||||
n.typ() = t
|
||||
n.typ = t
|
||||
else:
|
||||
globalError(conf, n.info, "string literal must be of some string type")
|
||||
of nkNilLit:
|
||||
if x.kind in NilableTypes+{tyString, tySequence}:
|
||||
n.typ() = t
|
||||
n.typ = t
|
||||
else:
|
||||
globalError(conf, n.info, "nil literal must be of some pointer type")
|
||||
else: discard
|
||||
|
||||
@@ -18,7 +18,7 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
|
||||
let typ = result[1].typ # new(x)
|
||||
if typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyRef and typ.skipTypes({tyGenericInst, tyAlias, tySink})[0].kind == tyObject:
|
||||
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[1].info, typ))
|
||||
asgnExpr.typ() = typ
|
||||
asgnExpr.typ = typ
|
||||
var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
|
||||
while true:
|
||||
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false)
|
||||
@@ -35,39 +35,38 @@ proc semAddr(c: PContext; n: PNode): PNode =
|
||||
let x = semExprWithType(c, n)
|
||||
if x.kind == nkSym:
|
||||
x.sym.flags.incl(sfAddrTaken)
|
||||
let aa = isAssignable(c, x)
|
||||
if aa notin {arLValue, arLocalLValue, arAddressableConst, arLentValue} and
|
||||
(aa != arDiscriminant or c.inUncheckedAssignSection <= 0):
|
||||
if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}:
|
||||
localError(c.config, n.info, errExprHasNoAddress)
|
||||
result.add x
|
||||
result.typ() = makePtrType(c, x.typ.skipTypes({tySink}))
|
||||
result.typ = makePtrType(c, x.typ)
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
let typExpr = semTypeOfImpl(c, n)
|
||||
var m = BiggestInt 1 # typeOfIter
|
||||
if n.len == 3:
|
||||
let mode = semConstExpr(c, n[2])
|
||||
if mode.kind != nkIntLit:
|
||||
localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
|
||||
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
|
||||
if typExpr.typ.kind == tyFromExpr:
|
||||
typExpr.typ.flags.incl tfNonConstExpr
|
||||
var t = typExpr.typ
|
||||
if t.kind == tyStatic:
|
||||
let base = t.skipTypes({tyStatic})
|
||||
if c.inGenericContext > 0 and base.containsGenericType:
|
||||
t = makeTypeFromExpr(c, copyTree(typExpr))
|
||||
t.flags.incl tfNonConstExpr
|
||||
else:
|
||||
t = base
|
||||
result.typ() = makeTypeDesc(c, t)
|
||||
result.typ = makeTypeDesc(c, typExpr.typ)
|
||||
|
||||
type
|
||||
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
|
||||
|
||||
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode
|
||||
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags, afterOverloading = false): PNode
|
||||
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||
|
||||
proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
result = newNodeI(nkBracketExpr, n.info)
|
||||
for i in 1..<n.len: result.add(n[i])
|
||||
result = semSubscript(c, result, flags, afterOverloading = true)
|
||||
result = semSubscript(c, result, flags)
|
||||
if result.isNil:
|
||||
let x = copyTree(n)
|
||||
x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
|
||||
@@ -77,23 +76,12 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
if a.typ != nil and a.typ.kind in {tyGenericParam, tyFromExpr}:
|
||||
# expression is compiled early in a generic body
|
||||
result = semGenericStmt(c, x)
|
||||
result.typ() = makeTypeFromExpr(c, copyTree(result))
|
||||
result.typ = makeTypeFromExpr(c, copyTree(result))
|
||||
result.typ.flags.incl tfNonConstExpr
|
||||
return
|
||||
let s = # extract sym from first arg
|
||||
if n.len > 1:
|
||||
if n[1].kind == nkSym: n[1].sym
|
||||
elif n[1].kind in nkSymChoices + {nkOpenSym} and n[1].len != 0:
|
||||
n[1][0].sym
|
||||
else: nil
|
||||
else: nil
|
||||
if s != nil and s.kind in routineKinds:
|
||||
# this is a failed generic instantiation
|
||||
# semSubscript should already error but this is better for cascading errors
|
||||
result = explicitGenericInstError(c, n)
|
||||
else:
|
||||
bracketNotFoundError(c, x, flags)
|
||||
result = errorNode(c, n)
|
||||
bracketNotFoundError(c, x, flags)
|
||||
#localError(c.config, n.info, "could not resolve: " & $n)
|
||||
result = errorNode(c, n)
|
||||
|
||||
proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
# rewrite `[]=`(a, i, x) back to ``a[i] = x``.
|
||||
@@ -201,15 +189,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
let preferStr = traitCall[2].strVal
|
||||
prefer = parseEnum[TPreferedDesc](preferStr)
|
||||
result = newStrNode(nkStrLit, operand.typeToString(prefer))
|
||||
result.typ() = getSysType(c.graph, traitCall[1].info, tyString)
|
||||
result.typ = getSysType(c.graph, traitCall[1].info, tyString)
|
||||
result.info = traitCall.info
|
||||
of "name", "$":
|
||||
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
|
||||
result.typ() = getSysType(c.graph, traitCall[1].info, tyString)
|
||||
result.typ = getSysType(c.graph, traitCall[1].info, tyString)
|
||||
result.info = traitCall.info
|
||||
of "arity":
|
||||
result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
|
||||
result.typ() = newType(tyInt, c.idgen, context)
|
||||
result.typ = newType(tyInt, c.idgen, context)
|
||||
result.info = traitCall.info
|
||||
of "genericHead":
|
||||
var arg = operand
|
||||
@@ -225,7 +213,10 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
of "stripGenericParams":
|
||||
result = uninstantiate(operand).toNode(traitCall.info)
|
||||
of "supportsCopyMem":
|
||||
result = newIntNodeT(toInt128(ord(supportsCopyMem(operand))), traitCall, c.idgen, c.graph)
|
||||
let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
|
||||
let complexObj = containsGarbageCollectedRef(t) or
|
||||
hasDestructor(t)
|
||||
result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.idgen, c.graph)
|
||||
of "hasDefaultValue":
|
||||
result = newIntNodeT(toInt128(ord(not operand.requiresInit)), traitCall, c.idgen, c.graph)
|
||||
of "isNamedTuple":
|
||||
@@ -233,17 +224,14 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
let cond = operand.kind == tyTuple and operand.n != nil
|
||||
result = newIntNodeT(toInt128(ord(cond)), traitCall, c.idgen, c.graph)
|
||||
of "tupleLen":
|
||||
var operand = operand.skipTypes({tyGenericInst, tyAlias})
|
||||
var operand = operand.skipTypes({tyGenericInst})
|
||||
assert operand.kind == tyTuple, $operand.kind
|
||||
result = newIntNodeT(toInt128(operand.len), traitCall, c.idgen, c.graph)
|
||||
of "distinctBase":
|
||||
var arg = operand.skipTypes(skippedTypes)
|
||||
var arg = operand.skipTypes({tyGenericInst})
|
||||
let rec = semConstExpr(c, traitCall[2]).intVal != 0
|
||||
while true:
|
||||
let distinctArg = arg.skipTypes(skippedTypes + {tyGenericInst})
|
||||
if distinctArg.kind != tyDistinct:
|
||||
break
|
||||
arg = distinctArg.base.skipTypes(skippedTypes)
|
||||
while arg.kind == tyDistinct:
|
||||
arg = arg.base.skipTypes(skippedTypes + {tyGenericInst})
|
||||
if not rec: break
|
||||
result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
|
||||
of "rangeBase":
|
||||
@@ -279,7 +267,7 @@ proc semOrd(c: PContext, n: PNode): PNode =
|
||||
discard
|
||||
else:
|
||||
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(parType, preferDesc))
|
||||
result.typ() = errorType(c)
|
||||
result.typ = errorType(c)
|
||||
|
||||
proc semBindSym(c: PContext, n: PNode): PNode =
|
||||
result = copyNode(n)
|
||||
@@ -395,7 +383,7 @@ proc semOf(c: PContext, n: PNode): PNode =
|
||||
message(c.config, n.info, hintConditionAlwaysTrue, renderTree(n))
|
||||
result = newIntNode(nkIntLit, 1)
|
||||
result.info = n.info
|
||||
result.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
result.typ = getSysType(c.graph, n.info, tyBool)
|
||||
return result
|
||||
elif diff == high(int):
|
||||
if commonSuperclass(a, b) == nil:
|
||||
@@ -404,10 +392,10 @@ proc semOf(c: PContext, n: PNode): PNode =
|
||||
message(c.config, n.info, hintConditionAlwaysFalse, renderTree(n))
|
||||
result = newIntNode(nkIntLit, 0)
|
||||
result.info = n.info
|
||||
result.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
result.typ = getSysType(c.graph, n.info, tyBool)
|
||||
else:
|
||||
localError(c.config, n.info, "'of' takes 2 arguments")
|
||||
n.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
n.typ = getSysType(c.graph, n.info, tyBool)
|
||||
result = n
|
||||
|
||||
proc semUnown(c: PContext; n: PNode): PNode =
|
||||
@@ -442,9 +430,9 @@ proc semUnown(c: PContext; n: PNode): PNode =
|
||||
result = t
|
||||
|
||||
result = copyTree(n[1])
|
||||
result.typ() = unownedType(c, result.typ)
|
||||
result.typ = unownedType(c, result.typ)
|
||||
# little hack for injectdestructors.nim (see bug #11350):
|
||||
#result[0].typ() = nil
|
||||
#result[0].typ = nil
|
||||
|
||||
proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym =
|
||||
# We need to do 2 things: Replace n.typ which is a 'ref T' by a 'var T' type.
|
||||
@@ -454,7 +442,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
|
||||
proc transform(c: PContext; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode =
|
||||
result = shallowCopy(n)
|
||||
if sameTypeOrNil(n.typ, old):
|
||||
result.typ() = fresh
|
||||
result.typ = fresh
|
||||
if n.kind == nkSym and n.sym == oldParam:
|
||||
result.sym = newParam
|
||||
for i in 0 ..< safeLen(n):
|
||||
@@ -465,7 +453,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
|
||||
result = copySym(orig, c.idgen)
|
||||
result.info = info
|
||||
result.flags.incl sfFromGeneric
|
||||
setOwner(result, orig)
|
||||
result.owner = orig
|
||||
let origParamType = orig.typ.firstParamType
|
||||
let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen)
|
||||
let oldParam = orig.typ.n[1].sym
|
||||
@@ -536,34 +524,32 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
|
||||
discard "already turned this one into a finalizer"
|
||||
else:
|
||||
if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move
|
||||
setOwner(fin, fin.instantiatedFrom)
|
||||
fin.owner = fin.instantiatedFrom
|
||||
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
|
||||
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
|
||||
selfSymNode.typ = fin.typ.firstParamType
|
||||
wrapperSym.flags.incl sfUsed
|
||||
|
||||
if fin.typ[1].skipTypes(abstractInst).kind != tyRef:
|
||||
bindTypeHook(c, fin, n, attachedDestructor)
|
||||
else:
|
||||
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
|
||||
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
|
||||
selfSymNode.typ() = fin.typ.firstParamType
|
||||
wrapperSym.flags.incl sfUsed
|
||||
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
|
||||
params = nkFormalParams.newTree(c.graph.emptyNode,
|
||||
newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
|
||||
fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
|
||||
),
|
||||
name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
|
||||
genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
|
||||
|
||||
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
|
||||
params = nkFormalParams.newTree(c.graph.emptyNode,
|
||||
newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
|
||||
fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
|
||||
),
|
||||
name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
|
||||
genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
|
||||
|
||||
var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
|
||||
setOwner(transFormedSym, fin)
|
||||
if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
|
||||
let origParamType = transFormedSym.ast[bodyPos][1].typ
|
||||
let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
|
||||
let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
|
||||
selfPtr.add transFormedSym.ast[bodyPos][1]
|
||||
selfPtr.typ() = selfSymbolType
|
||||
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor)
|
||||
var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
|
||||
transFormedSym.owner = fin
|
||||
if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
|
||||
let origParamType = transFormedSym.ast[bodyPos][1].typ
|
||||
let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
|
||||
let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
|
||||
selfPtr.add transFormedSym.ast[bodyPos][1]
|
||||
selfPtr.typ = selfSymbolType
|
||||
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
|
||||
# TODO: suppress var destructor warnings; if newFinalizer is not
|
||||
# TODO: deprecated, try to implement plain T destructor
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor, suppressVarDestructorWarning = true)
|
||||
result = addDefaultFieldForNew(c, n)
|
||||
|
||||
proc semPrivateAccess(c: PContext, n: PNode): PNode =
|
||||
@@ -605,10 +591,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mArrPut:
|
||||
result = semArrPut(c, n, flags)
|
||||
of mAsgn:
|
||||
case n[0].sym.name.s
|
||||
of "=", "=copy":
|
||||
if n[0].sym.name.s == "=":
|
||||
result = semAsgnOpr(c, n, nkAsgn)
|
||||
of "=sink":
|
||||
elif n[0].sym.name.s == "=sink":
|
||||
result = semAsgnOpr(c, n, nkSinkAsgn)
|
||||
else:
|
||||
result = semShallowCopy(c, n, flags)
|
||||
@@ -616,7 +601,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mTypeTrait: result = semTypeTraits(c, n)
|
||||
of mAstToStr:
|
||||
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph)
|
||||
result.typ() = getSysType(c.graph, n.info, tyString)
|
||||
result.typ = getSysType(c.graph, n.info, tyString)
|
||||
of mInstantiationInfo: result = semInstantiationInfo(c, n)
|
||||
of mOrd: result = semOrd(c, n)
|
||||
of mOf: result = semOf(c, n)
|
||||
@@ -629,7 +614,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
result = semDynamicBindSym(c, n)
|
||||
of mProcCall:
|
||||
result = n
|
||||
result.typ() = n[1].typ
|
||||
result.typ = n[1].typ
|
||||
of mDotDot:
|
||||
result = n
|
||||
of mPlugin:
|
||||
@@ -647,20 +632,49 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mNewFinalize:
|
||||
result = semNewFinalize(c, n)
|
||||
of mDestroy:
|
||||
result = replaceHookMagic(c, n, attachedDestructor)
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedDestructor)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
|
||||
if n[1].kind == nkSym and n[1].sym.kind == skParam and
|
||||
n[1].typ.kind == tyVar:
|
||||
result[1] = genDeref(n[1])
|
||||
else:
|
||||
result[1] = skipAddr(n[1])
|
||||
of mTrace:
|
||||
result = replaceHookMagic(c, n, attachedTrace)
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedTrace)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
of mDup:
|
||||
result = replaceHookMagic(c, n, attachedDup)
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedDup)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
if op.typ.len == 3:
|
||||
let boolLit = newIntLit(c.graph, n.info, 1)
|
||||
boolLit.typ = getSysType(c.graph, n.info, tyBool)
|
||||
result.add boolLit
|
||||
of mWasMoved:
|
||||
result = replaceHookMagic(c, n, attachedWasMoved)
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedWasMoved)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
let addrExp = newNodeIT(nkHiddenAddr, result[1].info, makePtrType(c, t))
|
||||
addrExp.add result[1]
|
||||
result[1] = addrExp
|
||||
of mUnown:
|
||||
result = semUnown(c, n)
|
||||
of mExists, mForall:
|
||||
result = semQuantifier(c, n)
|
||||
of mOld:
|
||||
result = semOld(c, n)
|
||||
of mSetLengthSeq, mSetLengthSeqUninit:
|
||||
of mSetLengthSeq:
|
||||
result = n
|
||||
let seqType = result[1].typ.skipTypes({tyPtr, tyRef, # in case we had auto-dereferencing
|
||||
tyVar, tyGenericInst, tyOwned, tySink,
|
||||
@@ -685,16 +699,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
result = n
|
||||
if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and
|
||||
expectedType.kind == tySequence and result.typ.elementType.kind == tyEmpty:
|
||||
result.typ() = expectedType # type inference for empty sequence # bug #21377
|
||||
result.typ = expectedType # type inference for empty sequence # bug #21377
|
||||
of mEnsureMove:
|
||||
result = n
|
||||
if n[1].kind in {nkStmtListExpr, nkBlockExpr,
|
||||
nkIfExpr, nkCaseStmt, nkTryStmt}:
|
||||
localError(c.config, n.info, "Nested expressions cannot be moved: '" & $n[1] & "'")
|
||||
of mMove:
|
||||
result = n
|
||||
if isCursor(n[1]):
|
||||
localError(c.config, n.info, errFailedMove,
|
||||
"cannot move cursor '" & $n[1] & "'; a cursor does not own its value")
|
||||
else:
|
||||
result = n
|
||||
|
||||
@@ -68,10 +68,6 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
|
||||
let assignment = initExpr[i]
|
||||
if assignment.kind != nkExprColonExpr:
|
||||
invalidObjConstr(c, assignment)
|
||||
elif nfPreventCg in assignment.flags:
|
||||
# this is an object constructor node generated by the VM and
|
||||
# this field is in an inactive case branch, just ignore it
|
||||
discard
|
||||
elif fieldId == considerQuotedIdent(c, assignment[0]).id:
|
||||
return assignment
|
||||
|
||||
@@ -83,7 +79,7 @@ proc semConstrField(c: PContext, flags: TExprFlags,
|
||||
if nfSkipFieldChecking in assignment[1].flags:
|
||||
discard
|
||||
elif not fieldVisible(c, field):
|
||||
localError(c.config, assignment[0].info,
|
||||
localError(c.config, initExpr.info,
|
||||
"the field '$1' is not accessible." % [field.name.s])
|
||||
return
|
||||
|
||||
@@ -192,7 +188,7 @@ proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode,
|
||||
newNodeIT(nkType, constrCtx.initExpr.info, asgnType)
|
||||
)
|
||||
asgnExpr.flags.incl nfSkipFieldChecking
|
||||
asgnExpr.typ() = recTyp
|
||||
asgnExpr.typ = recTyp
|
||||
defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr)
|
||||
|
||||
proc collectBranchFields(c: PContext, n: PNode, discriminatorVal: PNode,
|
||||
@@ -240,7 +236,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
let prevFields = fieldsPresentInBranch(selectedBranch)
|
||||
let currentFields = fieldsPresentInBranch(i)
|
||||
localError(c.config, constrCtx.initExpr.info,
|
||||
("The fields $1 and $2 cannot be initialized together, " &
|
||||
("The fields '$1' and '$2' cannot be initialized together, " &
|
||||
"because they are from conflicting branches in the case object.") %
|
||||
[prevFields, currentFields])
|
||||
result.status = initConflict
|
||||
@@ -469,20 +465,17 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
if t == nil:
|
||||
return localErrorNode(c, result, "object constructor needs an object type")
|
||||
|
||||
when false:
|
||||
# attempted type inference for generic object types,
|
||||
# doesn't work since n[0] isn't set and seems underspecified
|
||||
if t.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
|
||||
expectedType != nil and expectedType.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
|
||||
t = expectedType
|
||||
if t.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
|
||||
expectedType != nil and expectedType.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
|
||||
t = expectedType
|
||||
|
||||
t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if t.kind == tyRef:
|
||||
t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
# we have to watch out, there are also 'owned proc' types that can be used
|
||||
# multiple times as long as they don't have closures.
|
||||
result.typ.flags.incl tfHasOwned
|
||||
@@ -519,17 +512,13 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
invalidObjConstr(c, field)
|
||||
hasError = true
|
||||
continue
|
||||
elif nfPreventCg in field.flags:
|
||||
# this is an object constructor node generated by the VM and
|
||||
# this field is in an inactive case branch, just ignore it
|
||||
continue
|
||||
let id = considerQuotedIdent(c, field[0])
|
||||
# This node was not processed. There are two possible reasons:
|
||||
# 1) It was shadowed by a field with the same name on the left
|
||||
for j in 1..<i:
|
||||
let prevId = considerQuotedIdent(c, result[j][0])
|
||||
if prevId.id == id.id:
|
||||
localError(c.config, field[0].info, errFieldInitTwice % id.s)
|
||||
localError(c.config, field.info, errFieldInitTwice % id.s)
|
||||
hasError = true
|
||||
break
|
||||
# 2) No such field exists in the constructed type
|
||||
|
||||
@@ -407,9 +407,9 @@ proc transformSlices(g: ModuleGraph; idgen: IdGenerator; n: PNode): PNode =
|
||||
result = copyNode(n)
|
||||
var typ = newType(tyOpenArray, idgen, result.typ.owner)
|
||||
typ.add result.typ.elementType
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
let opSlice = newSymNode(createMagic(g, idgen, "slice", mSlice))
|
||||
opSlice.typ() = getSysType(g, n.info, tyInt)
|
||||
opSlice.typ = getSysType(g, n.info, tyInt)
|
||||
result.add opSlice
|
||||
result.add n[1]
|
||||
let slice = n[2].skipStmtList
|
||||
|
||||
@@ -82,12 +82,8 @@ type
|
||||
guards: TModel # nested guards
|
||||
locked: seq[PNode] # locked locations
|
||||
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
|
||||
canRaiseDefect: bool # defects are deliberately omitted from `exc`
|
||||
isInnerProc: bool
|
||||
inEnforcedNoSideEffects: bool
|
||||
isArrayIndexing: bool
|
||||
currentExceptType: PType
|
||||
unknownRaises: seq[(PSym, TLineInfo)]
|
||||
currOptions: TOptions
|
||||
optionsStack: seq[(TOptions, TNoteKinds)]
|
||||
config: ConfigRef
|
||||
@@ -128,11 +124,10 @@ proc collectObjectTree(graph: ModuleGraph, n: PNode) =
|
||||
else:
|
||||
graph.objectTree[root].add (depthLevel, typ)
|
||||
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit = false) =
|
||||
if typ == nil or (sfGeneratedOp in tracked.owner.flags and not explicit):
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
|
||||
if typ == nil or sfGeneratedOp in tracked.owner.flags:
|
||||
# don't create type bound ops for anything in a function with a `nodestroy` pragma
|
||||
# bug #21987
|
||||
# unless this is an explicit call, bug #24626
|
||||
return
|
||||
when false:
|
||||
let realType = typ.skipTypes(abstractInst)
|
||||
@@ -141,11 +136,6 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit
|
||||
createTypeBoundOps(tracked.graph, tracked.c, realType.lastSon, info)
|
||||
|
||||
createTypeBoundOps(tracked.graph, tracked.c, typ, info, tracked.c.idgen)
|
||||
for kind in TTypeAttachedOp:
|
||||
let op = getAttachedOp(tracked.graph, typ, kind)
|
||||
if op != nil and sfNeverRaises notin op.flags:
|
||||
tracked.canRaiseDefect = true
|
||||
break
|
||||
if (tfHasAsgn in typ.flags) or
|
||||
optSeqDestructors in tracked.config.globalOptions:
|
||||
tracked.owner.flags.incl sfInjectDestructors
|
||||
@@ -154,66 +144,6 @@ proc isLocalSym(a: PEffects, s: PSym): bool =
|
||||
s.typ != nil and (s.kind in {skLet, skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and
|
||||
sfGlobal notin s.flags and s.owner == a.owner
|
||||
|
||||
proc isRangeSupertype(conf: ConfigRef; wider, narrower: PType): bool =
|
||||
## Check if `wider` type fully contains `narrower` type
|
||||
## Returns true if narrower fits entirely within wider (safe conversion)
|
||||
if wider.isOrdinalType:
|
||||
let wideFirst = firstOrd(conf, wider)
|
||||
let wideLast = lastOrd(conf, wider)
|
||||
let narrowFirst = firstOrd(conf, narrower)
|
||||
let narrowLast = lastOrd(conf, narrower)
|
||||
result = narrowFirst >= wideFirst and narrowLast <= wideLast
|
||||
elif not narrower.isOrdinalType:
|
||||
let wideFirst = firstFloat(wider)
|
||||
let wideLast = lastFloat(wider)
|
||||
let narrowFirst = firstFloat(narrower)
|
||||
let narrowLast = lastFloat(narrower)
|
||||
result = narrowFirst >= wideFirst and narrowLast <= wideLast
|
||||
else:
|
||||
# int -> float ranges; warn
|
||||
result = false
|
||||
|
||||
proc shouldWarnRangeConversion(conf: ConfigRef; info: TLineInfo; formalType, argType: PType): bool =
|
||||
## Determine if an implicit range conversion should warn
|
||||
## We warn on conversions that are likely to cause panics
|
||||
let f = formalType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
let a = argType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if f.kind == tyRange:
|
||||
# Only warn if formal range doesn't fully contain argument range
|
||||
# Check if the ranges don't perfectly overlap
|
||||
if a.kind == tyInt and f.sym != nil and f.sym.owner != nil and
|
||||
sfSystemModule in f.sym.owner.flags and
|
||||
(f.sym.name.s == "Positive" or
|
||||
f.sym.name.s == "Natural"):
|
||||
# Positive and Natural are special cases that we do not warn on with
|
||||
# ImplicitRangeConversion, but may warn on with systemRangeConversion
|
||||
# if that warning is enabled.
|
||||
if conf.hasWarn(warnSystemRangeConversion):
|
||||
message(conf, info, warnSystemRangeConversion,
|
||||
typeToString(argType) & " -> " & typeToString(formalType))
|
||||
result = false
|
||||
else:
|
||||
result = not isRangeSupertype(conf, f, a)
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc conversionCanRaiseDefect(conf: ConfigRef; destType, sourceType: PType): bool =
|
||||
## Keep this in sync with the range checks introduced by `transformConv`.
|
||||
let
|
||||
dest = destType.skipTypes(abstractVarRange)
|
||||
source = sourceType.skipTypes(abstractVarRange)
|
||||
case dest.kind
|
||||
of tyInt..tyInt64, tyEnum, tyChar, tyUInt8..tyUInt32:
|
||||
if not source.isOrdinalType:
|
||||
result = dest.kind in tyInt..tyInt64
|
||||
else:
|
||||
result = firstOrd(conf, destType) > firstOrd(conf, sourceType) or
|
||||
lastOrd(conf, sourceType) > lastOrd(conf, destType)
|
||||
of tyFloat..tyFloat128:
|
||||
result = destType.skipTypes(abstractVar).kind == tyRange
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc lockLocations(a: PEffects; pragma: PNode) =
|
||||
if pragma.kind != nkExprColonExpr:
|
||||
localError(a.config, pragma.info, "locks pragma without argument")
|
||||
@@ -263,7 +193,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
|
||||
let dot = newNodeI(nkDotExpr, n.info, 2)
|
||||
dot[0] = n[0]
|
||||
dot[1] = newSymNode(g)
|
||||
dot.typ() = g.typ
|
||||
dot.typ = g.typ
|
||||
for L in a.locked:
|
||||
#if a.guards.sameSubexprs(dot, L): return
|
||||
if guards.sameTree(dot, L): return
|
||||
@@ -282,7 +212,6 @@ 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
|
||||
@@ -446,9 +375,7 @@ proc useVar(a: PEffects, n: PNode) =
|
||||
# If the variable is explicitly marked as .noinit. do not emit any error
|
||||
a.init.add s.id
|
||||
elif s.id notin a.init:
|
||||
if s.kind == skResult and tfRequiresInit in s.typ.flags:
|
||||
localError(a.config, n.info, "'result' requires explicit initialization")
|
||||
elif s.typ.requiresInit:
|
||||
if s.typ.requiresInit:
|
||||
message(a.config, n.info, warnProveInit, s.name.s)
|
||||
elif a.leftPartOfAsgn <= 0:
|
||||
if strictDefs in a.c.features:
|
||||
@@ -484,7 +411,7 @@ proc throws(tracked, n, orig: PNode) =
|
||||
if n.typ == nil or n.typ.kind != tyError:
|
||||
if orig != nil:
|
||||
let x = copyTree(orig)
|
||||
x.typ() = n.typ
|
||||
x.typ = n.typ
|
||||
tracked.add x
|
||||
else:
|
||||
tracked.add n
|
||||
@@ -499,12 +426,12 @@ proc excType(g: ModuleGraph; n: PNode): PType =
|
||||
|
||||
proc createRaise(g: ModuleGraph; n: PNode): PNode =
|
||||
result = newNode(nkType)
|
||||
result.typ() = getEbase(g, n.info)
|
||||
result.typ = getEbase(g, n.info)
|
||||
if not n.isNil: result.info = n.info
|
||||
|
||||
proc createTag(g: ModuleGraph; n: PNode): PNode =
|
||||
result = newNode(nkType)
|
||||
result.typ() = g.sysTypeFromName(n.info, "RootEffect")
|
||||
result.typ = g.sysTypeFromName(n.info, "RootEffect")
|
||||
if not n.isNil: result.info = n.info
|
||||
|
||||
proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
|
||||
@@ -516,38 +443,9 @@ proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
|
||||
if sameType(a.graph.excType(aa[i]), a.graph.excType(e)): return
|
||||
|
||||
if e.typ != nil:
|
||||
if isDefectException(e.typ):
|
||||
a.canRaiseDefect = true
|
||||
else:
|
||||
if not isDefectException(e.typ):
|
||||
throws(a.exc, e, comesFrom)
|
||||
|
||||
proc skipHiddenConv(n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
case result.kind
|
||||
of nkHiddenStdConv, nkHiddenSubConv:
|
||||
result = result[1]
|
||||
else: break
|
||||
|
||||
proc addRaiseEffectsFromExpr(a: PEffects, e, comesFrom: PNode) =
|
||||
if e.isNil:
|
||||
return
|
||||
case e.kind
|
||||
of nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr:
|
||||
if e.len > 0:
|
||||
addRaiseEffectsFromExpr(a, e.lastSon.skipHiddenConv, comesFrom)
|
||||
of nkIfExpr, nkIfStmt:
|
||||
for branch in items(e):
|
||||
if branch.len > 0:
|
||||
addRaiseEffectsFromExpr(a, branch.lastSon.skipHiddenConv, comesFrom)
|
||||
of nkCaseStmt:
|
||||
for i in 1..<e.len:
|
||||
let branch = e[i]
|
||||
if branch.len > 0:
|
||||
addRaiseEffectsFromExpr(a, branch.lastSon.skipHiddenConv, comesFrom)
|
||||
else:
|
||||
addRaiseEffect(a, e, comesFrom)
|
||||
|
||||
proc addTag(a: PEffects, e, comesFrom: PNode) =
|
||||
var aa = a.tags
|
||||
for i in 0..<aa.len:
|
||||
@@ -673,25 +571,11 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
|
||||
let b = n[i]
|
||||
if b.kind == nkExceptBranch:
|
||||
setLen(tracked.init, oldState)
|
||||
# If this except branch catches exactly one type, record it so an
|
||||
# empty `raise` inside the branch can be inferred as re-raising that
|
||||
# specific exception type instead of the generic `Exception`.
|
||||
var savedExcept: PType = tracked.currentExceptType
|
||||
var inferredExcept: PType = nil
|
||||
if b.len == 2:
|
||||
if b[0].isInfixAs():
|
||||
assert(b[0][1].kind == nkType)
|
||||
inferredExcept = b[0][1].typ
|
||||
else:
|
||||
assert(b[0].kind == nkType)
|
||||
inferredExcept = b[0].typ
|
||||
tracked.currentExceptType = inferredExcept
|
||||
for j in 0..<b.len - 1:
|
||||
if b[j].isInfixAs(): # skips initialization checks
|
||||
assert(b[j][2].kind == nkSym)
|
||||
tracked.init.add b[j][2].sym.id
|
||||
track(tracked, b[^1])
|
||||
tracked.currentExceptType = savedExcept
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i], bsNone)
|
||||
else:
|
||||
@@ -752,8 +636,6 @@ proc importedFromC(n: PNode): bool =
|
||||
proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||
let pragma = s.ast[pragmasPos]
|
||||
let spec = effectSpec(pragma, wRaises)
|
||||
if spec.isNil and sfForward in s.flags:
|
||||
tracked.unknownRaises.add (s, n.info)
|
||||
mergeRaises(tracked, spec, n)
|
||||
|
||||
let tagSpec = effectSpec(pragma, wTags)
|
||||
@@ -817,7 +699,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} =
|
||||
assert n.kind == nkEffectList
|
||||
n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil and n[forbiddenEffects] == nil)
|
||||
|
||||
proc isTrivial(caller: PNode): bool {.inline.} =
|
||||
proc isTrival(caller: PNode): bool {.inline.} =
|
||||
result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved, mSwap}
|
||||
|
||||
proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) =
|
||||
@@ -826,7 +708,7 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar
|
||||
let param = if formals != nil and formals.n != nil and argIndex < formals.n.len: formals.n[argIndex].sym else: nil
|
||||
# assume indirect calls are taken here:
|
||||
if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and
|
||||
not isTrivial(caller) and
|
||||
not isTrival(caller) and
|
||||
((param != nil and sfEffectsDelayed in param.flags) or laxEffects in tracked.c.config.legacyFeatures):
|
||||
|
||||
internalAssert tracked.config, op.n[0].kind == nkEffectList
|
||||
@@ -998,9 +880,8 @@ proc trackIf(tracked: PEffects, n: PNode) =
|
||||
setLen(tracked.guards.s, oldFacts)
|
||||
dec tracked.inIfStmt
|
||||
|
||||
proc trackBlock(tracked: PEffects, n: PNode; typ: PType) =
|
||||
proc trackBlock(tracked: PEffects, n: PNode) =
|
||||
if n.kind in {nkStmtList, nkStmtListExpr}:
|
||||
let myBlock = tracked.currentBlock
|
||||
var oldState = -1
|
||||
for i in 0..<n.len:
|
||||
if hasSubnodeWith(n[i], nkBreakStmt):
|
||||
@@ -1012,14 +893,6 @@ proc trackBlock(tracked: PEffects, n: PNode; typ: PType) =
|
||||
if oldState < 0: oldState = tracked.init.len
|
||||
track(tracked, n[i])
|
||||
if oldState > 0: setLen(tracked.init, oldState)
|
||||
if typ != nil and typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs}:
|
||||
let last = lastSon(n)
|
||||
let root = getRoot(last)
|
||||
if root != nil:
|
||||
let owner = tracked.scopes.getOrDefault(root.id, -1)
|
||||
if owner >= 0:
|
||||
localError(tracked.config, last.info, "'" & renderTree(last) & "' borrows from location '" & root.name.s &
|
||||
"' which does not live long enough")
|
||||
else:
|
||||
track(tracked, n)
|
||||
|
||||
@@ -1092,7 +965,7 @@ proc checkForSink(tracked: PEffects; n: PNode) =
|
||||
proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
|
||||
when defined(nimsuggest):
|
||||
proc internalMarkCaughtExceptions(tracked: PEffects; q: var SuggestFileSymbolDatabase; info: TLineInfo) =
|
||||
var si = q.findSymInfoIndex(info, true)
|
||||
var si = q.findSymInfoIndex(info)
|
||||
if si != -1:
|
||||
q.caughtExceptionsSet[si] = true
|
||||
for w1 in tracked.caughtExceptions.nodes:
|
||||
@@ -1102,25 +975,6 @@ proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s:
|
||||
if optIdeExceptionInlayHints in tracked.config.globalOptions:
|
||||
internalMarkCaughtExceptions(tracked, g.suggestSymbols.mgetOrPut(info.fileIndex, newSuggestFileSymbolDatabase(info.fileIndex, true)), info)
|
||||
|
||||
proc findHookKind(name: string): (bool, TTypeAttachedOp) =
|
||||
case name.normalize
|
||||
of "=wasmoved":
|
||||
result = (true, attachedWasMoved)
|
||||
of "=destroy":
|
||||
result = (true, attachedDestructor)
|
||||
of "=copy", "=":
|
||||
result = (true, attachedAsgn)
|
||||
of "=dup":
|
||||
result = (true, attachedDup)
|
||||
of "=sink":
|
||||
result = (true, attachedSink)
|
||||
of "=trace":
|
||||
result = (true, attachedTrace)
|
||||
of "=deepcopy":
|
||||
result = (true, attachedDeepCopy)
|
||||
else:
|
||||
result = (false, attachedWasMoved)
|
||||
|
||||
proc trackCall(tracked: PEffects; n: PNode) =
|
||||
template gcsafeAndSideeffectCheck() =
|
||||
if notGcSafe(op) and not importedFromC(a):
|
||||
@@ -1134,32 +988,6 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
markSideEffect(tracked, a, n.info)
|
||||
# p's effects are ours too:
|
||||
var a = n[0]
|
||||
if a.kind == nkSym:
|
||||
let s = a.sym
|
||||
case s.magic
|
||||
of mNone:
|
||||
if {sfNeverRaises, sfImportc, sfCompilerProc} * s.flags == {} and
|
||||
(sfSystemModule notin getModule(s).flags or
|
||||
sfSystemRaisesDefect in s.flags):
|
||||
tracked.canRaiseDefect = true
|
||||
of mUnaryMinusI..mAbsI, mAddI..mPred:
|
||||
if optOverflowCheck in tracked.currOptions:
|
||||
tracked.canRaiseDefect = true
|
||||
of mInc, mDec:
|
||||
let typ = n[1].typ.skipTypes({tyGenericInst, tyAlias, tySink,
|
||||
tyVar, tyLent, tyRange, tyDistinct})
|
||||
if optOverflowCheck in tracked.currOptions and
|
||||
typ.kind notin {tyUInt..tyUInt64}:
|
||||
tracked.canRaiseDefect = true
|
||||
of mDivU, mModU:
|
||||
tracked.canRaiseDefect = true
|
||||
of mAddF64..mDivF64:
|
||||
if {optNaNCheck, optInfCheck} * tracked.currOptions != {}:
|
||||
tracked.canRaiseDefect = true
|
||||
else:
|
||||
discard
|
||||
else:
|
||||
tracked.canRaiseDefect = true
|
||||
#if canRaise(a):
|
||||
# echo "this can raise ", tracked.config $ n.info
|
||||
let op = a.typ
|
||||
@@ -1204,9 +1032,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
else:
|
||||
if laxEffects notin tracked.c.config.legacyFeatures and a.kind == nkSym and
|
||||
a.sym.kind in routineKinds:
|
||||
let (isHook, opKind) = findHookKind(a.sym.name.s)
|
||||
if (not isHook) or opKind notin {attachedAsgn, attachedSink, attachedDup}:
|
||||
propagateEffects(tracked, n, a.sym)
|
||||
propagateEffects(tracked, n, a.sym)
|
||||
else:
|
||||
mergeRaises(tracked, effectList[exceptionEffects], n)
|
||||
mergeTags(tracked, effectList[tagEffects], n)
|
||||
@@ -1237,17 +1063,18 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
checkBounds(tracked, n[1], n[2])
|
||||
|
||||
|
||||
var n = n
|
||||
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
|
||||
tracked.owner.kind != skMacro:
|
||||
var (isHook, opKind) = findHookKind(a.sym.name.s)
|
||||
if isHook:
|
||||
var opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
|
||||
if a.sym.name.s == "=": opKind = attachedAsgn.int
|
||||
if opKind != -1:
|
||||
# rebind type bounds operations after createTypeBoundOps call
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
||||
if a.sym != getAttachedOp(tracked.graph, t, opKind):
|
||||
createTypeBoundOps(tracked, t, n.info, explicit = true)
|
||||
# replace builtin hooks with lifted ones
|
||||
n = replaceHookMagic(tracked.c, n, opKind)
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar})
|
||||
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
|
||||
createTypeBoundOps(tracked, t, n.info)
|
||||
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
|
||||
if op != nil:
|
||||
n[0].sym = op
|
||||
|
||||
if op != nil and op.kind == tyProc:
|
||||
for i in 1..<min(n.safeLen, op.signatureLen):
|
||||
@@ -1281,18 +1108,14 @@ type
|
||||
PragmaBlockContext = object
|
||||
oldLocked: int
|
||||
enforcedGcSafety, enforceNoSideEffects: bool
|
||||
oldInEnforcedGcSafe, oldInEnforcedNoSideEffects: bool
|
||||
oldExc, oldTags, oldForbids: int
|
||||
exc, tags, forbids: PNode
|
||||
excSource, tagsSource, forbidsSource: PNode
|
||||
|
||||
proc createBlockContext(tracked: PEffects): PragmaBlockContext =
|
||||
var oldForbidsLen = 0
|
||||
if tracked.forbids != nil: oldForbidsLen = tracked.forbids.len
|
||||
result = PragmaBlockContext(oldLocked: tracked.locked.len,
|
||||
enforcedGcSafety: false, enforceNoSideEffects: false,
|
||||
oldInEnforcedGcSafe: tracked.inEnforcedGcSafe,
|
||||
oldInEnforcedNoSideEffects: tracked.inEnforcedNoSideEffects,
|
||||
oldExc: tracked.exc.len, oldTags: tracked.tags.len,
|
||||
oldForbids: oldForbidsLen)
|
||||
|
||||
@@ -1301,27 +1124,25 @@ proc applyBlockContext(tracked: PEffects, bc: PragmaBlockContext) =
|
||||
if bc.enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
|
||||
|
||||
proc unapplyBlockContext(tracked: PEffects; bc: PragmaBlockContext) =
|
||||
if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = bc.oldInEnforcedGcSafe
|
||||
if bc.enforceNoSideEffects:
|
||||
tracked.inEnforcedNoSideEffects = bc.oldInEnforcedNoSideEffects
|
||||
if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = false
|
||||
if bc.enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
|
||||
setLen(tracked.locked, bc.oldLocked)
|
||||
if bc.exc != nil:
|
||||
# beware that 'raises: []' is very different from not saying
|
||||
# anything about 'raises' in the 'cast' at all. Same applies for 'tags'.
|
||||
setLen(tracked.exc.sons, bc.oldExc)
|
||||
for e in bc.exc:
|
||||
addRaiseEffect(tracked, e, if bc.excSource != nil: bc.excSource else: e)
|
||||
addRaiseEffect(tracked, e, e)
|
||||
if bc.tags != nil:
|
||||
setLen(tracked.tags.sons, bc.oldTags)
|
||||
for t in bc.tags:
|
||||
addTag(tracked, t, if bc.tagsSource != nil: bc.tagsSource else: t)
|
||||
addTag(tracked, t, t)
|
||||
if bc.forbids != nil:
|
||||
setLen(tracked.forbids.sons, bc.oldForbids)
|
||||
for t in bc.forbids:
|
||||
addNotTag(tracked, t, if bc.forbidsSource != nil: bc.forbidsSource else: t)
|
||||
addNotTag(tracked, t, t)
|
||||
|
||||
proc castBlock(tracked: PEffects, castPragma: PNode, bc: var PragmaBlockContext) =
|
||||
let pragma = castPragma[1]
|
||||
proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
|
||||
case whichPragma(pragma)
|
||||
of wGcSafe:
|
||||
bc.enforcedGcSafety = true
|
||||
@@ -1334,7 +1155,6 @@ proc castBlock(tracked: PEffects, castPragma: PNode, bc: var PragmaBlockContext)
|
||||
else:
|
||||
bc.tags = newNodeI(nkArgList, pragma.info)
|
||||
bc.tags.add n
|
||||
bc.tagsSource = castPragma
|
||||
of wForbids:
|
||||
let n = pragma[1]
|
||||
if n.kind in {nkCurly, nkBracket}:
|
||||
@@ -1342,7 +1162,6 @@ proc castBlock(tracked: PEffects, castPragma: PNode, bc: var PragmaBlockContext)
|
||||
else:
|
||||
bc.forbids = newNodeI(nkArgList, pragma.info)
|
||||
bc.forbids.add n
|
||||
bc.forbidsSource = castPragma
|
||||
of wRaises:
|
||||
let n = pragma[1]
|
||||
if n.kind in {nkCurly, nkBracket}:
|
||||
@@ -1350,7 +1169,6 @@ proc castBlock(tracked: PEffects, castPragma: PNode, bc: var PragmaBlockContext)
|
||||
else:
|
||||
bc.exc = newNodeI(nkArgList, pragma.info)
|
||||
bc.exc.add n
|
||||
bc.excSource = castPragma
|
||||
of wUncheckedAssign:
|
||||
discard "handled in sempass1"
|
||||
else:
|
||||
@@ -1387,14 +1205,12 @@ proc allowCStringConv(n: PNode): bool =
|
||||
|
||||
proc track(tracked: PEffects, n: PNode) =
|
||||
case n.kind
|
||||
of nkTypeOfExpr:
|
||||
discard "typeof() never evaluates its operand; not a definite-assignment use"
|
||||
of nkSym:
|
||||
useVar(tracked, n)
|
||||
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
|
||||
tracked.owner.flags.incl sfInjectDestructors
|
||||
# bug #15038: ensure consistency
|
||||
if n.typ == nil or (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) and
|
||||
n.typ.kind notin {tyVar, tyLent}:
|
||||
@@ -1402,11 +1218,10 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
else:
|
||||
track(tracked, n[0])
|
||||
of nkRaiseStmt:
|
||||
tracked.canRaiseDefect = true
|
||||
if n[0].kind != nkEmpty:
|
||||
n[0].info = n.info
|
||||
#throws(tracked.exc, n[0])
|
||||
addRaiseEffectsFromExpr(tracked, n[0], n)
|
||||
addRaiseEffect(tracked, n[0], n)
|
||||
for i in 0..<n.safeLen:
|
||||
track(tracked, n[i])
|
||||
createTypeBoundOps(tracked, n[0].typ, n.info)
|
||||
@@ -1414,14 +1229,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
# A `raise` with no arguments means we're going to re-raise the exception
|
||||
# being handled or, if outside of an `except` block, a `ReraiseDefect`.
|
||||
# Here we add a `Exception` tag in order to cover both the cases.
|
||||
if tracked.currentExceptType != nil:
|
||||
var en = newNode(nkType)
|
||||
en.typ = tracked.currentExceptType
|
||||
en.info = n.info
|
||||
addRaiseEffect(tracked, en, nil)
|
||||
createTypeBoundOps(tracked, tracked.currentExceptType, n.info)
|
||||
else:
|
||||
addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
|
||||
addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
|
||||
of nkCallKinds:
|
||||
trackCall(tracked, n)
|
||||
of nkDotExpr:
|
||||
@@ -1431,8 +1239,6 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
for i in 0..<n.len: track(tracked, n[i])
|
||||
tracked.leftPartOfAsgn = oldLeftPartOfAsgn
|
||||
of nkCheckedFieldExpr:
|
||||
if optFieldCheck in tracked.currOptions:
|
||||
tracked.canRaiseDefect = true
|
||||
track(tracked, n[0])
|
||||
if tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features:
|
||||
checkFieldAccess(tracked.guards, n, tracked.config, strictCaseObjects in tracked.c.features)
|
||||
@@ -1495,18 +1301,13 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
let last = lastSon(child)
|
||||
track(tracked, last)
|
||||
of nkCaseStmt: trackCase(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], n.typ)
|
||||
of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
|
||||
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
|
||||
of nkWhileStmt:
|
||||
# 'while true' loop?
|
||||
inc tracked.currentBlock
|
||||
if isTrue(n[0]):
|
||||
trackBlock(tracked, n[1], nil)
|
||||
trackBlock(tracked, n[1])
|
||||
else:
|
||||
# loop may never execute:
|
||||
let oldState = tracked.init.len
|
||||
@@ -1609,7 +1410,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
of wNoSideEffect:
|
||||
bc.enforceNoSideEffects = true
|
||||
of wCast:
|
||||
castBlock(tracked, pragmaList[i], bc)
|
||||
castBlock(tracked, pragmaList[i][1], bc)
|
||||
else:
|
||||
discard
|
||||
applyBlockContext(tracked, bc)
|
||||
@@ -1630,9 +1431,6 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
if tracked.owner.kind != skMacro:
|
||||
createTypeBoundOps(tracked, n.typ, n.info)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
if optRangeCheck in tracked.currOptions and
|
||||
conversionCanRaiseDefect(tracked.config, n.typ, n[1].typ):
|
||||
tracked.canRaiseDefect = true
|
||||
if n.kind in {nkHiddenStdConv, nkHiddenSubConv} and
|
||||
n.typ.skipTypes(abstractInst).kind == tyCstring and
|
||||
not allowCStringConv(n[1]):
|
||||
@@ -1644,13 +1442,6 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
message(tracked.config, n.info, warnPtrToCstringConv,
|
||||
$n[1].typ)
|
||||
|
||||
# Check for implicit range conversions. Compile-time constants are already
|
||||
# fully known here, so only non-constant values need the downsizing warning.
|
||||
if n.kind == nkHiddenStdConv and (not tracked.isArrayIndexing) and
|
||||
shouldWarnRangeConversion(tracked.config, n.info, n.typ, n[1].typ) and
|
||||
getConstExpr(tracked.ownerModule, n[1], tracked.c.idgen, tracked.graph) == nil:
|
||||
message(tracked.config, n.info, warnImplicitRangeConversion,
|
||||
typeToString(n[1].typ) & " -> " & typeToString(n.typ))
|
||||
|
||||
let t = n.typ.skipTypes(abstractInst)
|
||||
if t.kind == tyEnum:
|
||||
@@ -1670,11 +1461,6 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
if optStaticBoundsCheck in tracked.currOptions:
|
||||
checkRange(tracked, n[1], n.typ)
|
||||
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
|
||||
if n.kind in {nkObjUpConv, nkObjDownConv}:
|
||||
if optObjCheck in tracked.currOptions:
|
||||
tracked.canRaiseDefect = true
|
||||
elif optRangeCheck in tracked.currOptions:
|
||||
tracked.canRaiseDefect = true
|
||||
if n.len == 1:
|
||||
track(tracked, n[0])
|
||||
if tracked.owner.kind != skMacro:
|
||||
@@ -1689,19 +1475,12 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
if tracked.owner.kind != skMacro:
|
||||
createTypeBoundOps(tracked, n.typ, n.info)
|
||||
of nkBracketExpr:
|
||||
if optBoundsCheck in tracked.currOptions:
|
||||
tracked.canRaiseDefect = true
|
||||
if optStaticBoundsCheck in tracked.currOptions and n.len == 2:
|
||||
if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
|
||||
checkBounds(tracked, n[0], n[1])
|
||||
track(tracked, n[0])
|
||||
dec tracked.leftPartOfAsgn
|
||||
for i in 1 ..< n.len:
|
||||
if i == 1:
|
||||
tracked.isArrayIndexing = true
|
||||
track(tracked, n[i])
|
||||
if i == 1:
|
||||
tracked.isArrayIndexing = false
|
||||
for i in 1 ..< n.len: track(tracked, n[i])
|
||||
inc tracked.leftPartOfAsgn
|
||||
of nkError:
|
||||
localError(tracked.config, n.info, errorToString(tracked.config, n))
|
||||
@@ -1719,7 +1498,7 @@ proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
|
||||
|
||||
proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg: string, hints: bool;
|
||||
effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.};
|
||||
hintsArg: PNode = nil; isForbids: bool = false; unknownRaises: seq[(PSym, TLineInfo)] = @[]) =
|
||||
hintsArg: PNode = nil; isForbids: bool = false) =
|
||||
# check that any real exception is listed in 'spec'; mark those as used;
|
||||
# report any unused exception
|
||||
var used = initIntSet()
|
||||
@@ -1736,8 +1515,6 @@ proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg:
|
||||
pushInfoContext(g.config, spec.info)
|
||||
var rr = if r.kind == nkRaiseStmt: r[0] else: r
|
||||
while rr.kind in {nkStmtList, nkStmtListExpr} and rr.len > 0: rr = rr.lastSon
|
||||
for (s, info) in unknownRaises.items:
|
||||
message(g.config, info, hintUnknownRaises, s.name.s)
|
||||
message(g.config, r.info, if emitWarnings: warnEffect else: errGenerated,
|
||||
renderTree(rr) & " " & msg & typeToString(r.typ))
|
||||
popInfoContext(g.config)
|
||||
@@ -1786,18 +1563,13 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) =
|
||||
elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
|
||||
effects[exceptionEffects] = newNodeI(nkArgList, effects.info)
|
||||
|
||||
let forbidsSpec = effectSpec(n, wForbids)
|
||||
let tagsSpec = effectSpec(n, wTags)
|
||||
if not isNil(tagsSpec):
|
||||
effects[tagEffects] = tagsSpec
|
||||
elif not isNil(forbidsSpec):
|
||||
# `.forbids` without `.tags` still declares a known empty tag set.
|
||||
# Leaving this as nil would mean "unknown tags", which later widens
|
||||
# indirect calls to `RootEffect`.
|
||||
effects[tagEffects] = newNodeI(nkArgList, effects.info)
|
||||
elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
|
||||
effects[tagEffects] = newNodeI(nkArgList, effects.info)
|
||||
|
||||
let forbidsSpec = effectSpec(n, wForbids)
|
||||
if not isNil(forbidsSpec):
|
||||
effects[forbiddenEffects] = forbidsSpec
|
||||
elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
|
||||
@@ -1866,15 +1638,9 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod}:
|
||||
var res = s.ast[resultPos].sym # get result symbol
|
||||
t.scopes[res.id] = t.currentBlock
|
||||
if sfNoInit in s.flags:
|
||||
# marks result "noinit"
|
||||
incl res.flags, sfNoInit
|
||||
|
||||
track(t, body)
|
||||
|
||||
if t.exc.len == 0 and not t.canRaiseDefect:
|
||||
s.flags.incl sfNeverRaises
|
||||
|
||||
if s.kind != skMacro:
|
||||
let params = s.typ.n
|
||||
for i in 1..<params.len:
|
||||
@@ -1884,14 +1650,13 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
(t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
(isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
|
||||
createTypeBoundOps(t, typ, param.info)
|
||||
if isOutParam(typ) and param.id notin t.init and s.magic == mNone:
|
||||
if isOutParam(typ) and param.id notin t.init:
|
||||
message(g.config, param.info, warnProveInit, param.name.s)
|
||||
|
||||
if not isEmptyType(s.typ.returnType) and
|
||||
(s.typ.returnType.requiresInit or s.typ.returnType.skipTypes(abstractInst).kind == tyVar or
|
||||
strictDefs in c.features) and
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone and
|
||||
sfNoInit notin s.flags:
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
|
||||
var res = s.ast[resultPos].sym # get result symbol
|
||||
if res.id notin t.init and breaksBlock(body) != bsNoReturn:
|
||||
if tfRequiresInit in s.typ.returnType.flags:
|
||||
@@ -1903,7 +1668,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
if not isNil(raisesSpec):
|
||||
let useWarning = s.name.s == "=destroy"
|
||||
checkRaisesSpec(g, useWarning, raisesSpec, t.exc, "can raise an unlisted exception: ",
|
||||
hints=on, subtypeRelation, hintsArg=s.ast[0], unknownRaises = t.unknownRaises)
|
||||
hints=on, subtypeRelation, hintsArg=s.ast[0])
|
||||
# after the check, use the formal spec:
|
||||
effects[exceptionEffects] = raisesSpec
|
||||
else:
|
||||
|
||||
@@ -112,11 +112,11 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
dec(c.p.nestedLoopCounter)
|
||||
closeScope(c)
|
||||
if n[1].typ == c.enforceVoidContext:
|
||||
result.typ() = c.enforceVoidContext
|
||||
result.typ = c.enforceVoidContext
|
||||
elif efInTypeof in flags:
|
||||
result.typ() = n[1].typ
|
||||
result.typ = n[1].typ
|
||||
elif implicitlyDiscardable(n[1]):
|
||||
result[1].typ() = c.enforceVoidContext
|
||||
result[1].typ = c.enforceVoidContext
|
||||
|
||||
proc semProc(c: PContext, n: PNode): PNode
|
||||
|
||||
@@ -275,7 +275,7 @@ proc fixNilType(c: PContext; n: PNode) =
|
||||
elif n.kind in {nkStmtList, nkStmtListExpr}:
|
||||
n.transitionSonsKind(nkStmtList)
|
||||
for it in n: fixNilType(c, it)
|
||||
n.typ() = nil
|
||||
n.typ = nil
|
||||
|
||||
proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
|
||||
if c.matchedConcept != nil or efInTypeof in flags: return
|
||||
@@ -331,14 +331,14 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil):
|
||||
for it in n: discardCheck(c, it.lastSon, flags)
|
||||
result.transitionSonsKind(nkIfStmt)
|
||||
# propagate any enforced VoidContext:
|
||||
if typ == c.enforceVoidContext: result.typ() = c.enforceVoidContext
|
||||
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
|
||||
else:
|
||||
for it in n:
|
||||
let j = it.len-1
|
||||
if not endsInNoReturn(it[j]):
|
||||
it[j] = fitNode(c, typ, it[j], it[j].info)
|
||||
result.transitionSonsKind(nkIfExpr)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
|
||||
proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
var check = initIntSet()
|
||||
@@ -394,9 +394,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
elif a.len == 1:
|
||||
# count number of ``except: body`` blocks
|
||||
inc catchAllExcepts
|
||||
if noPanicOnExcept in c.graph.config.legacyFeatures:
|
||||
message(c.config, a.info, warnBareExcept,
|
||||
"The bare except clause is deprecated; use `except CatchableError:` instead")
|
||||
message(c.config, a.info, warnBareExcept,
|
||||
"The bare except clause is deprecated; use `except CatchableError:` instead")
|
||||
else:
|
||||
# support ``except KeyError, ValueError, ... : body``
|
||||
if catchAllExcepts > 0:
|
||||
@@ -439,7 +438,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
discardCheck(c, n[0], flags)
|
||||
for i in 1..<n.len: discardCheck(c, n[i].lastSon, flags)
|
||||
if typ == c.enforceVoidContext:
|
||||
result.typ() = c.enforceVoidContext
|
||||
result.typ = c.enforceVoidContext
|
||||
else:
|
||||
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
|
||||
if not endsInNoReturn(n[0]):
|
||||
@@ -449,7 +448,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
let j = it.len-1
|
||||
if not endsInNoReturn(it[j]):
|
||||
it[j] = fitNode(c, typ, it[j], it[j].info)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
|
||||
proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
|
||||
result = fitNode(c, typ, n, n.info)
|
||||
@@ -458,7 +457,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
|
||||
if r1.kind in {nkCharLit..nkUInt64Lit} and typ.skipTypes(abstractRange).kind in {tyFloat..tyFloat128}:
|
||||
result = newFloatNode(nkFloatLit, BiggestFloat r1.intVal)
|
||||
result.info = n.info
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
if not floatRangeCheck(result.floatVal, typ):
|
||||
localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)])
|
||||
elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring:
|
||||
@@ -493,8 +492,19 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = tru
|
||||
incl(result.flags, sfGlobal)
|
||||
result.options = c.config.options
|
||||
|
||||
proc getLineInfo(n: PNode): TLineInfo =
|
||||
case n.kind
|
||||
of nkPostfix:
|
||||
if len(n) > 1:
|
||||
return getLineInfo(n[1])
|
||||
of nkAccQuoted, nkPragmaExpr:
|
||||
if len(n) > 0:
|
||||
return getLineInfo(n[0])
|
||||
else:
|
||||
discard
|
||||
result = n.info
|
||||
let info = getLineInfo(n)
|
||||
if reportToNimsuggest:
|
||||
let info = getLineInfo(n)
|
||||
suggestSym(c.graph, info, result, c.graph.usageSym)
|
||||
|
||||
proc checkNilable(c: PContext; v: PSym) =
|
||||
@@ -595,45 +605,18 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
|
||||
obj.n.add newSymNode(field)
|
||||
n[0] = makeDeref x
|
||||
n[1] = newSymNode(field)
|
||||
n.typ() = field.typ
|
||||
n.typ = field.typ
|
||||
else:
|
||||
localError(c.config, n.info, "implicit object field construction " &
|
||||
"requires a .partial object, but got " & typeToString(obj))
|
||||
else:
|
||||
localError(c.config, n.info, "nkDotNode requires 2 children")
|
||||
|
||||
proc checkDefineType(c: PContext; v: PSym; t: PType) =
|
||||
# see semfold.foldDefine for acceptable types
|
||||
let typeKinds =
|
||||
case v.magic
|
||||
of mStrDefine: {tyString, tyCstring}
|
||||
# this used to be not typechecked, so anything that accepts int nodes for compatbility:
|
||||
of mIntDefine: {tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyChar, tyEnum}
|
||||
of mBoolDefine: {tyBool}
|
||||
of mGenericDefine: {tyString, tyCstring, tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyEnum}
|
||||
else: raiseAssert("unreachable")
|
||||
var skipped = abstractVarRange
|
||||
if v.magic == mGenericDefine:
|
||||
# no distinct types for generic define
|
||||
skipped.excl tyDistinct
|
||||
if t.skipTypes(skipped).kind notin typeKinds:
|
||||
let name =
|
||||
case v.magic
|
||||
of mStrDefine: "strdefine"
|
||||
of mIntDefine: "intdefine"
|
||||
of mBoolDefine: "booldefine"
|
||||
of mGenericDefine: "define"
|
||||
else: raiseAssert("unreachable")
|
||||
localError(c.config, v.info, "unsupported type for constant '" & v.name.s &
|
||||
"' with ." & name & " pragma: " & typeToString(t))
|
||||
|
||||
proc setVarType(c: PContext; v: PSym, typ: PType) =
|
||||
if v.typ != nil and not sameTypeOrNil(v.typ, typ):
|
||||
localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" &
|
||||
v.name.s & "': previous type was: " & typeToString(v.typ, preferDesc) &
|
||||
"; new type is: " & typeToString(typ, preferDesc))
|
||||
if v.kind == skConst and v.magic in {mGenericDefine, mIntDefine, mStrDefine, mBoolDefine}:
|
||||
checkDefineType(c, v, typ)
|
||||
v.typ = typ
|
||||
|
||||
proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
|
||||
@@ -726,33 +709,22 @@ template isLocalSym(sym: PSym): bool =
|
||||
sym.typ.kind == tyTypeDesc or
|
||||
sfCompileTime in sym.flags) or
|
||||
sym.kind in {skProc, skFunc, skIterator} and
|
||||
sfGlobal notin sym.flags and sym.typ.callConv == ccClosure
|
||||
sfGlobal notin sym.flags
|
||||
|
||||
template isLocalVarSym(n: PNode): bool =
|
||||
n.kind == nkSym and isLocalSym(n.sym)
|
||||
|
||||
proc usesLocalVar(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
result = isLocalSym(n.sym)
|
||||
of nkCallKinds, nkObjConstr:
|
||||
result = false
|
||||
for i in 1 ..< n.len:
|
||||
if usesLocalVar(n[i]):
|
||||
result = false
|
||||
for z in 1 ..< n.len:
|
||||
if n[z].isLocalVarSym:
|
||||
return true
|
||||
elif n[z].kind in nkCallKinds:
|
||||
if usesLocalVar(n[z]):
|
||||
return true
|
||||
of nkTupleConstr, nkPar, nkBracket, nkCurly:
|
||||
result = false
|
||||
for i in 0 ..< n.len:
|
||||
if usesLocalVar(n[i]):
|
||||
return true
|
||||
of nkDotExpr, nkCheckedFieldExpr,
|
||||
nkBracketExpr, nkAddr, nkHiddenAddr,
|
||||
nkObjDownConv, nkObjUpConv:
|
||||
result = usesLocalVar(n[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkCast, nkExprColonExpr:
|
||||
result = usesLocalVar(n[1])
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc globalVarInitCheck(c: PContext, n: PNode) =
|
||||
if usesLocalVar(n):
|
||||
if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n):
|
||||
localError(c.config, n.info, errCannotAssignToGlobal)
|
||||
|
||||
const
|
||||
@@ -936,7 +908,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
if sfGenSym notin v.flags:
|
||||
if not isDiscardUnderscore(v): addInterfaceDecl(c, v)
|
||||
else:
|
||||
if v.owner == nil: setOwner(v, c.p.owner)
|
||||
if v.owner == nil: v.owner = c.p.owner
|
||||
when oKeepVariableNames:
|
||||
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
|
||||
else:
|
||||
@@ -1054,7 +1026,7 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
when defined(nimsuggest):
|
||||
v.hasUserSpecifiedType = hasUserSpecifiedType
|
||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
styleCheckDef(c, v)
|
||||
onDef(a[j].info, v)
|
||||
|
||||
@@ -1096,12 +1068,7 @@ proc symForVar(c: PContext, n: PNode): PSym =
|
||||
proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
result = n
|
||||
let iterBase = n[^2].typ
|
||||
let iterType =
|
||||
if iterBase.kind == tyIterable:
|
||||
iterBase.skipModifier
|
||||
else:
|
||||
skipTypes(iterBase, {tyAlias, tySink, tyOwned})
|
||||
var iter = skipTypes(iterType, {tyGenericInst})
|
||||
var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
var iterAfterVarLent = iter.skipTypes({tyGenericInst, tyAlias, tyLent, tyVar})
|
||||
# n.len == 3 means that there is one for loop variable
|
||||
# and thus no tuple unpacking:
|
||||
@@ -1130,16 +1097,17 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
v.typ = iter[i]
|
||||
n[0][i] = newSymNode(v)
|
||||
if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
else:
|
||||
var v = symForVar(c, n[0])
|
||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||
# Use `iterType` here: it removes outer `tyIterable` / alias-like wrappers
|
||||
# from the loop source, but still preserves `tyGenericInst` for the loop var.
|
||||
v.typ = iterType
|
||||
# BUGFIX: don't use `iter` here as that would strip away
|
||||
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
|
||||
# for an example:
|
||||
v.typ = iterBase
|
||||
n[0] = newSymNode(v)
|
||||
if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
else:
|
||||
localError(c.config, n.info, errWrongNumberOfVariables)
|
||||
elif n.len-2 != iterAfterVarLent.len:
|
||||
@@ -1173,7 +1141,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
v.typ = iter[i][j]
|
||||
n[i][j] = newSymNode(v)
|
||||
if not isDiscardUnderscore(v): addDecl(c, v)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
else:
|
||||
var v = symForVar(c, n[i])
|
||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||
@@ -1188,7 +1156,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
n[i] = newSymNode(v)
|
||||
if sfGenSym notin v.flags:
|
||||
if not isDiscardUnderscore(v): addDecl(c, v)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
inc(c.p.nestedLoopCounter)
|
||||
let oldBreakInLoop = c.p.breakInLoop
|
||||
c.p.breakInLoop = true
|
||||
@@ -1200,14 +1168,14 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
c.p.breakInLoop = oldBreakInLoop
|
||||
dec(c.p.nestedLoopCounter)
|
||||
|
||||
proc implicitIterator(c: PContext, it: string, arg: PNode, flags: TExprFlags): PNode =
|
||||
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
|
||||
result = newNodeI(nkCall, arg.info)
|
||||
result.add(newIdentNode(getIdent(c.cache, it), arg.info))
|
||||
if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}:
|
||||
result.add newDeref(arg)
|
||||
else:
|
||||
result.add arg
|
||||
result = semExprNoDeref(c, result, flags + {efWantIterator})
|
||||
result = semExprNoDeref(c, result, {efWantIterator})
|
||||
|
||||
proc isTrivalStmtExpr(n: PNode): bool =
|
||||
for i in 0..<n.len-1:
|
||||
@@ -1293,8 +1261,7 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
if result != nil: return result
|
||||
openScope(c)
|
||||
result = n
|
||||
let iteratorFlags = flags * {efPreferIteratorForIterable}
|
||||
n[^2] = semExprNoDeref(c, n[^2], iteratorFlags + {efWantIterator})
|
||||
n[^2] = semExprNoDeref(c, n[^2], {efWantIterator})
|
||||
var call = n[^2]
|
||||
|
||||
if call.kind == nkStmtListExpr and (isTrivalStmtExpr(call) or (call.lastSon.kind in nkCallKinds and call.lastSon[0].sym.kind == skIterator)):
|
||||
@@ -1314,21 +1281,19 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
elif not isCallExpr or call[0].kind != nkSym or
|
||||
call[0].sym.kind != skIterator:
|
||||
if n.len == 3:
|
||||
n[^2] = implicitIterator(c, "items", n[^2], iteratorFlags)
|
||||
n[^2] = implicitIterator(c, "items", n[^2])
|
||||
elif n.len == 4:
|
||||
n[^2] = implicitIterator(c, "pairs", n[^2], iteratorFlags)
|
||||
n[^2] = implicitIterator(c, "pairs", n[^2])
|
||||
else:
|
||||
localError(c.config, n[^2].info, "iterator within for loop context expected")
|
||||
result = semForVars(c, n, flags)
|
||||
else:
|
||||
result = semForVars(c, n, flags)
|
||||
if n[^2].typ != nil and n[^2].typ.kind == tyIterable:
|
||||
n[^2].typ = n[^2].typ.skipModifier
|
||||
# propagate any enforced VoidContext:
|
||||
if n[^1].typ == c.enforceVoidContext:
|
||||
result.typ() = c.enforceVoidContext
|
||||
result.typ = c.enforceVoidContext
|
||||
elif efInTypeof in flags:
|
||||
result.typ() = result.lastSon.typ
|
||||
result.typ = result.lastSon.typ
|
||||
closeScope(c)
|
||||
|
||||
proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
@@ -1408,14 +1373,14 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil
|
||||
for i in 1..<n.len: discardCheck(c, n[i].lastSon, flags)
|
||||
# propagate any enforced VoidContext:
|
||||
if typ == c.enforceVoidContext:
|
||||
result.typ() = c.enforceVoidContext
|
||||
result.typ = c.enforceVoidContext
|
||||
else:
|
||||
for i in 1..<n.len:
|
||||
var it = n[i]
|
||||
let j = it.len-1
|
||||
if not endsInNoReturn(it[j]):
|
||||
it[j] = fitNode(c, typ, it[j], it[j].info)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
|
||||
proc semRaise(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
@@ -1510,7 +1475,7 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
|
||||
s = typsym
|
||||
# add it here, so that recursive types are possible:
|
||||
if sfGenSym notin s.flags: addInterfaceDecl(c, s)
|
||||
elif s.owner == nil: setOwner(s, getCurrOwner(c))
|
||||
elif s.owner == nil: s.owner = getCurrOwner(c)
|
||||
|
||||
if name.kind == nkPragmaExpr:
|
||||
if name[0].kind == nkPostfix:
|
||||
@@ -1733,14 +1698,12 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
obj.ast[0] = a[0].shallowCopy
|
||||
if a[0][0].kind == nkPostfix:
|
||||
obj.ast[0][0] = a[0][0].shallowCopy
|
||||
obj.ast[0][0][0] = a[0][0][0] # ident "*"
|
||||
obj.ast[0][0][1] = symNode
|
||||
else:
|
||||
obj.ast[0][0] = symNode
|
||||
obj.ast[0][1] = a[0][1]
|
||||
of nkPostfix:
|
||||
obj.ast[0] = a[0].shallowCopy
|
||||
obj.ast[0][0] = a[0][0] # ident "*"
|
||||
obj.ast[0][1] = symNode
|
||||
else: assert(false)
|
||||
obj.ast[1] = a[1]
|
||||
@@ -1787,23 +1750,6 @@ proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
|
||||
internalAssert c.config, false
|
||||
|
||||
proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
# a son that still was a `tyForward` could not propagate `tfHasAsgn` and
|
||||
# friends to its owner back then, see `rememberFlagUpdate`. Now that every
|
||||
# forward declaration has a body, redo those propagations. They are recorded
|
||||
# in declaration order rather than dependency order and an owner can itself
|
||||
# be the son of another pair, so repeat until nothing changes; this
|
||||
# terminates because flags are only ever added.
|
||||
if c.forwardFlagUpdates.len > 0:
|
||||
let updates = move c.forwardFlagUpdates
|
||||
c.staleTypeFlags = initIntSet()
|
||||
var changed = true
|
||||
while changed:
|
||||
changed = false
|
||||
for (owner, elem) in updates:
|
||||
let before = owner.flags
|
||||
propagateToOwner(owner, elem)
|
||||
if owner.flags != before: changed = true
|
||||
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
if a.kind == nkCommentStmt: continue
|
||||
@@ -1812,17 +1758,6 @@ 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:
|
||||
@@ -1853,13 +1788,6 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
x[0].kind in {nkObjectTy, nkTupleTy})
|
||||
):
|
||||
checkForMetaFields(c, baseType.n, hasError)
|
||||
|
||||
if s.typ.kind in {tySet, tyArray, tySequence, tyUncheckedArray} and s.typ.elementType.kind == tyNone:
|
||||
# magic generics are not filled but tyNone is added to its elements by default,
|
||||
# we lift them to tyBuiltInTypeClass here
|
||||
s.typ = newTypeS(tyBuiltInTypeClass, c,
|
||||
newTypeS(s.typ.kind, c))
|
||||
|
||||
if not hasError:
|
||||
checkConstructedType(c.config, s.info, s.typ)
|
||||
#instAllTypeBoundOp(c, n.info)
|
||||
@@ -2041,13 +1969,13 @@ proc semProcAnnotation(c: PContext, prc: PNode;
|
||||
|
||||
return result
|
||||
|
||||
proc semInferredLambda(c: PContext, pt: LayeredIdTable, n: PNode): PNode =
|
||||
proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
|
||||
## used for resolving 'auto' in lambdas based on their callsite
|
||||
var n = n
|
||||
let original = n[namePos].sym
|
||||
let s = original #copySym(original, false)
|
||||
#incl(s.flags, sfFromGeneric)
|
||||
#s.owner() = original
|
||||
#s.owner = original
|
||||
|
||||
n = replaceTypesInBody(c, pt, n, original)
|
||||
result = n
|
||||
@@ -2062,7 +1990,7 @@ proc semInferredLambda(c: PContext, pt: LayeredIdTable, n: PNode): PNode =
|
||||
tyFromExpr}+tyTypeClasses:
|
||||
localError(c.config, params[i].info, "cannot infer type of parameter: " &
|
||||
params[i].sym.name.s)
|
||||
#params[i].sym.owner() = s
|
||||
#params[i].sym.owner = s
|
||||
openScope(c)
|
||||
pushOwner(c, s)
|
||||
addParams(c, params, skProc)
|
||||
@@ -2074,7 +2002,7 @@ proc semInferredLambda(c: PContext, pt: LayeredIdTable, n: PNode): PNode =
|
||||
popOwner(c)
|
||||
closeScope(c)
|
||||
if optOwnedRefs in c.config.globalOptions and result.typ != nil:
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
# alternative variant (not quite working):
|
||||
# var prc = arg[0].sym
|
||||
# let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
|
||||
@@ -2107,75 +2035,55 @@ proc canonType(c: PContext, t: PType): PType =
|
||||
else:
|
||||
result = t
|
||||
|
||||
proc prevDestructor(c: PContext; op: TTypeAttachedOp; prevOp: PSym; obj: PType; info: TLineInfo) =
|
||||
var msg = "cannot bind another '" & AttachedOpToStr[op] & "' to: " & typeToString(obj)
|
||||
if prevOp == nil:
|
||||
# happens if the destructor was implicitly constructed for a specific instance,
|
||||
# not the entire generic type
|
||||
msg.add "; previous declaration was constructed implicitly"
|
||||
elif sfOverridden notin prevOp.flags:
|
||||
proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
|
||||
var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
|
||||
if sfOverridden notin prevOp.flags:
|
||||
msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
|
||||
else:
|
||||
msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
|
||||
localError(c.config, info, errGenerated, msg)
|
||||
|
||||
proc checkedForDestructor(t: PType): bool =
|
||||
if tfCheckedForDestructor in t.flags:
|
||||
return true
|
||||
# maybe another instance was instantiated, marking the generic root:
|
||||
let root = genericRoot(t)
|
||||
if root != nil and tfGenericHasDestructor in root.flags:
|
||||
return true
|
||||
result = false
|
||||
|
||||
proc normalizeTypeHook(t: PType; markAsgn = false): PType =
|
||||
proc whereToBindTypeHook(c: PContext; t: PType): PType =
|
||||
result = t
|
||||
while true:
|
||||
if markAsgn:
|
||||
incl(result.flags, tfHasAsgn)
|
||||
if result.kind == tyCompositeTypeClass and result.base.kind == tyGenericBody:
|
||||
result = result.base
|
||||
elif result.kind in {tyGenericBody, tyGenericInst}:
|
||||
result = result.skipModifier
|
||||
elif result.kind == tyGenericInvocation:
|
||||
result = result.genericHead
|
||||
else:
|
||||
break
|
||||
|
||||
proc whereToBindTypeHook(c: PContext; t: PType): PType =
|
||||
result = normalizeTypeHook(t)
|
||||
if result.kind in {tyGenericBody, tyGenericInst}: result = result.skipModifier
|
||||
elif result.kind == tyGenericInvocation: result = result[0]
|
||||
else: break
|
||||
if result.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||
result = canonType(c, result)
|
||||
|
||||
proc bindHookToType(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp;
|
||||
typeToBind: PType): bool =
|
||||
var obj = typeToBind
|
||||
if obj.kind notin {tyObject, tyDistinct, tySequence, tyString}:
|
||||
return false
|
||||
obj = canonType(c, obj)
|
||||
let ao = getAttachedOp(c.graph, obj, op)
|
||||
if ao == s:
|
||||
discard "forward declared hook"
|
||||
elif ao.isNil and not checkedForDestructor(obj):
|
||||
setAttachedOp(c.graph, c.module.position, obj, op, s)
|
||||
else:
|
||||
prevDestructor(c, op, ao, obj, n.info)
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
result = true
|
||||
|
||||
proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
let cond = t.len == 2 and t.returnType != nil
|
||||
|
||||
if cond:
|
||||
var obj = normalizeTypeHook(t.firstParamType, markAsgn = true)
|
||||
let res = normalizeTypeHook(t.returnType)
|
||||
var obj = t.firstParamType
|
||||
while true:
|
||||
incl(obj.flags, tfHasAsgn)
|
||||
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
|
||||
else: break
|
||||
|
||||
if sameType(obj, res):
|
||||
noError = bindHookToType(c, s, n, op, obj)
|
||||
var res = t.returnType
|
||||
while true:
|
||||
if res.kind in {tyGenericBody, tyGenericInst}: res = res.skipModifier
|
||||
elif res.kind == tyGenericInvocation: res = res.genericHead
|
||||
else: break
|
||||
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, res):
|
||||
obj = canonType(c, obj)
|
||||
let ao = getAttachedOp(c.graph, obj, op)
|
||||
if ao == s:
|
||||
discard "forward declared destructor"
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
setAttachedOp(c.graph, c.module.position, obj, op, s)
|
||||
else:
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
noError = true
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
|
||||
if not noError and sfSystemModule notin s.owner.flags:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
@@ -2184,20 +2092,16 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverridden)
|
||||
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) =
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
template notRefc: bool =
|
||||
# fixes refc with non-var destructor; cancel warnings (#23156)
|
||||
c.config.backend == backendJs or
|
||||
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}
|
||||
let cond = case op
|
||||
of attachedWasMoved:
|
||||
t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
|
||||
of attachedTrace:
|
||||
t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar and t[2].kind == tyPointer
|
||||
of attachedDestructor:
|
||||
if notRefc:
|
||||
if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
t.len == 2 and t.returnType == nil
|
||||
else:
|
||||
t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
|
||||
@@ -2205,15 +2109,35 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
t.len >= 2 and t.returnType == nil
|
||||
|
||||
if cond:
|
||||
var obj = normalizeTypeHook(t.firstParamType.skipTypes({tyVar}), markAsgn = true)
|
||||
noError = bindHookToType(c, s, n, op, obj)
|
||||
var obj = t.firstParamType.skipTypes({tyVar})
|
||||
while true:
|
||||
incl(obj.flags, tfHasAsgn)
|
||||
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||
if (not suppressVarDestructorWarning) and op == attachedDestructor and t.firstParamType.kind == tyVar and
|
||||
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter")
|
||||
obj = canonType(c, obj)
|
||||
let ao = getAttachedOp(c.graph, obj, op)
|
||||
if ao == s:
|
||||
discard "forward declared destructor"
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
setAttachedOp(c.graph, c.module.position, obj, op, s)
|
||||
else:
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
noError = true
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
if not noError and sfSystemModule notin s.owner.flags:
|
||||
case op
|
||||
of attachedTrace:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=trace' must be proc[T: object](x: var T; env: pointer)")
|
||||
of attachedDestructor:
|
||||
if notRefc:
|
||||
if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)")
|
||||
else:
|
||||
@@ -2273,12 +2197,35 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead")
|
||||
let t = s.typ
|
||||
if t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar:
|
||||
var obj = normalizeTypeHook(t.firstParamType.elementType, markAsgn = true)
|
||||
let objB = normalizeTypeHook(t[2])
|
||||
if sameType(obj, objB):
|
||||
var obj = t.firstParamType.elementType
|
||||
while true:
|
||||
incl(obj.flags, tfHasAsgn)
|
||||
if obj.kind == tyGenericBody: obj = obj.skipModifier
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
|
||||
else: break
|
||||
var objB = t[2]
|
||||
while true:
|
||||
if objB.kind == tyGenericBody: objB = objB.skipModifier
|
||||
elif objB.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
objB = objB.genericHead
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
|
||||
# attach these ops to the canonical tySequence
|
||||
obj = canonType(c, obj)
|
||||
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
|
||||
let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink
|
||||
if bindHookToType(c, s, n, k, obj): return
|
||||
let ao = getAttachedOp(c.graph, obj, k)
|
||||
if ao == s:
|
||||
discard "forward declared op"
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
setAttachedOp(c.graph, c.module.position, obj, k, s)
|
||||
else:
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
|
||||
return
|
||||
if sfSystemModule notin s.owner.flags:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)")
|
||||
@@ -2362,7 +2309,7 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) =
|
||||
isInitializer = false
|
||||
break
|
||||
var j = 0
|
||||
while p[j].kind == nkSym and p[j].sym.kind == skParam:
|
||||
while p[j].sym.kind == skParam:
|
||||
initializerCall.add val
|
||||
inc j
|
||||
if isInitializer:
|
||||
@@ -2412,11 +2359,10 @@ 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
|
||||
setOwner(s, c.getCurrOwner)
|
||||
s.owner = c.getCurrOwner
|
||||
else:
|
||||
# Highlighting needs to be done early so the position for
|
||||
# name isn't changed (see taccent_highlight). We don't want to check if this is the
|
||||
@@ -2527,9 +2473,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
if not hasProto:
|
||||
implicitPragmas(c, s, n.info, validPragmas)
|
||||
|
||||
if {sfError, sfExportc} * s.flags == {sfError, sfExportc}:
|
||||
localError(c.config, n.info, "{.error.} and {.exportc.} pragmas are incompatible")
|
||||
|
||||
if n[pragmasPos].kind != nkEmpty and sfBorrow notin s.flags:
|
||||
setEffectsForProcType(c.graph, s.typ, n[pragmasPos], s)
|
||||
s.typ.flags.incl tfEffectSystemWorkaround
|
||||
@@ -2590,11 +2533,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
elif s.name.s == "()" and callOperator notin c.features:
|
||||
localError(c.config, n.info, "the overloaded " & s.name.s &
|
||||
" operator has to be enabled with {.experimental: \"callOperator\".}")
|
||||
elif sfImportc notin s.flags and (s.name.s == ">" or s.name.s == ">=" or s.name.s == "!="):
|
||||
# ignore imported procs as these operators in backend language might have different semantics
|
||||
let op1 = if s.name.s == "!=": "==" elif s.name.s == ">": "<" else: "<="
|
||||
message(c.config, n.info, warnInvalidCmpOp, "define `" & op1 & "` instead of `" & s.name.s & "` to implement user defined comparison operator. " &
|
||||
"it allows you to use `" & s.name.s & "` automatically.")
|
||||
|
||||
if sfBorrow in s.flags and c.config.cmd notin cmdDocLike:
|
||||
result[bodyPos] = c.graph.emptyNode
|
||||
@@ -2635,18 +2573,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
else:
|
||||
nil
|
||||
# semantic checking also needed with importc in case used in VM
|
||||
|
||||
let isInlineIterator = isInlineIterator(s.typ)
|
||||
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType))
|
||||
# unfortunately we cannot skip this step when in 'system.compiles'
|
||||
# context as it may even be evaluated in 'system.compiles':
|
||||
|
||||
if isInlineIterator and s.typ.callConv == ccClosure:
|
||||
# iterators without explicit callconvs are lifted to closure,
|
||||
# we need to add a result symbol for them
|
||||
maybeAddResult(c, s, n)
|
||||
|
||||
|
||||
trackProc(c, s, s.ast[bodyPos])
|
||||
else:
|
||||
if (s.typ.returnType != nil and s.kind != skIterator):
|
||||
@@ -2679,9 +2608,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
c.patterns.add(s)
|
||||
if isAnon:
|
||||
n.transitionSonsKind(nkLambda)
|
||||
result.typ() = s.typ
|
||||
result.typ = s.typ
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
elif isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure:
|
||||
localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid")
|
||||
|
||||
@@ -2700,7 +2629,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
|
||||
# gensym'ed iterator?
|
||||
if n[namePos].kind == nkSym:
|
||||
# gensym'ed iterators might need to become closure iterators:
|
||||
setOwner(n[namePos].sym, getCurrOwner(c))
|
||||
n[namePos].sym.owner = getCurrOwner(c)
|
||||
n[namePos].sym.transitionRoutineSymKind(skIterator)
|
||||
result = semProcAux(c, n, skIterator, iteratorPragmas)
|
||||
# bug #7093: if after a macro transformation we don't have an
|
||||
@@ -2718,10 +2647,10 @@ proc semIterator(c: PContext, n: PNode): PNode =
|
||||
incl(s.typ.flags, tfCapturesEnv)
|
||||
else:
|
||||
s.typ.callConv = ccInline
|
||||
if result[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
|
||||
if n[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
|
||||
localError(c.config, n.info, errImplOfXexpected % s.name.s)
|
||||
if optOwnedRefs in c.config.globalOptions and result.typ != nil:
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
result.typ.callConv = ccClosure
|
||||
|
||||
proc semProc(c: PContext, n: PNode): PNode =
|
||||
@@ -2832,24 +2761,9 @@ proc recursiveSetFlag(n: PNode, flag: TNodeFlag) =
|
||||
for i in 0..<n.safeLen: recursiveSetFlag(n[i], flag)
|
||||
incl(n.flags, flag)
|
||||
|
||||
proc enterPragmaBlock(c: PContext): POptionEntry =
|
||||
result = POptionEntry(options: c.config.options,
|
||||
notes: c.config.notes,
|
||||
warningAsErrors: c.config.warningAsErrors,
|
||||
features: c.features)
|
||||
|
||||
proc leavePragmaBlock(c: PContext, p: POptionEntry) =
|
||||
c.config.options = p.options
|
||||
c.config.notes = p.notes
|
||||
c.config.warningAsErrors = p.warningAsErrors
|
||||
c.features = p.features
|
||||
|
||||
proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
checkSonsLen(n, 2, c.config)
|
||||
let pragmaList = n[0]
|
||||
|
||||
let oldOptionEntry = enterPragmaBlock(c)
|
||||
|
||||
pragma(c, nil, pragmaList, exprPragmas, isStatement = true)
|
||||
|
||||
var inUncheckedAssignSection = 0
|
||||
@@ -2867,27 +2781,23 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
n[1] = semExpr(c, n[1], expectedType = expectedType)
|
||||
dec c.inUncheckedAssignSection, inUncheckedAssignSection
|
||||
result = n
|
||||
result.typ() = n[1].typ
|
||||
result.typ = n[1].typ
|
||||
for i in 0..<pragmaList.len:
|
||||
case whichPragma(pragmaList[i])
|
||||
of wLine: setInfoRecursive(result, pragmaList[i].info)
|
||||
of wNoRewrite: recursiveSetFlag(result, nfNoRewrite)
|
||||
else: discard
|
||||
|
||||
leavePragmaBlock(c, oldOptionEntry)
|
||||
|
||||
proc semStaticStmt(c: PContext, n: PNode): PNode =
|
||||
#echo "semStaticStmt"
|
||||
#writeStackTrace()
|
||||
let oldErrorCount = c.config.errorCounter
|
||||
inc c.inStaticContext
|
||||
openScope(c)
|
||||
let a = semStmt(c, n[0], {})
|
||||
closeScope(c)
|
||||
dec c.inStaticContext
|
||||
n[0] = a
|
||||
if c.config.errorCounter == oldErrorCount:
|
||||
evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
|
||||
evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
|
||||
when false:
|
||||
# for incremental replays, keep the AST as required for replays:
|
||||
result = n
|
||||
@@ -2956,14 +2866,14 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
|
||||
else: discard
|
||||
if n[i].typ == c.enforceVoidContext: #or usesResult(n[i]):
|
||||
voidContext = true
|
||||
n.typ() = c.enforceVoidContext
|
||||
n.typ = c.enforceVoidContext
|
||||
if i == last and (n.len == 1 or ({efWantValue, efInTypeof} * flags != {})):
|
||||
n.typ() = n[i].typ
|
||||
n.typ = n[i].typ
|
||||
if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr)
|
||||
elif i != last or voidContext:
|
||||
discardCheck(c, n[i], flags)
|
||||
else:
|
||||
n.typ() = n[i].typ
|
||||
n.typ = n[i].typ
|
||||
if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr)
|
||||
var m = n[i]
|
||||
while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates
|
||||
|
||||
@@ -67,9 +67,7 @@ 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)
|
||||
# possibly not final field sym
|
||||
incl(s.flags, sfUsed)
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
markUsed(c, info, s)
|
||||
onUse(info, s)
|
||||
else:
|
||||
result = n
|
||||
@@ -239,10 +237,10 @@ proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bo
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
of skGenericParam:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else:
|
||||
@@ -252,7 +250,7 @@ proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bo
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
of skParam:
|
||||
result = n
|
||||
of skType:
|
||||
@@ -270,10 +268,10 @@ proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bo
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
else:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else:
|
||||
@@ -283,7 +281,7 @@ proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bo
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
result.typ = nil
|
||||
# Issue #12832
|
||||
when defined(nimsuggest):
|
||||
suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false)
|
||||
@@ -537,20 +535,13 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
of nkConverterDef:
|
||||
result = semRoutineInTemplBody(c, n, skConverter)
|
||||
of nkPragmaExpr:
|
||||
result = semTemplBodySons(c, n)
|
||||
result[0] = semTemplBody(c, n[0])
|
||||
of nkPostfix:
|
||||
result[1] = semTemplBody(c, n[1])
|
||||
of nkPragma:
|
||||
for i in 0 ..< n.len:
|
||||
let x = n[i]
|
||||
let prag = whichPragma(x)
|
||||
if prag == wInvalid:
|
||||
# only sem if not a language-level pragma
|
||||
result[i] = semTemplBody(c, x)
|
||||
elif x.kind in nkPragmaCallKinds:
|
||||
# is pragma, but value still needs to be checked
|
||||
for j in 1 ..< x.len:
|
||||
x[j] = semTemplBody(c, x[j])
|
||||
for x in n:
|
||||
if x.kind == nkExprColonExpr:
|
||||
x[1] = semTemplBody(c, x[1])
|
||||
of nkBracketExpr:
|
||||
if n.typ == nil:
|
||||
# if a[b] is nested inside a typed expression, don't convert it
|
||||
@@ -693,9 +684,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
s = semIdentVis(c, skTemplate, n[namePos], {})
|
||||
assert s.kind == skTemplate
|
||||
|
||||
let info = getLineInfo(n[namePos])
|
||||
suggestSym(c.graph, info, s, c.graph.usageSym)
|
||||
|
||||
styleCheckDef(c, s)
|
||||
onDef(n[namePos].info, s)
|
||||
# check parameter list:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user