Compare commits

..

20 Commits

Author SHA1 Message Date
ringabout
112e274804 switch on 2023-12-11 14:12:55 +00:00
ringabout
f1a7bfce47 switch on 2023-12-11 14:12:47 +00:00
ringabout
68bf6c7f93 what's problem os cancelled CI 2023-12-11 11:00:02 +00:00
ringabout
7cd431804b test CI 2023-12-11 09:07:41 +00:00
ringabout
01388dc816 Merge branch 'devel' into pr_string_v3 2023-12-11 13:16:03 +08:00
ringabout
bee9baa8bf reprieve IC 2023-12-09 11:56:17 +00:00
ringabout
147621ec21 fixes a critical issue 2023-12-09 02:41:05 +00:00
ringabout
5bde480596 workaround a cstring conversion bug 2023-12-09 02:34:22 +00:00
ringabout
33f911e691 fixes C++ compilation 2023-12-08 14:13:21 +00:00
ringabout
ec03e476d5 fixes more problems 2023-12-08 08:41:16 +00:00
ringabout
4adb79f7cb a small fix 2023-12-08 07:29:36 +00:00
ringabout
1da417b818 tests nimSeqsV3 2023-12-08 07:26:32 +00:00
ringabout
7ef0f43f35 basic examples work now 2023-12-08 07:04:15 +00:00
ringabout
da277cf1b8 more fixes 2023-12-07 14:47:51 +00:00
ringabout
cb172328ba simple cases compile 2023-12-07 13:56:33 +00:00
ringabout
9af21cf719 progress 2023-12-07 12:53:06 +00:00
ringabout
f7bdec6f0d progress 2023-12-06 14:25:34 +00:00
ringabout
88c0ac44fc simple additions 2023-12-06 06:09:25 +00:00
ringabout
119cfe8bc8 some improvements 2023-12-06 02:27:05 +00:00
ringabout
ae99903236 wip: intern strings 2023-12-05 14:51:24 +00:00
1312 changed files with 24209 additions and 48199 deletions

View File

@@ -7,16 +7,17 @@ body:
- type: markdown
attributes:
value: |
Please provide a minimal code example that reproduces the bug if possible.
Reports with a reproducible example or detailed information will likely receive fixes faster.
- **Please provide a minimal code example that reproduces the Bug!** :bug:
Reports with a reproducible example and descriptive detailed information will likely receive fixes faster.
- type: textarea
id: description
attributes:
label: Description
description: |
Describe the problem. Code example can be given here.
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
Use DETAILED DESCRIPTIVE information about the problem.
Here, you go into more details about your Bug report. This section can be a few paragraphs long.
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
validations:
required: true
@@ -24,9 +25,7 @@ body:
id: nim-version
attributes:
label: Nim Version
description: |
Can be obtained from `nim -v` on the command line along with the OS/architecture.
For development versions, make sure to include the commit hash.
description: Copy and paste the output of `nim -v` on the command line. For development versions, make sure to include the commit hash.
validations:
required: true
@@ -35,7 +34,7 @@ body:
attributes:
label: Current Output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
render: text
- type: textarea
@@ -43,14 +42,14 @@ body:
attributes:
label: Expected Output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
render: text
- type: textarea
id: known-workarounds
id: possible-solution
attributes:
label: Known Workarounds
description: Provide any known workarounds.
label: Possible Solution
description: Propose a possible solution.
validations:
required: false
@@ -65,9 +64,13 @@ body:
- type: markdown
attributes:
value: |
- Please check whether the problem still exists in the devel branch, which can be installed via [choosenim](https://github.com/nim-lang/choosenim/), [nightlies](https://github.com/nim-lang/nightlies/), or by [creating a temporary build of the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler-building-an-instrumented-compiler).
- Thanks for your contributions!, your Bug report will receive feedback from the community soon...
- Please check whether the problem still exists in the devel branch, see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
- Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
- If it's a pre-existing compiler bug, see [debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler) which should give more context on a compiler crash.
- If it's a regression, you can help us by identifying which version introduced the bug by [bisecting](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions) or at least trying known past releases (e.g. `choosenim 2.0.0`).
The Nim repo also supports bisecting in issue comments by adding `!nim c`, `!nim js` etc. before a code block, see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
- If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler)
which should give more context on a compiler crash.
- If it's a regression, you can help us by identifying which version introduced the bug,
see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions),
or at least try known past releases (e.g. `choosenim 2.0.0`). The Nim repo also supports online bisecting
via making a comment, which contains a code block starting by `!nim c`, `!nim js` etc. , see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)

View File

@@ -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'

115
.github/workflows/ci_bench.yml vendored Normal file
View File

@@ -0,0 +1,115 @@
name: Benchmarks CI
on:
pull_request:
push:
branches:
- 'devel'
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04]
cpu: [amd64]
name: '${{ matrix.os }}'
runs-on: ${{ matrix.os }}
timeout-minutes: 60 # refs bug #18178
steps:
- name: 'Checkout'
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: 'Install node.js 20.x'
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
run: |
sudo apt-fast update -qq
DEBIAN_FRONTEND='noninteractive' \
sudo apt-fast install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
valgrind libc6-dbg libblas-dev xorg-dev
- name: 'Add build binaries to PATH'
shell: bash
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
- name: 'Build csourcesAny'
shell: bash
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
- name: 'Build koch'
shell: bash
run: nim c koch
- name: 'Build Nim'
shell: bash
run: ./koch boot -d:release -d:nimStrictMode --lib:lib
- name: 'Build Nimble'
shell: bash
run: ./koch nimble
- name: 'Action'
shell: bash
run: nim c -r -d:release ci/action.nim
- name: 'Checkout minimize'
uses: actions/checkout@v4
with:
repository: 'nim-lang/ci_bench'
path: minimize
- name: 'Run minimize benchmarks'
shell: bash
run: ./minimize/minimize ci-bench
- name: 'Restore minimize cached database'
uses: actions/cache/restore@v3
with:
path: minimize.csv
key: minimize-db-key
- name: 'Update minimize db'
shell: bash
run: ./minimize/minimize update-db
- name: 'Save minimize cached database'
if: |
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
matrix.target == 'linux'
uses: actions/cache/save@v3
with:
path: minimize.csv
key: minimize-db-key
- name: 'Generate minimize report'
shell: bash
run: ./minimize/minimize generate-report
- name: 'Archive minimize report'
uses: actions/upload-artifact@v3
with:
name: minimize-report
path: |
minimize/minimize.html
minimize/minimize.csv
# Requires additional permissions, see:
# https://github.com/nim-lang/Nim/actions/runs/4778177321/jobs/8494423792?pr=21566
# - name: 'Publish HTML report'
# uses: rossjrw/pr-preview-action@v1
# with:
# source-dir: minimize
# umbrella-dir: minimize
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Extract md summary
run: |
cat minimize/summary.md >> $GITHUB_STEP_SUMMARY

View File

@@ -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-11
name: ${{ matrix.target }}
runs-on: ${{ matrix.os }}
@@ -109,7 +109,7 @@ jobs:
if: |
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
matrix.target == 'linux'
uses: crazy-max/ghaction-github-pages@v4
uses: crazy-max/ghaction-github-pages@v3
with:
build_dir: doc/html
env:

View File

@@ -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-11]
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

View File

@@ -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@v6
with:
script: |
const fs = require('fs');
@@ -76,3 +87,4 @@ jobs:
} catch (err) {
console.error(err);
}

View File

@@ -9,7 +9,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v11
- uses: actions/stale@v8
with:
days-before-pr-stale: 365
days-before-pr-close: 30

1
.gitignore vendored
View File

@@ -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

View File

@@ -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-11'
CPU: amd64
OSX_amd64_cpp:
vmImage: 'macOS-11'
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

View File

@@ -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

View File

@@ -1,154 +1,48 @@
# 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.
- The `default` parameter of `tables.getOrDefault` has been renamed to `def` to
avoid conflicts with `system.default`, so named argument usage for this
parameter like `getOrDefault(..., default = ...)` will have to be changed.
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
- Unknown warnings and hints now gives warnings `warnUnknownNotes` instead of
errors.
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
- The bare `except:` now panics on `Defect`. Use `except Exception:` or `except Defect:` to catch `Defect`. `--legacy:noPanicOnExcept` is provided for a transition period.
- With `-d:nimPreviewCStringComparisons`, comparsions (`<`, `>`, `<=`, `>=`) between cstrings switch from reference semantics to value semantics like `==` and `!=`.
- `std/parsesql` has been moved to a nimble package, use `nimble` or `atlas` to install it.
- With `-d:nimPreviewDuplicateModuleError`, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Use `import foo as foo1` (or other aliases) to avoid collisions.
- Adds the switch `--mangle:nim|cpp`, which selects `nim` or `cpp` style name mangling when used with `debuginfo` on, defaults to `cpp`.
- The second parameter of `succ`, `pred`, `inc`, and `dec` in `system` now accepts `SomeInteger` (previously `Ordinal`).
- Bitshift operators (`shl`, `shr`, `ashr`) now apply bitmasking to the right operand in the C/C++/VM/JS backends.
- 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.
- `-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.
## 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 initalize
slots when enlarging a sequence.
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
- Added Viewport API for the JavaScript targets in the `dom` module.
[//]: # "Deprecations:"
- 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.
```nim
# objs.nim
import std/hashes
type
Obj* = object
x*, y*: int
z*: string # to be ignored for equality
proc `==`*(a, b: Obj): bool =
a.x == b.x and a.y == b.y
proc hash*(a: Obj): Hash =
$!(hash(a.x) &! hash(a.y))
```
```nim
# main.nim
{.experimental: "typeBoundOps".}
from objs import Obj # objs.hash, objs.`==` not imported
import std/tables
var t: Table[Obj, int]
t[Obj(x: 3, y: 4, z: "debug")] = 34
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
```
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#typeminusbound-overloads)
for more information.
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.htm#constructor-initializer
- `member` can be used to attach a procedure to a C++ type.
- C++ `constructor` now reuses `result` instead creating `this`.
## 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.

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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

View File

@@ -72,7 +72,7 @@
- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
`sink` for optimization purposes.
- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fulfill its promises.
- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises.
- The `{.this.}` pragma, deprecated since 0.19, has been removed.
- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.

View File

@@ -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.

View File

@@ -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!

View File

@@ -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

View File

@@ -51,16 +51,14 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
case a.kind
of tyObject:
if a.baseClass != nil:
result = isPartOfAux(a.baseClass.skipTypes(skipPtrs), b, marker)
if a[0] != nil:
result = isPartOfAux(a[0].skipTypes(skipPtrs), b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct, tyAlias, tySink:
result = isPartOfAux(skipModifier(a), b, marker)
of tySet, tyArray:
result = isPartOfAux(a.elementType, b, marker)
of tyTuple:
for aa in a.kids:
result = isPartOfAux(aa, b, marker)
result = isPartOfAux(lastSon(a), b, marker)
of tyArray, tySet, tyTuple:
for i in 0..<a.len:
result = isPartOfAux(a[i], b, marker)
if result == arYes: return
else: discard
@@ -107,7 +105,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:

File diff suppressed because it is too large Load Diff

View File

@@ -12,18 +12,24 @@
# the data structures here are used in various places of the compiler.
import
ast, astyaml, options, lineinfos, idents, rodutils,
ast, options, lineinfos, ropes, idents, rodutils,
msgs
import std/[hashes, intsets]
import std/strutils except addf
export astyaml.treeToYaml, astyaml.typeToYaml, astyaml.symToYaml, astyaml.lineInfoToStr
when defined(nimPreviewSlimSystem):
import std/assertions
proc hashNode*(p: RootRef): Hash
proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
# Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes.
# If maxRecDepht <> -1 then it won't print the whole graph.
proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
# these are for debugging only: They are not really deprecated, but I want
# the warning so that release versions do not contain debugging statements:
@@ -31,6 +37,15 @@ proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
proc typekinds*(t: PType) {.deprecated.} =
var t = t
var s = ""
while t != nil and t.len > 0:
s.add $t.kind
s.add " "
t = t.lastSon
echo s
template debug*(x: PSym|PType|PNode) {.deprecated.} =
when compiles(c.config):
debug(c.config, x)
@@ -65,6 +80,16 @@ template mdbg*: bool {.deprecated.} =
else:
error()
# --------------------------- ident tables ----------------------------------
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
proc idTableGet*(t: TIdTable, key: int): RootRef
proc idTablePut*(t: var TIdTable, key: PIdObj, val: RootRef)
proc idTableHasObjectAsKey*(t: TIdTable, key: PIdObj): bool
# checks if `t` contains the `key` (compared by the pointer value, not only
# `key`'s id)
proc idNodeTableGet*(t: TIdNodeTable, key: PIdObj): PNode
proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode)
# ---------------------------------------------------------------------------
proc lookupInRecord*(n: PNode, field: PIdent): PSym
@@ -220,6 +245,170 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc rspaces(x: int): Rope =
# returns x spaces
result = rope(spaces(x))
proc toYamlChar(c: char): string =
case c
of '\0'..'\x1F', '\x7F'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4)
of '\'', '\"', '\\': result = '\\' & c
else: result = $c
proc makeYamlString*(s: string): Rope =
# We have to split long strings into many ropes. Otherwise
# this could trigger InternalError(111). See the ropes module for
# further information.
const MaxLineLength = 64
result = ""
var res = "\""
for i in 0..<s.len:
if (i + 1) mod MaxLineLength == 0:
res.add('\"')
res.add("\n")
result.add(rope(res))
res = "\"" # reset
res.add(toYamlChar(s[i]))
res.add('\"')
result.add(rope(res))
proc flagsToStr[T](flags: set[T]): Rope =
if flags == {}:
result = rope("[]")
else:
result = ""
for x in items(flags):
if result != "": result.add(", ")
result.add(makeYamlString($x))
result = "[" & result & "]"
proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope =
result = "[$1, $2, $3]" % [makeYamlString(toFilename(conf, info)),
rope(toLinenumber(info)),
rope(toColumn(info))]
proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet,
indent, maxRecDepth: int): Rope
proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet,
indent, maxRecDepth: int): Rope
proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet,
indent, maxRecDepth: int): Rope
proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
if n == nil:
result = rope("null")
elif containsOrIncl(marker, n.id):
result = "\"$1\"" % [rope(n.name.s)]
else:
var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1)
#rope("typ"), typeToYamlAux(conf, n.typ, marker,
# indent + 2, maxRecDepth - 1),
let istr = rspaces(indent + 2)
result = rope("{")
result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
result.addf("$N$1\"name\": $2", [istr, makeYamlString(n.name.s)])
result.addf("$N$1\"typ\": $2", [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth - 1)])
if conf != nil:
# if we don't pass the config, we probably don't care about the line info
result.addf("$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
if card(n.flags) > 0:
result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
result.addf("$N$1\"magic\": $2", [istr, makeYamlString($n.magic)])
result.addf("$N$1\"ast\": $2", [istr, ast])
result.addf("$N$1\"options\": $2", [istr, flagsToStr(n.options)])
result.addf("$N$1\"position\": $2", [istr, rope(n.position)])
result.addf("$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)])
result.addf("$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)])
if card(n.loc.flags) > 0:
result.addf("$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)])
result.addf("$N$1\"r\": $2", [istr, n.loc.r])
result.addf("$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)])
result.addf("$N$1}", [rspaces(indent)])
proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
var sonsRope: Rope
if n == nil:
result = ""
sonsRope = rope("null")
elif containsOrIncl(marker, n.id):
result = ""
sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
strutils.toHex(cast[int](n), sizeof(n) * 2))]
else:
if n.len > 0:
sonsRope = rope("[")
for i in 0..<n.len:
if i > 0: sonsRope.add(",")
sonsRope.addf("$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n[i],
marker, indent + 4, maxRecDepth - 1)])
sonsRope.addf("$N$1]", [rspaces(indent + 2)])
else:
sonsRope = rope("null")
let istr = rspaces(indent + 2)
result = rope("{")
result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
result.addf("$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)])
result.addf("$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)])
if card(n.flags) > 0:
result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
result.addf("$N$1\"callconv\": $2", [istr, makeYamlString($n.callConv)])
result.addf("$N$1\"size\": $2", [istr, rope(n.size)])
result.addf("$N$1\"align\": $2", [istr, rope(n.align)])
result.addf("$N$1\"sons\": $2", [istr, sonsRope])
proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
if n == nil:
result = rope("null")
else:
var istr = rspaces(indent + 2)
result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
if maxRecDepth != 0:
if conf != nil:
result.addf(",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
case n.kind
of nkCharLit..nkUInt64Lit:
result.addf(",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
result.addf(",$N$1\"floatVal\": $2",
[istr, rope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit:
result.addf(",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
result.addf(",$N$1\"sym\": $2",
[istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)])
of nkIdent:
if n.ident != nil:
result.addf(",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
else:
result.addf(",$N$1\"ident\": null", [istr])
else:
if n.len > 0:
result.addf(",$N$1\"sons\": [", [istr])
for i in 0..<n.len:
if i > 0: result.add(",")
result.addf("$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n[i],
marker, indent + 4, maxRecDepth - 1)])
result.addf("$N$1]", [istr])
result.addf(",$N$1\"typ\": $2",
[istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
result.addf("$N$1}", [rspaces(indent)])
proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
result = treeToYamlAux(conf, n, marker, indent, maxRecDepth)
proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
result = typeToYamlAux(conf, n, marker, indent, maxRecDepth)
proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
result = symToYamlAux(conf, n, marker, indent, maxRecDepth)
import std/tables
const backrefStyle = "\e[90m"
@@ -384,12 +573,14 @@ proc value(this: var DebugPrinter; value: PType) =
this.key "n"
this.value value.n
this.key "sons"
this.openBracket
for i, a in value.ikids:
if i > 0: this.comma
this.value a
this.closeBracket
if value.len > 0:
this.key "sons"
this.openBracket
for i in 0..<value.len:
this.value value[i]
if i != value.len - 1:
this.comma
this.closeBracket
if value.n != nil:
this.key "n"
@@ -458,33 +649,30 @@ proc value(this: var DebugPrinter; value: PNode) =
proc debug(n: PSym; conf: ConfigRef) =
var this = DebugPrinter(
visited: initTable[pointer, int](),
renderSymType: true,
useColor: not defined(windows)
)
var this: DebugPrinter
this.visited = initTable[pointer, int]()
this.renderSymType = true
this.useColor = not defined(windows)
this.value(n)
echo($this.res)
proc debug(n: PType; conf: ConfigRef) =
var this = DebugPrinter(
visited: initTable[pointer, int](),
renderSymType: true,
useColor: not defined(windows)
)
var this: DebugPrinter
this.visited = initTable[pointer, int]()
this.renderSymType = true
this.useColor = not defined(windows)
this.value(n)
echo($this.res)
proc debug(n: PNode; conf: ConfigRef) =
var this = DebugPrinter(
visited: initTable[pointer, int](),
renderSymType: false,
useColor: not defined(windows)
)
var this: DebugPrinter
this.visited = initTable[pointer, int]()
#this.renderSymType = true
this.useColor = not defined(windows)
this.value(n)
echo($this.res)
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
proc nextTry(h, maxHash: Hash): Hash =
result = ((5 * h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
@@ -707,75 +895,124 @@ proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
result = nextIter(ti, tab)
iterator items*(tab: TStrTable): PSym =
var it: TTabIter = default(TTabIter)
var it: TTabIter
var s = initTabIter(it, tab)
while s != nil:
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 =
proc hasEmptySlot(data: TIdPairSeq): bool =
for h in 0..high(data):
if isNil(data[h].key):
if data[h].key == nil:
return true
result = false
proc idTableRawGet[T](t: TIdTable[T], key: int): int =
proc idTableRawGet(t: TIdTable, 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:
while t.data[h].key != nil:
if t.data[h].key.id == 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))
proc idTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool =
var index = idTableRawGet(t, key.id)
if index >= 0: result = t.data[index].key == key
else: result = false
proc idTableGet(t: TIdTable, key: PIdObj): RootRef =
var index = idTableRawGet(t, key.id)
if index >= 0: result = t.data[index].val
else: result = default(T)
else: result = nil
template idTableGet*[T](t: TIdTable[T], key: PType | PSym): T =
getOrDefault(t, key.itemId)
proc idTableGet(t: TIdTable, key: int): RootRef =
var index = idTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc idTableRawInsert[T](data: var TIdPairSeq[T], key: ItemId, val: T) =
iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
for i in 0..high(t.data):
if t.data[i].key != nil:
yield (t.data[i].key.id, t.data[i].val)
proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
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 = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
h = nextTry(h, high(data))
assert(isNil(data[h].key))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc `[]=`*[T](t: var TIdTable[T], key: ItemId, val: T) =
proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
var
index: int
n: TIdPairSeq[T]
index = idTableRawGet(t, toId(key))
n: TIdPairSeq
index = idTableRawGet(t, key.id)
if index >= 0:
assert(not isNil(t.data[index].key))
assert(t.data[index].key != nil)
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):
if t.data[i].key != nil:
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] =
iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
for i in 0..high(t.data):
if not isNil(t.data[i].key):
yield (t.data[i].key, t.data[i].val)
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
var h: Hash
h = key.id and high(t.data) # start with real hash value
while t.data[h].key != nil:
if t.data[h].key.id == key.id:
return h
h = nextTry(h, high(t.data))
result = - 1
proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
var index: int
index = idNodeTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: Hash
h = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
h = nextTry(h, high(data))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
var index = idNodeTableRawGet(t, key)
if index >= 0:
assert(t.data[index].key != nil)
t.data[index].val = val
else:
if mustRehash(t.data.len, t.counter):
var n: TIdNodePairSeq
newSeq(n, t.data.len * GrowthFactor)
for i in 0..high(t.data):
if t.data[i].key != nil:
idNodeTableRawInsert(n, t.data[i].key, t.data[i].val)
swap(t.data, n)
idNodeTableRawInsert(t.data, key, val)
inc(t.counter)
iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
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
@@ -822,6 +1059,14 @@ proc iiTablePut(t: var TIITable, key, val: int) =
iiTableRawInsert(t.data, key, val)
inc(t.counter)
proc isAddrNode*(n: PNode): bool =
case n.kind
of nkAddr, nkHiddenAddr: true
of nkCallKinds:
if n[0].kind == nkSym and n[0].sym.magic == mAddr: true
else: false
else: false
proc listSymbolNames*(symbols: openArray[PSym]): string =
result = ""
for sym in symbols:

View File

@@ -5,7 +5,7 @@ import options, ast, msgs
proc typSym*(t: PType): PSym =
result = t.sym
if result == nil and t.kind == tyGenericInst: # this might need to be refined
result = t.genericHead.sym
result = t[0].sym
proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) =
result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)]
@@ -24,12 +24,6 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
result.add "]"
proc addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
result.add " [$1" % typ.kind.toHumanStr
if typ.sym != nil:
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
result.add "]"
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)

View File

@@ -1,160 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# AST YAML printing
import "."/[ast, lineinfos, msgs, options, rodutils]
import std/[intsets, strutils]
proc addYamlString*(res: var string; s: string) =
res.add "\""
for c in s:
case c
of '\0' .. '\x1F', '\x7F' .. '\xFF':
res.add("\\u" & strutils.toHex(ord(c), 4))
of '\"', '\\':
res.add '\\' & c
else:
res.add c
res.add('\"')
proc makeYamlString(s: string): string =
result = ""
result.addYamlString(s)
proc flagsToStr[T](flags: set[T]): string =
if flags == {}:
result = "[]"
else:
result = ""
for x in items(flags):
if result != "":
result.add(", ")
result.addYamlString($x)
result = "[" & result & "]"
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): string =
result = "["
result.addYamlString(toFilename(conf, 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 symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; nl: bool, indent: int; maxRecDepth: int) =
if n == nil:
res.add("null")
elif containsOrIncl(marker, n.id):
res.addYamlString(n.name.s)
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)
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)])
if card(n.flags) > 0:
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.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)])
res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)])
if card(n.loc.flags) > 0:
res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
res.addf("\n$1lode: ", [istr])
res.treeToYamlAux(conf, n.loc.lode, marker, true, indent + 1, maxRecDepth - 1)
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; nl: bool, 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)
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)
for a in n.kids:
res.addf("\n$1 - ", istr)
res.typeToYamlAux(conf, a, marker, false, indent + 1, maxRecDepth - 1)
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; nl: bool, 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:
res.addf("\n$1intVal: $2", [istr, $(n.intVal)])
of nkFloatLit .. nkFloat128Lit:
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)
of nkIdent:
if n.ident != nil:
res.addf("\n$1ident: $2", [istr, makeYamlString(n.ident.s)])
else:
res.addf("\n$1ident: null", [istr])
else:
if n.len > 0:
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)
if n.typ != nil:
res.addf("\n$1typ: ", [istr])
res.typeToYamlAux(conf, n.typ, marker, true, 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)
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)
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)

View File

@@ -0,0 +1,25 @@
import pragmas, options, ast, trees
proc pushBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
optionsStack.add options
proc popBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
options = optionsStack[^1]
optionsStack.setLen(optionsStack.len-1)
proc processPushBackendOption*(optionsStack: var seq[TOptions], options: var TOptions,
n: PNode, start: int) =
pushBackendOption(optionsStack, options)
for i in start..<n.len:
let it = n[i]
if it.kind in nkPragmaCallKinds and it.len == 2 and it[1].kind == nkIntLit:
let sw = whichPragma(it[0])
let opts = pragmaToOptions(sw)
if opts != {}:
if it[1].intVal != 0:
options.incl opts
else:
options.excl opts
template processPopBackendOption*(optionsStack: var seq[TOptions], options: var TOptions) =
popBackendOption(optionsStack, options)

View File

@@ -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")

View File

@@ -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
@@ -80,23 +76,6 @@ proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool =
else:
result = false
proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
if returnType.kind in {tyVar, tyLent}:
# we don't need to worry about var/lent return types
result = false
elif hasDestructor(returnType) and getAttachedOp(p.module.g.graph, returnType, attachedDestructor) != nil:
let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor)
var op = initLocExpr(p, newSymNode(dtor))
var callee = rdLoc(op)
let destroy = if dtor.typ.firstParamType.kind == tyVar:
callee & "(&" & rdLoc(tmp) & ")"
else:
callee & "(" & rdLoc(tmp) & ")"
raiseExitCleanup(p, destroy)
result = true
else:
result = false
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
callee, params: Rope) =
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
@@ -104,17 +83,13 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
var pl = callee & "(" & params
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
if typ.returnType != nil:
var flags: TAssignmentFlags = {}
if typ.returnType.kind in {tyOpenArray, tyVarargs}:
# perhaps generate no temp if the call doesn't have side effects
flags.incl needTempForOpenArray
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if params.len != 0: pl.add(", ")
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
# Great, we can use 'd':
if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
if d.k == locNone: d = getTemp(p, typ[0], needsInit=true)
elif d.k notin {locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
discard "resetLoc(p, d)"
@@ -122,7 +97,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
pl.add(");\n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var tmp: TLoc = getTemp(p, typ[0], needsInit=true)
pl.add(addrLoc(p.config, tmp))
pl.add(");\n")
line(p, cpsStmts, pl)
@@ -136,56 +111,43 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.snippet = pl
d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
d = getTempCpp(p, typ.returnType, pl)
if d.k == locNone and p.splitDecls == 0:
d = getTempCpp(p, typ[0], pl)
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: d = getTemp(p, typ[0])
var list = initLoc(locCall, d.lode, OnUnknown)
list.snippet = pl
genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
elif isHarmlessStore(p, canRaise, d):
var useTemp = false
if d.k == locNone:
useTemp = true
d = getTemp(p, typ.returnType)
if d.k == locNone: d = getTemp(p, typ[0])
assert(d.t != nil) # generate an assignment to d:
var list = initLoc(locCall, d.lode, OnUnknown)
list.snippet = pl
genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying
if canRaise:
if not (useTemp and cleanupTemp(p, typ.returnType, d)):
raiseExit(p)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var tmp: TLoc = getTemp(p, typ[0], needsInit=true)
var list = initLoc(locCall, d.lode, OnUnknown)
list.snippet = pl
genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying
if canRaise:
if not cleanupTemp(p, typ.returnType, tmp):
raiseExit(p)
list.r = pl
genAssignment(p, tmp, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {})
else:
pl.add(");\n")
line(p, cpsStmts, pl)
if canRaise: raiseExit(p)
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
proc reifiedOpenArray(n: PNode): bool {.inline.} =
var x = n
while true:
case x.kind
of {nkAddr, nkHiddenAddr, nkHiddenDeref}:
x = x[0]
of nkHiddenStdConv:
x = x[1]
else:
break
while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}:
x = x[0]
if x.kind == nkSym and x.sym.kind == skParam:
result = false
else:
@@ -195,15 +157,12 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
var a = initLocExpr(p, q[1])
var b = initLocExpr(p, q[2])
var c = initLocExpr(p, q[3])
# bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
# are mapped into ctPtrToArray, the dereference of which is skipped
# in the `genDeref`. We need to skip these ptrs here
let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
# but first produce the required index checks:
if optBoundsCheck in p.options:
genBoundsCheck(p, a, b, c, ty)
genBoundsCheck(p, a, b, c)
if prepareForMutation:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
let ty = skipTypes(a.t, abstractVar+{tyPtr})
let dest = getTypeDesc(p.module, destType)
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
case ty.kind
@@ -235,11 +194,11 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if atyp.kind in {tyVar} and not compileToCpp(p.module):
result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
[rdLoc(a), rdLoc(b), dataField(p, ty.kind == tyString), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
lengthExpr)
else:
result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
[rdLoc(a), rdLoc(b), dataField(p, ty.kind == tyString), dest, dataFieldAccessor(p, rdLoc(a))],
lengthExpr)
else:
result = ("", "")
@@ -259,11 +218,12 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
for i in 0..<q.len-1:
genStmts(p, q[i])
q = q.lastSon
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType)
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
result.add x & ", " & y
else:
var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
case skipTypes(a.t, abstractVar+{tyStatic}).kind
let typKind = skipTypes(a.t, abstractVar+{tyStatic}).kind
case typKind
of tyOpenArray, tyVarargs:
if reifiedOpenArray(n):
if a.t.kind in {tyVar, tyLent}:
@@ -278,24 +238,26 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
[a.rdLoc, lenExpr(p, t, typKind == tyString), dataField(p, typKind == tyString),
dataFieldAccessor(p, "*" & a.rdLoc)]
else:
result.add "($4) ? ($1$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
[a.rdLoc, lenExpr(p, a, typKind == tyString), dataField(p, typKind == tyString), dataFieldAccessor(p, a.rdLoc)]
of tyArray:
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
of tyPtr, tyRef:
case elementType(a.t).kind
case lastSon(a.t).kind
of tyString, tySequence:
var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
[a.rdLoc, lenExpr(p, t, typKind == tyString), dataField(p, typKind == tyString),
dataFieldAccessor(p, "*" & a.rdLoc)]
of tyArray:
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, elementType(a.t)))]
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
else:
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
@@ -311,7 +273,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, {})
@@ -326,42 +288,27 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
var n = if n.kind != nkHiddenAddr: n else: n[0]
openArrayLoc(p, param.typ, n, result)
elif ccgIntroducedPtr(p.config, param, call[0].typ.returnType) and
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]) and
(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
n.kind == nkHiddenAddr:
# bug #23748: we need to introduce a temporary here. The expression type
# will be a reference in C++ and we cannot create a temporary reference
# variable. Thus, we create a temporary pointer variable instead.
let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
if needsIndirect:
n.typ() = n.typ.exactReplica
n.typ.flags.incl tfVarIsPtr
a = initLocExprSingleUse(p, n)
a = withTmpIfNeeded(p, a, needsTmp)
if needsIndirect: a.flags.incl lfIndirect
a = initLocExprSingleUse(p, n[0])
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
# means '*T'. See posix.nim for lots of examples that do that in the wild.
let callee = call[0]
if callee.kind == nkSym and
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and
needsIndirect:
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
addAddrLoc(p.config, a, result)
else:
addRdLoc(a, result)
else:
a = initLocExprSingleUse(p, n)
if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
let typ = skipTypes(param.typ, abstractPtrs)
if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
a.snippet = "(($1) ($2))" %
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
#assert result != nil
@@ -406,7 +353,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
of nkCallKinds:
case n.getMagic:
of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mAddr, mNew, mNewFinalize, mWasMoved, mDestroy:
mAddr, mNew, mNewFinalize, mWasMoved, mDestroy, mReset:
getPotentialWrites(n[1], true, result)
for i in 2..<n.len:
getPotentialWrites(n[i], mutate, result)
@@ -444,15 +391,13 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
if not needTmp[i - 1]:
needTmp[i - 1] = potentialAlias(n, potentialWrites)
getPotentialWrites(ri[i], false, potentialWrites)
when false:
# this optimization is wrong, see bug #23748
if ri[i].kind in {nkHiddenAddr, nkAddr}:
# Optimization: don't use a temp, if we would only take the address anyway
needTmp[i - 1] = false
if ri[i].kind in {nkHiddenAddr, nkAddr}:
# Optimization: don't use a temp, if we would only take the address anyway
needTmp[i - 1] = false
var oldLen = result.len
for i in 1..<ri.len:
if i < typ.n.len:
if i < typ.len:
assert(typ.n[i].kind == nkSym)
let paramType = typ.n[i]
if not paramType.typ.isCompileTimeOnly:
@@ -477,6 +422,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInstOwned)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
var params = newRopeAppender()
genParams(p, ri, typ, params)
@@ -499,6 +445,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInstOwned)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
var pl = newRopeAppender()
genParams(p, ri, typ, pl)
@@ -511,14 +458,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
let rawProc = getClosureType(p.module, typ, clHalf)
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
if typ.returnType != nil:
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if ri.len > 1: pl.add(", ")
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
# Great, we can use 'd':
if d.k == locNone:
d = getTemp(p, typ.returnType, needsInit=true)
d = getTemp(p, typ[0], needsInit=true)
elif d.k notin {locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
discard "resetLoc(p, d)"
@@ -526,29 +473,29 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
genCallPattern()
if canRaise: raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var tmp: TLoc = getTemp(p, typ[0], needsInit=true)
pl.add(addrLoc(p.config, tmp))
genCallPattern()
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {}) # no need for deep copying
elif isHarmlessStore(p, canRaise, d):
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: d = getTemp(p, typ[0])
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType)
var tmp: TLoc = getTemp(p, typ[0])
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, tmp, list, {})
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {})
@@ -558,14 +505,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
argsCounter: var int) =
if i < typ.n.len:
if i < typ.len:
# 'var T' is 'T&' in C++. This means we ignore the request of
# any nkHiddenAddr when it's a 'var T'.
let paramType = typ.n[i]
assert(paramType.kind == nkSym)
if paramType.typ.isCompileTimeOnly:
discard
elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
if argsCounter > 0: result.add ", "
genArgNoParam(p, ri[i][0], result)
inc argsCounter
@@ -640,7 +587,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
# for better or worse c2nim translates the 'this' argument to a 'var T'.
# However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience.
internalAssert p.config, i < typ.n.len
internalAssert p.config, i < typ.len
assert(typ.n[i].kind == nkSym)
# if the parameter is lying (tyVar) and thus we required an additional deref,
# skip the deref:
@@ -730,27 +677,28 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.snippet
let pat = $ri[0].sym.loc.r
internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@', '\''}):
var pl = newRopeAppender()
genPatternCall(p, ri, pat, typ, pl)
# simpler version of 'fixupCall' that works with the pl+params combination:
var typ = skipTypes(ri[0].typ, abstractInst)
if typ.returnType != nil:
if typ[0] != nil:
if p.module.compileToCpp and lfSingleUse in d.flags:
# do not generate spurious temporaries for C++! For C we're better off
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.snippet = pl
d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: d = getTemp(p, typ[0])
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
list.snippet = pl
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add(";\n")
@@ -760,9 +708,10 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var argsCounter = 0
if 1 < ri.len:
genThisArg(p, ri, 1, typ, pl)
pl.add(op.snippet)
pl.add(op.r)
var params = newRopeAppender()
for i in 2..<ri.len:
assert(typ.len == typ.n.len)
genOtherArg(p, ri, i, typ, params, argsCounter)
fixupCall(p, le, ri, d, pl, params)
@@ -773,14 +722,15 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.snippet
let pat = $ri[0].sym.loc.r
internalAssert p.config, pat.len > 0
var start = 3
if ' ' in pat:
start = 1
pl.add(op.snippet)
pl.add(op.r)
if ri.len > 1:
pl.add(": ")
genArg(p, ri[1], typ.n[1].sym, ri, pl)
@@ -789,12 +739,13 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
if ri.len > 1:
genArg(p, ri[1], typ.n[1].sym, ri, pl)
pl.add(" ")
pl.add(op.snippet)
pl.add(op.r)
if ri.len > 2:
pl.add(": ")
genArg(p, ri[2], typ.n[2].sym, ri, pl)
for i in start..<ri.len:
if i >= typ.n.len:
assert(typ.len == typ.n.len)
if i >= typ.len:
internalError(p.config, ri.info, "varargs for objective C method?")
assert(typ.n[i].kind == nkSym)
var param = typ.n[i].sym
@@ -802,29 +753,29 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
pl.add(param.name.s)
pl.add(": ")
genArg(p, ri[i], param, ri, pl)
if typ.returnType != nil:
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if ri.len > 1: pl.add(" ")
# beware of 'result = p(result)'. We always allocate a temporary:
if d.k in {locTemp, locNone}:
# We already got a temp. Great, special case it:
if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
if d.k == locNone: d = getTemp(p, typ[0], needsInit=true)
pl.add("Result: ")
pl.add(addrLoc(p.config, d))
pl.add("];\n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var tmp: TLoc = getTemp(p, typ[0], needsInit=true)
pl.add(addrLoc(p.config, tmp))
pl.add("];\n")
line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying
else:
pl.add("]")
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: d = getTemp(p, typ[0])
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, ri, OnUnknown)
list.snippet = pl
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add("];\n")

File diff suppressed because it is too large Load Diff

View File

@@ -94,6 +94,48 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Ro
pureLit = m.tmpBase & rope(id)
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
# ------ Version 3: destructor based strings and seqs -----------------------
# strings are enhanced by interned strings
proc toConstLenV3(len: int): string =
result = rope((len shl 1) or 1)
proc genStringLiteralDataOnlyV3(m: BModule, s: string; result: Rope; isConst: bool) =
# TODO: fixme: perhaps use makeCString for clarity for C
m.s[cfsStrData].addf("static $4 NIM_CHAR $1[$2] = $3;$n",
[result, rope(s.len), makeCCharArray(s),
rope(if isConst: "const" else: "")])
proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
if id == m.labels:
let pureLit = getTempName(m)
genStringLiteralDataOnlyV3(m, n.strVal, pureLit, isConst)
let tmp = getTempName(m)
result.add tmp
cgsym(m, "NimStringV3")
# string literal not found in the cache:
m.s[cfsStrData].addf("static $4 NimStringV3 $1 = {$2, (NIM_CHAR*)&$3};$n",
[tmp, toConstLenV3(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
else:
let tmp = getTempName(m)
result.add tmp
m.s[cfsStrData].addf("static $4 NimStringV3 $1 = {$2, (NIM_CHAR*)&$3};$n",
[tmp, toConstLenV3(n.strVal.len), m.tmpBase & rope(id),
rope(if isConst: "const" else: "")])
proc genStringLiteralV3Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var pureLit: Rope
if id == m.labels:
pureLit = getTempName(m)
cgsym(m, "NimStringV3")
# string literal not found in the cache:
genStringLiteralDataOnlyV3(m, n.strVal, pureLit, isConst)
else:
pureLit = m.tmpBase & rope(id)
result.addf "{$1, (NIM_CHAR*)&$2}", [toConstLenV3(n.strVal.len), pureLit]
# ------ Version selector ---------------------------------------------------
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
@@ -104,6 +146,10 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
let tmp = getTempName(m)
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
result.add tmp
of 3:
let tmp = getTempName(m)
genStringLiteralDataOnlyV3(m, s, tmp, isConst)
result.add tmp
else:
localError(m.config, info, "cannot determine how to produce code for string literal")
@@ -114,5 +160,6 @@ proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
case detectStrVersion(m)
of 0, 1: genStringLiteralV1(m, n, result)
of 2: genStringLiteralV2(m, n, isConst = true, result)
of 3: genStringLiteralV3(m, n, isConst = true, result)
else:
localError(m.config, n.info, "cannot determine how to produce code for string literal")

View File

@@ -24,10 +24,10 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
of nkRecCase:
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
let disc = n[0].sym
if disc.loc.snippet == "": fillObjectFields(p.module, typ)
if disc.loc.r == "": fillObjectFields(p.module, typ)
if disc.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
for i in 1..<n.len:
let branch = n[i]
assert branch.kind in {nkOfBranch, nkElse}
@@ -38,14 +38,14 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
specializeResetN(p, accessor, lastSon(branch), typ)
lineF(p, cpsStmts, "break;$n", [])
lineF(p, cpsStmts, "} $n", [])
specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t)
specializeResetT(p, "$1.$2" % [accessor, disc.loc.r], disc.loc.t)
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
if field.loc.snippet == "": fillObjectFields(p.module, typ)
if field.loc.r == "": fillObjectFields(p.module, typ)
if field.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
else: internalError(p.config, n.info, "specializeResetN()")
proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
@@ -54,45 +54,24 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
case typ.kind
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
tySink, tyOwned:
specializeResetT(p, accessor, skipModifier(typ))
specializeResetT(p, accessor, lastSon(typ))
of tyArray:
let arraySize = lengthOrd(p.config, typ.indexType)
let arraySize = lengthOrd(p.config, typ[0])
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.snippet, arraySize])
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
[i.r, arraySize])
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ[1])
lineF(p, cpsStmts, "}$n", [])
of tyObject:
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)])
for i in 0..<typ.len:
var x = typ[i]
if x != nil: x = x.skipTypes(skipPtrs)
specializeResetT(p, accessor.parentObj(p.module), x)
if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
of tyTuple:
let typ = getUniqueType(typ)
for i, a in typ.ikids:
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
for i in 0..<typ.len:
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), typ[i])
of tyString, tyRef, tySequence:
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
@@ -103,7 +82,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
else:
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
of tyChar, tyBool, tyEnum, tyInt..tyUInt64:
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
@@ -117,8 +96,8 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
else:
raiseAssert "unexpected set type kind"
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs,
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
discard

View File

@@ -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)
@@ -110,10 +110,10 @@ proc genVarTuple(p: BProc, n: PNode) =
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
var field = initLoc(locExpr, vn, tup.storage)
if t.kind == tyTuple:
field.snippet = "$1.Field$2" % [rdLoc(tup), rope(i)]
field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
else:
if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
field.snippet = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
putLocIntoDest(p, v.loc, field)
if forHcr or isGlobalInBlock:
hcrGlobals.add((loc: v.loc, tp: "NULL"))
@@ -128,7 +128,7 @@ proc genVarTuple(p: BProc, n: PNode) =
lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", [hcrCond])
for curr in hcrGlobals:
lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N",
[hcrCond, curr.loc.snippet, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
[hcrCond, curr.loc.r, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
@@ -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", [])
@@ -267,11 +267,11 @@ proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
if n[0].kind == nkClosure:
a = initLocExpr(p, n[0][1])
d.snippet = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
else:
a = initLocExpr(p, n[0])
# the environment is guaranteed to contain the 'state' field at offset 1:
d.snippet = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}:
@@ -289,32 +289,22 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
#echo "New code produced for ", v.name.s, " ", p.config $ value.info
genBracedInit(p, value, isConst = false, v.typ, result)
proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string =
proc genCppParamsForCtor(p: BProc; call: PNode): string =
result = ""
var argsCounter = 0
let typ = skipTypes(call[0].typ, abstractInst)
assert(typ.kind == tyProc)
for i in 1..<call.len:
assert(typ.len == typ.n.len)
#if it's a type we can just generate here another initializer as we are in an initializer context
if call[i].kind == nkCall and call[i][0].kind == nkSym and call[i][0].sym.kind == skType:
if argsCounter > 0: result.add ","
result.add genCppInitializer(p.module, p, call[i][0].sym.typ, didGenTemp)
result.add genCppInitializer(p.module, p, call[i][0].sym.typ)
else:
#We need to test for temp in globals, see: #23657
let param =
if typ[i].kind in {tyVar} and call[i].kind == nkHiddenAddr:
call[i][0]
else:
call[i]
if not param.typ.isCompileTimeOnly and (param.kind != nkBracketExpr or param.typ.kind in
{tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray,
tyVarargs, tySequence, tyString, tyCstring, tyTuple}):
let tempLoc = initLocExprSingleUse(p, param)
didGenTemp = didGenTemp or tempLoc.k == locTemp
genOtherArg(p, call, i, typ, result, argsCounter)
proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope, didGenTemp: var bool) =
let params = genCppParamsForCtor(p, call, didGenTemp)
proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) =
let params = genCppParamsForCtor(p, call)
if params.len == 0:
decl = runtimeFormat("$#;\n", [decl])
else:
@@ -341,14 +331,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
# v.owner.kind != skModule:
targetProc = p.module.preInitProc
if isCppCtorCall and not containsHiddenPointer(v.typ):
var didGenTemp = false
callGlobalVarCppCtor(targetProc, v, vn, value, didGenTemp)
if didGenTemp:
message(p.config, vn.info, warnGlobalVarConstructorTemporary, vn.sym.name.s)
#We fail to call the constructor in the global scope so we do the call inside the main proc
assignGlobalVar(targetProc, vn, valueAsRope)
var loc = initLocExprSingleUse(targetProc, value)
genAssignment(targetProc, v.loc, loc, {})
callGlobalVarCppCtor(targetProc, v, vn, value)
else:
assignGlobalVar(targetProc, vn, valueAsRope)
@@ -383,15 +366,11 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
var decl = localVarDecl(p, vn)
var tmp: TLoc
if isCppCtorCall:
var didGenTemp = false
genCppVarForCtor(p, value, decl, didGenTemp)
genCppVarForCtor(p, value, decl)
line(p, cpsStmts, decl)
else:
tmp = initLocExprSingleUse(p, value)
if value.kind == nkEmpty:
lineF(p, cpsStmts, "$#;\n", [decl])
else:
lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc])
lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc])
return
assignLocalVar(p, vn)
initLocalVar(p, v, imm)
@@ -405,7 +384,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
# put it in the locals section - mainly because of loops which
# use the var in a call to resetLoc() in the statements section
lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n",
[v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
# nothing special left to do later on - let's avoid closing and reopening blocks
forHcr = false
@@ -414,7 +393,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
# be able to re-run it but without the top level code - just the init of globals
if forHcr:
lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
[v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
startBlock(targetProc)
if value.kind != nkEmpty and valueAsRope.len == 0:
genLineDir(targetProc, vn)
@@ -509,8 +488,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.
@@ -756,39 +734,13 @@ proc raiseExit(p: BProc) =
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
[p.nestedTryStmts[^1].label])
proc raiseExitCleanup(p: BProc, destroy: string) =
assert p.config.exc == excGoto
if nimErrorFlagDisabled notin p.flags:
p.flags.incl nimErrorFlagAccessed
if p.nestedTryStmts.len == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$1; goto BeforeRet_;}$n", [destroy])
else:
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$2; goto LA$1_;}$n",
[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:
@@ -811,14 +763,10 @@ proc genRaiseStmt(p: BProc, t: PNode) =
var e = rdLoc(a)
discard getTypeDesc(p.module, t[0].typ)
var typ = skipTypes(t[0].typ, abstractPtrs)
case p.config.exc
of excCpp:
# XXX For reasons that currently escape me, this is only required by the new
# C++ based exception handling:
if p.config.exc == excCpp:
blockLeaveActions(p, howManyTrys = 0, howManyExcepts = p.inExceptBlockLen)
of excGoto:
blockLeaveActions(p, howManyTrys = 0,
howManyExcepts = (if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: 1 else: 0))
else:
discard
genLineDir(p, t)
if isImportedException(typ, p.config):
lineF(p, cpsStmts, "throw $1;$n", [e])
@@ -1074,7 +1022,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: */
@@ -1090,11 +1038,11 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
inc(p.labels, 2)
let etmp = p.labels
#init on locals, fixes #23306
lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp])
lineCg(p, cpsStmts, "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 +1073,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 +1202,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 +1271,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 +1423,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", [])
@@ -1542,12 +1489,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
var res = ""
let offset =
if isAsmStmt: 1 # first son is pragmas
else: 0
for i in offset..<t.len:
let it = t[i]
for it in t.sons:
case it.kind
of nkStrLit..nkTripleStrLit:
res.add(it.strVal)
@@ -1561,7 +1503,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
else:
discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
fillBackendName(p.module, sym)
res.add($sym.loc.snippet)
res.add($sym.loc.r)
of nkTypeOfExpr:
res.add($getTypeDesc(p.module, it.typ))
else:
@@ -1591,21 +1533,6 @@ proc genAsmStmt(p: BProc, t: PNode) =
assert(t.kind == nkAsmStmt)
genLineDir(p, t)
var s = newRopeAppender()
var asmSyntax = ""
if (let p = t[0]; p.kind == nkPragma):
for i in p:
if whichPragma(i) == wAsmSyntax:
asmSyntax = i[1].strVal
if asmSyntax != "" and
not (
asmSyntax == "gcc" and hasGnuAsm in CC[p.config.cCompiler].props or
asmSyntax == "vcc" and hasGnuAsm notin CC[p.config.cCompiler].props):
localError(
p.config, t.info,
"Your compiler does not support the specified inline assembler")
genAsmOrEmitStmt(p, t, isAsmStmt=true, s)
# see bug #2362, "top level asm statements" seem to be a mis-feature
# but even if we don't do this, the example in #2362 cannot possibly
@@ -1643,9 +1570,9 @@ proc genPragma(p: BProc, n: PNode) =
case whichPragma(it)
of wEmit: genEmit(p, it)
of wPush:
processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
processPushBackendOption(p.optionsStack, p.options, n, i+1)
of wPop:
processPopBackendOption(p.config, p.optionsStack, p.options)
processPopBackendOption(p.optionsStack, p.options)
else: discard

View File

@@ -30,7 +30,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
# allocator for it :-(
if not containsOrIncl(m.g.nimtvDeclared, s.id):
m.g.nimtvDeps.add(s.loc.t)
m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.snippet])
m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
else:
if isExtern: m.s[cfsVars].add("extern ")
elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
@@ -41,7 +41,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
m.s[cfsVars].add("NIM_THREAD_LOCAL ")
else: m.s[cfsVars].add("NIM_THREADVAR ")
m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
m.s[cfsVars].addf(" $1;$n", [s.loc.snippet])
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
proc generateThreadLocalStorage(m: BModule) =
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):

View File

@@ -34,10 +34,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
var p = c.p
let disc = n[0].sym
if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
if disc.loc.r == "": fillObjectFields(c.p.module, typ)
if disc.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
for i in 1..<n.len:
let branch = n[i]
assert branch.kind in {nkOfBranch, nkElse}
@@ -51,10 +51,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
if field.loc.r == "": fillObjectFields(c.p.module, typ)
if field.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")
genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
else: internalError(c.p.config, n.info, "genTraverseProc()")
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
@@ -71,36 +71,37 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
case typ.kind
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
tySink, tyOwned:
genTraverseProc(c, accessor, skipModifier(typ))
genTraverseProc(c, accessor, lastSon(typ))
of tyArray:
let arraySize = lengthOrd(c.p.config, typ.indexType)
let arraySize = lengthOrd(c.p.config, typ[0])
var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
var oldCode = p.s(cpsStmts)
freeze oldCode
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.snippet, arraySize])
[i.r, arraySize])
let oldLen = p.s(cpsStmts).len
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ[1])
if p.s(cpsStmts).len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
else:
lineF(p, cpsStmts, "}$n", [])
of tyObject:
var x = typ.baseClass
if x != nil: x = x.skipTypes(skipPtrs)
genTraverseProc(c, accessor.parentObj(c.p.module), x)
for i in 0..<typ.len:
var x = typ[i]
if x != nil: x = x.skipTypes(skipPtrs)
genTraverseProc(c, accessor.parentObj(c.p.module), x)
if typ.n != nil: genTraverseProc(c, accessor, typ.n, typ)
of tyTuple:
let typ = getUniqueType(typ)
for i, a in typ.ikids:
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a)
for i in 0..<typ.len:
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), typ[i])
of tyRef:
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
of tySequence:
if optSeqDestructors notin c.p.module.config.globalOptions:
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
elif containsGarbageCollectedRef(typ.elementType):
elif containsGarbageCollectedRef(typ.lastSon):
# destructor based seqs are themselves not traced but their data is, if
# they contain a GC'ed type:
lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
@@ -117,15 +118,15 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
var p = c.p
assert typ.kind == tySequence
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
var oldCode = p.s(cpsStmts)
freeze oldCode
var a = TLoc(snippet: accessor)
var a: TLoc = TLoc(r: accessor)
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.snippet, lenExpr(c.p, a)])
[i.r, lenExpr(c.p, a)])
let oldLen = p.s(cpsStmts).len
genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType)
genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ[0])
if p.s(cpsStmts).len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
@@ -133,6 +134,7 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
lineF(p, cpsStmts, "}$n", [])
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
var c: TTraversalClosure
var p = newProc(nil, m)
result = "Marker_" & getTypeName(m, origTyp, sig)
let
@@ -145,19 +147,18 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
lineF(p, cpsLocals, "$1 a;$n", [t])
lineF(p, cpsInit, "a = ($1)p;$n", [t])
var c = TTraversalClosure(p: p,
visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
)
c.p = p
c.visitorFrmt = "op" # "#nimGCvisit((void*)$1, op);$n"
assert typ.kind != tyTypeDesc
if typ.kind == tySequence:
genTraverseProcSeq(c, "a".rope, typ)
else:
if skipTypes(typ.elementType, typedescInst+{tyOwned}).kind == tyArray:
if skipTypes(typ[0], typedescInst+{tyOwned}).kind == tyArray:
# C's arrays are broken beyond repair:
genTraverseProc(c, "a".rope, typ.elementType)
genTraverseProc(c, "a".rope, typ[0])
else:
genTraverseProc(c, "(*a)".rope, typ.elementType)
genTraverseProc(c, "(*a)".rope, typ[0])
let generatedProc = "$1 {$n$2$3$4}\n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
@@ -173,6 +174,7 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
discard genTypeInfoV1(m, s.loc.t, info)
var c: TTraversalClosure
var p = newProc(nil, m)
var sLoc = rdLoc(s.loc)
result = getTempName(m)
@@ -181,10 +183,8 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
accessThreadLocalVar(p, s)
sLoc = "NimTV_->" & sLoc
var c = TTraversalClosure(p: p,
visitorFrmt: "0" # "#nimGCvisit((void*)$1, 0);$n"
)
c.visitorFrmt = "0" # "#nimGCvisit((void*)$1, 0);$n"
c.p = p
let header = "static N_NIMCALL(void, $1)(void)" % [result]
genTraverseProc(c, sLoc, s.loc.t)

File diff suppressed because it is too large Load Diff

View File

@@ -11,9 +11,9 @@
import
ast, types, msgs, wordrecg,
platform, trees, options, cgendata, mangleutils, renderer
platform, trees, options, cgendata
import std/[hashes, strutils, formatfloat]
import std/[hashes, strutils]
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -68,6 +68,53 @@ proc makeSingleLineCString*(s: string): string =
c.toCChar(result)
result.add('\"')
proc mangle*(name: string): string =
result = newStringOfCap(name.len)
var start = 0
if name[0] in Digits:
result.add("X" & name[0])
start = 1
var requiresUnderscore = false
template special(x) =
result.add x
requiresUnderscore = true
for i in start..<name.len:
let c = name[i]
case c
of 'a'..'z', '0'..'9', 'A'..'Z':
result.add(c)
of '_':
# we generate names like 'foo_9' for scope disambiguations and so
# disallow this here:
if i > 0 and i < name.len-1 and name[i+1] in Digits:
discard
else:
result.add(c)
of '$': special "dollar"
of '%': special "percent"
of '&': special "amp"
of '^': special "roof"
of '!': special "emark"
of '?': special "qmark"
of '*': special "star"
of '+': special "plus"
of '-': special "minus"
of '/': special "slash"
of '\\': special "backslash"
of '=': special "eq"
of '<': special "lt"
of '>': special "gt"
of '~': special "tilde"
of ':': special "colon"
of '.': special "dot"
of '@': special "at"
of '|': special "bar"
else:
result.add("X" & toHex(ord(c), 2))
requiresUnderscore = true
if requiresUnderscore:
result.add "_"
proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
case int(getSize(conf, typ))
of 1: result = ctInt8
@@ -79,10 +126,10 @@ proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
var pt = skipTypes(s.typ, typedescInst)
assert skResult != s.kind
#note precedence: params override types
if optByRef in s.options: return true
elif sfByCopy in s.flags: return false
elif sfByCopy in s.flags: return false
elif tfByRef in pt.flags: return true
elif tfByCopy in pt.flags: return false
case pt.kind
@@ -90,9 +137,6 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
if s.typ.sym != nil and sfForward in s.typ.sym.flags:
# forwarded objects are *always* passed by pointers for consistency!
result = true
elif s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
# bug #23354:
result = false
elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
result = true # requested anyway
elif (tfFinal in pt.flags) and (pt[0] == nil):
@@ -109,67 +153,3 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
pt.kind == tySet and mapSetType(conf, pt) == ctArray)
proc encodeName*(name: string): string =
result = mangle(name)
result = $result.len & result
proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
result = if name == "": s.name.s else: name
result.add "__"
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
result.add "_u"
result.add $s.itemId.item
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string =
#Module::Type
var name = s.name.s & extra
if makeUnique:
name = makeUnique(m, s, name)
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
result = ""
var kindName = ($t.kind)[2..^1]
kindName[0] = toLower($kindName[0])[0]
case t.kind
of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
result = encodeSym(m, t.sym)
of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
result = encodeName(t[0].sym.name.s)
result.add "I"
for i in 1..<t.len - 1:
result.add encodeType(m, t[i], staticLists)
result.add "E"
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
tyPtr, tyRef, tyVar, tyLent, tySink, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
result =
case t.kind:
of tySequence: encodeName("seq")
else: encodeName(kindName)
result.add "I"
for i in 0..<t.len:
let s = t[i]
if s.isNil: continue
result.add encodeType(m, s, staticLists)
result.add "E"
of tyStatic:
if t.n != nil:
staticLists.add "_s" & renderTree(t.n)
else:
raiseAssert "unreachable"
of tyRange:
var val = "range_"
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
val.addFloat t.n[0].floatVal
val.add "_"
val.addFloat t.n[1].floatVal
else:
val.add $t.n[0].intVal & "_" & $t.n[1].intVal
result = encodeName(val)
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
result = encodeName(kindName)
of tyAlias, tyInferred, tyOwned:
result = encodeType(m, t.elementType, staticLists)
else:
assert false, "encodeType " & $t.kind

View File

@@ -14,9 +14,8 @@ import
nversion, nimsets, msgs, bitsets, idents, types,
ccgutils, ropes, wordrecg, treetab, cgmeth,
rodutils, renderer, cgendata, aliases,
lowerings, lineinfos, pathutils, transf,
injectdestructors, astmsgs, modulepaths, pushpoppragmas,
mangleutils
lowerings, ndi, lineinfos, pathutils, transf,
injectdestructors, astmsgs, modulepaths, backendpragmas
from expanddefaults import caseObjDefaultBranch
@@ -33,12 +32,6 @@ import std/strutils except `%`, addf # collides with ropes.`%`
from ic / ic import ModuleBackendFlag
import std/[dynlib, math, tables, sets, os, intsets, hashes]
const
# we use some ASCII control characters to insert directives that will be converted to real code in a postprocessing pass
postprocessDirStart = '\1'
postprocessDirSep = '\31'
postprocessDirEnd = '\23'
when not declared(dynlib.libCandidates):
proc libCandidates(s: string, dest: var seq[string]) =
## given a library name pattern `s` write possible library names to `dest`.
@@ -72,7 +65,8 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc =
result = TLoc(k: k, storage: s, lode: lode,
snippet: "", flags: flags)
r: "", flags: flags
)
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} =
# fills the loc if it is not already initialized
@@ -80,7 +74,7 @@ proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.i
a.k = k
a.lode = lode
a.storage = s
if a.snippet == "": a.snippet = r
if a.r == "": a.r = r
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} =
# fills the loc if it is not already initialized
@@ -97,7 +91,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)
@@ -272,28 +266,24 @@ proc safeLineNm(info: TLineInfo): int =
result = toLinenumber(info)
if result < 0: result = 0 # negative numbers are not allowed in #line
proc genPostprocessDir(field1, field2, field3: string): string =
result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd
proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) =
proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
assert line >= 0
if optLineDir in conf.options and line > 0:
if fileIdx == InvalidFileIdx:
r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
else:
r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
r.addf("\n#line $2 $1\n",
[rope(makeSingleLineCString(filename)), rope(line)])
proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
proc genCLineDir(r: var Rope, filename: string, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
assert line >= 0
if optLineDir in p.config.options and line > 0:
if fileIdx == InvalidFileIdx:
r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
if lastFileIndex == info.fileIndex:
r.addf("\n#line $1\n", [rope(line)])
else:
r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
r.addf("\n#line $2 $1\n",
[rope(makeSingleLineCString(filename)), rope(line)])
proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
if optLineDir in conf.options:
genCLineDir(r, info.fileIndex, info.safeLineNm, conf)
genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
proc freshLineInfo(p: BProc; info: TLineInfo): bool =
if p.lastLineInfo.line != info.line or
@@ -308,25 +298,27 @@ proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) =
if optLineDir in conf.options:
let lastFileIndex = p.lastLineInfo.fileIndex
if freshLineInfo(p, info):
genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex)
genCLineDir(r, toFullPath(conf, info), info.safeLineNm, p, info, lastFileIndex)
proc genLineDir(p: BProc, t: PNode) =
if p == p.module.preInitProc: return
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:
genCLineDir(p.s(cpsStmts), t.info.fileIndex, line, p, t.info, lastFileIndex)
genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p, t.info, lastFileIndex)
if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
if freshLine:
line(p, cpsStmts, genPostprocessDir("nimln", $line, $t.info.fileIndex.int32))
if lastFileIndex == t.info.fileIndex:
linefmt(p, cpsStmts, "nimln_($1);",
[line])
else:
linefmt(p, cpsStmts, "nimlf_($1, $2);",
[line, quotedFilename(p.config, t.info)])
proc accessThreadLocalVar(p: BProc, s: PSym)
proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
@@ -343,22 +335,25 @@ proc getTempName(m: BModule): Rope =
proc rdLoc(a: TLoc): Rope =
# 'read' location (deref if indirect)
if lfIndirect in a.flags:
result = "(*" & a.snippet & ")"
result = "(*" & a.r & ")"
else:
result = a.snippet
result = a.r
proc addRdLoc(a: TLoc; result: var Rope) =
if lfIndirect in a.flags:
result.add "(*" & a.snippet & ")"
result.add "(*" & a.r & ")"
else:
result.add a.snippet
result.add a.r
proc lenField(p: BProc): Rope {.inline.} =
result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
proc lenExpr(p: BProc; a: TLoc): Rope =
proc lenExpr(p: BProc; a: TLoc; isString = false): Rope =
if optSeqDestructors in p.config.globalOptions:
result = rdLoc(a) & ".len"
if isString and p.config.isDefined("nimSeqsV3"):
result = ropecg(p.module, "(#nimStrLenV3($1))", [rdLoc(a)])
else:
result = rdLoc(a) & ".len"
else:
result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
@@ -368,15 +363,18 @@ proc dataFieldAccessor(p: BProc, sym: Rope): Rope =
else:
result = sym
proc dataField(p: BProc): Rope =
proc dataField(p: BProc; isString: bool = false): Rope =
# TODO: revisit this after unify strings and seqs
if optSeqDestructors in p.config.globalOptions:
result = rope".p->data"
if isString and p.config.isDefined("nimSeqsV3"):
result = rope".p"
else:
result = rope".p->data"
else:
result = rope"->data"
proc genProcPrototype(m: BModule, sym: PSym)
include cbuilder
include ccgliterals
include ccgtypes
@@ -389,22 +387,22 @@ template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) =
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
result.add "(&" & a.snippet & ")"
result.add "(&" & a.r & ")"
else:
result.add a.snippet
result.add a.r
proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
result = "(&" & a.snippet & ")"
result = "(&" & a.r & ")"
else:
result = a.snippet
result = a.r
proc byRefLoc(p: BProc; a: TLoc): Rope =
if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a) == skParam) != ctArray and not
p.module.compileToCpp:
result = "(&" & a.snippet & ")"
result = "(&" & a.r & ")"
else:
result = a.snippet
result = a.r
proc rdCharLoc(a: TLoc): Rope =
# read a location that may need a char-cast:
@@ -415,9 +413,6 @@ proc rdCharLoc(a: TLoc): Rope =
type
TAssignmentFlag = enum
needToCopy
needToCopySinkParam
needTempForOpenArray
needAssignCall
TAssignmentFlags = set[TAssignmentFlag]
proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
@@ -486,22 +481,25 @@ include ccgreset
proc resetLoc(p: BProc, loc: var TLoc) =
let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t)
let typ = skipTypes(loc.t, abstractVarRange)
if isImportedCppType(typ):
var didGenTemp = false
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(loc), genCppInitializer(p.module, p, typ, didGenTemp)])
return
if isImportedCppType(typ): return
if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
assert loc.snippet != ""
assert loc.r != ""
let atyp = skipTypes(loc.t, abstractInst)
if atyp.kind in {tyVar, tyLent}:
linefmt(p, cpsStmts, "$1->len = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)])
if p.config.isDefined("nimSeqsV3") and typ.kind == tyString:
if atyp.kind in {tyVar, tyLent}:
linefmt(p, cpsStmts, "$1->rawlen = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)])
else:
linefmt(p, cpsStmts, "$1.rawlen = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
else:
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
if atyp.kind in {tyVar, tyLent}:
linefmt(p, cpsStmts, "$1->len = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)])
else:
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
elif not isComplexValueType(typ):
if containsGcRef:
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
nilLoc.snippet = rope("NIM_NIL")
nilLoc.r = rope("NIM_NIL")
genRefAssign(p, loc, nilLoc)
else:
linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)])
@@ -534,18 +532,22 @@ proc resetLoc(p: BProc, loc: var TLoc) =
proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
let typ = loc.t
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
let typKind = skipTypes(typ, abstractInst + {tyStatic}).kind
if optSeqDestructors in p.config.globalOptions and typKind in {tyString, tySequence}:
if typKind == tyString and p.config.isDefined("nimSeqsV3"):
linefmt(p, cpsStmts, "$1.rawlen = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
else:
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
elif not isComplexValueType(typ):
if containsGarbageCollectedRef(loc.t):
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
nilLoc.snippet = rope("NIM_NIL")
nilLoc.r = rope("NIM_NIL")
genRefAssign(p, loc, nilLoc)
else:
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))])
else:
if (not isTemp or containsGarbageCollectedRef(loc.t)) and not hasNoInit(loc.t):
if not isTemp or containsGarbageCollectedRef(loc.t):
# don't use nimZeroMem for temporary values for performance if we can
# avoid it:
if not isOrHasImportedCppType(typ):
@@ -567,14 +569,13 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
inc(p.labels)
result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
storage: OnStack, flags: {})
if p.module.compileToCpp and isOrHasImportedCppType(t):
var didGenTemp = false
linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.snippet,
genCppInitializer(p.module, p, t, didGenTemp)])
linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r,
genCppInitializer(p.module, p, t)])
else:
linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.snippet])
linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r])
constructLoc(p, result, not needsInit)
when false:
# XXX Introduce a compiler switch in order to detect these easily.
@@ -587,16 +588,16 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc =
inc(p.labels)
result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
storage: OnStack, flags: {})
linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.snippet, value])
linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, dkVar), result.r, value])
proc getIntTemp(p: BProc): TLoc =
inc(p.labels)
result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp,
result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp,
storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt),
flags: {})
linefmt(p, cpsLocals, "NI $1;$n", [result.snippet])
linefmt(p, cpsLocals, "NI $1;$n", [result.r])
proc localVarDecl(p: BProc; n: PNode): Rope =
result = ""
@@ -618,9 +619,9 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
if sfVolatile in s.flags: result.add(" volatile")
if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
result.add(" ")
result.add(s.loc.snippet)
result.add(s.loc.r)
else:
result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet])
result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
proc assignLocalVar(p: BProc, n: PNode) =
#assert(s.loc.k == locNone) # not yet assigned
@@ -629,8 +630,7 @@ proc assignLocalVar(p: BProc, n: PNode) =
let nl = if optLineDir in p.config.options: "" else: "\n"
var decl = localVarDecl(p, n)
if p.module.compileToCpp and isOrHasImportedCppType(n.typ):
var didGenTemp = false
decl.add genCppInitializer(p.module, p, n.typ, didGenTemp)
decl.add genCppInitializer(p.module, p, n.typ)
decl.add ";" & nl
line(p, cpsLocals, decl)
@@ -661,11 +661,22 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) =
if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
else:
if value != "":
decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.snippet, value])
decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
else:
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.snippet])
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool)
proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope)
proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) =
let s = vn.sym
fillBackendName(p.module, s)
fillLoc(s.loc, locGlobalVar, vn, OnHeap)
var decl: Rope = ""
let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
genGlobalVarDecl(p, vn, td, "", decl)
decl.add " " & $s.loc.r
genCppVarForCtor(p, value, decl)
p.module.s[cfsVars].add decl
proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
let s = n.sym
@@ -679,7 +690,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
if q != nil and not containsOrIncl(q.declaredThings, s.id):
varInDynamicLib(q, s)
else:
s.loc.snippet = mangleDynLibProc(s)
s.loc.r = mangleDynLibProc(s)
if value != "":
internalError(p.config, n.info, ".dynlib variables cannot have a value")
return
@@ -710,31 +721,19 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
# [^0]: https://en.cppreference.com/w/cpp/language/aggregate_initialization
# [^1]: https://cplusplus.github.io/CWG/issues/1518.html
# [^2]: https://eel.is/c++draft/over.match.ctor
decl.addf(" $1;$n", [s.loc.snippet])
decl.addf(" $1;$n", [s.loc.r])
else:
decl.addf(" $1 = $2;$n", [s.loc.snippet, value])
decl.addf(" $1 = $2;$n", [s.loc.r, value])
else:
decl.addf(" $1;$n", [s.loc.snippet])
decl.addf(" $1;$n", [s.loc.r])
p.module.s[cfsVars].add(decl)
if p.withinLoop > 0 and value == "":
# fixes tests/run/tzeroarray:
resetLoc(p, s.loc)
proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var bool) =
let s = vn.sym
fillBackendName(p.module, s)
fillLoc(s.loc, locGlobalVar, vn, OnHeap)
var decl: Rope = ""
let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
genGlobalVarDecl(p, vn, td, "", decl)
decl.add " " & $s.loc.snippet
genCppVarForCtor(p, value, decl, didGenTemp)
if didGenTemp: return # generated in the caller
p.module.s[cfsVars].add decl
proc assignParam(p: BProc, s: PSym, retType: PType) =
assert(s.loc.snippet != "")
assert(s.loc.r != "")
scopeMangledParam(p, s)
proc fillProcLoc(m: BModule; n: PNode) =
@@ -760,7 +759,6 @@ proc intLiteral(i: BiggestInt; result: var Rope)
proc genLiteral(p: BProc, n: PNode; result: var Rope)
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int)
proc raiseExit(p: BProc)
proc raiseExitCleanup(p: BProc, destroy: string)
proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc =
result = initLoc(locNone, e, OnUnknown, flags)
@@ -787,11 +785,15 @@ $1define nimfr_(proc, file) \
TFrame FR_; \
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_);
$1define nimln_(n) \
FR_.line = n;
$1define nimfrs_(proc, file, slots, length) \
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename;NI len;VarSlot s[slots];} FR_; \
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_);
$1define nimlf_(n, file) \
FR_.line = n; FR_.filename = file;
$1define nimln_(n) \
FR_.line = n;
$1define nimlf_(n, file) \
FR_.line = n; FR_.filename = file;
"""
if p.module.s[cfsFrameDefines].len == 0:
@@ -855,7 +857,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
p.options.excl optStackTrace
p.flags.incl nimErrorFlagDisabled
var dest: TLoc = initLoc(locTemp, lib.path, OnStack)
dest.snippet = getTempName(m)
dest.r = getTempName(m)
appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
[getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)])
expr(p, lib.path, dest)
@@ -873,7 +875,7 @@ proc mangleDynLibProc(sym: PSym): Rope =
# we have to build this as a single rope in order not to trip the
# optimization in genInfixCall, see test tests/cpp/t8241.nim
if sfCompilerProc in sym.flags:
# NOTE: sym.loc.snippet is the external name!
# NOTE: sym.loc.r is the external name!
result = rope(sym.name.s)
else:
result = rope(strutils.`%`("Dl_$1_", $sym.id))
@@ -881,10 +883,10 @@ proc mangleDynLibProc(sym: PSym): Rope =
proc symInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex
let isCall = isGetProcAddr(lib)
var extname = sym.loc.snippet
var extname = sym.loc.r
if not isCall: loadDynamicLib(m, lib)
var tmp = mangleDynLibProc(sym)
sym.loc.snippet = tmp # from now on we only need the internal name
sym.loc.r = tmp # from now on we only need the internal name
sym.typ.sym = nil # generate a new name
inc(m.labels, 2)
if isCall:
@@ -911,24 +913,24 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
appcg(m, m.s[cfsDynLibInit],
"\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
[tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
m.s[cfsVars].addf("$2 $1;$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
proc varInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex
var extname = sym.loc.snippet
var extname = sym.loc.r
loadDynamicLib(m, lib)
incl(sym.loc.flags, lfIndirect)
var tmp = mangleDynLibProc(sym)
sym.loc.snippet = tmp # from now on we only need the internal name
sym.loc.r = tmp # from now on we only need the internal name
inc(m.labels, 2)
appcg(m, m.s[cfsDynLibInit],
"$1 = ($2*) #nimGetProcAddr($3, $4);$n",
[tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
m.s[cfsVars].addf("$2* $1;$n",
[sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
[sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
proc symInDynamicLibPartial(m: BModule, sym: PSym) =
sym.loc.snippet = mangleDynLibProc(sym)
sym.loc.r = mangleDynLibProc(sym)
sym.typ.sym = nil # generate a new name
proc cgsymImpl(m: BModule; sym: PSym) {.inline.} =
@@ -951,7 +953,7 @@ proc cgsymValue(m: BModule, name: string): Rope =
cgsymImpl m, sym
else:
rawMessage(m.config, errGenerated, "system module needs: " & name)
result = sym.loc.snippet
result = sym.loc.r
if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}:
result.addActualSuffixForHCR(m.module, sym)
@@ -1047,7 +1049,7 @@ proc easyResultAsgn(n: PNode): PNode =
type
InitResultEnum = enum Unknown, InitSkippable, InitRequired
proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
proc allPathsAsgnResult(n: PNode): InitResultEnum =
# Exceptions coming from calls don't have not be considered here:
#
# proc bar(): string = raise newException(...)
@@ -1062,7 +1064,7 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
# echo "a was not written to"
#
template allPathsInBranch(it) =
let a = allPathsAsgnResult(p, it)
let a = allPathsAsgnResult(it)
case a
of InitRequired: return InitRequired
of InitSkippable: discard
@@ -1074,20 +1076,14 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
case n.kind
of nkStmtList, nkStmtListExpr:
for it in n:
result = allPathsAsgnResult(p, it)
result = allPathsAsgnResult(it)
if result != Unknown: return result
of nkAsgn, nkFastAsgn, nkSinkAsgn:
if n[0].kind == nkSym and n[0].sym.kind == skResult:
if not containsResult(n[1]):
if allPathsAsgnResult(p, n[1]) == InitRequired:
result = InitRequired
else:
result = InitSkippable
if not containsResult(n[1]): result = InitSkippable
else: result = InitRequired
elif containsResult(n):
result = InitRequired
else:
result = allPathsAsgnResult(p, n[1])
of nkReturnStmt:
if n.len > 0:
if n[0].kind == nkEmpty and result != InitSkippable:
@@ -1096,7 +1092,7 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
# initialized. This avoids cases like #9286 where this heuristic lead to
# wrong code being generated.
result = InitRequired
else: result = allPathsAsgnResult(p, n[0])
else: result = allPathsAsgnResult(n[0])
of nkIfStmt, nkIfExpr:
var exhaustive = false
result = InitSkippable
@@ -1122,9 +1118,9 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
of nkWhileStmt:
# some dubious code can assign the result in the 'while'
# condition and that would be fine. Everything else isn't:
result = allPathsAsgnResult(p, n[0])
result = allPathsAsgnResult(n[0])
if result == Unknown:
result = allPathsAsgnResult(p, n[1])
result = allPathsAsgnResult(n[1])
# we cannot assume that the 'while' loop is really executed at least once:
if result == InitSkippable: result = Unknown
of harmless:
@@ -1149,28 +1145,9 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
allPathsInBranch(n[0])
for i in 1..<n.len:
if n[i].kind == nkFinally:
result = allPathsAsgnResult(p, n[i].lastSon)
result = allPathsAsgnResult(n[i].lastSon)
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
result = InitRequired
else:
for i in 0..<n.safeLen:
allPathsInBranch(n[i])
of nkRaiseStmt:
result = InitRequired
of nkChckRangeF, nkChckRange64, nkChckRange:
# TODO: more checks might need to be covered like overflow, indexDefect etc.
# bug #22852
result = InitRequired
else:
for i in 0..<n.safeLen:
allPathsInBranch(n[i])
@@ -1211,7 +1188,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
let tmpInfo = prc.info
discard freshLineInfo(p, prc.info)
if sfPure notin prc.flags and prc.typ.returnType != nil:
if sfPure notin prc.flags and prc.typ[0] != nil:
if resultPos >= prc.ast.len:
internalError(m.config, prc.info, "proc has no result symbol")
let resNode = prc.ast[resultPos]
@@ -1225,9 +1202,9 @@ proc genProcAux*(m: BModule, prc: PSym) =
else:
# declare the result symbol:
assignLocalVar(p, resNode)
assert(res.loc.snippet != "")
assert(res.loc.r != "")
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
allPathsAsgnResult(p, procBody) == InitSkippable:
allPathsAsgnResult(procBody) == InitSkippable:
# In an ideal world the codegen could rely on injectdestructors doing its job properly
# and then the analysis step would not be required.
discard "result init optimized out"
@@ -1237,10 +1214,9 @@ proc genProcAux*(m: BModule, prc: PSym) =
elif sfConstructor in prc.flags:
resNode.sym.loc.flags.incl lfIndirect
fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
prc.loc.snippet = getTypeDesc(m, resNode.sym.loc.t, dkVar)
else:
fillResult(p.config, resNode, prc.typ)
assignParam(p, res, prc.typ.returnType)
assignParam(p, res, prc.typ[0])
# We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)'
# to 'unsureAsgn(result, x)'
# Sketch why this is correct: If 'result' points to a stack location
@@ -1248,7 +1224,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
# global is either 'nil' or points to valid memory and so the RC operation
# succeeds without touching not-initialized memory.
if sfNoInit in prc.flags: discard
elif allPathsAsgnResult(p, procBody) == InitSkippable: discard
elif allPathsAsgnResult(procBody) == InitSkippable: discard
else:
resetLoc(p, res.loc)
if skipTypes(res.typ, abstractInst).kind == tyArray:
@@ -1258,7 +1234,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
for i in 1..<prc.typ.n.len:
let param = prc.typ.n[i].sym
if param.typ.isCompileTimeOnly: continue
assignParam(p, param, prc.typ.returnType)
assignParam(p, param, prc.typ[0])
closureSetup(p, prc)
genProcBody(p, procBody)
@@ -1302,7 +1278,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
m.s[cfsProcs].add(generatedProc)
if isReloadable(m, prc):
m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n",
[prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
[prc.loc.r, prc.loc.r & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
result = (sfCompileToCpp in m.module.flags and
@@ -1362,7 +1338,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
# the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded)
if isReloadable(q, prc):
q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n",
[prc.loc.snippet, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
[prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
else:
symInDynamicLibPartial(m, prc)
elif prc.typ.callConv == ccInline:
@@ -1379,8 +1355,8 @@ proc genProcNoForward(m: BModule, prc: PSym) =
#elif {sfExportc, sfImportc} * prc.flags == {}:
# # reset name to restore consistency in case of hashing collisions:
# echo "resetting ", prc.id, " by ", m.module.name.s
# prc.loc.snippet = nil
# prc.loc.snippet = mangleName(m, prc)
# prc.loc.r = nil
# prc.loc.r = mangleName(m, prc)
genProcPrototype(m, prc)
genProcAux(m, prc)
elif sfImportc notin prc.flags:
@@ -1392,7 +1368,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
if isReloadable(m, prc) and prc.id notin m.declaredProtos and
q != nil and q.module.id != m.module.id:
m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
[prc.loc.snippet, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
[prc.loc.r, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
genProcPrototype(m, prc)
if q != nil and not containsOrIncl(q.declaredThings, prc.id):
# make sure there is a "prototype" in the external module
@@ -1445,7 +1421,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
return
if sym.owner.id != m.module.id:
# else we already have the symbol generated!
assert(sym.loc.snippet != "")
assert(sym.loc.r != "")
incl(m.declaredThings, sym.id)
if sfThread in sym.flags:
declareThreadVar(m, sym, true)
@@ -1459,9 +1435,9 @@ proc genVarPrototype(m: BModule, n: PNode) =
if sfRegister in sym.flags: m.s[cfsVars].add(" register")
if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile")
if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS")
m.s[cfsVars].addf(" $1;$n", [sym.loc.snippet])
m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)])
proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
@@ -1494,13 +1470,15 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope =
## Returns a mangled module name.
result = mangleModuleName(conf, filename).mangle
result = ""
result.add mangleModuleName(conf, filename).mangle
proc getSomeNameForModule(m: BModule): Rope =
## Returns a mangled module name.
assert m.module.kind == skModule
assert m.module.owner.kind == skPackage
result = mangleModuleName(m.g.config, m.filename).mangle
result = ""
result.add mangleModuleName(m.g.config, m.filename).mangle
proc getSomeInitName(m: BModule, suffix: string): Rope =
if not m.hcrOn:
@@ -1836,7 +1814,7 @@ proc genDatInitCode(m: BModule) =
# we don't want to break into such init code - could happen if a line
# directive from a function written by the user spills after itself
genCLineDir(prc, InvalidFileIdx, 999999, m.config)
genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
for i in cfsTypeInit1..cfsDynLibInit:
if m.s[i].len != 0:
@@ -1858,11 +1836,11 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R
var extname = prefix & sym
var tmp = mangleDynLibProc(prc)
prc.loc.snippet = tmp
prc.loc.r = tmp
prc.typ.sym = nil
if not containsOrIncl(m.declaredThings, prc.id):
m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.snippet, getTypeDesc(m, prc.loc.t, dkVar)])
m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, dkVar)])
result = "\t$1 = ($2) $3($4, $5);$n" %
[tmp, getTypeDesc(m, prc.typ, dkVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
@@ -1877,7 +1855,7 @@ proc genInitCode(m: BModule) =
[rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname]
# we don't want to break into such init code - could happen if a line
# directive from a function written by the user spills after itself
genCLineDir(prc, InvalidFileIdx, 999999, m.config)
genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
if m.typeNodes > 0:
if m.hcrOn:
appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName])
@@ -1947,6 +1925,9 @@ proc genInitCode(m: BModule) =
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
prc.add(deinitFrame(m.initProc))
elif m.config.exc == excGoto:
if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
prc.addf("}$N", [])
@@ -1989,40 +1970,6 @@ proc genInitCode(m: BModule) =
registerModuleToMain(m.g, m)
proc postprocessCode(conf: ConfigRef, r: var Rope) =
# find the first directive
var f = r.find(postprocessDirStart)
if f == -1:
return
var
nimlnDirLastF = ""
var res: Rope = r.substr(0, f - 1)
while f != -1:
var
e = r.find(postprocessDirEnd, f + 1)
dir = r.substr(f + 1, e - 1).split(postprocessDirSep)
case dir[0]
of "nimln":
if dir[2] == nimlnDirLastF:
res.add("nimln_(" & dir[1] & ");")
else:
res.add("nimlf_(" & dir[1] & ", " & quotedFilename(conf, dir[2].parseInt.FileIndex) & ");")
nimlnDirLastF = dir[2]
else:
raiseAssert "unexpected postprocess directive"
# find the next directive
f = r.find(postprocessDirStart, e + 1)
# copy the code until the next directive
if f != -1:
res.add(r.substr(e + 1, f - 1))
else:
res.add(r.substr(e + 1))
r = res
proc genModule(m: BModule, cfile: Cfile): Rope =
var moduleIsEmpty = true
@@ -2051,17 +1998,9 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
if m.config.cppCustomNamespace.len > 0:
closeNamespaceNim(result)
if optLineDir in m.config.options:
var srcFileDefs = ""
for fi in 0..m.config.m.fileInfos.high:
srcFileDefs.add("#define FX_" & $fi & " " & makeSingleLineCString(toFullPath(m.config, fi.FileIndex)) & "\n")
result = srcFileDefs & result
if moduleIsEmpty:
result = ""
postprocessCode(m.config, result)
proc initProcOptions(m: BModule): TOptions =
let opts = m.config.options
if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
@@ -2095,6 +2034,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 +2120,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 +2135,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 +2166,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 +2184,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)
@@ -2274,23 +2200,6 @@ proc updateCachedModule(m: BModule) =
cf.flags = {CfileFlag.Cached}
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)
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
if isDynlib:
incl(result.loc.flags, lfExportLib)
let theProc = newNodeI(nkProcDef, m.module.info, bodyPos+1)
for i in 0..<theProc.len: theProc[i] = newNodeI(nkEmpty, m.module.info)
theProc[namePos] = newSymNode(result)
theProc[bodyPos] = body
result.ast = theProc
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
## Also called from IC.
if sfMainModule in m.module.flags:
@@ -2302,13 +2211,6 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}:
for i in countdown(high(graph.globalDestructors), 0):
n.add graph.globalDestructors[i]
else:
var body = newNodeI(nkStmtList, m.module.info)
for i in countdown(high(graph.globalDestructors), 0):
body.add graph.globalDestructors[i]
body.flags.incl nfTransf # should not be further transformed
let dtor = generateLibraryDestroyGlobals(graph, m, body, optGenDynLib in m.config.globalOptions)
genProcAux(m, dtor)
if pipelineutils.skipCodegen(m.config, n): return
if moduleHasChanged(graph, m.module):
# if the module is cached, we don't regenerate the main proc

View File

@@ -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
@@ -91,7 +88,7 @@ type
options*: TOptions # options that should be used for code
# generation; this is the same as prc.options
# unless prc == nil
optionsStack*: seq[(TOptions, TNoteKinds)]
optionsStack*: seq[TOptions]
module*: BModule # used to prevent excessive parameter passing
withinLoop*: int # > 0 if we are within a loop
splitDecls*: int # > 0 if we are in some context for C++ that
@@ -140,7 +137,6 @@ type
# unconditionally...
# nimtvDeps is VERY hard to cache because it's
# not a list of IDs nor can it be made to be one.
mangledPrcs*: HashSet[string]
TCGen = object of PPassContext # represents a C source file
s*: TCFileSections # sections of the C file
@@ -154,7 +150,7 @@ type
typeABICache*: HashSet[SigHash] # cache for ABI checks; reusing typeCache
# would be ideal but for some reason enums
# don't seem to get cached so it'd generate
# 1 ABI check per occurrence in code
# 1 ABI check per occurence in code
forwTypeCache*: TypeCache # cache for forward declarations of types
declaredThings*: IntSet # things we have declared in this .c file
declaredProtos*: IntSet # prototypes we have declared in this .c file
@@ -175,6 +171,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

View File

@@ -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:
@@ -69,23 +69,21 @@ type
proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
result = No
if a.name.id != b.name.id: return
if a.typ.signatureLen != b.typ.signatureLen:
if a.typ.len != b.typ.len:
return
var i = 0
for x, y in paramTypePairs(a.typ, b.typ):
inc i
var aa = x
var bb = y
for i in 1..<a.typ.len:
var aa = a.typ[i]
var bb = b.typ[i]
while true:
aa = skipTypes(aa, {tyGenericInst, tyAlias})
bb = skipTypes(bb, {tyGenericInst, tyAlias})
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
aa = aa.elementType
bb = bb.elementType
aa = aa.lastSon
bb = bb.lastSon
else:
break
if sameType(x, y):
if sameType(a.typ[i], b.typ[i]):
if aa.kind == tyObject and result != Invalid:
result = Yes
elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
@@ -104,10 +102,10 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
if result == Yes:
# check for return type:
# ignore flags of return types; # bug #22673
if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}):
if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped:
if not sameTypeOrNil(a.typ[0], b.typ[0], {IgnoreFlags}):
if b.typ[0] != nil and b.typ[0].kind == tyUntyped:
# infer 'auto' from the base to make it consistent:
b.typ.setReturnType a.typ.returnType
b.typ[0] = a.typ[0]
else:
return No
@@ -133,8 +131,8 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
disp.ast = copyTree(s.ast)
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
disp.loc.snippet = ""
if s.typ.returnType != nil:
disp.loc.r = ""
if s.typ[0] != nil:
if disp.ast.len > resultPos:
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
else:
@@ -159,14 +157,9 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
var witness: PSym = nil
if s.typ.firstParamType.owner.getModule != s.getModule and vtables in g.config.features and not
g.config.isDefined("nimInternalNonVtablesTesting"):
if s.typ[1].owner.getModule != s.getModule and vtables in g.config.features and not g.config.isDefined("nimInternalNonVtablesTesting"):
localError(g.config, s.info, errGenerated, "method `" & s.name.s &
"` can be defined only in the same module with its type (" & s.typ.firstParamType.typeToString() & ")")
if sfImportc in s.flags:
localError(g.config, s.info, errGenerated, "method `" & s.name.s &
"` is not allowed to have 'importc' pragmas")
"` can be defined only in the same module with its type (" & s.typ[1].typeToString() & ")")
for i in 0..<g.methods.len:
let disp = g.methods[i].dispatcher
case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
@@ -186,10 +179,10 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
if witness.isNil: witness = g.methods[i].methods[0]
# create a new dispatcher:
# stores the id and the position
if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable:
g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1
if s.typ[1].skipTypes(skipPtrs).itemId notin g.bucketTable:
g.bucketTable[s.typ[1].skipTypes(skipPtrs).itemId] = 1
else:
g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId)
g.bucketTable.inc(s.typ[1].skipTypes(skipPtrs).itemId)
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
#echo "adding ", s.info
if witness != nil:
@@ -210,7 +203,7 @@ proc relevantCol*(methods: seq[PSym], col: int): bool =
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
result = 0
for col in FirstParamAt..<a.typ.signatureLen:
for col in 1..<a.typ.len:
if contains(relevantCols, col):
var aa = skipTypes(a.typ[col], skipPtrs)
var bb = skipTypes(b.typ[col], skipPtrs)
@@ -240,13 +233,13 @@ proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
var base = methods[0].ast[dispatcherPos].sym
result = base
var paramLen = base.typ.signatureLen
var paramLen = base.typ.len
var nilchecks = newNodeI(nkStmtList, base.info)
var disp = newNodeI(nkIfStmt, base.info)
var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
let boolType = getSysType(g, unknownLineInfo, tyBool)
for col in FirstParamAt..<paramLen:
for col in 1..<paramLen:
if contains(relevantCols, col):
let param = base.typ.n[col].sym
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
@@ -255,7 +248,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
for meth in 0..high(methods):
var curr = methods[meth] # generate condition:
var cond: PNode = nil
for col in FirstParamAt..<paramLen:
for col in 1..<paramLen:
if contains(relevantCols, col):
var isn = newNodeIT(nkCall, base.info, boolType)
isn.add newSymNode(iss)
@@ -270,7 +263,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
cond = a
else:
cond = isn
let retTyp = base.typ.returnType
let retTyp = base.typ[0]
let call = newNodeIT(nkCall, base.info, retTyp)
call.add newSymNode(curr)
for col in 1..<paramLen:
@@ -299,7 +292,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
for bucket in 0..<g.methods.len:
var relevantCols = initIntSet()
for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen:
for col in 1..<g.methods[bucket].methods[0].typ.len:
if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
if optMultiMethods notin g.config.globalOptions:
# if multi-methods are not enabled, we are interested only in the first field

File diff suppressed because it is too large Load Diff

View File

@@ -57,11 +57,6 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi
if conf.cmd == cmdNimscript:
incl(conf.globalOptions, optWasNimscript)
loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
# restores `conf.notes` after loading config files
# because it has overwrites the notes when compiling the system module which
# is a foreign module compared to the project
if conf.cmd in cmdBackends:
conf.notes = conf.mainPackageNotes
if not self.suggestMode:
let scriptFile = conf.projectFull.changeFileExt("nims")

View File

@@ -24,7 +24,7 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils]
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
import
msgs, options, nversion, condsyms, extccomp, platform,
wordrecg, nimblecmd, lineinfos, pathutils
@@ -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
@@ -249,7 +248,6 @@ const
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
template warningOptionNoop(switch: string) =
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
@@ -305,12 +303,6 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
else:
result = false
localError(conf, info, errInvalidExceptionSystem % arg)
of "experimental":
try:
result = conf.features.contains parseEnum[Feature](arg)
except ValueError:
result = false
localError(conf, info, errInvalidFeatureButXFound % arg)
else:
result = false
invalidCmdLineOption(conf, passCmd1, switch, info)
@@ -364,7 +356,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 +451,7 @@ template handleStdinOrCmdInput =
conf.outDir = getNimcacheDir(conf)
proc handleStdinInput*(conf: ConfigRef) =
conf.projectName = conf.stdinFile.string
conf.projectName = "stdinfile"
conf.projectIsStdin = true
handleStdinOrCmdInput()
@@ -471,6 +462,7 @@ proc handleCmdInput*(conf: ConfigRef) =
proc parseCommand*(command: string): Command =
case command.normalize
of "c", "cc", "compile", "compiletoc": cmdCompileToC
of "nir": cmdCompileToNir
of "cpp", "compiletocpp": cmdCompileToCpp
of "objc", "compiletooc": cmdCompileToOC
of "js", "compiletojs": cmdCompileToJS
@@ -508,6 +500,7 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
of cmdCompileToCpp: conf.backend = backendCpp
of cmdCompileToOC: conf.backend = backendObjc
of cmdCompileToJS: conf.backend = backendJs
of cmdCompileToNir: conf.backend = backendNir
else: discard
proc setCommandEarly*(conf: ConfigRef, command: string) =
@@ -761,14 +754,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 +769,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 +876,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 +912,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 +923,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 +1064,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 +1095,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":

View File

@@ -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,419 +70,157 @@ 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)
result = matchType(c, f.lastSon, 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:
if a.kind == tyTypeDesc and f.len == a.len:
for i in 0..<a.len:
if not matchType(c, f[i], a[i], m): return false
return true
else:
result = false
of tyGenericInvocation:
result = false
if a.kind == tyGenericInst and a[0].kind == tyGenericBody:
if sameType(f[0], a[0]) and f.len == a.len-1:
for i in 1 ..< f.len:
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.len > 0 and f[0].kind != tyNone:
# also check the generic's constraints:
let oldLen = m.inferred.len
result = matchType(c, f[0], 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, lastSon ak))
result = true
elif f.hasElementType:
result = matchType(c, f.elementType, a.elementType, m)
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.
if a.kind == f.kind:
result = matchType(c, f.elementType, a.elementType, m)
result = matchType(c, f[0], a[0], 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)
result = matchType(c, f[0], a, 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.len > 0 and ak[0].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:
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.len == ak.len:
for i in 0..<ak.len:
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()
let r = matchType(c, ff, aa, m)
for i in 0..<f.len:
for j in 0..<a.len:
let oldLenB = m.inferred.len
let r = matchType(c, f[i], a[j], m)
if r:
inc covered
break
m.bindings.setToPreviousLayer()
m.inferred.setLen oldLenB
result = covered >= a.kidsLen
result = covered >= a.len
if not result:
m.inferred.setLen oldLen
else:
for ff in f.kids:
m.bindings = m.bindings.newTypeMapLayer()
result = matchType(c, ff, a, m)
result = false
for i in 0..<f.len:
result = matchType(c, f[i], a, m)
if result: break # and remember the binding!
m.bindings.setToPreviousLayer()
of tySet:
result = false
if a.kind == tySet:
result = matchType(c, f.elementType, a.elementType, m)
m.inferred.setLen oldLen
of tyNot:
if a.kind == tyNot:
result = matchType(c, f[0], a[0], m)
else:
let oldLen = m.inferred.len
result = not matchType(c, f[0], 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 +230,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[0], candidate.typ[0], m):
m.inferred.setLen oldLen
return false
# all other parameters have to be optional parameters:
@@ -532,7 +261,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 +269,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,94 +307,35 @@ 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 TIdTable; invocation: PType): bool =
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fullfill the
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.)
## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the
## `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.len == arg.len-1:
# bind even more generic parameters
assert invocation.kind == tyGenericInvocation
for i in 1 ..< invocation.len:
bindings.idTablePut(invocation[i], arg[i])

View File

@@ -157,7 +157,6 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimAllowNonVarDestructor")
defineSymbol("nimHasQuirky")
defineSymbol("nimHasEnsureMove")
defineSymbol("nimHasNoReturnError")
defineSymbol("nimUseStrictDefs")
defineSymbol("nimHasNolineTooLong")
@@ -166,13 +165,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasWarnStdPrefix")
defineSymbol("nimHasVtables")
defineSymbol("nimHasGenericsOpenSym2")
defineSymbol("nimHasGenericsOpenSym3")
defineSymbol("nimHasJsNoLambdaLifting")
defineSymbol("nimHasDefaultFloatRoundtrip")
defineSymbol("nimHasXorSet")
defineSymbol("nimHasPreviewDuplicateModuleError")
defineSymbol("nimHasSetLengthSeqUninitMagic")
defineSymbol("nimHasImplicitRangeConversion")
defineSymbol("nimHasSeqsV3")

View File

@@ -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")

View File

@@ -46,10 +46,10 @@ type
case isTryBlock: bool
of false:
label: PSym
breakFixups: seq[(TPosition, seq[PNode])] # Contains the gotos for the breaks along with their pending finales
breakFixups: seq[(TPosition, seq[PNode])] #Contains the gotos for the breaks along with their pending finales
of true:
finale: PNode
raiseFixups: seq[TPosition] # Contains the gotos for the raises
raiseFixups: seq[TPosition] #Contains the gotos for the raises
Con = object
code: ControlFlowGraph
@@ -181,6 +181,14 @@ proc genIf(c: var Con, n: PNode) =
goto Lend3
L3:
D
goto Lend3 # not eliminated to simplify the join generation
Lend3:
join F3
Lend2:
join F2
Lend:
join F1
]#
var endings: seq[TPosition] = @[]
let oldInteresting = c.interestingInstructions
@@ -205,6 +213,7 @@ proc genAndOr(c: var Con; n: PNode) =
# fork lab1
# asgn dest, b
# lab1:
# join F1
c.gen(n[1])
forkT:
c.gen(n[2])
@@ -315,7 +324,7 @@ proc genRaise(c: var Con; n: PNode) =
if c.blocks[i].isTryBlock:
genBreakOrRaiseAux(c, i, n)
return
assert false # Unreachable
assert false #Unreachable
else:
genNoReturn(c)
@@ -371,7 +380,7 @@ proc genCall(c: var Con; n: PNode) =
if t != nil: t = t.skipTypes(abstractInst)
for i in 1..<n.len:
gen(c, n[i])
if t != nil and i < t.signatureLen and isOutParam(t[i]):
if t != nil and i < t.len and isOutParam(t[i]):
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
genDef(c, n[i])
# every call can potentially raise:
@@ -381,6 +390,7 @@ proc genCall(c: var Con; n: PNode) =
# fork lab1
# goto exceptionHandler (except or finally)
# lab1:
# join F1
forkT:
for i in countdown(c.blocks.high, 0):
if c.blocks[i].isTryBlock:
@@ -439,8 +449,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 +464,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'"

View File

@@ -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])
@@ -289,7 +289,7 @@ template declareClosures(currentFilename: AbsoluteFile, destFile: string) =
let outDirPath: RelativeFile =
presentationPath(conf, AbsoluteFile(basedir / targetRelPath))
# use presentationPath because `..` path can be be mangled to `_._`
result = (string(conf.outDir / outDirPath), "")
result.targetPath = string(conf.outDir / outDirPath)
if not fileExists(result.targetPath):
# this can happen if targetRelPath goes to parent directory `OUTDIR/..`.
# Trying it, this may cause ambiguities, but allows us to insert
@@ -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,8 +830,8 @@ proc getName(n: PNode): string =
of nkAccQuoted:
result = "`"
for i in 0..<n.len: result.add(getName(n[i]))
result.add('`')
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = "`"
of nkOpenSymChoice, nkClosedSymChoice:
result = getName(n[0])
else:
result = ""
@@ -853,7 +849,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
var r = ""
for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
result = getIdent(cache, r)
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
result = getNameIdent(cache, n[0])
else:
result = nil
@@ -867,7 +863,7 @@ proc getRstName(n: PNode): PRstNode =
of nkAccQuoted:
result = getRstName(n[0])
for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
result = getRstName(n[0])
else:
result = nil
@@ -1004,9 +1000,8 @@ proc getTypeKind(n: PNode): string =
proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
## Converts symbol info (names/types/parameters) in `n` into format
## `LangSymbol` convenient for ``rst.nim``/``dochelpers.nim``.
result = LangSymbol(name: baseName.nimIdentNormalize,
symKind: k.toHumanStr
)
result.name = baseName.nimIdentNormalize
result.symKind = k.toHumanStr
if k in routineKinds:
var
paramTypes: seq[string] = @[]
@@ -1035,7 +1030,7 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
if genNode != nil:
var literal = ""
var r: TSrcGen = initTokRender(genNode, {renderNoBody, renderNoComments,
renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
renderNoPragmas, renderNoProcDefs, renderExpandUsing})
var kind = tkEof
while true:
getNextTok(r, kind, literal)
@@ -1063,7 +1058,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
# Obtain the plain rendered string for hyperlink titles.
var r: TSrcGen = initTokRender(n, {renderNoBody, renderNoComments, renderDocComments,
renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
renderNoPragmas, renderNoProcDefs, renderExpandUsing})
while true:
getNextTok(r, kind, literal)
if kind == tkEof:
@@ -1090,9 +1085,6 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
symbolOrIdEnc = encodeUrl(symbolOrId, usePlus = false)
deprecationMsg = genDeprecationMsg(d, pragmaNode)
rstLangSymbol = toLangSymbol(k, n, cleanPlainSymbol)
symNameNode =
if nameNode.kind == nkPostfix: nameNode[1]
else: nameNode
# we generate anchors automatically for subsequent use in doc comments
let lineinfo = rstast.TLineInfo(
@@ -1103,10 +1095,10 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
priority = symbolPriority(k), info = lineinfo,
module = addRstFileIndex(d, FileIndex d.module.position))
var renderFlags = {renderNoBody, renderNoComments, renderDocComments,
renderSyms, renderExpandUsing, renderNoPostfix}
if nonExports:
renderFlags.incl renderNonExportedFields
let renderFlags =
if nonExports: {renderNoBody, renderNoComments, renderDocComments, renderSyms,
renderExpandUsing, renderNonExportedFields}
else: {renderNoBody, renderNoComments, renderDocComments, renderSyms, renderExpandUsing}
nodeToHighlightedHtml(d, n, result, renderFlags, symbolOrIdEnc)
let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int)
@@ -1129,19 +1121,18 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').changeFileExt(HtmlExt).string
var attype = ""
if k in routineKinds and symNameNode.kind == nkSym:
if k in routineKinds and nameNode.kind == nkSym:
let att = attachToType(d, nameNode.sym)
if att != nil:
attype = esc(d.target, att.name.s)
elif k == skType and symNameNode.kind == nkSym and
symNameNode.sym.typ.kind in {tyEnum, tyBool}:
let etyp = symNameNode.sym.typ
elif k == skType and nameNode.kind == nkSym and nameNode.sym.typ.kind in {tyEnum, tyBool}:
let etyp = nameNode.sym.typ
for e in etyp.n:
if e.sym.kind != skEnumField: continue
let plain = renderPlainSymbolName(e)
let symbolOrId = d.newUniquePlainSymbol(plain)
setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId,
term = plain, linkTitle = symNameNode.sym.name.s & '.' & plain,
term = plain, linkTitle = nameNode.sym.name.s & '.' & plain,
linkDesc = xmltree.escape(getPlainDocstring(e).docstringSummary),
line = n.info.line.int)
@@ -1162,8 +1153,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
linkTitle = detailedName,
linkDesc = xmltree.escape(plainDocstring.docstringSummary),
line = n.info.line.int)
if k == skType and symNameNode.kind == nkSym:
d.types.strTableAdd symNameNode.sym
if k == skType and nameNode.kind == nkSym:
d.types.strTableAdd nameNode.sym
proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): JsonItem =
if not isVisible(d, nameNode): return
@@ -1171,21 +1162,15 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
name = getNameEsc(d, nameNode)
comm = genRecComment(d, n)
r: TSrcGen
renderFlags = {renderNoBody, renderNoComments, renderDocComments,
renderExpandUsing, renderNoPostfix}
renderFlags = {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing}
if nonExports:
renderFlags.incl renderNonExportedFields
r = initTokRender(n, renderFlags)
result = JsonItem(json: %{ "name": %name, "type": %($k), "line": %n.info.line.int,
result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"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:
@@ -1214,7 +1199,8 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
var param = %{"name": %($genericParam)}
if genericParam.sym.typ.len > 0:
param["types"] = newJArray()
param["types"] = %($genericParam.sym.typ.elementType)
for kind in genericParam.sym.typ:
param["types"].add %($kind)
result.json["signature"]["genericParams"].add param
if optGenIndex in d.conf.globalOptions:
genItem(d, n, nameNode, k, kForceExport)
@@ -1328,7 +1314,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)
@@ -1415,18 +1401,14 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
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
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 +1882,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 +1902,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:

View File

@@ -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)
@@ -67,8 +63,8 @@ proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
proc searchObjCase(t: PType; field: PSym): PNode =
result = searchObjCaseImpl(t.n, field)
if result == nil and t.baseClass != nil:
result = searchObjCase(t.baseClass.skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
if result == nil and t.len > 0:
result = searchObjCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
doAssert result != nil
proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =

View File

@@ -99,7 +99,7 @@ proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
result = addr libffi.type_pointer
of tyDistinct, tyAlias, tySink:
result = mapType(conf, t.skipModifier)
result = mapType(conf, t[0])
else:
result = nil
# too risky:
@@ -126,16 +126,16 @@ proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
else:
result = sizeof(pointer) + packSize(conf, v[0], typ.elementType)
result = sizeof(pointer) + packSize(conf, v[0], typ.lastSon)
of tyDistinct, tyGenericInst, tyAlias, tySink:
result = packSize(conf, v, typ.skipModifier)
result = packSize(conf, v, typ[0])
of tyArray:
# consider: ptr array[0..1000_000, int] which is common for interfacing;
# we use the real length here instead
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
elif v.len != 0:
result = v.len * packSize(conf, v[0], typ.elementType)
result = v.len * packSize(conf, v[0], typ[1])
else:
result = 0
else:
@@ -234,19 +234,19 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
packRecCheck = 0
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
inc packRecCheck
pack(conf, v[0], typ.elementType, res +! sizeof(pointer))
pack(conf, v[0], typ.lastSon, res +! sizeof(pointer))
dec packRecCheck
awr(pointer, res +! sizeof(pointer))
of tyArray:
let baseSize = getSize(conf, typ.elementType)
let baseSize = getSize(conf, typ[1])
for i in 0..<v.len:
pack(conf, v[i], typ.elementType, res +! i * baseSize)
pack(conf, v[i], typ[1], res +! i * baseSize)
of tyObject, tyTuple:
packObject(conf, v, typ, res)
of tyNil:
discard
of tyDistinct, tyGenericInst, tyAlias, tySink:
pack(conf, v, typ.skipModifier, res)
pack(conf, v, typ[0], res)
else:
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
@@ -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,15 +298,15 @@ 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
if result.kind != nkBracket:
globalError(conf, n.info, "cannot map value from FFI")
let baseSize = getSize(conf, typ.elementType)
let baseSize = getSize(conf, typ[1])
for i in 0..<result.len:
result[i] = unpack(conf, x +! i * baseSize, typ.elementType, result[i])
result[i] = unpack(conf, x +! i * baseSize, typ[1], result[i])
proc canonNodeKind(k: TNodeKind): TNodeKind =
case k
@@ -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)
@@ -387,7 +387,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
awi(nkPtrLit, cast[int](p))
elif n != nil and n.len == 1:
internalAssert(conf, n.kind == nkRefTy)
n[0] = unpack(conf, p, typ.elementType, n[0])
n[0] = unpack(conf, p, typ.lastSon, n[0])
result = n
else:
result = nil
@@ -405,7 +405,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
of tyNil:
setNil()
of tyDistinct, tyGenericInst, tyAlias, tySink:
result = unpack(conf, x, typ.skipModifier, n)
result = unpack(conf, x, typ.lastSon, n)
else:
# XXX what to do with 'array' here?
result = nil
@@ -427,14 +427,14 @@ 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
proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
internalAssert conf, call[0].kind == nkPtrLit
var cif: TCif = default(TCif)
var cif: TCif
var sig: ParamList = default(ParamList)
# use the arguments' types for varargs support:
for i in 1..<call.len:
@@ -444,7 +444,7 @@ proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
let typ = call[0].typ
if prep_cif(cif, mapCallConv(conf, typ.callConv, call.info), cuint(call.len-1),
mapType(conf, typ.returnType), sig) != OK:
mapType(conf, typ[0]), sig) != OK:
globalError(conf, call.info, "error in FFI call")
var args: ArgList = default(ArgList)
@@ -453,15 +453,15 @@ proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
var t = call[i].typ
args[i-1] = alloc0(packSize(conf, call[i], t))
pack(conf, call[i], t, args[i-1])
let retVal = if isEmptyType(typ.returnType): pointer(nil)
else: alloc(getSize(conf, typ.returnType).int)
let retVal = if isEmptyType(typ[0]): pointer(nil)
else: alloc(getSize(conf, typ[0]).int)
libffi.call(cif, fn, retVal, args)
if retVal.isNil:
result = newNode(nkEmpty)
else:
result = unpack(conf, retVal, typ.returnType, nil)
result = unpack(conf, retVal, typ[0], nil)
result.info = call.info
if retVal != nil: dealloc retVal
@@ -474,14 +474,14 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
info: TLineInfo): PNode =
internalAssert conf, fn.kind == nkPtrLit
var cif: TCif = default(TCif)
var cif: TCif
var sig: ParamList = default(ParamList)
for i in 0..len-1:
var aTyp = args[i+start].typ
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")

View File

@@ -17,8 +17,8 @@ type
owner, genSymOwner: PSym
instLines: bool # use the instantiation lines numbers
isDeclarative: bool
mapping: SymMapping # every gensym'ed symbol needs to be mapped to some
# new symbol
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
# new symbol
config: ConfigRef
ic: IdentCache
instID: int
@@ -33,19 +33,6 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
let x = param
if x.kind == nkArgList:
for y in items(x): result.add(y)
elif nfDefaultRefsParam in x.flags:
# value of default param needs to be evaluated like template body
# if it contains other template params
var res: PNode
if isAtom(x):
res = newNodeI(nkPar, x.info)
evalTemplateAux(x, actual, c, res)
if res.len == 1: res = res[0]
else:
res = copyNode(x)
for i in 0..<x.safeLen:
evalTemplateAux(x[i], actual, c, res)
result.add res
else:
result.add copyTree(x)
@@ -57,19 +44,18 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
handleParam actual[s.position]
elif (s.owner != nil) and (s.kind == skGenericParam or
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam):
handleParam actual[s.owner.typ.signatureLen + s.position - 1]
handleParam actual[s.owner.typ.len + s.position - 1]
else:
internalAssert c.config, sfGenSym in s.flags or s.kind == skType
var x = idTableGet(c.mapping, s)
var x = PSym(idTableGet(c.mapping, s))
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)
if sfGenSym in s.flags:
# TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID)
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
if c.instLines: actual.info else: templ.info)
else:
@@ -130,7 +116,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
# now that we have working untyped parameters.
genericParams = if fromHlo: 0
else: s.ast[genericParamsPos].len
expectedRegularParams = s.typ.paramsLen
expectedRegularParams = s.typ.len-1
givenRegularParams = totalParams - genericParams
if givenRegularParams < 0: givenRegularParams = 0
@@ -182,7 +168,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;
@@ -196,14 +182,14 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
# replace each param by the corresponding node:
var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
var ctx = TemplCtx(owner: tmpl,
genSymOwner: genSymOwner,
config: conf,
ic: ic,
mapping: initSymMapping(),
instID: instID[],
idgen: idgen
)
var ctx: TemplCtx
ctx.owner = tmpl
ctx.genSymOwner = genSymOwner
ctx.config = conf
ctx.ic = ic
ctx.mapping = initIdTable()
ctx.instID = instID[]
ctx.idgen = idgen
let body = tmpl.ast[bodyPos]
#echo "instantion of ", renderTree(body, {renderIds})

View File

@@ -55,8 +55,8 @@ proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) =
discard
proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) =
if t.baseClass != nil:
expandDefaultObj(t.baseClass, info, res)
if t[0] != nil:
expandDefaultObj(t[0], info, res)
expandDefaultN(t.n, info, res)
proc expandDefault(t: PType; info: TLineInfo): PNode =
@@ -82,13 +82,13 @@ proc expandDefault(t: PType; info: TLineInfo): PNode =
result = newZero(t, info, nkIntLit)
of tyRange:
# Could use low(T) here to finally fix old language quirks
result = expandDefault(skipModifier t, info)
result = expandDefault(t[0], info)
of tyVoid: result = newZero(t, info, nkEmpty)
of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned:
result = expandDefault(t.skipModifier, info)
result = expandDefault(t.lastSon, info)
of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
if t.hasElementType:
result = expandDefault(t.skipModifier, info)
if t.len > 0:
result = expandDefault(t.lastSon, info)
else:
result = newZero(t, info, nkEmpty)
of tyFromExpr:
@@ -100,16 +100,16 @@ proc expandDefault(t: PType; info: TLineInfo): PNode =
result = newZero(t, info, nkBracket)
let n = toInt64(lengthOrd(nil, t))
for i in 0..<n:
result.add expandDefault(t.elementType, info)
result.add expandDefault(t[1], info)
of tyPtr, tyRef, tyProc, tyPointer, tyCstring:
result = newZero(t, info, nkNilLit)
of tyVar, tyLent:
let e = t.elementType
let e = t.lastSon
if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
# skip the modifier, `var openArray` is a (ptr, len) pair too:
result = expandDefault(e, info)
else:
result = newZero(e, info, nkNilLit)
result = newZero(t.lastSon, info, nkNilLit)
of tySet:
result = newZero(t, info, nkCurly)
of tyObject:
@@ -118,14 +118,14 @@ proc expandDefault(t: PType; info: TLineInfo): PNode =
expandDefaultObj(t, info, result)
of tyTuple:
result = newZero(t, info, nkTupleConstr)
for it in t.kids:
for it in t:
result.add expandDefault(it, info)
of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray:
result = newZero(t, info, nkBracket)
of tyString:
result = newZero(t, info, nkStrLit)
of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass,
tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
result = newZero(t, info, nkEmpty) # bug indicator

View File

@@ -19,7 +19,7 @@ import std/[os, osproc, streams, sequtils, times, strtabs, json, jsonutils, suga
import std / strutils except addf
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
import std/syncio
import ../dist/checksums/src/checksums/sha1
@@ -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: "",
@@ -176,22 +172,6 @@ compiler vcc:
cppXsupport: "",
props: {hasCpp, hasAssume, hasDeclspec})
# Nvidia CUDA NVCC Compiler
compiler nvcc:
result = gcc()
result.name = "nvcc"
result.compilerExe = "nvcc"
result.cppCompiler = "nvcc"
result.compileTmpl = "-c -x cu -Xcompiler=\"$options\" $include -o $objfile $file"
result.linkTmpl = "$buildgui $builddll -o $exefile $objfiles -Xcompiler=\"$options\""
# AMD HIPCC Compiler (rocm/cuda)
compiler hipcc:
result = clang()
result.name = "hipcc"
result.compilerExe = "hipcc"
result.cppCompiler = "hipcc"
compiler clangcl:
result = vcc()
result.name = "clang_cl"
@@ -257,8 +237,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",
@@ -305,9 +285,7 @@ const
envcc(),
icl(),
icc(),
clangcl(),
hipcc(),
nvcc()]
clangcl()]
hExt* = ".h"
@@ -341,7 +319,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
var fullSuffix = suffix
case conf.backend
of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
of backendC: discard
of backendC, backendNir: discard
of backendInvalid:
# during parsing of cfg files; we don't know the backend yet, no point in
# guessing wrong thing
@@ -474,11 +452,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 +759,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 +783,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("&lt;")
of '>': result.add("&gt;")
of '&': result.add("&amp;")
of '"': result.add("&quot;")
of '\'': result.add("&apos;")
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 +967,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 +982,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
@@ -1084,7 +999,7 @@ type BuildCache = object
depfiles: seq[(string, string)]
nimexe: string
proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
proc writeJsonBuildInstructions*(conf: ConfigRef) =
var linkFiles = collect(for it in conf.externalToLink:
var it = it
if conf.noAbsolutePaths: it = it.extractFilename
@@ -1105,17 +1020,11 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
currentDir: getCurrentDir())
if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
bcache.cmdline = conf.commandLine
for it in conf.m.fileInfos:
bcache.depfiles = collect(for it in conf.m.fileInfos:
let path = it.fullPath.string
if isAbsolute(path): # TODO: else?
if path in deps:
bcache.depfiles.add (path, deps[path])
else: # backup for configs etc.
bcache.depfiles.add (path, $secureHashFile(path))
(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 +1045,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 +1061,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

View File

@@ -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}:
@@ -1063,7 +1063,7 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
let b = fact[2]
if a.kind == nkSym: replacements.add((a,b))
else: replacements.add((b,a))
var m = TModel()
var m: TModel
var a = aa
var b = bb
if replacements.len > 0:
@@ -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

View File

@@ -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)

View File

@@ -16,7 +16,7 @@ from std/os import removeFile, isAbsolute
import ../../dist/checksums/src/checksums/sha1
import iclineinfos
import ".." / nir / nirlineinfos
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions, formatfloat]
@@ -59,8 +59,8 @@ type
emittedTypeInfo*: seq[string]
backendFlags*: set[ModuleBackendFlag]
syms*: OrderedTable[int32, PackedSym]
types*: OrderedTable[int32, PackedType]
syms*: seq[PackedSym]
types*: seq[PackedType]
strings*: BiTable[string] # we could share these between modules.
numbers*: BiTable[BiggestInt] # we also store floats in here so
# that we can assure that every bit is kept
@@ -362,15 +362,15 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item)
if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item):
#if t.uniqueId.item >= m.types.len:
# setLen m.types, t.uniqueId.item+1
if t.uniqueId.item >= m.types.len:
setLen m.types, t.uniqueId.item+1
var p = PackedType(id: t.uniqueId.item, kind: t.kind, flags: t.flags, callConv: t.callConv,
var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv,
size: t.size, align: t.align, nonUniqueId: t.itemId.item,
paddingAtEnd: t.paddingAtEnd)
storeNode(p, t, n)
p.typeInst = t.typeInst.storeType(c, m)
for kid in kids t:
for kid in items t:
p.types.add kid.storeType(c, m)
c.addMissing t.sym
p.sym = t.sym.safeItemId(c, m)
@@ -383,9 +383,10 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib =
## the plib hangs off the psym via the .annex field
if l.isNil: return
result = PackedLib(kind: l.kind, generated: l.generated,
isOverridden: l.isOverridden, name: toLitId($l.name, m)
)
result.kind = l.kind
result.generated = l.generated
result.isOverridden = l.isOverridden
result.name = toLitId($l.name, m)
storeNode(result, l, path)
proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId =
@@ -396,12 +397,12 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item)
if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item):
#if s.itemId.item >= m.syms.len:
# setLen m.syms, s.itemId.item+1
if s.itemId.item >= m.syms.len:
setLen m.syms, s.itemId.item+1
assert sfForward notin s.flags
var p = PackedSym(id: s.itemId.item, kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
position: s.position, offset: s.offset, disamb: s.disamb, options: s.options,
name: s.name.s.toLitId(m))
@@ -414,7 +415,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
p.bitsize = s.bitsize
p.alignment = s.alignment
p.externalName = toLitId(s.loc.snippet, m)
p.externalName = toLitId(s.loc.r, m)
p.locFlags = s.loc.flags
c.addMissing s.typ
p.typ = s.typ.storeType(c, m)
@@ -433,11 +434,11 @@ proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pac
let info = n.info.toPackedInfo(c, m)
if n.typ != n.sym.typ:
ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total
info = info, flags = n.flags,
info = info,
typeId = storeTypeLater(n.typ, c, m))
else:
ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total
info = info, flags = n.flags)
info = info)
ir.addNode(kind = nkNone, info = info,
operand = toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
ir.addNode(kind = nkNone, info = info,
@@ -613,10 +614,6 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
f.loadSection section
f.loadSeq data
template loadTableSection(section, data) {.dirty.} =
f.loadSection section
f.loadOrderedTable data
template loadTabSection(section, data) {.dirty.} =
f.loadSection section
f.load data
@@ -649,8 +646,8 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
loadTabSection topLevelSection, m.topLevel
loadTabSection bodiesSection, m.bodies
loadTableSection symsSection, m.syms
loadTableSection typesSection, m.types
loadSeqSection symsSection, m.syms
loadSeqSection typesSection, m.types
loadSeqSection typeInstCacheSection, m.typeInstCache
loadSeqSection procInstCacheSection, m.procInstCache
@@ -695,10 +692,6 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
f.storeSection section
f.store data
template storeTableSection(section, data) {.dirty.} =
f.storeSection section
f.storeOrderedTable data
storeTabSection stringsSection, m.strings
storeSeqSection checkSumsSection, m.includes
@@ -722,9 +715,9 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
storeTabSection topLevelSection, m.topLevel
storeTabSection bodiesSection, m.bodies
storeTableSection symsSection, m.syms
storeSeqSection symsSection, m.syms
storeTableSection typesSection, m.types
storeSeqSection typesSection, m.types
storeSeqSection typeInstCacheSection, m.typeInstCache
storeSeqSection procInstCacheSection, m.procInstCache
@@ -775,8 +768,8 @@ type
status*: ModuleStatus
symsInit, typesInit, loadedButAliveSetChanged*: bool
fromDisk*: PackedModule
syms: OrderedTable[int32, PSym] # indexed by itemId
types: OrderedTable[int32, PType]
syms: seq[PSym] # indexed by itemId
types: seq[PType]
module*: PSym # the one true module symbol.
iface, ifaceHidden: Table[PIdent, seq[PackedItemId]]
# PackedItemId so that it works with reexported symbols too
@@ -837,8 +830,7 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
of nkSym:
result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].soperand))
if result.typ == nil:
result.typ() = result.sym.typ
if result.typ == nil: result.typ = result.sym.typ
of externIntLit:
result.intVal = g[thisModule].fromDisk.numbers[n.litId]
of nkStrLit..nkTripleStrLit:
@@ -851,8 +843,7 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
assert n2.kind == nkNone
transitionNoneToSym(result)
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand))
if result.typ == nil:
result.typ() = result.sym.typ
if result.typ == nil: result.typ = result.sym.typ
else:
for n0 in sonsReadonly(tree, n):
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
@@ -942,10 +933,10 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
result.guard = loadSym(c, g, si, s.guard)
result.bitsize = s.bitsize
result.alignment = s.alignment
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
result.loc.r = rope externalName
result.loc.flags = s.locFlags
result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom)
@@ -969,11 +960,11 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s:
loadToReplayNodes(g, c.config, c.cache, m, g[int m])
assert g[si].status in {loaded, storing, stored}
#if not g[si].symsInit:
# g[si].symsInit = true
# setLen g[si].syms, g[si].fromDisk.syms.len
if not g[si].symsInit:
g[si].symsInit = true
setLen g[si].syms, g[si].fromDisk.syms.len
if g[si].syms.getOrDefault(s.item) == nil:
if g[si].syms[s.item] == nil:
if g[si].fromDisk.syms[s.item].kind != skModule:
result = symHeaderFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item)
# store it here early on, so that recursions work properly:
@@ -998,7 +989,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)
@@ -1020,11 +1011,11 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t
assert g[si].status in {loaded, storing, stored}
assert t.item > 0
#if not g[si].typesInit:
# g[si].typesInit = true
# setLen g[si].types, g[si].fromDisk.types.len
if not g[si].typesInit:
g[si].typesInit = true
setLen g[si].types, g[si].fromDisk.types.len
if g[si].types.getOrDefault(t.item) == nil:
if g[si].types[t.item] == nil:
result = typeHeaderFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item)
# store it here early on, so that recursions work properly:
g[si].types[t.item] = result
@@ -1062,7 +1053,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;
@@ -1163,7 +1154,10 @@ proc loadProcBody*(config: ConfigRef, cache: IdentCache;
proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; module: int; id: PackedItemId): PType =
bench g.loadType:
result = g[module].types.getOrDefault(id.item)
if id.item < g[module].types.len:
result = g[module].types[id.item]
else:
result = nil
if result == nil:
var decoder = PackedDecoder(
lastModule: int32(-1),
@@ -1176,7 +1170,10 @@ proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
proc loadSymFromId*(config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; module: int; id: PackedItemId): PSym =
bench g.loadSym:
result = g[module].syms.getOrDefault(id.item)
if id.item < g[module].syms.len:
result = g[module].syms[id.item]
else:
result = nil
if result == nil:
var decoder = PackedDecoder(
lastModule: int32(-1),
@@ -1192,6 +1189,19 @@ proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; confi
else:
ItemId(module: toFileIndex(id.module, g[thisModule].fromDisk, config).int32, item: id.item)
proc checkForHoles(m: PackedModule; config: ConfigRef; moduleId: int) =
var bugs = 0
for i in 1 .. high(m.syms):
if m.syms[i].kind == skUnknown:
echo "EMPTY ID ", i, " module ", moduleId, " ", toFullPath(config, FileIndex(moduleId))
inc bugs
assert bugs == 0
when false:
var nones = 0
for i in 1 .. high(m.types):
inc nones, m.types[i].kind == tyNone
assert nones < 1
proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
moduleSym: PSym; m: PackedModule) =
# For now only used for heavy debugging. In the future we could use this to reduce the

View File

@@ -10,7 +10,7 @@
## Integrity checking for a set of .rod files.
## The set must cover a complete Nim project.
import std/[sets, tables]
import std/sets
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -108,18 +108,18 @@ proc checkModule(c: var CheckedContext; m: PackedModule) =
# We check that:
# - Every symbol references existing types and symbols.
# - Every tree node references existing types and symbols.
for _, v in pairs(m.syms):
checkLocalSym c, v.id
for i in 0..high(m.syms):
checkLocalSym c, int32(i)
checkTree c, m.toReplay
checkTree c, m.topLevel
for e in m.exports:
#assert e[1] >= 0 and e[1] < m.syms.len
assert e[1] >= 0 and e[1] < m.syms.len
assert e[0] == m.syms[e[1]].name
for e in m.compilerProcs:
#assert e[1] >= 0 and e[1] < m.syms.len
assert e[1] >= 0 and e[1] < m.syms.len
assert e[0] == m.syms[e[1]].name
checkLocalSymIds c, m, m.converters

View File

@@ -11,7 +11,7 @@
## IDE-like features. It uses the set of .rod files to accomplish
## its task. The set must cover a complete Nim project.
import std/[sets, tables]
import std/sets
from std/os import nil
from std/private/miscdollars import toLocation
@@ -20,7 +20,7 @@ when defined(nimPreviewSlimSystem):
import std/assertions
import ".." / [ast, modulegraphs, msgs, options]
import iclineinfos
import ".." / nir / nirlineinfos
import packed_ast, bitabs, ic
type

View File

@@ -16,7 +16,7 @@ import std/[hashes, tables, strtabs]
import bitabs, rodfiles
import ".." / [ast, options]
import iclineinfos
import ".." / nir / nirlineinfos
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -47,7 +47,6 @@ type
path*: NodeId
PackedSym* = object
id*: int32
kind*: TSymKind
name*: LitId
typ*: PackedItemId
@@ -72,7 +71,6 @@ type
instantiatedFrom*: PackedItemId
PackedType* = object
id*: int32
kind*: TTypeKind
callConv*: TCallingConvention
#nodekind*: TNodeKind
@@ -126,7 +124,7 @@ proc `==`*(a, b: NodePos): bool {.borrow.}
proc `==`*(a, b: NodeId): bool {.borrow.}
proc newTreeFrom*(old: PackedTree): PackedTree =
result = PackedTree(nodes: @[])
result.nodes = @[]
when false: result.sh = old.sh
proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =

View File

@@ -19,8 +19,6 @@ from std/typetraits import supportsCopyMem
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
import std / tables
## Overview
## ========
## `RodFile` represents a Rod File (versioned binary format), and the
@@ -172,18 +170,6 @@ proc storeSeq*[T](f: var RodFile; s: seq[T]) =
for i in 0..<s.len:
storePrim(f, s[i])
proc storeOrderedTable*[K, T](f: var RodFile; s: OrderedTable[K, T]) =
if f.err != ok: return
if s.len >= high(int32):
setError f, tooBig
return
var lenPrefix = int32(s.len)
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
for _, v in s:
storePrim(f, v)
proc loadPrim*(f: var RodFile; s: var string) =
## Read a string, the length was stored as a prefix
if f.err != ok: return
@@ -225,19 +211,6 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
for i in 0..<lenPrefix:
loadPrim(f, s[i])
proc loadOrderedTable*[K, T](f: var RodFile; s: var OrderedTable[K, T]) =
## `T` must be compatible with `copyMem`, see `loadPrim`
if f.err != ok: return
var lenPrefix = int32(0)
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
s = initOrderedTable[K, T](lenPrefix)
for i in 0..<lenPrefix:
var x = default T
loadPrim(f, x)
s[x.id] = x
proc storeHeader*(f: var RodFile; cookie = defaultCookie) =
## stores the header which is described by `cookie`.
if f.err != ok: return

View File

@@ -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]
@@ -144,7 +144,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
# for an enumeration we have to add all identifiers
if multiImport:
# for a overloadable syms add all overloaded routines
var it: ModuleIter = default(ModuleIter)
var it: ModuleIter
var e = initModuleIter(it, c.graph, fromMod, s.name)
while e != nil:
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
@@ -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,16 +246,12 @@ 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))
c.unusedImports.add((result, n.info))
c.importModuleMap[result.id] = realModule.id
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
result = (nil, false)
var ret = default(typeof(result))
var ret: typeof(result)
proc processPragma(n2: PNode): PNode =
let (result2, kws) = splitPragmas(c, n2)
result = result2
@@ -290,11 +286,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,19 +302,11 @@ 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) =
if n.kind == nkImportAs:
suggestMod(n[0], realModule)
elif n.kind == nkInfix:
suggestMod(n[2], s)
else:
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
suggestMod(n, result)
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
importStmtResult.add newSymNode(result, n.info)
#newStrNode(toFullPath(c.config, f), n.info)
else:
@@ -338,7 +325,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 +362,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 +377,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)

View File

@@ -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,32 @@ 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
if not hasDestructor(c, n.typ): 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 +196,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 '\''
@@ -219,7 +221,7 @@ proc makePtrType(c: var Con, baseType: PType): PType =
proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
var addrExp: PNode
if op.typ != nil and op.typ.signatureLen > 1 and op.typ.firstParamType.kind != tyVar:
if op.typ != nil and op.typ.len > 1 and op.typ[1].kind != tyVar:
addrExp = dest
else:
addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
@@ -244,12 +246,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 +274,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 +290,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:
@@ -302,7 +299,7 @@ proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFla
proc isCriticalLink(dest: PNode): bool {.inline.} =
#[
Lins's idea that only "critical" links can introduce a cycle. This is
critical for the performance guarantees that we strive for: If you
critical for the performance gurantees that we strive for: If you
traverse a data structure, no tracing will be performed at all.
ORC is about this promise: The GC only touches the memory that the
mutator touches too.
@@ -319,10 +316,9 @@ 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
let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
if c.graph.config.selectedGC == gcOrc:
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
if cyclicType(c.graph, t):
result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
@@ -334,7 +330,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 +358,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 +398,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,37 +406,16 @@ 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)
if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0:
assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) or
(n.typ.kind == tyPtr and n.sym.typ.kind == tyRef)
# bug #23505; transformed by `transf`: addr (deref ref) -> ptr
# we know it's really a pointer; so here we assign it directly
assert n.kind != nkSym or not hasDestructor(c, n.sym.typ)
result = copyTree(n)
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,64 +441,62 @@ proc isCapturedVar(n: PNode): bool =
else: result = false
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
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.
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 tmp = c.getTemp(s, n.typ, n.info)
if hasDestructor(c, n.typ):
let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil and tfHasOwned notin typ.flags:
if sfError in op.flags:
c.checkForErrorPragma(n.typ, n, "=dup")
else:
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
if copyOp != nil and sfError in copyOp.flags and
sfOverridden notin op.flags:
c.checkForErrorPragma(n.typ, n, "=dup", inferredFromCopy = true)
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
)
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 n.typ.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:
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)
if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
assert(not containsManagedMemory(n.typ))
if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
# Since we know somebody will take over the produced copy, there is
# no need to destroy it.
result.add tmp
proc isDangerousSeq(t: PType): bool {.inline.} =
let t = t.skipTypes(abstractInst)
result = t.kind == tySequence and tfHasOwned notin t.elementType.flags
result = t.kind == tySequence and tfHasOwned notin t[0].flags
proc containsConstSeq(n: PNode): bool =
if n.kind == nkBracket and n.len > 0 and n.typ != nil and isDangerousSeq(n.typ):
return true
result = false
case n.kind
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
result = containsConstSeq(n[1])
of nkObjConstr, nkClosure:
for i in 1..<n.len:
@@ -540,7 +513,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 +553,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,11 +592,9 @@ 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}:
# bug #23247 we don't own the data, so it's harmful to destroy it
let cpy = if hasDestructor(c, ret.typ):
s.parent[].final.add c.genDestroy(tmp)
moveOrCopy(tmp, ret, c, s, {IsDecl})
else:
@@ -675,7 +646,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
for j in 0 ..< it.len-1:
branch[j] = copyTree(it[j])
var ofScope = nestedScope(s, it.lastSon)
branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
processScope(c, ofScope, maybeVoid(it[^1], ofScope))
else:
processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags)
@@ -723,7 +694,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
#Condition needs to be destroyed outside of the condition/branch scope
branch[0] = p(it[0], c, s, normal)
branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
processScope(c, branchScope, maybeVoid(it[^1], branchScope))
else:
processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
@@ -780,10 +751,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
@@ -798,35 +769,6 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
result.add copyNode(n[0])
s.needsTry = true
template isCustomDestructor(c: Con, t: PType): bool =
hasDestructor(c, t) and
getAttachedOp(c.graph, t, attachedDestructor) != nil and
sfOverridden in getAttachedOp(c.graph, t, attachedDestructor).flags
proc hasCustomDestructor(c: Con, t: PType): bool =
result = isCustomDestructor(c, t)
var obj = t
while obj.baseClass != nil:
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 +782,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,17 +802,14 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
# allow conversions from owned to unowned via this little hack:
let nTyp = n[1].typ
n[1].typ() = n.typ
n[1].typ = n.typ
result[1] = p(n[1], c, s, sinkArg)
result[1].typ() = nTyp
result[1].typ = nTyp
else:
result[1] = p(n[1], c, s, sinkArg)
elif n.kind in {nkObjDownConv, nkObjUpConv}:
result = copyTree(n)
result[0] = p(n[0], c, s, sinkArg)
elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}:
result = copyTree(n)
result[1] = p(n[1], c, s, sinkArg)
elif n.typ == nil:
# 'raise X' can be part of a 'case' expression. Deal with it here:
result = p(n, c, s, normal)
@@ -906,25 +853,31 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
for i in 1..<n.len:
if n[i].kind == nkExprColonExpr:
let field = lookupFieldAgain(t, n[i][0].sym)
if field != nil and (sfCursor in field.flags or field.typ.kind in {tyOpenArray, tyVarargs}):
# don't sink fields with openarray types
if field != nil and sfCursor in field.flags:
result[i][1] = p(n[i][1], c, s, normal)
else:
result[i][1] = p(n[i][1], c, s, m)
else:
result[i] = p(n[i], c, s, m)
if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
if mode == normal and (isRefConstr or (hasDestructor(c, t) and
getAttachedOp(c.graph, t, attachedDestructor) != nil and
sfOverridden in getAttachedOp(c.graph, t, attachedDestructor).flags)):
result = ensureDestruction(result, n, c, s)
of nkCallKinds:
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
inc c.inEnsureMove
result = p(n[1], c, s, sinkArg)
dec c.inEnsureMove
return
let inSpawn = c.inSpawn
if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
c.inSpawn.inc
elif c.inSpawn > 0:
c.inSpawn.dec
# bug #23907; skips tyGenericInst for generic callbacks
let parameters = if n[0].typ != nil: n[0].typ.skipTypes(abstractInst) else: n[0].typ
let L = if parameters != nil: parameters.signatureLen else: 0
let parameters = n[0].typ
let L = if parameters != nil: parameters.len else: 0
when false:
var isDangerous = false
@@ -933,19 +886,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:
@@ -960,10 +907,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
result[0] = p(n[0], c, s, normal)
if canRaise(n[0]): s.needsTry = true
if mode == normal:
if result.typ != nil and result.typ.kind notin {tyOpenArray, tyVarargs}:
# Returns of openarray types shouldn't be destroyed
# bug #19435; # bug #23247
result = ensureDestruction(result, n, c, s)
result = ensureDestruction(result, n, c, s)
of nkDiscardStmt: # Small optimization
result = shallowCopy(n)
if n[0].kind != nkEmpty:
@@ -973,9 +917,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 +932,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 +961,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 +996,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 +1085,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 +1130,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 +1148,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 +1156,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
result = c.genCopy(dest, ri, flags)
dec c.inEnsureMove, isEnsureMove
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, 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:
@@ -1244,9 +1166,9 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(s, dest, ri, flags)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
elif ri.sym.kind != skParam and
isAnalysableFieldAccess(ri, c.owner) and
isLastRead(ri, c, s) and canBeMoved(c, dest.typ):
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri) and
not ({sfGlobal, sfPure} <= ri.sym.flags):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(s, dest, ri, flags)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
@@ -1255,24 +1177,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 +1197,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) =

View File

@@ -6,11 +6,11 @@ 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
macosx: i386;amd64;powerpc64;arm64
solaris: i386;amd64;sparc;sparc64
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
netbsd: i386;amd64;arm64
netbsd: i386;amd64
openbsd: i386;amd64;arm;arm64
dragonfly: i386;amd64
crossos: amd64
@@ -147,4 +147,4 @@ licenses: "bin/nim,MIT;lib/*,MIT;"
[nimble]
pkgName: "nim"
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css;doc/nimdoc.cls"
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"

View File

@@ -34,7 +34,6 @@ proc `$`*(a: Int128): string
proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
{.noSideEffect.}:
result = Zero
when T is bool: result.sdata(0) = int32(arg)
elif T is SomeUnsignedInt:
when sizeof(arg) <= 4:
@@ -209,35 +208,30 @@ proc `==`*(a, b: Int128): bool =
return true
proc bitnot*(a: Int128): Int128 =
result = Zero
result.udata[0] = not a.udata[0]
result.udata[1] = not a.udata[1]
result.udata[2] = not a.udata[2]
result.udata[3] = not a.udata[3]
proc bitand*(a, b: Int128): Int128 =
result = Zero
result.udata[0] = a.udata[0] and b.udata[0]
result.udata[1] = a.udata[1] and b.udata[1]
result.udata[2] = a.udata[2] and b.udata[2]
result.udata[3] = a.udata[3] and b.udata[3]
proc bitor*(a, b: Int128): Int128 =
result = Zero
result.udata[0] = a.udata[0] or b.udata[0]
result.udata[1] = a.udata[1] or b.udata[1]
result.udata[2] = a.udata[2] or b.udata[2]
result.udata[3] = a.udata[3] or b.udata[3]
proc bitxor*(a, b: Int128): Int128 =
result = Zero
result.udata[0] = a.udata[0] xor b.udata[0]
result.udata[1] = a.udata[1] xor b.udata[1]
result.udata[2] = a.udata[2] xor b.udata[2]
result.udata[3] = a.udata[3] xor b.udata[3]
proc `shr`*(a: Int128, b: int): Int128 =
result = Zero
let b = b and 127
if b < 32:
result.sdata(3) = a.sdata(3) shr b
@@ -264,7 +258,6 @@ proc `shr`*(a: Int128, b: int): Int128 =
result.sdata(0) = a.sdata(3) shr (b and 31)
proc `shl`*(a: Int128, b: int): Int128 =
result = Zero
let b = b and 127
if b < 32:
result.udata[0] = a.udata[0] shl b
@@ -288,7 +281,6 @@ proc `shl`*(a: Int128, b: int): Int128 =
result.udata[3] = a.udata[0] shl (b and 31)
proc `+`*(a, b: Int128): Int128 =
result = Zero
let tmp0 = uint64(a.udata[0]) + uint64(b.udata[0])
result.udata[0] = cast[uint32](tmp0)
let tmp1 = uint64(a.udata[1]) + uint64(b.udata[1]) + (tmp0 shr 32)
@@ -321,7 +313,6 @@ proc abs(a: int32): int =
if a < 0: -a else: a
proc `*`(a: Int128, b: uint32): Int128 =
result = Zero
let tmp0 = uint64(a.udata[0]) * uint64(b)
let tmp1 = uint64(a.udata[1]) * uint64(b)
let tmp2 = uint64(a.udata[2]) * uint64(b)
@@ -344,7 +335,6 @@ proc `*=`(a: var Int128, b: int32) =
a = a * b
proc makeInt128(high, low: uint64): Int128 =
result = Zero
result.udata[0] = cast[uint32](low)
result.udata[1] = cast[uint32](low shr 32)
result.udata[2] = cast[uint32](high)
@@ -383,8 +373,6 @@ proc fastLog2*(a: Int128): int =
proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
assert(divisor != Zero)
result = (Zero, Zero)
let isNegativeA = isNegative(dividend)
let isNegativeB = isNegative(divisor)
@@ -551,28 +539,24 @@ proc toInt128*(arg: float64): Int128 =
return res
proc maskUInt64*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0]
result.udata[1] = arg.udata[1]
result.udata[2] = 0
result.udata[3] = 0
proc maskUInt32*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0]
result.udata[1] = 0
result.udata[2] = 0
result.udata[3] = 0
proc maskUInt16*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0] and 0xffff
result.udata[1] = 0
result.udata[2] = 0
result.udata[3] = 0
proc maskUInt8*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0] and 0xff
result.udata[1] = 0
result.udata[2] = 0

View File

@@ -54,18 +54,18 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool =
of tyObject:
if isFinal(ret):
result = canAliasN(arg, ret.n, marker)
if not result and ret.baseClass != nil:
result = canAlias(arg, ret.baseClass, marker)
if not result and ret.len > 0 and ret[0] != nil:
result = canAlias(arg, ret[0], marker)
else:
result = true
of tyTuple:
result = false
for r in ret.kids:
result = canAlias(arg, r, marker)
for i in 0..<ret.len:
result = canAlias(arg, ret[i], marker)
if result: break
of tyArray, tySequence, tyDistinct, tyGenericInst,
tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
result = canAlias(arg, ret.skipModifier, marker)
result = canAlias(arg, ret.lastSon, marker)
of tyProc:
result = ret.callConv == ccClosure
else:
@@ -119,16 +119,14 @@ proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult =
if result != NotFound: return result
case t.kind
of tyObject:
if t.baseClass != nil:
result = containsDangerousRefAux(t.baseClass.skipTypes(skipPtrs), marker)
if t[0] != nil:
result = containsDangerousRefAux(t[0].skipTypes(skipPtrs), marker)
if result == NotFound: result = containsDangerousRefAux(t.n, marker)
of tyGenericInst, tyDistinct, tyAlias, tySink:
result = containsDangerousRefAux(skipModifier(t), marker)
of tyArray, tySet, tySequence:
result = containsDangerousRefAux(t.elementType, marker)
of tyTuple:
for a in t.kids:
result = containsDangerousRefAux(a, marker)
result = containsDangerousRefAux(lastSon(t), marker)
of tyArray, tySet, tyTuple, tySequence:
for i in 0..<t.len:
result = containsDangerousRefAux(t[i], marker)
if result == Found: return result
else:
discard
@@ -166,7 +164,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

File diff suppressed because it is too large Load Diff

View File

@@ -69,7 +69,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
else: internalError(p.config, n.info, "genObjectFields")
proc objHasTypeField(t: PType): bool {.inline.} =
tfInheritable in t.flags or t.baseClass != nil
tfInheritable in t.flags or t[0] != nil
proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
let kind = if objHasTypeField(typ): tyObject else: tyTuple
@@ -79,9 +79,9 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
p.g.typeInfo.addf("var NNI$1 = $2;$n",
[rope(typ.id), genObjectFields(p, typ, typ.n)])
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
if (typ.kind == tyObject) and (typ.baseClass != nil):
if (typ.kind == tyObject) and (typ[0] != nil):
p.g.typeInfo.addf("$1.base = $2;$n",
[name, genTypeInfo(p, typ.baseClass.skipTypes(skipPtrs))])
[name, genTypeInfo(p, typ[0].skipTypes(skipPtrs))])
proc genTupleFields(p: PProc, typ: PType): Rope =
var s: Rope = ""
@@ -117,17 +117,17 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
prepend(p.g.typeInfo, s)
p.g.typeInfo.add(n)
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
if typ.baseClass != nil:
if typ[0] != nil:
p.g.typeInfo.addf("$1.base = $2;$n",
[name, genTypeInfo(p, typ.baseClass)])
[name, genTypeInfo(p, typ[0])])
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
of tyDistinct:
result = genTypeInfo(p, t.skipModifier)
result = genTypeInfo(p, t[0])
of tyPointer, tyProc, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64:
var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
@@ -139,18 +139,18 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
p.g.typeInfo.addf("$1.base = $2;$n",
[result, genTypeInfo(p, t.elementType)])
[result, genTypeInfo(p, t.lastSon)])
of tyArray:
var s =
"var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
p.g.typeInfo.addf("$1.base = $2;$n",
[result, genTypeInfo(p, t.elementType)])
[result, genTypeInfo(p, t[1])])
of tyEnum: genEnumInfo(p, t, result)
of tyObject: genObjectInfo(p, t, result)
of tyTuple: genTupleInfo(p, t, result)
of tyStatic:
if t.n != nil: result = genTypeInfo(p, skipModifier t)
if t.n != nil: result = genTypeInfo(p, lastSon t)
else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')

View File

@@ -145,14 +145,11 @@ proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info)
result.typ = createClosureIterStateType(g, iter, idgen)
template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
# YYY meh, just add the state field for every closure for now, it's too
# hard to figure out if it comes from a closure iterator:
result = createObj(g, idgen, owner, info, final=false)
result.flags.incl tfFinal
if owner.isIterator:
rawAddField(result, createStateField(g, owner, idgen))
rawAddField(result, createStateField(g, owner, idgen))
proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
if resultPos < iter.ast.len:
@@ -160,7 +157,7 @@ proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym
else:
# XXX a bit hacky:
result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {})
result.typ = iter.typ.returnType
result.typ = iter.typ[0]
incl(result.flags, sfUsed)
iter.ast.add newSymNode(result)
@@ -175,23 +172,26 @@ proc addHiddenParam(routine: PSym, param: PSym) =
assert sfFromGeneric in param.flags
#echo "produced environment: ", param.id, " for ", routine.id
proc getEnvParam*(routine: PSym): PSym =
if routine.ast.isNil: return nil
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
let params = routine.ast[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
result = hidden.sym
assert sfFromGeneric in result.flags
else:
result = nil
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
result = getEnvParam(routine)
if result.isNil:
# writeStackTrace()
localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
result = routine
proc getEnvParam*(routine: PSym): PSym =
let params = routine.ast[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.name.s == paramName:
result = hidden.sym
assert sfFromGeneric in result.flags
else:
result = nil
proc interestingVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
sfGlobal notin s.flags and
@@ -200,7 +200,7 @@ proc interestingVar(s: PSym): bool {.inline.} =
proc illegalCapture(s: PSym): bool {.inline.} =
result = classifyViewType(s.typ) != noView or s.kind == skResult
proc isInnerProc*(s: PSym): bool =
proc isInnerProc(s: PSym): bool =
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
result = s.skipGenericOwner.kind in routineKinds
else:
@@ -230,14 +230,23 @@ 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
template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
proc interestingIterVar(s: PSym): bool {.inline.} =
# XXX optimization: Only lift the variable if it lives across
# yield/return boundaries! This can potentially speed up
# closure iterators quite a bit.
result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure
proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
## lambda lifting can be harmful for JS-like code generators.
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
jsNoLambdaLifting in conf.legacyFeatures and conf.backend == backendJs and not isCompileTime
result = conf.backend == backendJs and not isCompileTime
proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
if owner.kind != skMacro:
createTypeBoundOps(g, nil, refType.elementType, info, idgen)
createTypeBoundOps(g, nil, refType.lastSon, info, idgen)
createTypeBoundOps(g, nil, refType, info, idgen)
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
owner.flags.incl sfInjectDestructors
@@ -255,7 +264,8 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
let iter = n.sym
assert iter.isIterator
result = newNodeIT(nkStmtListExpr, n.info, iter.typ)
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let hp = getHiddenParam(g, iter)
var env: PNode
if owner.isIterator:
@@ -276,6 +286,15 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
result.add makeClosure(g, idgen, iter, env, n.info)
proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
let envParam = getHiddenParam(g, owner)
let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
let field = addField(obj, s, g.cache, idgen)
var access = newSymNode(envParam)
assert obj.kind == tyObject
result = rawIndirectAccess(access, field, s.info)
# ------------------ new stuff -------------------------------------------
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
@@ -285,8 +304,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])
@@ -323,13 +342,9 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
info: TLineInfo): PType =
result = c.ownerToType.getOrDefault(owner.id)
if result.isNil:
let env = getEnvParam(owner)
if env.isNil or not owner.isIterator:
result = newType(tyRef, c.idgen, owner)
let obj = createEnvObj(c.graph, c.idgen, owner, info)
rawAddSon(result, obj)
else:
result = env.typ
result = newType(tyRef, c.idgen, owner)
let obj = createEnvObj(c.graph, c.idgen, owner, info)
rawAddSon(result, obj)
c.ownerToType[owner.id] = result
proc asOwnedRef(c: var DetectionPass; t: PType): PType =
@@ -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,22 @@ 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 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.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 """
@@ -544,8 +551,8 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let envParam = getHiddenParam(g, owner)
if not envParam.isNil:
var access = newSymNode(envParam)
var obj = access.typ.elementType
while true:
let obj = access.typ[0]
assert obj.kind == tyObject
let field = getFieldFromObj(obj, s)
if field != nil:
@@ -553,7 +560,6 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let upField = lookupInRecord(obj.n, getIdent(g.cache, upName))
if upField == nil: break
access = rawIndirectAccess(access, upField, n.info)
obj = access.typ.baseClass
localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
result = n
@@ -565,7 +571,7 @@ proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idge
when false:
if owner.kind == skIterator and owner.typ.callConv == ccClosure:
let it = getHiddenParam(owner)
addUniqueField(it.typ.elementType, v)
addUniqueField(it.typ[0], v)
result = indirectAccess(newSymNode(it), v, v.info)
else:
result = newSymNode(v)
@@ -622,7 +628,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 +668,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 +686,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 +725,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 +772,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 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 +794,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:
@@ -887,18 +882,19 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType;
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
idgen: IdGenerator; flags: TransformFlags): PNode =
# XXX backend == backendJs does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ...
# However we can do lifting for the stuff which is *only* compiletime.
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and
if body.kind == nkEmpty or (
g.config.backend == backendJs and not isCompileTime) or
(fn.skipGenericOwner.kind != skModule and force notin flags):
# ignore forward declaration:
result = body
tooEarly = true
if fn.isIterator:
var d = initDetectionPass(g, fn, idgen)
addClosureParam(d, fn, body.info)
else:
var d = initDetectionPass(g, fn, idgen)
detectCapturedVars(body, fn, d)
@@ -987,20 +983,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))
@@ -1011,15 +993,12 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
# gather vars in a tuple:
var v2 = newNodeI(nkLetSection, body.info)
var vpart = newNodeI(if body.len == 3: nkIdentDefs else: nkVarTuple, body.info)
if body.len == 3 and body[0].kind == nkVarTuple:
vpart = body[0] # fixes for (i,j) in walk() # bug #15924
else:
for i in 0..<body.len-2:
if body[i].kind == nkSym:
body[i].sym.transitionToLet()
vpart.add body[i]
for i in 0..<body.len-2:
if body[i].kind == nkSym:
body[i].sym.transitionToLet()
vpart.add body[i]
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
if not env.isNil:
call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info)
vpart.add call

View File

@@ -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)

View File

@@ -9,7 +9,7 @@
## Layouter for nimpretty.
import idents, lexer, ast, lineinfos, llstream, options, msgs, strutils, pathutils
import idents, lexer, lineinfos, llstream, options, msgs, strutils, pathutils
const
MinLineLen = 15
@@ -243,28 +243,23 @@ proc renderTokens*(em: var Emitter): string =
return content
type
FinalCheck = proc (content: string; origAst: PNode): bool {.nimcall.}
proc writeOut*(em: Emitter; content: string; origAst: PNode; check: FinalCheck) =
proc writeOut*(em: Emitter, content: string) =
## Write to disk
let outFile = em.config.absOutFile
if fileExists(outFile) and readFile(outFile.string) == content:
discard "do nothing, see #9499"
return
var f = llStreamOpen(outFile, fmWrite)
if f == nil:
rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
return
f.llStreamWrite content
llStreamClose(f)
if check(content, origAst):
var f = llStreamOpen(outFile, fmWrite)
if f == nil:
rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
return
f.llStreamWrite content
llStreamClose(f)
proc closeEmitter*(em: var Emitter; origAst: PNode; check: FinalCheck) =
proc closeEmitter*(em: var Emitter) =
## Renders emitter tokens and write to a file
let content = renderTokens(em)
em.writeOut(content, origAst, check)
em.writeOut(content)
proc wr(em: var Emitter; x: string; lt: LayoutToken) =
em.tokens.add x
@@ -451,13 +446,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]:

View File

@@ -300,7 +300,8 @@ proc getNumber(L: var Lexer, result: var Token) =
# Used to get slightly human friendlier err messages.
const literalishChars = {'A'..'Z', 'a'..'z', '0'..'9', '_', '.', '\''}
var msgPos = L.bufpos
var t = Token(literal: "")
var t: Token
t.literal = ""
L.bufpos = startpos # Use L.bufpos as pos because of matchChars
matchChars(L, t, literalishChars)
# We must verify +/- specifically so that we're not past the literal
@@ -316,28 +317,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 +492,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 +897,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 +1323,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.

View File

@@ -49,17 +49,17 @@ 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)
op != nil and sfOverridden in op.flags
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
for i, a in t.ikids:
for i in 0..<t.len:
let lit = lowerings.newIntLit(c.g, x.info, i)
let b = if c.kind == attachedTrace: y else: y.at(lit, a)
fillBody(c, a, body, x.at(lit, a), b)
let b = if c.kind == attachedTrace: y else: y.at(lit, t[i])
fillBody(c, t[i], body, x.at(lit, t[i]), b)
proc dotField(x: PNode, f: PSym): PNode =
result = newNodeI(nkDotExpr, x.info, 2)
@@ -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)
@@ -139,25 +139,25 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
result.add minusExpr
proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
var destroy = newNodeIT(nkCall, x.info, op.typ.returnType)
var destroy = newNodeIT(nkCall, x.info, op.typ[0])
destroy.add(newSymNode(op))
if op.typ.firstParamType.kind != tyVar:
if op.typ[1].kind != tyVar:
destroy.add x
else:
destroy.add genAddr(c, x)
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
proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ.returnType)
result = newNodeIT(nkCall, x.info, op.typ[0])
result.add(newSymNode(op))
result.add genAddr(c, x)
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) =
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
case n.kind
of nkSym:
if c.filterDiscriminator != nil: return
@@ -167,8 +167,6 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
enforceDefaultOp:
defaultOp(c, f.typ, body, x.dotField(f), b)
else:
if enforceWasMoved:
body.add genBuiltin(c, mWasMoved, "wasMoved", x.dotField(f))
fillBody(c, f.typ, body, x.dotField(f), b)
of nkNilLit: discard
of nkRecCase:
@@ -207,7 +205,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
branch[^1] = newNodeI(nkStmtList, c.info)
fillBodyObj(c, n[i].lastSon, branch[^1], x, y,
enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn)
enforceDefaultOp = localEnforceDefaultOp)
if branch[^1].len == 0: inc emptyBranches
caseStmt.add(branch)
if emptyBranches != n.len-1:
@@ -218,44 +216,20 @@ 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)
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
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()
if t.len > 0 and t[0] != nil:
fillBody(c, skipTypes(t[0], abstractPtrs), body, x, y)
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)
var obj = t
while obj.baseClass != nil:
obj = skipTypes(obj.baseClass, abstractPtrs)
while obj.len > 0 and obj[0] != nil:
obj = skipTypes(obj[0], abstractPtrs)
hasCase = hasCase or isCaseObj(obj.n)
if hasCase and c.kind in {attachedAsgn, attachedDeepCopy}:
@@ -280,7 +254,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 +266,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
@@ -306,17 +279,16 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
c.kind = attachedDestructor
fillBodyObjTImpl(c, t, body, blob, y)
c.kind = prevKind
else:
fillBodyObjTImpl(c, t, body, x, y)
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}
if c.fn.typ.len == 3 + ord(c.kind == attachedAsgn):
if c.fn.typ.len == 4:
result = c.fn.typ.n.lastSon
assert result.kind == nkSym
assert result.sym.name.s == "cyclic"
@@ -330,35 +302,27 @@ proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
result.add newSymNode(op)
if sfNeverRaises notin op.flags:
c.canRaise = true
if op.typ.firstParamType.kind == tyVar:
if op.typ[1].kind == tyVar:
result.add genAddr(c, x)
else:
result.add x
if y != nil:
result.add y
if op.typ.signatureLen == 4:
if op.typ.len == 4:
assert y != nil
if c.fn.typ.signatureLen == 4:
if c.fn.typ.len == 4:
result.add getCycleParam(c)
else:
# assume the worst: A cycle is created:
result.add boolLit(c.g, y.info, true)
proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ.returnType)
result = newNodeIT(nkCall, x.info, op.typ[0])
result.add(newSymNode(op))
result.add x
if sfNeverRaises notin op.flags:
c.canRaise = true
if c.kind == attachedDup and op.typ.len == 3:
assert x != nil
if c.fn.typ.len == 3:
result.add getCycleParam(c)
else:
# assume the worst: A cycle is created:
result.add boolLit(c.g, x.info, true)
proc newDeepCopyCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
result = newAsgnStmt(x, newOpCall(c, op, y))
@@ -380,10 +344,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 +525,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)
@@ -585,7 +545,7 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let counterIdx = body.len
let i = declareCounter(c, body, toInt64(firstOrd(c.g.config, t)))
let whileLoop = genWhileLoop(c, i, x)
let elemType = t.elementType
let elemType = t.lastSon
let b = if c.kind == attachedTrace: y else: y.at(i, elemType)
fillBody(c, elemType, whileLoop[1], x.at(i, elemType), b)
if whileLoop[1].len > 0:
@@ -599,37 +559,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 +575,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 +592,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 +630,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,11 +652,11 @@ 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
of tyRef: result = types.canFormAcycle(g, t.elementType)
of tyRef: result = types.canFormAcycle(g, t.lastSon)
of tyProc: result = t.callConv == ccClosure
else: result = false
@@ -748,7 +681,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
]#
var actions = newNodeI(nkStmtList, c.info)
let elemType = t.elementType
let elemType = t.lastSon
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(c.g, elemType)
@@ -767,7 +700,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 +710,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 +718,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 +745,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 +766,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 +782,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 +794,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 +806,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 +818,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,19 +846,19 @@ 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)
let elemType = t.skipModifier
let elemType = t.lastSon
#fillBody(c, elemType, actions, genDeref(x), genDeref(y))
#var disposeCall = genBuiltin(c, mDispose, "dispose", x)
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 +874,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 +897,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 +914,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 +932,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 +982,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 +1001,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)
@@ -1090,7 +1017,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
fillBodyObjT(c, t, body, x, y)
of tyDistinct:
if not considerUserDefinedOp(c, t, body, x, y):
fillBody(c, t.elementType, body, x, y)
fillBody(c, t[0], body, x, y)
of tyTuple:
fillBodyTup(c, t, body, x, y)
of tyVarargs, tyOpenArray:
@@ -1099,7 +1026,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
else:
discard "cannot copy openArray"
of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass,
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
@@ -1107,14 +1034,14 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
discard
of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyAlias, tySink:
fillBody(c, skipModifier(t), body, x, y)
fillBody(c, lastSon(t), body, x, y)
of tyConcept, tyIterable: raiseAssert "unreachable"
proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
kind: TTypeAttachedOp; info: TLineInfo;
idgen: IdGenerator): PSym =
assert typ.kind == tyDistinct
let baseType = typ.elementType
let baseType = typ[0]
if getAttachedOp(g, baseType, kind) == nil:
discard produceSym(g, c, baseType, kind, info, idgen)
result = getAttachedOp(g, baseType, kind)
@@ -1166,7 +1093,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}):
dest.typ = typ
else:
dest.typ = makeVarType(typ.owner, typ, idgen)
@@ -1203,8 +1130,8 @@ 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;
@@ -1220,7 +1147,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
fn: result)
let dest = if kind == attachedDup: result.ast[resultPos].sym else: result.typ.n[1].sym
let d = if result.typ.firstParamType.kind == tyVar: newDeref(newSymNode(dest)) else: newSymNode(dest)
let d = if result.typ[1].kind == tyVar: newDeref(newSymNode(dest)) else: newSymNode(dest)
let src = case kind
of {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
of attachedDup: newSymNode(result.typ.n[1].sym)
@@ -1233,14 +1160,12 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
## compiler can use a combination of `=destroy` and memCopy for sink op
dest.flags.incl sfCursor
let op = getAttachedOp(g, typ, attachedDestructor)
result.ast[bodyPos].add newOpCall(a, op, if op.typ.firstParamType.kind == tyVar: d[0] else: d)
result.ast[bodyPos].add newOpCall(a, op, if op.typ[1].kind == tyVar: d[0] else: d)
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,18 +1175,11 @@ 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)
if not a.canRaise:
incl result.flags, sfNeverRaises
result.ast[pragmasPos] = newNodeI(nkPragma, info)
result.ast[pragmasPos].add newTree(nkExprColonExpr,
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))
if kind == attachedDestructor:
incl result.options, optQuirky
if not a.canRaise: incl result.flags, sfNeverRaises
completePartialOp(g, idgen.module, typ, kind, result)
@@ -1321,7 +1239,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 +1249,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 +1290,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!

View File

@@ -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]

View File

@@ -92,15 +92,8 @@ type
warnStmtListLambda = "StmtListLambda",
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 +107,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] = [
@@ -203,15 +196,8 @@ const
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
warnBareExcept: "$1",
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 +231,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 +252,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 +264,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

View File

@@ -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)

View File

@@ -11,7 +11,7 @@
import
pathutils
import std/strutils
when defined(nimPreviewSlimSystem):
import std/syncio
@@ -68,7 +68,6 @@ when not declared(readLineFromStdin):
# fallback implementation:
proc readLineFromStdin(prompt: string, line: var string): bool =
stdout.write(prompt)
stdout.flushFile()
result = readLine(stdin, line)
if not result:
stdout.write("\n")
@@ -86,47 +85,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 +92,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 +108,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)

View File

@@ -50,7 +50,7 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
case x.kind
of nkIdent: id.add(x.ident.s)
of nkSym: id.add(x.sym.name.s)
of nkSymChoices, nkOpenSym:
of nkSymChoices:
if x[0].kind == nkSym:
id.add(x[0].sym.name.s)
else:
@@ -58,7 +58,7 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
else: handleError(n, origin)
result = getIdent(c.cache, id)
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
if n[0].kind == nkSym:
result = n[0].sym.name
else:
@@ -75,13 +75,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) =
@@ -140,7 +137,7 @@ proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule;
return result
iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym =
var ti: ModuleIter = default(ModuleIter)
var ti: ModuleIter
var candidate = initIdentIter(ti, marked, im, name, g)
while candidate != nil:
yield candidate
@@ -153,7 +150,7 @@ iterator importedItems*(c: PContext; name: PIdent): PSym =
yield s
proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
var ti: TIdentIter = default(TIdentIter)
var ti: TIdentIter
result = @[]
var res = initIdentIter(ti, c.pureEnumFields, name)
while res != nil:
@@ -222,33 +219,28 @@ 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)
var ti: TIdentIter
var candidate = initIdentIter(ti, scope.symbols, s)
while candidate != nil:
if candidate.kind in filter:
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):
var ti: TIdentIter = default(TIdentIter)
var ti: TIdentIter
var candidate = initIdentIter(ti, scope.symbols, s)
while candidate != nil:
if candidate.kind in filter:
@@ -258,25 +250,17 @@ proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PS
candidate = nextIdentIter(ti, scope.symbols)
if result.len == 0:
searchImportsAll(c, s, filter, result)
proc cmpScopes*(ctx: PContext, s: PSym): int =
# Do not return a negative number
if s.originatingModule == ctx.module:
result = 2
var owner = s
while true:
owner = owner.skipGenericOwner
if owner.kind == skModule: break
inc result
else:
result = 1
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 isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool =
result = false
block outer:
for scope in allScopes(c.currentScope):
var ti: TIdentIter = default(TIdentIter)
var ti: TIdentIter
var candidate = initIdentIter(ti, scope.symbols, s)
var scopeHasCandidate = false
while candidate != nil:
@@ -307,16 +291,8 @@ proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): boo
# imports had a candidate but wasn't ambiguous
return false
proc errorSym*(c: PContext, ident: PIdent, info: TLineInfo): PSym =
## creates an error symbol to avoid cascading errors (for IDE support)
result = newSym(skError, ident, c.idgen, getCurrOwner(c), info, {})
result.typ = errorType(c)
incl(result.flags, sfDiscardable)
# pretend it's from the top level scope to prevent cascading errors:
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
c.moduleScope.addSym(result)
proc errorSym*(c: PContext, n: PNode): PSym =
## creates an error symbol to avoid cascading errors (for IDE support)
var m = n
# ensure that 'considerQuotedIdent' can't fail:
if m.kind == nkDotExpr: m = m[1]
@@ -324,7 +300,12 @@ proc errorSym*(c: PContext, n: PNode): PSym =
considerQuotedIdent(c, m)
else:
getIdent(c.cache, "err:" & renderTree(m))
result = errorSym(c, ident, n.info)
result = newSym(skError, ident, c.idgen, getCurrOwner(c), n.info, {})
result.typ = errorType(c)
incl(result.flags, sfDiscardable)
# pretend it's from the top level scope to prevent cascading errors:
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
c.moduleScope.addSym(result)
type
TOverloadIterMode* = enum
@@ -351,7 +332,7 @@ proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string =
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
# check if all symbols have been used and defined:
var it: TTabIter = default(TTabIter)
var it: TTabIter
var s = initTabIter(it, scope.symbols)
var missingImpls = 0
var unusedSyms: seq[tuple[sym: PSym, key: string]] = @[]
@@ -388,7 +369,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 +444,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
@@ -515,7 +487,7 @@ proc mustFixSpelling(c: PContext): bool {.inline.} =
result = c.config.spellSuggestMax != 0 and c.compilesContextId == 0
# don't slowdown inside compiles()
proc fixSpelling(c: PContext, ident: PIdent, result: var string) =
proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
## when we cannot find the identifier, suggest nearby spellings
var list = initHeapQueue[SpellCandidate]()
let name0 = ident.s.nimIdentNormalize
@@ -571,47 +543,33 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PS
amb = false
proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
var amb: bool = false
var amb: bool
discard errorUseQualifier(c, info, s, amb)
proc ambiguousIdentifierMsg*(candidates: seq[PSym], prefix = "use one of", indent = 0): string =
result = ""
for i in 0 ..< indent:
result.add(' ')
result.add "ambiguous identifier: '" & candidates[0].name.s & "'"
proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
var i = 0
for candidate in candidates:
if i == 0: result.add " -- $1 the following:\n" % prefix
else: result.add "\n"
for i in 0 ..< indent:
result.add(' ')
result.add " " & candidate.owner.name.s & "." & candidate.name.s
result.add ": " & typeToString(candidate.typ)
if i == 0: err.add " -- $1 the following:\n" % prefix
else: err.add "\n"
err.add " " & candidate.owner.name.s & "." & candidate.name.s
err.add ": " & typeToString(candidate.typ)
inc i
localError(c.config, info, errGenerated, err)
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates))
proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string =
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
var candidates = newSeq[PSym](choices.len)
let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate"
for i, n in choices:
candidates[i] = n.sym
result = ambiguousIdentifierMsg(candidates, prefix, indent)
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices))
errorUseQualifier(c, info, candidates, prefix)
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
var err: string
if name == "_":
err = "the special identifier '_' is ignored in declarations and cannot be used"
else:
err = "undeclared identifier: '" & name & "'"
if "`gensym" in name:
err.add "; if declared in a template, this identifier may be inconsistently marked inject or gensym"
if extra.len != 0:
err.add extra
err = "undeclared identifier: '" & name & "'" & extra
if c.recursiveDep.len > 0:
err.add "\nThis might be caused by a recursive module dependency:\n"
err.add c.recursiveDep
@@ -619,11 +577,11 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extr
c.recursiveDep = ""
localError(c.config, info, errGenerated, err)
proc errorUndeclaredIdentifierHint*(c: PContext; ident: PIdent; info: TLineInfo): PSym =
proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym =
var extra = ""
if c.mustFixSpelling: fixSpelling(c, ident, extra)
errorUndeclaredIdentifier(c, info, ident.s, extra)
result = errorSym(c, ident, info)
if c.mustFixSpelling: fixSpelling(c, n, ident, extra)
errorUndeclaredIdentifier(c, n.info, ident.s, extra)
result = errorSym(c, n)
proc lookUp*(c: PContext, n: PNode): PSym =
# Looks up a symbol. Generates an error in case of nil.
@@ -631,13 +589,13 @@ proc lookUp*(c: PContext, n: PNode): PSym =
case n.kind
of nkIdent:
result = searchInScopes(c, n.ident, amb)
if result == nil: result = errorUndeclaredIdentifierHint(c, n.ident, n.info)
if result == nil: result = errorUndeclaredIdentifierHint(c, n, n.ident)
of nkSym:
result = n.sym
of nkAccQuoted:
var ident = considerQuotedIdent(c, n)
result = searchInScopes(c, ident, amb)
if result == nil: result = errorUndeclaredIdentifierHint(c, ident, n.info)
if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident)
else:
internalError(c.config, n.info, "lookUp")
return nil
@@ -651,30 +609,16 @@ type
TLookupFlag* = enum
checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
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)
if skEnumField in filter and (result.len == 0 or includePureEnum):
result.add allPureEnumFields(c, ident)
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
case n.kind
of nkIdent, nkAccQuoted:
var amb = false
var ident = considerQuotedIdent(c, n)
if checkModule in flags:
result = searchInScopes(c, ident, amb)
if result == nil:
let candidates = allPureEnumFields(c, ident)
if candidates.len > 0:
result = candidates[0]
amb = candidates.len > 1
if amb and checkAmbiguity in flags:
errorUseQualifier(c, n.info, candidates)
else:
let candidates = lookUpCandidates(c, ident, allExceptModule)
let candidates = searchInScopesFilterBy(c, ident, allExceptModule)
if candidates.len > 0:
result = candidates[0]
amb = candidates.len > 1
@@ -682,47 +626,46 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
errorUseQualifier(c, n.info, candidates)
else:
result = nil
if result == nil:
let candidates = allPureEnumFields(c, ident)
if candidates.len > 0:
result = candidates[0]
amb = candidates.len > 1
if amb and checkAmbiguity in flags:
errorUseQualifier(c, n.info, candidates)
if result == nil and checkUndeclared in flags:
result = errorUndeclaredIdentifierHint(c, ident, n.info)
result = errorUndeclaredIdentifierHint(c, n, ident)
elif checkAmbiguity in flags and result != nil and amb:
result = errorUseQualifier(c, n.info, result, amb)
c.isAmbiguous = amb
of nkSym:
result = n.sym
of nkOpenSym:
result = qualifiedLookUp(c, n[0], flags)
of nkDotExpr:
result = nil
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
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)
result = initIdentIter(ti, c.topLevelScope.symbols, ident)
if result != nil and nextIdentIter(ti, c.topLevelScope.symbols) != nil:
# another symbol exists with same name
c.isAmbiguous = true
result = strTableGet(c.topLevelScope.symbols, ident)
else:
var amb: bool = false
if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
var amb: bool
result = errorUseQualifier(c, n.info, m, amb)
else:
result = someSymAmb(c.graph, m, ident, amb)
if amb: c.isAmbiguous = true
result = someSym(c.graph, m, ident)
if result == nil and checkUndeclared in flags:
result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
result = errorUndeclaredIdentifierHint(c, n[1], ident)
elif n[1].kind == nkSym:
result = n[1].sym
if result.owner != nil and result.owner != m and checkUndeclared in flags:
# dotExpr in templates can end up here
result = errorUndeclaredIdentifierHint(c, result.name, n[1].info)
result = errorUndeclaredIdentifierHint(c, n[1], considerQuotedIdent(c, n[1]))
elif checkUndeclared in flags and
n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
localError(c.config, n[1].info, "identifier expected, but got: " &
@@ -734,10 +677,6 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
if result != nil and result.kind == skStub: loadStub(result)
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if n.kind == nkOpenSym:
# maybe the logic in semexprs should be mirrored here instead
# for now it only seems this is called for `pickSym` in `getTypeIdent`
return initOverloadIter(o, c, n[0])
o.importIdx = -1
o.marked = initIntSet()
case n.kind

View File

@@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem):
import std/assertions
proc newDeref*(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.elementType)
result = newNodeIT(nkHiddenDeref, n.info, n.typ[0])
result.add n
proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
@@ -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,19 +250,19 @@ 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)[0]
var t = deref.typ.skipTypes(abstractInst)
var field: PSym
while true:
assert t.kind == tyObject
field = lookupInRecord(t.n, b)
if field != nil: break
t = t.baseClass
t = t[0]
if t == nil: break
t = t.skipTypes(skipPtrs)
#if field == nil:
@@ -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)[0]
var t = deref.typ.skipTypes(abstractInst)
var field: PSym
let bb = getIdent(cache, b)
@@ -286,7 +286,7 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): P
assert t.kind == tyObject
field = getSymFromList(t.n, bb)
if field != nil: break
t = t.baseClass
t = t[0]
if t == nil: break
t = t.skipTypes(skipPtrs)
#if field == nil:
@@ -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
@@ -306,7 +306,7 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
assert t.kind == tyObject
result = lookupInRecord(t.n, v.itemId)
if result != nil: break
t = t.baseClass
t = t[0]
if t == nil: break
t = t.skipTypes(skipPtrs)
@@ -320,12 +320,12 @@ 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 =
result = newNodeIT(k, n.info,
n.typ.skipTypes(abstractInst).elementType)
n.typ.skipTypes(abstractInst)[0])
result.add n
proc callCodegenProc*(g: ModuleGraph; name: string;
@@ -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[0]
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

View File

@@ -11,7 +11,7 @@
import
ast, astalgo, msgs, platform, idents,
modulegraphs, lineinfos, types
modulegraphs, lineinfos
export createMagic
@@ -35,7 +35,7 @@ proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSy
for r in systemModuleSyms(g, id):
if r.magic == m:
# prefer the tyInt variant:
if r.typ.returnType != nil and r.typ.returnType.kind == tyInt: return r
if r.typ[0] != nil and r.typ[0].kind == tyInt: return r
result = r
if result != nil: return result
localError(g.config, info, "system module needs: " & name)
@@ -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)

View File

@@ -22,6 +22,8 @@ import
modules,
modulegraphs, lineinfos, pathutils, vmprofiler
# ensure NIR compiles:
import nir / nir
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
@@ -46,6 +48,9 @@ proc writeDepsFile(g: ModuleGraph) =
f.writeLine(toFullPath(g.config, k))
f.close()
proc writeNinjaFile(g: ModuleGraph) =
discard "to implement"
proc writeCMakeDepsFile(conf: ConfigRef) =
## write a list of C files for build systems like CMake.
## only updated when the C file list changes.
@@ -150,12 +155,32 @@ proc commandCompileToC(graph: ModuleGraph) =
extccomp.callCCompiler(conf)
# for now we do not support writing out a .json file with the build instructions when HCR is on
if not conf.hcrOn:
extccomp.writeJsonBuildInstructions(conf, graph.cachedFiles)
extccomp.writeJsonBuildInstructions(conf)
if optGenScript in graph.config.globalOptions:
writeDepsFile(graph)
if optGenCDeps in graph.config.globalOptions:
writeCMakeDepsFile(conf)
proc commandCompileToNir(graph: ModuleGraph) =
let conf = graph.config
extccomp.initVars(conf)
if conf.symbolFiles == disabledSf:
if {optRun, optForceFullMake} * conf.globalOptions == {optRun}:
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
# nothing changed
graph.config.notes = graph.config.mainPackageNotes
return
if not extccomp.ccHasSaneOverflow(conf):
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
if conf.symbolFiles == disabledSf:
setPipeLinePass(graph, NirPass)
else:
setPipeLinePass(graph, SemPass)
compilePipelineProject(graph)
writeNinjaFile(graph)
proc commandJsonScript(graph: ModuleGraph) =
extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
@@ -172,13 +197,16 @@ proc commandCompileToJS(graph: ModuleGraph) =
if optGenScript in conf.globalOptions:
writeDepsFile(graph)
proc commandInteractive(graph: ModuleGraph) =
proc commandInteractive(graph: ModuleGraph; useNir: bool) =
graph.config.setErrorMaxHighMaybe
initDefines(graph.config.symbols)
defineSymbol(graph.config.symbols, "nimscript")
if useNir:
defineSymbol(graph.config.symbols, "noSignalHandler")
else:
defineSymbol(graph.config.symbols, "nimscript")
# note: seems redundant with -d:nimHasLibFFI
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
setPipeLinePass(graph, InterpreterPass)
setPipeLinePass(graph, if useNir: NirReplPass else: InterpreterPass)
compilePipelineSystemModule(graph)
if graph.config.commandArgs.len > 0:
discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
@@ -194,7 +222,7 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
var stream = llStreamOpen(f, fmRead)
if stream != nil:
var
L: Lexer = default(Lexer)
L: Lexer
tok: Token = default(Token)
openLexer(L, f, stream, cache, config)
while true:
@@ -265,6 +293,8 @@ proc mainCommand*(graph: ModuleGraph) =
# and it has added this define implictly, so we must undo that here.
# A better solution might be to fix system.nim
undefSymbol(conf.symbols, "useNimRtl")
of backendNir:
if conf.exc == excNone: conf.exc = excGoto
of backendInvalid: raiseAssert "unreachable"
proc compileToBackend() =
@@ -275,6 +305,7 @@ proc mainCommand*(graph: ModuleGraph) =
of backendCpp: commandCompileToC(graph)
of backendObjc: commandCompileToC(graph)
of backendJs: commandCompileToJS(graph)
of backendNir: commandCompileToNir(graph)
of backendInvalid: raiseAssert "unreachable"
template docLikeCmd(body) =
@@ -295,8 +326,7 @@ proc mainCommand*(graph: ModuleGraph) =
# so by default should not end up in $PWD nor in $projectPath.
var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
else: conf.projectPath
if not ret.string.isAbsolute: # `AbsoluteDir` is not a real guarantee
rawMessage(conf, errCannotOpenFile, ret.string & "/")
doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex}:
ret = ret / htmldocsDir
conf.outDir = ret
@@ -413,7 +443,7 @@ proc mainCommand*(graph: ModuleGraph) =
wantMainModule(conf)
commandView(graph)
#msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
of cmdInteractive: commandInteractive(graph)
of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir"))
of cmdNimscript:
if conf.projectIsCmd or conf.projectIsStdin: discard
elif not fileExists(conf.projectFull):

View File

@@ -1,59 +0,0 @@
import std/strutils
import ast, modulegraphs
proc mangle*(name: string): string =
result = newStringOfCap(name.len)
var start = 0
if name[0] in Digits:
result.add("X" & name[0])
start = 1
var requiresUnderscore = false
template special(x) =
result.add x
requiresUnderscore = true
for i in start..<name.len:
let c = name[i]
case c
of 'a'..'z', '0'..'9', 'A'..'Z':
result.add(c)
of '_':
# we generate names like 'foo_9' for scope disambiguations and so
# disallow this here:
if i > 0 and i < name.len-1 and name[i+1] in Digits:
discard
else:
result.add(c)
of '$': special "dollar"
of '%': special "percent"
of '&': special "amp"
of '^': special "roof"
of '!': special "emark"
of '?': special "qmark"
of '*': special "star"
of '+': special "plus"
of '-': special "minus"
of '/': special "slash"
of '\\': special "backslash"
of '=': special "eq"
of '<': special "lt"
of '>': special "gt"
of '~': special "tilde"
of ':': special "colon"
of '.': special "dot"
of '@': special "at"
of '|': special "bar"
else:
result.add("X" & toHex(ord(c), 2))
requiresUnderscore = true
if requiresUnderscore:
result.add "_"
proc mangleParamExt*(s: PSym): string =
result = "_p"
result.addInt s.position
proc mangleProcNameExt*(graph: ModuleGraph, s: PSym): string =
result = "__"
result.add graph.ifaces[s.itemId.module].uniqueName
result.add "_u"
result.addInt s.itemId.item # s.disamb #

View File

@@ -11,9 +11,9 @@
## 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]
import ../dist/checksums/src/checksums/md5
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
import ic / [packed_ast, ic]
@@ -55,6 +55,11 @@ type
concreteTypes*: seq[FullId]
inst*: PInstantiation
SymInfoPair* = object
sym*: PSym
info*: TLineInfo
isDecl*: bool
PipelinePass* = enum
NonePass
SemPass
@@ -62,6 +67,8 @@ type
CgenPass
EvalPass
InterpreterPass
NirPass
NirReplPass
GenDependPass
Docgen2TexPass
Docgen2JsonPass
@@ -88,7 +95,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
@@ -102,7 +108,7 @@ type
doStopCompile*: proc(): bool {.closure.}
usageSym*: PSym # for nimsuggest
owners*: seq[PSym]
suggestSymbols*: SuggestSymbolDatabase
suggestSymbols*: Table[FileIndex, seq[SymInfoPair]]
suggestErrors*: Table[FileIndex, seq[Suggest]]
methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
bucketTable*: CountTable[ItemId]
@@ -134,10 +140,6 @@ type
idgen*: IdGenerator
operators*: Operators
cachedFiles*: StringTableRef
procGlobals*: seq[PNode]
TPassContext* = object of RootObj # the pass's context
idgen*: IdGenerator
PPassContext* = ref TPassContext
@@ -260,7 +262,7 @@ proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
let importHidden = optImportHidden in m.options
if isCachedModule(g, m):
var rodIt: RodIter = default(RodIter)
var rodIt: RodIter
var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden)
while r != nil:
yield r
@@ -277,30 +279,11 @@ proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
else:
result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym =
let importHidden = optImportHidden in m.options
if isCachedModule(g, m):
result = nil
for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden):
if result == nil:
# set result to the first symbol
result = s
else:
# another symbol found
amb = true
break
else:
var ti: TIdentIter = default(TIdentIter)
result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
# another symbol exists with same name
amb = true
proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
result = someSym(g, g.systemModule, name)
iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
var mi: ModuleIter = default(ModuleIter)
var mi: ModuleIter
var r = initModuleIter(mi, g, g.systemModule, name)
while r != nil:
yield r
@@ -331,7 +314,7 @@ proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
t.inst = result
assert result != nil
proc resolveAttachedOp*(g: ModuleGraph; t: var LazySym): PSym =
proc resolveAttachedOp(g: ModuleGraph; t: var LazySym): PSym =
result = t.sym
if result == nil:
result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
@@ -410,15 +393,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 +439,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
@@ -480,49 +457,6 @@ proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic):
proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym =
result = createMagic(g, g.idgen, name, m)
proc uniqueModuleName*(conf: ConfigRef; m: PSym): string =
## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
## so that it is useful as a C identifier snippet.
let fid = FileIndex(m.position)
let path = AbsoluteFile toFullPath(conf, fid)
var isLib = false
var rel = ""
if path.string.startsWith(conf.libpath.string):
isLib = true
rel = relativeTo(path, conf.libpath).string
else:
rel = relativeTo(path, conf.projectPath).string
if not isLib and not belongsToProjectPackage(conf, m):
# special handlings for nimble packages
when DirSep == '\\':
let rel2 = replace(rel, '\\', '/')
else:
let rel2 = rel
const pkgs2 = "pkgs2/"
var start = rel2.find(pkgs2)
if start >= 0:
start += pkgs2.len
start += skipUntil(rel2, {'/'}, start)
if start+1 < rel2.len:
rel = "pkg/" & rel2[start+1..<rel.len] # strips paths
let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
result = newStringOfCap(trunc)
for i in 0..<trunc:
let c = rel[i]
case c
of 'a'..'z', '0'..'9':
result.add c
of {os.DirSep, os.AltSep}:
result.add 'Z' # because it looks a bit like '/'
of '.':
result.add 'O' # a circle
else:
# We mangle upper letters too so that there cannot
# be clashes with our special meanings of 'Z' and 'O'
result.addInt ord(c)
proc registerModule*(g: ModuleGraph; m: PSym) =
assert m != nil
assert m.kind == skModule
@@ -534,7 +468,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
setLen(g.packed.pm, m.position + 1)
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
uniqueName: rope(uniqueModuleName(g.config, m)))
uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position))))
initStrTables(g, m)
proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
@@ -543,21 +477,19 @@ proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
proc initOperators*(g: ModuleGraph): Operators =
# These are safe for IC.
# Public because it's used by DrNim.
result = Operators(
opLe: createMagic(g, "<=", mLeI),
opLt: createMagic(g, "<", mLtI),
opAnd: createMagic(g, "and", mAnd),
opOr: createMagic(g, "or", mOr),
opIsNil: createMagic(g, "isnil", mIsNil),
opEq: createMagic(g, "==", mEqI),
opAdd: createMagic(g, "+", mAddI),
opSub: createMagic(g, "-", mSubI),
opMul: createMagic(g, "*", mMulI),
opDiv: createMagic(g, "div", mDivI),
opLen: createMagic(g, "len", mLengthSeq),
opNot: createMagic(g, "not", mNot),
opContains: createMagic(g, "contains", mInSet)
)
result.opLe = createMagic(g, "<=", mLeI)
result.opLt = createMagic(g, "<", mLtI)
result.opAnd = createMagic(g, "and", mAnd)
result.opOr = createMagic(g, "or", mOr)
result.opIsNil = createMagic(g, "isnil", mIsNil)
result.opEq = createMagic(g, "==", mEqI)
result.opAdd = createMagic(g, "+", mAddI)
result.opSub = createMagic(g, "-", mSubI)
result.opMul = createMagic(g, "*", mMulI)
result.opDiv = createMagic(g, "div", mDivI)
result.opLen = createMagic(g, "len", mLengthSeq)
result.opNot = createMagic(g, "not", mNot)
result.opContains = createMagic(g, "contains", mInSet)
proc initModuleGraphFields(result: ModuleGraph) =
# A module ID of -1 means that the symbol is not attached to a module at all,
@@ -570,7 +502,7 @@ proc initModuleGraphFields(result: ModuleGraph) =
result.importStack = @[]
result.inclToMod = initTable[FileIndex, FileIndex]()
result.owners = @[]
result.suggestSymbols = initTable[FileIndex, SuggestFileSymbolDatabase]()
result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]()
result.suggestErrors = initTable[FileIndex, seq[Suggest]]()
result.methods = @[]
result.compilerprocs = initStrTable()
@@ -584,7 +516,6 @@ proc initModuleGraphFields(result: ModuleGraph) =
result.symBodyHashes = initTable[int, SigHash]()
result.operators = initOperators(result)
result.emittedTypeInfo = initTable[string, FileIndex]()
result.cachedFiles = newStringTable()
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
result = ModuleGraph()
@@ -686,6 +617,7 @@ proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
if m != nil:
g.suggestSymbols.del(fileIdx)
g.suggestErrors.del(fileIdx)
g.resetForBackend
incl m.flags, sfDirty
proc unmarkAllDirty*(g: ModuleGraph) =
@@ -748,6 +680,8 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
proc configComplete*(g: ModuleGraph) =
rememberStartupConfig(g.startupPackedConfig, g.config)
from std/strutils import repeat, `%`
proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
let conf = graph.config
let isNimscript = conf.isDefined("nimscript")
@@ -773,14 +707,16 @@ func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
## Check if symbol belongs to the 'stdlib' package.
sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId
proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): SuggestFileSymbolDatabase =
result = graph.suggestSymbols.getOrDefault(fileIdx, newSuggestFileSymbolDatabase(fileIdx, optIdeExceptionInlayHints in graph.config.globalOptions))
doAssert(result.fileIndex == fileIdx)
proc `==`*(a, b: SymInfoPair): bool =
result = a.sym == b.sym and a.info.exactEquals(b.info)
proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): seq[SymInfoPair] =
result = graph.suggestSymbols.getOrDefault(fileIdx, @[])
iterator suggestSymbolsIter*(g: ModuleGraph): SymInfoPair =
for xs in g.suggestSymbols.values:
for i in xs.lineInfo.low..xs.lineInfo.high:
yield xs.getSymInfoPair(i)
for x in xs:
yield x
iterator suggestErrorsIter*(g: ModuleGraph): Suggest =
for xs in g.suggestErrors.values:

View File

@@ -38,18 +38,11 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
localError(n.info, "only '/' supported with $package notation")
result = ""
else:
if n0.kind in nkIdentKinds:
let ident = n0.getPIdent
if ident != nil and ident.s[0] == '/':
let modname = getModuleName(conf, n[2])
# hacky way to implement 'x / y /../ z':
result = getModuleName(conf, n1)
result.add renderTree(n0, {renderNoComments}).replace(" ")
result.add modname
else:
result = ""
else:
result = ""
let modname = getModuleName(conf, n[2])
# hacky way to implement 'x / y /../ z':
result = getModuleName(conf, n1)
result.add renderTree(n0, {renderNoComments}).replace(" ")
result.add modname
of nkPrefix:
when false:
if n[0].kind == nkIdent and n[0].ident.s == "$":
@@ -79,10 +72,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 +80,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": ":"})

View File

@@ -14,9 +14,6 @@ import
idents, lexer, syntaxes, modulegraphs,
lineinfos, pathutils
import ../dist/checksums/src/checksums/sha1
import std/strtabs
proc resetSystemArtifacts*(g: ModuleGraph) =
magicsys.resetSysTypes(g)
@@ -25,7 +22,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 =
@@ -45,8 +42,6 @@ proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
graph.addDep(s, fileIdx)
graph.addIncludeDep(s.position.FileIndex, fileIdx)
let path = toFullPath(graph.config, fileIdx)
graph.cachedFiles[path] = $secureHashFile(path)
proc wantMainModule*(conf: ConfigRef) =
if conf.projectFull.isEmpty:

View File

@@ -60,13 +60,31 @@ proc makeCString*(s: string): Rope =
toCChar(s[i], result)
result.add('\"')
proc makeCCharArray*(s: string): Rope =
result = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
result.add("{")
for i in 0..<s.len:
# line wrapping of string litterals in cgen'd code was a bad idea, e.g. causes: bug #16265
# It also makes reading c sources or grepping harder, for zero benefit.
# const MaxLineLength = 64
# if (i + 1) mod MaxLineLength == 0:
# res.add("\"\L\"")
if i != 0:
result.add ", "
result.add '\''
toCChar(s[i], result)
result.add '\''
result.add('}')
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
result = TFileInfo(fullPath: fullPath, projPath: projPath,
shortName: fullPath.extractFilename,
quotedFullName: fullPath.string.makeCString,
lines: @[]
)
result.fullPath = fullPath
#shallow(result.fullPath)
result.projPath = projPath
#shallow(result.projPath)
result.shortName = fullPath.extractFilename
result.quotedName = result.shortName.makeCString
result.quotedFullName = fullPath.string.makeCString
result.lines = @[]
when defined(nimpretty):
if not result.fullPath.isEmpty:
try:
@@ -123,18 +141,18 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool
conf.m.filenameToIndexTbl[canon2] = result
proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
var dummy: bool = false
var dummy: bool
result = fileInfoIdx(conf, filename, dummy)
proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile; isKnownFile: var bool): FileIndex =
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), isKnownFile)
proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex =
var dummy: bool = false
var dummy: bool
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy)
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
result = TLineInfo(fileIndex: fileInfoIdx)
result.fileIndex = fileInfoIdx
if line < int high(uint16):
result.line = uint16(line)
else:
@@ -429,8 +447,7 @@ To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 f
proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) =
if msg in fatalMsgs:
if conf.cmd == cmdIdeTools: log(s)
if conf.cmd != cmdIdeTools or msg != errFatal:
quit(conf, msg)
quit(conf, msg)
if msg >= errMin and msg <= errMax or
(msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg):
inc(conf.errorCounter)
@@ -438,11 +455,7 @@ proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string,
if conf.errorCounter >= conf.errorMax:
# only really quit when we're not in the new 'nim check --def' mode:
if conf.ideCmd == ideNone:
when defined(nimsuggest):
#we need to inform the user that something went wrong when initializing NimSuggest
raiseRecoverableError(s)
else:
quit(conf, msg)
quit(conf, msg)
elif eh == doAbort and conf.cmd != cmdIdeTools:
quit(conf, msg)
elif eh == doRaise:
@@ -560,10 +573,9 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
ignoreMsg = not conf.hasHint(msg)
if not ignoreMsg and msg in conf.warningAsErrors:
title = ErrorTitle
color = ErrorColor
else:
title = HintTitle
color = HintColor
color = HintColor
inc(conf.hintCounter)
let s = if isRaw: arg else: getMessageStr(msg, arg)
@@ -629,7 +641,7 @@ proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "
message(conf, info, warnDeprecated, msg)
proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return
if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
writeContext(conf, info)
liMessage(conf, info, errInternal, errMsg, doAbort, info2)
@@ -648,21 +660,16 @@ 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 =
if fi.int32 < 0:
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
if i.fileIndex.int32 < 0:
result = makeCString "???"
elif optExcessiveStackTrace in conf.globalOptions:
result = conf.m.fileInfos[fi.int32].quotedFullName
result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
else:
result = conf.m.fileInfos[fi.int32].quotedName
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
quotedFilename(conf, i.fileIndex)
result = conf.m.fileInfos[i.fileIndex.int32].quotedName
template listMsg(title, r) =
msgWriteln(conf, title, {msgNoUnitSep})
@@ -671,6 +678,31 @@ template listMsg(title, r) =
proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string =
## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
## so that it is useful as a C identifier snippet.
let path = AbsoluteFile toFullPath(conf, fid)
let rel =
if path.string.startsWith(conf.libpath.string):
relativeTo(path, conf.libpath).string
else:
relativeTo(path, conf.projectPath).string
let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
result = newStringOfCap(trunc)
for i in 0..<trunc:
let c = rel[i]
case c
of 'a'..'z':
result.add c
of {os.DirSep, os.AltSep}:
result.add 'Z' # because it looks a bit like '/'
of '.':
result.add 'O' # a circle
else:
# We mangle upper letters and digits too so that there cannot
# be clashes with our special meanings of 'Z' and 'O'
result.addInt ord(c)
proc genSuccessX*(conf: ConfigRef) =
let mem =
when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"

52
compiler/ndi.nim Normal file
View 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.r)
f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
if f.enabled: f.syms.add s
proc open*(f: var NdiFile; filename: AbsoluteFile; conf: ConfigRef) =
f.enabled = not filename.isEmpty
if f.enabled:
f.filename = filename
f.buf = newStringOfCap(20)
proc close*(f: var NdiFile, conf: ConfigRef) =
if f.enabled:
f.f = open(f.filename.string, fmWrite, 8000)
doAssert f.f != nil, f.filename.string
for s in f.syms:
doWrite(f, s, conf)
close(f.f)
f.syms.reset
f.filename.reset

View File

@@ -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
@@ -492,7 +498,7 @@ proc checkCall(n, ctx, map): Check =
# check args and handle possible mutations
var isNew = false
result = Check(map: map)
result.map = map
for i, child in n:
discard check(child, ctx, map)
@@ -501,7 +507,7 @@ proc checkCall(n, ctx, map): Check =
# as it might have been mutated
# TODO similar for normal refs and fields: find dependent exprs: brackets
if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ.elementType.kind == tyRef:
if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ[0].kind == tyRef:
if not isNew:
result.map = newNilMap(map)
isNew = true
@@ -747,7 +753,6 @@ proc checkReturn(n, ctx, map): Check =
proc checkIf(n, ctx, map): Check =
## check branches based on condition
result = default(Check)
var mapIf: NilMap = map
# first visit the condition
@@ -820,7 +825,7 @@ proc checkFor(n, ctx, map): Check =
var check2 = check(n.sons[2], ctx, m)
var map2 = check2.map
result = Check(map: ctx.union(map0, m))
result.map = ctx.union(map0, m)
result.map = ctx.union(result.map, map2)
result.nilability = Safe
@@ -848,7 +853,7 @@ proc checkWhile(n, ctx, map): Check =
var check2 = check(n.sons[1], ctx, m)
var map2 = check2.map
result = Check(map: ctx.union(map0, map1))
result.map = ctx.union(map0, map1)
result.map = ctx.union(result.map, map2)
result.nilability = Safe
@@ -894,7 +899,7 @@ proc checkInfix(n, ctx, map): Check =
proc checkIsNil(n, ctx, map; isElse: bool = false): Check =
## check isNil calls
## update the map depending on if it is not isNil or isNil
result = Check(map: newNilMap(map))
result.map = newNilMap(map)
let value = n[1]
result.map.store(ctx, ctx.index(n[1]), if not isElse: Nil else: Safe, TArg, n.info, n)
@@ -913,7 +918,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 +928,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)
@@ -942,7 +947,7 @@ proc checkCase(n, ctx, map): Check =
# c2
# also a == true is a , a == false is not a
let base = n[0]
result = Check(map: map.copyMap())
result.map = map.copyMap()
result.nilability = Safe
var a: PNode = nil
for child in n:
@@ -1214,7 +1219,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
result = check(n.sons[1], ctx, map)
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkElse:
result = Check(map: map)
result.map = map
if n.kind in {nkObjConstr, nkTupleConstr}:
# TODO deeper nested elements?
# A(field: B()) #
@@ -1241,10 +1246,10 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
result = checkIf(n, ctx, map)
of nkAsgn, nkFastAsgn, nkSinkAsgn:
result = checkAsgn(n[0], n[1], ctx, map)
of nkVarSection, nkLetSection:
result = Check(map: map)
of nkVarSection:
result.map = map
for child in n:
result = checkAsgn(child[0].skipPragmaExpr, child[2], ctx, result.map)
result = checkAsgn(child[0], child[2], ctx, result.map)
of nkForStmt:
result = checkFor(n, ctx, map)
of nkCaseStmt:
@@ -1268,7 +1273,8 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
else:
var elementMap = map.copyMap()
var elementCheck = Check(map: elementMap)
var elementCheck: Check
elementCheck.map = elementMap
for element in n:
elementCheck = check(element, ctx, elementCheck.map)
@@ -1361,7 +1367,7 @@ proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) =
continue
map.store(context, context.index(child), typeNilability(child.typ), TArg, child.info, child)
map.store(context, resultExprIndex, if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef: Nil else: Safe, TResult, s.ast.info)
map.store(context, resultExprIndex, if not s.typ[0].isNil and s.typ[0].kind == tyRef: Nil else: Safe, TResult, s.ast.info)
# echo "checking ", s.name.s, " ", filename
@@ -1377,5 +1383,5 @@ proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) =
# (ANotNil, BNotNil) :
# do we check on asgn nilability at all?
if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef and tfNotNil in s.typ.returnType.flags:
if not s.typ[0].isNil and s.typ[0].kind == tyRef and tfNotNil in s.typ[0].flags:
checkResult(s.ast, context, res.map)

View File

@@ -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

View File

@@ -116,9 +116,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
conf.backend = backendC
if conf.selectedGC == gcUnselected:
if conf.backend in {backendC, backendCpp, backendObjc} or
(conf.cmd in cmdDocLike and conf.backend != backendJs) or
conf.cmd == cmdGendepend:
if conf.backend in {backendC, backendCpp, backendObjc, backendNir} or
(conf.cmd == cmdInteractive and isDefined(conf, "nir")):
initOrcDefines(conf)
mainCommand(graph)

View File

@@ -79,8 +79,6 @@ proc getPathVersionChecksum*(p: string): tuple[name, version, checksum: string]
## ``/home/user/.nimble/pkgs/package-0.1-febadeaea2345e777f0f6f8433f7f0a52edd5d1b`` into
## ``("/home/user/.nimble/pkgs/package", "0.1", "febadeaea2345e777f0f6f8433f7f0a52edd5d1b")``
result = ("", "", "")
const checksumSeparator = '-'
const versionSeparator = '-'
const specialVersionSepartator = "-#"

View File

@@ -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)
@@ -222,7 +223,7 @@ proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache;
stream = llStreamOpen(filename, fmRead)
if stream != nil:
openLexer(L, filename, stream, cache, config)
tok = Token(tokType: tkEof) # to avoid a pointless warning
tok.tokType = tkEof # to avoid a pointless warning
var condStack: seq[bool] = @[]
confTok(L, tok, config, condStack) # read in the first token
while tok.tokType != tkEof: parseAssignment(L, tok, config, filename, condStack)
@@ -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)

View File

@@ -11,8 +11,8 @@
import
ast, modules, condsyms,
options, llstream, lineinfos, vm,
vmdef, modulegraphs, idents, pathutils,
scriptconfig, std/[compilesettings, tables, os]
vmdef, modulegraphs, idents, os, pathutils,
scriptconfig, std/[compilesettings, tables]
import pipelines
@@ -40,7 +40,7 @@ proc selectUniqueSymbol*(i: Interpreter; name: string;
assert i != nil
assert i.mainModule != nil, "no main module selected"
let n = getIdent(i.graph.cache, name)
var it: ModuleIter = default(ModuleIter)
var it: ModuleIter
var s = initModuleIter(it, i.graph, i.mainModule, n)
result = nil
while s != nil:

View File

@@ -65,7 +65,7 @@ proc toBitSet*(conf: ConfigRef; s: PNode): TBitSet =
result = @[]
var first: Int128 = Zero
var j: Int128 = Zero
first = firstOrd(conf, s.typ.elementType)
first = firstOrd(conf, s.typ[0])
bitSetInit(result, int(getSize(conf, s.typ)))
for i in 0..<s.len:
if s[i].kind == nkRange:
@@ -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

2645
compiler/nir/ast2ir.nim Normal file

File diff suppressed because it is too large Load Diff

983
compiler/nir/cir.nim Normal file
View File

@@ -0,0 +1,983 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# We produce C code as a list of tokens.
import std / [assertions, syncio, tables, intsets, formatfloat]
from std / strutils import toOctal
import .. / ic / [bitabs, rodfiles]
import nirtypes, nirinsts, nirfiles
import ../../dist/checksums/src/checksums/md5
type
Token = LitId # indexing into the tokens BiTable[string]
PredefinedToken = enum
IgnoreMe = "<unused>"
EmptyToken = ""
CurlyLe = "{"
CurlyRi = "}"
ParLe = "("
ParRi = ")"
BracketLe = "["
BracketRi = "]"
NewLine = "\n"
Semicolon = ";"
Comma = ", "
Space = " "
Colon = ": "
Dot = "."
Arrow = "->"
Star = "*"
Amp = "&"
AsgnOpr = " = "
ScopeOpr = "::"
ConstKeyword = "const "
StaticKeyword = "static "
ExternKeyword = "extern "
WhileKeyword = "while "
IfKeyword = "if ("
ElseKeyword = "else "
SwitchKeyword = "switch "
CaseKeyword = "case "
DefaultKeyword = "default:"
BreakKeyword = "break"
NullPtr = "nullptr"
IfNot = "if (!("
ReturnKeyword = "return "
TypedefStruct = "typedef struct "
TypedefUnion = "typedef union "
IncludeKeyword = "#include "
proc fillTokenTable(tab: var BiTable[string]) =
for e in EmptyToken..high(PredefinedToken):
let id = tab.getOrIncl $e
assert id == LitId(e), $(id, " ", ord(e))
type
GeneratedCode* = object
m: NirModule
includes: seq[LitId]
includedHeaders: IntSet
data: seq[LitId]
protos: seq[LitId]
code: seq[LitId]
init: seq[LitId]
tokens: BiTable[string]
emittedStrings: IntSet
needsPrefix: IntSet
generatedTypes: IntSet
mangledModules: Table[LitId, LitId]
proc initGeneratedCode*(m: sink NirModule): GeneratedCode =
result = GeneratedCode(m: m, code: @[], tokens: initBiTable[string]())
fillTokenTable(result.tokens)
proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} =
g.code.add Token(t)
proc add*(g: var GeneratedCode; s: string) {.inline.} =
g.code.add g.tokens.getOrIncl(s)
proc mangleModuleName(c: var GeneratedCode; key: LitId): LitId =
result = c.mangledModules.getOrDefault(key, LitId(0))
if result == LitId(0):
let u {.cursor.} = c.m.lit.strings[key]
var last = u.len - len(".nim") - 1
var start = last
while start >= 0 and u[start] != '/': dec start
var sum = getMD5(u)
sum.setLen(8)
let dest = u.substr(start+1, last) & sum
result = c.tokens.getOrIncl(dest)
c.mangledModules[key] = result
type
CppFile = object
f: File
proc write(f: var CppFile; s: string) = write(f.f, s)
proc write(f: var CppFile; c: char) = write(f.f, c)
proc writeTokenSeq(f: var CppFile; s: seq[Token]; c: GeneratedCode) =
var indent = 0
for i in 0..<s.len:
let x = s[i]
case x
of Token(CurlyLe):
inc indent
write f, c.tokens[x]
write f, "\n"
for i in 1..indent*2: write f, ' '
of Token(CurlyRi):
dec indent
write f, c.tokens[x]
if i+1 < s.len and s[i+1] == Token(CurlyRi):
discard
else:
write f, "\n"
for i in 1..indent*2: write f, ' '
of Token(Semicolon):
write f, c.tokens[x]
if i+1 < s.len and s[i+1] == Token(CurlyRi):
discard "no newline before }"
else:
write f, "\n"
for i in 1..indent*2: write f, ' '
of Token(NewLine):
write f, c.tokens[x]
for i in 1..indent*2: write f, ' '
else:
write f, c.tokens[x]
# Type graph
type
TypeList = object
processed: IntSet
s: seq[(TypeId, PredefinedToken)]
proc add(dest: var TypeList; elem: TypeId; decl: PredefinedToken) =
if not containsOrIncl(dest.processed, int(elem)):
dest.s.add (elem, decl)
type
TypeOrder = object
forwardedDecls, ordered: TypeList
typeImpls: Table[string, TypeId]
lookedAt: IntSet
proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId)
proc recordDependency(types: TypeGraph; lit: Literals; c: var TypeOrder; parent, child: TypeId) =
var ch = child
var viaPointer = false
while true:
case types[ch].kind
of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
viaPointer = true
ch = elementType(types, ch)
of LastArrayTy:
ch = elementType(types, ch)
else:
break
case types[ch].kind
of ObjectTy, UnionTy:
let decl = if types[ch].kind == ObjectTy: TypedefStruct else: TypedefUnion
let obj = c.typeImpls.getOrDefault(lit.strings[types[ch].litId])
if viaPointer:
c.forwardedDecls.add obj, decl
else:
if not containsOrIncl(c.lookedAt, obj.int):
traverseObject(types, lit, c, obj)
c.ordered.add obj, decl
of ArrayTy:
if viaPointer:
c.forwardedDecls.add ch, TypedefStruct
else:
if not containsOrIncl(c.lookedAt, ch.int):
traverseObject(types, lit, c, ch)
c.ordered.add ch, TypedefStruct
else:
discard "uninteresting type as we only focus on the required struct declarations"
proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId) =
for x in sons(types, t):
case types[x].kind
of FieldDecl:
recordDependency types, lit, c, t, x.firstSon
of ObjectTy:
# inheritance
recordDependency types, lit, c, t, x
else: discard
proc traverseTypes(types: TypeGraph; lit: Literals; c: var TypeOrder) =
for t in allTypes(types):
if types[t].kind in {ObjectDecl, UnionDecl}:
assert types[t.firstSon].kind == NameVal
c.typeImpls[lit.strings[types[t.firstSon].litId]] = t
for t in allTypesIncludingInner(types):
case types[t].kind
of ObjectDecl, UnionDecl:
traverseObject types, lit, c, t
let decl = if types[t].kind == ObjectDecl: TypedefStruct else: TypedefUnion
c.ordered.add t, decl
of ArrayTy:
traverseObject types, lit, c, t
c.ordered.add t, TypedefStruct
else: discard
when false:
template emitType(s: string) = c.types.add c.tokens.getOrIncl(s)
template emitType(t: Token) = c.types.add t
template emitType(t: PredefinedToken) = c.types.add Token(t)
proc genType(g: var GeneratedCode; types: TypeGraph; lit: Literals; t: TypeId; name = "") =
template maybeAddName =
if name != "":
g.add Space
g.add name
template atom(s: string) =
g.add s
maybeAddName()
case types[t].kind
of VoidTy: atom "void"
of IntTy: atom "NI" & $types[t].integralBits
of UIntTy: atom "NU" & $types[t].integralBits
of FloatTy: atom "NF" & $types[t].integralBits
of BoolTy: atom "NB" & $types[t].integralBits
of CharTy: atom "NC" & $types[t].integralBits
of ObjectTy, UnionTy, NameVal, AnnotationVal:
atom lit.strings[types[t].litId]
of VarargsTy:
g.add "..."
of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
genType g, types, lit, elementType(types, t)
g.add Star
maybeAddName()
of ArrayTy:
genType g, types, lit, arrayName(types, t)
maybeAddName()
of LastArrayTy:
genType g, types, lit, elementType(types, t)
maybeAddName()
g.add BracketLe
g.add BracketRi
of ProcTy:
let (retType, callConv) = returnType(types, t)
genType g, types, lit, retType
g.add Space
g.add ParLe
genType g, types, lit, callConv
g.add Star # "(*fn)"
maybeAddName()
g.add ParRi
g.add ParLe
var i = 0
for ch in params(types, t):
if i > 0: g.add Comma
genType g, types, lit, ch
inc i
g.add ParRi
of ObjectDecl, UnionDecl:
atom lit.strings[types[t.firstSon].litId]
of IntVal, SizeVal, AlignVal, OffsetVal, FieldDecl:
#raiseAssert "did not expect: " & $types[t].kind
g.add "BUG "
atom $types[t].kind
proc generateTypes(g: var GeneratedCode; types: TypeGraph; lit: Literals; c: TypeOrder) =
for (t, declKeyword) in c.forwardedDecls.s:
let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
let s {.cursor.} = lit.strings[types[name].litId]
g.add declKeyword
g.add s
g.add Space
g.add s
g.add Semicolon
for (t, declKeyword) in c.ordered.s:
let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
let litId = types[name].litId
if not g.generatedTypes.containsOrIncl(litId.int):
let s {.cursor.} = lit.strings[litId]
g.add declKeyword
g.add CurlyLe
if types[t].kind == ArrayTy:
genType g, types, lit, elementType(types, t), "a"
g.add BracketLe
g.add $arrayLen(types, t)
g.add BracketRi
g.add Semicolon
else:
var i = 0
for x in sons(types, t):
case types[x].kind
of FieldDecl:
genType g, types, lit, x.firstSon, "F" & $i
g.add Semicolon
inc i
of ObjectTy:
genType g, types, lit, x, "P"
g.add Semicolon
else: discard
g.add CurlyRi
g.add s
g.add Semicolon
# Procs
proc toCChar*(c: char; result: var string) {.inline.} =
case c
of '\0'..'\x1F', '\x7F'..'\xFF':
result.add '\\'
result.add toOctal(c)
of '\'', '\"', '\\', '?':
result.add '\\'
result.add c
else:
result.add c
proc makeCString(s: string): string =
result = newStringOfCap(s.len + 10)
result.add('"')
for c in s: toCChar(c, result)
result.add('"')
template emitData(s: string) = c.data.add c.tokens.getOrIncl(s)
template emitData(t: Token) = c.data.add t
template emitData(t: PredefinedToken) = c.data.add Token(t)
proc genStrLit(c: var GeneratedCode; lit: Literals; litId: LitId): Token =
result = Token(c.tokens.getOrIncl "QStr" & $litId)
if not containsOrIncl(c.emittedStrings, int(litId)):
let s {.cursor.} = lit.strings[litId]
emitData "static const struct "
emitData CurlyLe
emitData "NI cap"
emitData Semicolon
emitData "NC8 data"
emitData BracketLe
emitData $s.len
emitData "+1"
emitData BracketRi
emitData Semicolon
emitData CurlyRi
emitData result
emitData AsgnOpr
emitData CurlyLe
emitData $s.len
emitData " | NIM_STRLIT_FLAG"
emitData Comma
emitData makeCString(s)
emitData CurlyRi
emitData Semicolon
proc genIntLit(c: var GeneratedCode; lit: Literals; litId: LitId) =
let i = lit.numbers[litId]
if i > low(int32) and i <= high(int32):
c.add $i
elif i == low(int32):
# Nim has the same bug for the same reasons :-)
c.add "(-2147483647 -1)"
elif i > low(int64):
c.add "IL64("
c.add $i
c.add ")"
else:
c.add "(IL64(-9223372036854775807) - IL64(1))"
proc gen(c: var GeneratedCode; t: Tree; n: NodePos)
proc genDisplayName(c: var GeneratedCode; symId: SymId) =
let displayName = c.m.symnames[symId]
if displayName != LitId(0):
c.add "/*"
c.add c.m.lit.strings[displayName]
c.add "*/"
proc genSymDef(c: var GeneratedCode; t: Tree; n: NodePos) =
if t[n].kind == SymDef:
let symId = t[n].symId
c.needsPrefix.incl symId.int
genDisplayName c, symId
gen c, t, n
proc genGlobal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
c.add annotation
let m: string
if t[name].kind == SymDef:
let symId = t[name].symId
m = c.tokens[mangleModuleName(c, c.m.namespace)] & "__" & $symId
genDisplayName c, symId
else:
assert t[name].kind == ModuleSymUse
let (x, y) = sons2(t, name)
m = c.tokens[mangleModuleName(c, t[x].litId)] & "__" & $t[y].immediateVal
genType c, c.m.types, c.m.lit, t[typ].typeId, m
proc genLocal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
assert t[name].kind == SymDef
c.add annotation
let symId = t[name].symId
genType c, c.m.types, c.m.lit, t[typ].typeId, "q" & $symId
genDisplayName c, symId
proc genProcDecl(c: var GeneratedCode; t: Tree; n: NodePos; isExtern: bool) =
let signatureBegin = c.code.len
let name = n.firstSon
var prc = n.firstSon
next t, prc
while true:
case t[prc].kind
of PragmaPair:
let (x, y) = sons2(t, prc)
let key = cast[PragmaKey](t[x].rawOperand)
case key
of HeaderImport:
let lit = t[y].litId
let headerAsStr {.cursor.} = c.m.lit.strings[lit]
let header = c.tokens.getOrIncl(headerAsStr)
# headerAsStr can be empty, this has the semantics of the `nodecl` pragma:
if headerAsStr.len > 0 and not c.includedHeaders.containsOrIncl(int header):
if headerAsStr[0] == '#':
discard "skip the #include"
else:
c.includes.add Token(IncludeKeyword)
c.includes.add header
c.includes.add Token NewLine
# do not generate code for importc'ed procs:
return
of DllImport:
let lit = t[y].litId
raiseAssert "cannot eval: " & c.m.lit.strings[lit]
else: discard
of PragmaId: discard
else: break
next t, prc
var resultDeclPos = NodePos(-1)
if t[prc].kind == SummonResult:
resultDeclPos = prc
gen c, t, prc.firstSon
next t, prc
else:
c.add "void"
c.add Space
genSymDef c, t, name
c.add ParLe
var params = 0
while t[prc].kind == SummonParam:
if params > 0: c.add Comma
let (typ, sym) = sons2(t, prc)
genLocal c, t, sym, typ, ""
next t, prc
inc params
if params == 0:
c.add "void"
c.add ParRi
for i in signatureBegin ..< c.code.len:
c.protos.add c.code[i]
c.protos.add Token Semicolon
if isExtern:
c.code.setLen signatureBegin
else:
c.add CurlyLe
if resultDeclPos.int >= 0:
gen c, t, resultDeclPos
for ch in sonsRest(t, n, prc):
gen c, t, ch
c.add CurlyRi
template triop(opr) =
let (typ, a, b) = sons3(t, n)
c.add ParLe
c.add ParLe
gen c, t, typ
c.add ParRi
gen c, t, a
c.add opr
gen c, t, b
c.add ParRi
template cmpop(opr) =
let (_, a, b) = sons3(t, n)
c.add ParLe
gen c, t, a
c.add opr
gen c, t, b
c.add ParRi
template binaryop(opr) =
let (typ, a) = sons2(t, n)
c.add ParLe
c.add ParLe
gen c, t, typ
c.add ParRi
c.add opr
gen c, t, a
c.add ParRi
template checkedBinaryop(opr) =
let (typ, labIdx, a, b) = sons4(t, n)
let bits = integralBits(c.m.types[t[typ].typeId])
let lab = t[labIdx].label
c.add (opr & $bits)
c.add ParLe
c.gen t, a
c.add Comma
c.gen t, b
c.add Comma
c.add "L" & $lab.int
c.add ParRi
proc genNumberConv(c: var GeneratedCode; t: Tree; n: NodePos) =
let (typ, arg) = sons2(t, n)
if t[arg].kind == IntVal:
let litId = t[arg].litId
c.add ParLe
c.add ParLe
gen c, t, typ
c.add ParRi
case c.m.types[t[typ].typeId].kind
of UIntTy:
let x = cast[uint64](c.m.lit.numbers[litId])
c.add $x
of FloatTy:
let x = cast[float64](c.m.lit.numbers[litId])
c.add $x
else:
gen c, t, arg
c.add ParRi
else:
binaryop ""
template moveToDataSection(body: untyped) =
let oldLen = c.code.len
body
for i in oldLen ..< c.code.len:
c.data.add c.code[i]
setLen c.code, oldLen
proc gen(c: var GeneratedCode; t: Tree; n: NodePos) =
case t[n].kind
of Nop:
discard "nothing to emit"
of ImmediateVal:
c.add $t[n].immediateVal
of IntVal:
genIntLit c, c.m.lit, t[n].litId
of StrVal:
c.code.add genStrLit(c, c.m.lit, t[n].litId)
of Typed:
genType c, c.m.types, c.m.lit, t[n].typeId
of SymDef, SymUse:
let s = t[n].symId
if c.needsPrefix.contains(s.int):
c.code.add mangleModuleName(c, c.m.namespace)
c.add "__"
c.add $s
else:
# XXX Use proper names here
c.add "q"
c.add $s
of ModuleSymUse:
let (x, y) = sons2(t, n)
let u = mangleModuleName(c, t[x].litId)
let s = t[y].immediateVal
c.code.add u
c.add "__"
c.add $s
of NilVal:
c.add NullPtr
of LoopLabel:
c.add WhileKeyword
c.add ParLe
c.add "1"
c.add ParRi
c.add CurlyLe
of GotoLoop:
c.add CurlyRi
of Label:
let lab = t[n].label
c.add "L"
c.add $lab.int
c.add Colon
c.add Semicolon
of Goto:
let lab = t[n].label
c.add "goto L"
c.add $lab.int
c.add Semicolon
of CheckedGoto:
discard "XXX todo"
of ArrayConstr:
c.add CurlyLe
c.add ".a = "
c.add CurlyLe
var i = 0
for ch in sonsFrom1(t, n):
if i > 0: c.add Comma
c.gen t, ch
inc i
c.add CurlyRi
c.add CurlyRi
of ObjConstr:
c.add CurlyLe
var i = 0
for ch in sonsFrom1(t, n):
if i mod 2 == 0:
if i > 0: c.add Comma
c.add ".F" & $t[ch].immediateVal
c.add AsgnOpr
else:
c.gen t, ch
inc i
c.add CurlyRi
of Ret:
c.add ReturnKeyword
c.gen t, n.firstSon
c.add Semicolon
of Select:
c.add SwitchKeyword
c.add ParLe
let (_, selector) = sons2(t, n)
c.gen t, selector
c.add ParRi
c.add CurlyLe
for ch in sonsFromN(t, n, 2):
c.gen t, ch
c.add CurlyRi
of SelectPair:
let (le, ri) = sons2(t, n)
c.gen t, le
c.gen t, ri
of SelectList:
for ch in sons(t, n):
c.gen t, ch
of SelectValue:
c.add CaseKeyword
c.gen t, n.firstSon
c.add Colon
of SelectRange:
let (le, ri) = sons2(t, n)
c.add CaseKeyword
c.gen t, le
c.add " ... "
c.gen t, ri
c.add Colon
of ForeignDecl:
c.data.add LitId(ExternKeyword)
c.gen t, n.firstSon
of SummonGlobal:
moveToDataSection:
let (typ, sym) = sons2(t, n)
c.genGlobal t, sym, typ, ""
c.add Semicolon
of SummonThreadLocal:
moveToDataSection:
let (typ, sym) = sons2(t, n)
c.genGlobal t, sym, typ, "__thread "
c.add Semicolon
of SummonConst:
moveToDataSection:
let (typ, sym, val) = sons3(t, n)
c.genGlobal t, sym, typ, "const "
c.add AsgnOpr
c.gen t, val
c.add Semicolon
of Summon, SummonResult:
let (typ, sym) = sons2(t, n)
c.genLocal t, sym, typ, ""
c.add Semicolon
of SummonParam:
raiseAssert "SummonParam should have been handled in genProc"
of Kill:
discard "we don't care about Kill instructions"
of AddrOf:
let (_, arg) = sons2(t, n)
c.add "&"
gen c, t, arg
of DerefArrayAt:
let (_, a, i) = sons3(t, n)
gen c, t, a
c.add BracketLe
gen c, t, i
c.add BracketRi
of ArrayAt:
let (_, a, i) = sons3(t, n)
gen c, t, a
c.add Dot
c.add "a"
c.add BracketLe
gen c, t, i
c.add BracketRi
of FieldAt:
let (_, a, b) = sons3(t, n)
gen c, t, a
let field = t[b].immediateVal
c.add Dot
c.add "F" & $field
of DerefFieldAt:
let (_, a, b) = sons3(t, n)
gen c, t, a
let field = t[b].immediateVal
c.add Arrow
c.add "F" & $field
of Load:
let (_, arg) = sons2(t, n)
c.add ParLe
c.add "*"
gen c, t, arg
c.add ParRi
of Store:
raiseAssert "Assumption was that Store is unused!"
of Asgn:
let (_, dest, src) = sons3(t, n)
gen c, t, dest
c.add AsgnOpr
gen c, t, src
c.add Semicolon
of CheckedRange:
c.add "nimCheckRange"
c.add ParLe
let (_, gotoInstr, x, a, b) = sons5(t, n)
gen c, t, x
c.add Comma
gen c, t, a
c.add Comma
gen c, t, b
c.add Comma
c.add "L" & $t[gotoInstr].label.int
c.add ParRi
of CheckedIndex:
c.add "nimCheckIndex"
c.add ParLe
let (gotoInstr, x, a) = sons3(t, n)
gen c, t, x
c.add Comma
gen c, t, a
c.add Comma
c.add "L" & $t[gotoInstr].label.int
c.add ParRi
of Call, IndirectCall:
let (typ, fn) = sons2(t, n)
gen c, t, fn
c.add ParLe
var i = 0
for ch in sonsFromN(t, n, 2):
if i > 0: c.add Comma
gen c, t, ch
inc i
c.add ParRi
if c.m.types[t[typ].typeId].kind == VoidTy:
c.add Semicolon
of CheckedCall, CheckedIndirectCall:
let (typ, gotoInstr, fn) = sons3(t, n)
gen c, t, fn
c.add ParLe
var i = 0
for ch in sonsFromN(t, n, 3):
if i > 0: c.add Comma
gen c, t, ch
inc i
c.add ParRi
if c.m.types[t[typ].typeId].kind == VoidTy:
c.add Semicolon
of CheckedAdd: checkedBinaryop "nimAddInt"
of CheckedSub: checkedBinaryop "nimSubInt"
of CheckedMul: checkedBinaryop "nimMulInt"
of CheckedDiv: checkedBinaryop "nimDivInt"
of CheckedMod: checkedBinaryop "nimModInt"
of Add: triop " + "
of Sub: triop " - "
of Mul: triop " * "
of Div: triop " / "
of Mod: triop " % "
of BitShl: triop " << "
of BitShr: triop " >> "
of BitAnd: triop " & "
of BitOr: triop " | "
of BitXor: triop " ^ "
of BitNot: binaryop " ~ "
of BoolNot: binaryop " !"
of Eq: cmpop " == "
of Le: cmpop " <= "
of Lt: cmpop " < "
of Cast: binaryop ""
of NumberConv: genNumberConv c, t, n
of CheckedObjConv: binaryop ""
of ObjConv: binaryop ""
of Emit: raiseAssert "cannot interpret: Emit"
of ProcDecl: genProcDecl c, t, n, false
of ForeignProcDecl: genProcDecl c, t, n, true
of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc:
c.add "cannot interpret: " & $t[n].kind
const
Prelude = """
/* GENERATED CODE. DO NOT EDIT. */
#ifdef __cplusplus
#define NB8 bool
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
// see #13798: to avoid conflicts for code emitting `#include <stdbool.h>`
#define NB8 _Bool
#else
typedef unsigned char NB8; // best effort
#endif
typedef unsigned char NC8;
typedef float NF32;
typedef double NF64;
#if defined(__BORLANDC__) || defined(_MSC_VER)
typedef signed char NI8;
typedef signed short int NI16;
typedef signed int NI32;
typedef __int64 NI64;
/* XXX: Float128? */
typedef unsigned char NU8;
typedef unsigned short int NU16;
typedef unsigned int NU32;
typedef unsigned __int64 NU64;
#elif defined(HAVE_STDINT_H)
#ifndef USE_NIM_NAMESPACE
# include <stdint.h>
#endif
typedef int8_t NI8;
typedef int16_t NI16;
typedef int32_t NI32;
typedef int64_t NI64;
typedef uint8_t NU8;
typedef uint16_t NU16;
typedef uint32_t NU32;
typedef uint64_t NU64;
#elif defined(HAVE_CSTDINT)
#ifndef USE_NIM_NAMESPACE
# include <cstdint>
#endif
typedef std::int8_t NI8;
typedef std::int16_t NI16;
typedef std::int32_t NI32;
typedef std::int64_t NI64;
typedef std::uint8_t NU8;
typedef std::uint16_t NU16;
typedef std::uint32_t NU32;
typedef std::uint64_t NU64;
#else
/* Unknown compiler/version, do our best */
#ifdef __INT8_TYPE__
typedef __INT8_TYPE__ NI8;
#else
typedef signed char NI8;
#endif
#ifdef __INT16_TYPE__
typedef __INT16_TYPE__ NI16;
#else
typedef signed short int NI16;
#endif
#ifdef __INT32_TYPE__
typedef __INT32_TYPE__ NI32;
#else
typedef signed int NI32;
#endif
#ifdef __INT64_TYPE__
typedef __INT64_TYPE__ NI64;
#else
typedef long long int NI64;
#endif
/* XXX: Float128? */
#ifdef __UINT8_TYPE__
typedef __UINT8_TYPE__ NU8;
#else
typedef unsigned char NU8;
#endif
#ifdef __UINT16_TYPE__
typedef __UINT16_TYPE__ NU16;
#else
typedef unsigned short int NU16;
#endif
#ifdef __UINT32_TYPE__
typedef __UINT32_TYPE__ NU32;
#else
typedef unsigned int NU32;
#endif
#ifdef __UINT64_TYPE__
typedef __UINT64_TYPE__ NU64;
#else
typedef unsigned long long int NU64;
#endif
#endif
#ifdef NIM_INTBITS
# if NIM_INTBITS == 64
typedef NI64 NI;
typedef NU64 NU;
# elif NIM_INTBITS == 32
typedef NI32 NI;
typedef NU32 NU;
# elif NIM_INTBITS == 16
typedef NI16 NI;
typedef NU16 NU;
# elif NIM_INTBITS == 8
typedef NI8 NI;
typedef NU8 NU;
# else
# error "invalid bit width for int"
# endif
#endif
#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
#define nimAddInt64(a, b, L) ({long long int res; if(__builtin_saddll_overflow(a, b, &res)) goto L; res})
#define nimSubInt64(a, b, L) ({long long int res; if(__builtin_ssubll_overflow(a, b, &res)) goto L; res})
#define nimMulInt64(a, b, L) ({long long int res; if(__builtin_smulll_overflow(a, b, &res)) goto L; res})
#define nimAddInt32(a, b, L) ({long int res; if(__builtin_sadd_overflow(a, b, &res)) goto L; res})
#define nimSubInt32(a, b, L) ({long int res; if(__builtin_ssub_overflow(a, b, &res)) goto L; res})
#define nimMulInt32(a, b, L) ({long int res; if(__builtin_smul_overflow(a, b, &res)) goto L; res})
#define nimCheckRange(x, a, b, L) ({if (x < a || x > b) goto L; x})
#define nimCheckIndex(x, a, L) ({if (x >= a) goto L; x})
"""
proc traverseCode(c: var GeneratedCode) =
const AllowedInToplevelC = {SummonConst, SummonGlobal, SummonThreadLocal,
ProcDecl, ForeignDecl, ForeignProcDecl}
var i = NodePos(0)
while i.int < c.m.code.len:
let oldLen = c.code.len
let moveToInitSection = c.m.code[NodePos(i)].kind notin AllowedInToplevelC
gen c, c.m.code, NodePos(i)
next c.m.code, i
if moveToInitSection:
for i in oldLen ..< c.code.len:
c.init.add c.code[i]
setLen c.code, oldLen
proc generateCode*(inp, outp: string) =
var c = initGeneratedCode(load(inp))
var co = TypeOrder()
traverseTypes(c.m.types, c.m.lit, co)
generateTypes(c, c.m.types, c.m.lit, co)
let typeDecls = move c.code
traverseCode c
var f = CppFile(f: open(outp, fmWrite))
f.write "#define NIM_INTBITS " & $c.m.intbits & "\n"
f.write Prelude
writeTokenSeq f, c.includes, c
writeTokenSeq f, typeDecls, c
writeTokenSeq f, c.data, c
writeTokenSeq f, c.protos, c
writeTokenSeq f, c.code, c
if c.init.len > 0:
f.write "void __attribute__((constructor)) init(void) {"
writeTokenSeq f, c.init, c
f.write "}\n\n"
f.f.close

105
compiler/nir/nir.nim Normal file
View File

@@ -0,0 +1,105 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nim Intermediate Representation, designed to capture all of Nim's semantics without losing too much
## precious information. Can easily be translated into C. And to JavaScript, hopefully.
from std/os import addFileExt, `/`, createDir
import std / assertions
import ".." / [ast, modulegraphs, renderer, transf, options, msgs, lineinfos]
import nirtypes, nirinsts, ast2ir, nirlineinfos, nirfiles, nirvm
import ".." / ic / [rodfiles, bitabs]
type
PCtx* = ref object of TPassContext
m: ModuleCon
c: ProcCon
oldErrorCount: int
bytecode: Bytecode
proc newCtx*(module: PSym; g: ModuleGraph; idgen: IdGenerator): PCtx =
var lit = Literals()
var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
var m = initModuleCon(g, g.config, idgen, module, nirm)
m.noModularity = true
PCtx(m: m, c: initProcCon(m, nil, g.config), idgen: idgen, bytecode: initBytecode(nirm))
proc refresh*(c: PCtx; module: PSym; idgen: IdGenerator) =
#c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module, c.m.nirm)
#c.m.noModularity = true
c.c = initProcCon(c.m, nil, c.m.graph.config)
c.idgen = idgen
proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) =
if graph.repl.isNil:
graph.repl = newCtx(module, graph, idgen)
#registerAdditionalOps(PCtx graph.repl)
else:
refresh(PCtx graph.repl, module, idgen)
proc setupNirReplGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
setupGlobalCtx(module, graph, idgen)
result = PCtx graph.repl
proc evalStmt(c: PCtx; n: PNode) =
let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
let pc = genStmt(c.c, n)
#var res = ""
#toString c.m.nirm.code, NodePos(pc), c.m.nirm.lit.strings, c.m.nirm.lit.numbers, c.m.symnames, res
#res.add "\n--------------------------\n"
#toString res, c.m.types.g
if pc.int < c.m.nirm.code.len:
c.bytecode.interactive = c.m.graph.interactive
execCode c.bytecode, c.m.nirm.code, pc
#echo res
proc runCode*(c: PPassContext; n: PNode): PNode =
let c = PCtx(c)
# don't eval errornous code:
if c.oldErrorCount == c.m.graph.config.errorCounter:
evalStmt(c, n)
result = newNodeI(nkEmpty, n.info)
else:
result = n
c.oldErrorCount = c.m.graph.config.errorCounter
type
NirPassContext* = ref object of TPassContext
m: ModuleCon
c: ProcCon
proc openNirBackend*(g: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
var lit = Literals()
var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
let m = initModuleCon(g, g.config, idgen, module, nirm)
NirPassContext(m: m, c: initProcCon(m, nil, g.config), idgen: idgen)
proc gen(c: NirPassContext; n: PNode) =
let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
let pc = genStmt(c.c, n)
proc nirBackend*(c: PPassContext; n: PNode): PNode =
gen(NirPassContext(c), n)
result = n
proc closeNirBackend*(c: PPassContext; finalNode: PNode) =
discard nirBackend(c, finalNode)
let c = NirPassContext(c)
let nimcache = getNimcacheDir(c.c.config).string
createDir nimcache
let outp = nimcache / c.m.module.name.s.addFileExt("nir")
#c.m.nirm.code = move c.c.code
try:
store c.m.nirm[], outp
echo "created: ", outp
except IOError:
rawMessage(c.c.config, errFatal, "serialization failed: " & outp)

52
compiler/nir/nirc.nim Normal file
View File

@@ -0,0 +1,52 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nir Compiler.
import ".." / ic / [bitabs, rodfiles]
import nirinsts, nirtypes, nirlineinfos, nirfiles, cir
proc view(filename: string) =
let m = load(filename)
var res = ""
allTreesToString m.code, m.lit.strings, m.lit.numbers, m.symnames, res
res.add "\n# TYPES\n"
nirtypes.toString res, m.types
echo res
import std / [syncio, parseopt]
proc writeHelp =
echo """Usage: nirc view|c <file.nir>"""
quit 0
proc main =
var inp = ""
var cmd = ""
for kind, key, val in getopt():
case kind
of cmdArgument:
if cmd.len == 0: cmd = key
elif inp.len == 0: inp = key
else: quit "Error: too many arguments"
of cmdLongOption, cmdShortOption:
case key
of "help", "h": writeHelp()
of "version", "v": stdout.write "1.0\n"
of cmdEnd: discard
if inp.len == 0:
quit "Error: no input file specified"
case cmd
of "", "view":
view inp
of "c":
let outp = inp & ".c"
cir.generateCode inp, outp
main()

83
compiler/nir/nirfiles.nim Normal file
View File

@@ -0,0 +1,83 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import ".." / ic / [bitabs, rodfiles]
import nirinsts, nirtypes, nirlineinfos
type
NirModule* = object
code*: Tree
man*: LineInfoManager
types*: TypeGraph
lit*: Literals
namespace*: LitId
intbits*: uint32
symnames*: SymNames
proc load*(filename: string): NirModule =
let lit = Literals()
result = NirModule(lit: lit, types: initTypeGraph(lit))
var r = rodfiles.open(filename)
try:
r.loadHeader(nirCookie)
r.loadSection stringsSection
r.load result.lit.strings
r.loadSection numbersSection
r.load result.lit.numbers
r.loadSection bodiesSection
r.load result.code
r.loadSection typesSection
r.load result.types
r.loadSection sideChannelSection
r.load result.man
r.loadSection namespaceSection
r.loadPrim result.namespace
r.loadPrim result.intbits
r.loadSection symnamesSection
r.load result.symnames
finally:
r.close()
proc store*(m: NirModule; outp: string) =
var r = rodfiles.create(outp)
try:
r.storeHeader(nirCookie)
r.storeSection stringsSection
r.store m.lit.strings
r.storeSection numbersSection
r.store m.lit.numbers
r.storeSection bodiesSection
r.store m.code
r.storeSection typesSection
r.store m.types
r.storeSection sideChannelSection
r.store m.man
r.storeSection namespaceSection
r.storePrim m.namespace
r.storePrim m.intbits
r.storeSection symnamesSection
r.store m.symnames
finally:
r.close()
if r.err != ok:
raise newException(IOError, "could store into: " & outp)

582
compiler/nir/nirinsts.nim Normal file
View File

@@ -0,0 +1,582 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## NIR instructions. Somewhat inspired by LLVM's instructions.
import std / [assertions, hashes]
import .. / ic / [bitabs, rodfiles]
import nirlineinfos, nirtypes
const
NirVersion = 1
nirCookie* = [byte(0), byte('N'), byte('I'), byte('R'),
byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(NirVersion)]
type
SymId* = distinct int
proc `$`*(s: SymId): string {.borrow.}
proc hash*(s: SymId): Hash {.borrow.}
proc `==`*(a, b: SymId): bool {.borrow.}
type
Opcode* = enum
Nop,
ImmediateVal,
IntVal,
StrVal,
SymDef,
SymUse,
Typed, # with type ID
PragmaId, # with Pragma ID, possible values: see PragmaKey enum
NilVal,
Label,
Goto,
CheckedGoto,
LoopLabel,
GotoLoop, # last atom
ModuleSymUse, # `"module".x`
ArrayConstr,
ObjConstr,
Ret,
Yld,
Select,
SelectPair, # ((values...), Label)
SelectList, # (values...)
SelectValue, # (value)
SelectRange, # (valueA..valueB)
ForeignDecl, # Can wrap SummonGlobal, SummonThreadLocal, SummonConst
SummonGlobal,
SummonThreadLocal,
Summon, # x = Summon Typed <Type ID>; x begins to live
SummonResult,
SummonParam,
SummonConst,
Kill, # `Kill x`: scope end for `x`
AddrOf,
ArrayAt, # a[i]
DerefArrayAt, # a[i] where `a` is a PtrArray; `a[][i]`
FieldAt, # obj.field
DerefFieldAt, # obj[].field
Load, # a[]
Store, # a[] = b
Asgn, # a = b
SetExc,
TestExc,
CheckedRange,
CheckedIndex,
Call,
IndirectCall,
CheckedCall, # call that can raise
CheckedIndirectCall, # call that can raise
CheckedAdd, # with overflow checking etc.
CheckedSub,
CheckedMul,
CheckedDiv,
CheckedMod,
Add,
Sub,
Mul,
Div,
Mod,
BitShl,
BitShr,
BitAnd,
BitOr,
BitXor,
BitNot,
BoolNot,
Eq,
Le,
Lt,
Cast,
NumberConv,
CheckedObjConv,
ObjConv,
TestOf,
Emit,
ProcDecl,
ForeignProcDecl,
PragmaPair
type
PragmaKey* = enum
FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall,
CoreName,
ExternName,
HeaderImport,
DllImport,
DllExport,
ObjExport
const
LastAtomicValue = GotoLoop
OpcodeBits = 8'u32
OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal}
ValueProducing* = {
ImmediateVal,
IntVal,
StrVal,
SymUse,
NilVal,
ModuleSymUse,
ArrayConstr,
ObjConstr,
CheckedAdd,
CheckedSub,
CheckedMul,
CheckedDiv,
CheckedMod,
Add,
Sub,
Mul,
Div,
Mod,
BitShl,
BitShr,
BitAnd,
BitOr,
BitXor,
BitNot,
BoolNot,
Eq,
Le,
Lt,
Cast,
NumberConv,
CheckedObjConv,
ObjConv,
AddrOf,
Load,
ArrayAt,
DerefArrayAt,
FieldAt,
DerefFieldAt,
TestOf
}
type
Instr* = object # 8 bytes
x: uint32
info*: PackedLineInfo
template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask)
template operand(n: Instr): uint32 = (n.x shr OpcodeBits)
template rawOperand*(n: Instr): uint32 = (n.x shr OpcodeBits)
template toX(k: Opcode; operand: uint32): uint32 =
uint32(k) or (operand shl OpcodeBits)
template toX(k: Opcode; operand: LitId): uint32 =
uint32(k) or (operand.uint32 shl OpcodeBits)
type
Tree* = object
nodes: seq[Instr]
Values* = object
numbers: BiTable[int64]
strings: BiTable[string]
type
PatchPos* = distinct int
NodePos* = distinct int
const
InvalidPatchPos* = PatchPos(-1)
proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos =
result = PatchPos tree.nodes.len
tree.nodes.add Instr(x: toX(kind, 1'u32), info: info)
proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
proc patch*(tree: var Tree; pos: PatchPos) =
let pos = pos.int
let k = tree.nodes[pos].kind
assert k > LastAtomicValue
let distance = int32(tree.nodes.len - pos)
assert distance > 0
tree.nodes[pos].x = toX(k, cast[uint32](distance))
template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) =
let pos = prepare(tree, info, kind)
body
patch(tree, pos)
template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
let pos = prepare(tree, info, kind)
tree.addTyped info, typ
body
patch(tree, pos)
proc len*(tree: Tree): int {.inline.} = tree.nodes.len
template rawSpan(n: Instr): int = int(operand(n))
proc nextChild(tree: Tree; pos: var int) {.inline.} =
if tree.nodes[pos].kind > LastAtomicValue:
assert tree.nodes[pos].operand > 0'u32
inc pos, tree.nodes[pos].rawSpan
else:
inc pos
proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos)
template firstSon*(n: NodePos): NodePos = NodePos(n.int+1)
template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2)
iterator sons*(tree: Tree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > LastAtomicValue
let last = pos + tree.nodes[pos].rawSpan
inc pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sonsFrom1*(tree: Tree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > LastAtomicValue
let last = pos + tree.nodes[pos].rawSpan
inc pos
nextChild tree, pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > LastAtomicValue
let last = pos + tree.nodes[pos].rawSpan
inc pos
for i in 1..toSkip:
nextChild tree, pos
while pos < last:
yield NodePos pos
nextChild tree, pos
template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int]
iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos =
var pos = n.int
assert tree[parent].kind > LastAtomicValue
let last = parent.int + tree[parent].rawSpan
while pos < last:
yield NodePos pos
nextChild tree, pos
proc span(tree: Tree; pos: int): int {.inline.} =
if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
proc copyTree*(dest: var Tree; src: Tree) =
let pos = 0
let L = span(src, pos)
let d = dest.nodes.len
dest.nodes.setLen(d + L)
assert L > 0
for i in 0..<L:
dest.nodes[d+i] = src.nodes[pos+i]
proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
result = (NodePos a, NodePos b)
proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
result = (NodePos a, NodePos b, NodePos c)
proc sons4*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
let d = c + span(tree, c)
result = (NodePos a, NodePos b, NodePos c, NodePos d)
proc sons5*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
let d = c + span(tree, c)
let e = d + span(tree, d)
result = (NodePos a, NodePos b, NodePos c, NodePos d, NodePos e)
proc typeId*(ins: Instr): TypeId {.inline.} =
assert ins.kind == Typed
result = TypeId(ins.operand)
proc symId*(ins: Instr): SymId {.inline.} =
assert ins.kind in {SymUse, SymDef}
result = SymId(ins.operand)
proc immediateVal*(ins: Instr): int {.inline.} =
assert ins.kind == ImmediateVal
result = cast[int](ins.operand)
proc litId*(ins: Instr): LitId {.inline.} =
assert ins.kind in {StrVal, IntVal}
result = LitId(ins.operand)
type
LabelId* = distinct int
proc `==`*(a, b: LabelId): bool {.borrow.}
proc hash*(a: LabelId): Hash {.borrow.}
proc label*(ins: Instr): LabelId {.inline.} =
assert ins.kind in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
result = LabelId(ins.operand)
proc newLabel*(labelGen: var int): LabelId {.inline.} =
result = LabelId labelGen
inc labelGen
proc newLabels*(labelGen: var int; n: int): LabelId {.inline.} =
result = LabelId labelGen
inc labelGen, n
proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId =
assert k in {Label, LoopLabel}
result = LabelId labelGen
t.nodes.add Instr(x: toX(k, uint32(result)), info: info)
inc labelGen
proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) =
assert k in {Goto, GotoLoop, CheckedGoto}
t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} =
assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info)
proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
proc addNop*(t: var Tree; info: PackedLineInfo) {.inline.} =
t.nodes.add Instr(x: toX(Nop, 0'u32), info: info)
proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} =
assert typ.int >= 0
t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} =
assert typ.int >= 0
assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam, SummonResult}
let x = prepare(t, info, opc)
t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
patch t, x
proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) =
assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) =
t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info)
proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
buildTyped t, info, NumberConv, typ:
t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info)
proc boolVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; b: bool) =
buildTyped t, info, NumberConv, Bool8Id:
t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(ord b))), info: info)
proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info)
proc addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) =
t.nodes.add Instr(x: toX(StrVal, uint32(s)), info: info)
proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) =
buildTyped t, info, NumberConv, typ:
t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info)
proc store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes
proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes
proc escapeToNimLit*(s: string; result: var string) =
result.add '"'
for c in items s:
if c < ' ' or int(c) >= 128:
result.add '\\'
result.addInt int(c)
elif c == '\\':
result.add r"\\"
elif c == '\n':
result.add r"\n"
elif c == '\r':
result.add r"\r"
elif c == '\t':
result.add r"\t"
else:
result.add c
result.add '"'
type
SymNames* = object
s: seq[LitId]
proc `[]=`*(t: var SymNames; key: SymId; val: LitId) =
let k = int(key)
if k >= t.s.len: t.s.setLen k+1
t.s[k] = val
proc `[]`*(t: SymNames; key: SymId): LitId =
let k = int(key)
if k < t.s.len: result = t.s[k]
else: result = LitId(0)
template localName(s: SymId): string =
let name = names[s]
if name != LitId(0):
strings[name]
else:
$s.int
proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s)
proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s)
proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64];
names: SymNames;
r: var string; nesting = 0) =
if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}:
r.add ' '
case t[pos].kind
of Nop: r.add "Nop"
of ImmediateVal:
r.add $t[pos].operand
of IntVal:
r.add "IntVal "
r.add $integers[LitId t[pos].operand]
of StrVal:
escapeToNimLit(strings[LitId t[pos].operand], r)
of SymDef:
r.add "SymDef "
r.add localName(SymId t[pos].operand)
of SymUse:
r.add "SymUse "
r.add localName(SymId t[pos].operand)
of PragmaId:
r.add $cast[PragmaKey](t[pos].operand)
of Typed:
r.add "T<"
r.add $t[pos].operand
r.add ">"
of NilVal:
r.add "NilVal"
of Label:
# undo the nesting:
var spaces = r.len-1
while spaces >= 0 and r[spaces] == ' ': dec spaces
r.setLen spaces+1
r.add "\n L"
r.add $t[pos].operand
of Goto, CheckedGoto, LoopLabel, GotoLoop:
r.add $t[pos].kind
r.add " L"
r.add $t[pos].operand
else:
r.add $t[pos].kind
r.add "{\n"
for i in 0..<(nesting+1)*2: r.add ' '
for p in sons(t, pos):
toString t, p, strings, integers, names, r, nesting+1
r.add "\n"
for i in 0..<nesting*2: r.add ' '
r.add "}"
proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64];
names: SymNames;
r: var string) =
var i = 0
while i < t.len:
toString t, NodePos(i), strings, integers, names, r
nextChild t, i
type
Value* = distinct Tree
proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} =
assert k in ValueProducing - ValueProducingAtoms
result = prepare(Tree(dest), info, k)
proc patch*(dest: var Value; pos: PatchPos) {.inline.} =
patch(Tree(dest), pos)
proc localToValue*(info: PackedLineInfo; s: SymId): Value =
result = Value(Tree())
Tree(result).addSymUse info, s
proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0
proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0
proc extractTemp*(v: Value): SymId =
if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse:
result = SymId(Tree(v)[NodePos 0].operand)
else:
result = SymId(-1)
proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src)
proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) =
assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) =
let pos = prepare(Tree(tree), info, kind)
body
patch(tree, pos)
proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} =
addTyped(Tree(t), info, typ)
template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
let pos = prepare(tree, info, kind)
tree.addTyped info, typ
body
patch(tree, pos)
proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
addStrVal(Tree(t), strings, info, s)
proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) =
addNilVal Tree(t), info, typ
proc addIntVal*(t: var Value; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
addIntVal Tree(t), integers, info, typ, x

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2024 Andreas Rumpf
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

104
compiler/nir/nirslots.nim Normal file
View File

@@ -0,0 +1,104 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Management of slots. Similar to "register allocation"
## in lower level languages.
import std / [assertions, tables]
import nirtypes, nirinsts
type
SlotManagerFlag* = enum
ReuseTemps,
ReuseVars
SlotKind* = enum
Temp, Perm
SlotManager* = object # "register allocator"
live: Table[SymId, (SlotKind, TypeId)]
dead: Table[TypeId, seq[SymId]]
flags: set[SlotManagerFlag]
inScope: seq[SymId]
proc initSlotManager*(flags: set[SlotManagerFlag]): SlotManager {.inline.} =
SlotManager(flags: flags)
proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag; k: SlotKind;
symIdgen: var int32): SymId {.inline.} =
if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0:
result = m.dead[t].pop()
else:
inc symIdgen
result = SymId(symIdgen)
m.inScope.add result
m.live[result] = (k, t)
proc allocTemp*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} =
result = allocRaw(m, t, ReuseTemps, Temp, symIdgen)
proc allocVar*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} =
result = allocRaw(m, t, ReuseVars, Perm, symIdgen)
proc freeLoc*(m: var SlotManager; s: SymId) =
let t = m.live.getOrDefault(s)
assert t[1].int != 0
m.live.del s
m.dead.mgetOrPut(t[1], @[]).add s
proc freeTemp*(m: var SlotManager; s: SymId) =
let t = m.live.getOrDefault(s)
if t[1].int != 0 and t[0] == Temp:
m.live.del s
m.dead.mgetOrPut(t[1], @[]).add s
iterator stillAlive*(m: SlotManager): (SymId, TypeId) =
for k, v in pairs(m.live):
yield (k, v[1])
proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s][1]
proc openScope*(m: var SlotManager) =
m.inScope.add SymId(-1) # add marker
proc closeScope*(m: var SlotManager) =
var i = m.inScope.len - 1
while i >= 0:
if m.inScope[i] == SymId(-1):
m.inScope.setLen i
break
dec i
when isMainModule:
var symIdgen: int32
var m = initSlotManager({ReuseTemps})
var g = initTypeGraph(Literals())
let a = g.openType ArrayTy
g.addBuiltinType Int8Id
g.addArrayLen 5
let finalArrayType = finishType(g, a)
let obj = g.openType ObjectDecl
g.addName "MyType"
g.addField "p", finalArrayType, 0
let objB = finishType(g, obj)
let x = m.allocTemp(objB, symIdgen)
assert x.int == 0
let y = m.allocTemp(objB, symIdgen)
assert y.int == 1
let z = m.allocTemp(Int8Id, symIdgen)
assert z.int == 2
m.freeLoc y
let y2 = m.allocTemp(objB, symIdgen)
assert y2.int == 1

Some files were not shown because too many files have changed in this diff Show More