diff options
663 files changed, 21853 insertions, 17421 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 014513865..1e46e0544 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -7,17 +7,16 @@ body: - type: markdown attributes: value: | - - **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. + 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. - type: textarea id: description attributes: label: Description description: | - 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. + Describe the problem. Code example can be given here. + placeholder: Bug reports with reproducible code or detailed information will be fixed faster. validations: required: true @@ -25,7 +24,9 @@ body: id: nim-version attributes: label: Nim Version - description: Copy and paste the output of `nim -v` on the command line. For development versions, make sure to include the commit hash. + 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. validations: required: true @@ -34,7 +35,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 and detailed information will be fixed faster. + placeholder: Bug reports with reproducible code or detailed information will be fixed faster. render: text - type: textarea @@ -42,14 +43,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 and detailed information will be fixed faster. + placeholder: Bug reports with reproducible code or detailed information will be fixed faster. render: text - type: textarea - id: possible-solution + id: known-workarounds attributes: - label: Possible Solution - description: Propose a possible solution. + label: Known Workarounds + description: Provide any known workarounds. validations: required: false @@ -64,13 +65,9 @@ body: - type: markdown attributes: value: | - - 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). + - 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). - 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, - 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). + - 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). - [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html) diff --git a/.github/workflows/ci_bench.yml b/.github/workflows/ci_bench.yml deleted file mode 100644 index 7c76ed89b..000000000 --- a/.github/workflows/ci_bench.yml +++ /dev/null @@ -1,115 +0,0 @@ -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 diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 32e8e050e..2faa37ce0 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -45,7 +45,7 @@ jobs: - target: windows os: windows-2019 - target: osx - os: macos-11 + os: macos-12 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@v3 + uses: crazy-max/ghaction-github-pages@v4 with: build_dir: doc/html env: diff --git a/.github/workflows/ci_gcc14.yml b/.github/workflows/ci_gcc14.yml new file mode 100644 index 000000000..fb286887a --- /dev/null +++ b/.github/workflows/ci_gcc14.yml @@ -0,0 +1,76 @@ +name: GCC 14 +on: + pull_request: + push: + branches: + - 'devel' + + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04] + cpu: [amd64] + name: '${{ matrix.os }}' + runs-on: ${{ matrix.os }} + timeout-minutes: 60 # refs bug #18178 + steps: + - name: 'Checkout' + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: 'Install node.js 20.x' + uses: actions/setup-node@v4 + with: + node-version: '20.x' + + - name: 'Install dependencies (Linux amd64)' + if: runner.os == 'Linux' && matrix.cpu == 'amd64' + run: | + sudo apt update -qq + sudo apt remove needrestart + DEBIAN_FRONTEND='noninteractive' \ + sudo apt install --no-install-recommends -yq \ + libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \ + valgrind libc6-dbg libblas-dev xorg-dev + - name: 'Install dependencies (Linux amd64 gcc 14)' + if: runner.os == 'Linux' && matrix.cpu == 'amd64' + run: | + sudo add-apt-repository universe + sudo apt update -qq + sudo apt install -y gcc-14 g++-14 libpcre3 liblapack-dev + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14 + - name: 'Install dependencies (macOS)' + if: runner.os == 'macOS' + run: brew install boehmgc make sfml gtk+3 + - name: 'Install dependencies (Windows)' + if: runner.os == 'Windows' + shell: bash + run: | + set -e + . ci/funs.sh + nimInternalInstallDepsWindows + echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" + + - name: 'Add build binaries to PATH' + shell: bash + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" + + - name: 'NIM_TESTAMENT_DISABLE_SSL' + shell: bash + run: echo "NIM_TESTAMENT_DISABLE_SSL=1" >> $GITHUB_ENV + + - name: 'System information' + shell: bash + run: . ci/funs.sh && nimCiSystemInfo + + - name: 'Build csourcesAny' + shell: bash + run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}' + + - name: 'koch, Run CI' + shell: bash + run: . ci/funs.sh && nimInternalBuildKochAndRunCI diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 91ac83aec..2ea5092e3 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, macos-11] + os: [ubuntu-20.04, macos-12] cpu: [amd64] batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num` name: '${{ matrix.os }} (batch: ${{ matrix.batch }})' diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml index 58da92206..decfe953e 100644 --- a/.github/workflows/ci_publish.yml +++ b/.github/workflows/ci_publish.yml @@ -71,7 +71,7 @@ jobs: run: nim c -r -d:release ci/action.nim - name: 'Comment' - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const fs = require('fs'); diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index d89a4888a..0c5a533e1 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -9,7 +9,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: days-before-pr-stale: 365 days-before-pr-close: 30 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3f20fb866..9ef202948 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -29,10 +29,10 @@ jobs: # vmImage: 'ubuntu-18.04' # CPU: i386 OSX_amd64: - vmImage: 'macOS-11' + vmImage: 'macOS-12' CPU: amd64 OSX_amd64_cpp: - vmImage: 'macOS-11' + vmImage: 'macOS-12' CPU: amd64 NIM_COMPILE_TO_CPP: true Windows_amd64_batch0_3: diff --git a/changelog.md b/changelog.md index f21ab39da..8acd2120a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,48 +1,18 @@ -# v2.2.0 - yyyy-mm-dd +# v2.x.x - yyyy-mm-dd ## Changes affecting backward compatibility -- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility. -- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec. -- 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 -[//]: # "Changes:" - -- 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 -- `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 -- `--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 in tow. diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md index 950fa4069..24dc4edad 100644 --- a/changelogs/changelog_2_0_0_details.md +++ b/changelogs/changelog_2_0_0_details.md @@ -72,7 +72,7 @@ - `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and `sink` for optimization purposes. -- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises. +- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fulfill its promises. - The `{.this.}` pragma, deprecated since 0.19, has been removed. - `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead. diff --git a/changelogs/changelog_2_2_0.md b/changelogs/changelog_2_2_0.md index 0a293d35f..b50cbeb27 100644 --- a/changelogs/changelog_2_2_0.md +++ b/changelogs/changelog_2_2_0.md @@ -1,12 +1,248 @@ -# v2.2.0 - 2023-mm-dd +# v2.2.0 - 2024-10-02 + ## 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. + +- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors. + +- 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. + +- C++ `constructor` now reuses `result` instead creating `this`. + +- Tuple unpacking changes: + - Tuple unpacking assignment now supports using underscores to discard values. + ```nim + var a, c: int + (a, _, c) = (1, 2, 3) + ``` + - Tuple unpacking variable declarations now support type annotations, but + only for the entire tuple. + ```nim + let (a, b): (int, int) = (1, 2) + let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc")) + ``` + +- The experimental option `--experimental:openSym` has been added to allow + captured symbols in generic routine and template bodies respectively to be + replaced by symbols injected locally by templates/macros at instantiation + time. `bind` may be used to keep the captured symbols over the injected ones + regardless of enabling the option, but other methods like renaming the + captured symbols should be used instead so that the code is not affected by + context changes. + + Since this change may affect runtime behavior, the experimental switch + `openSym` needs to be enabled; and a warning is given in the case where an + injected symbol would replace a captured symbol not bound by `bind` and + the experimental switch isn't enabled. + + ```nim + 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. diff --git a/changelogs/changelog_X_XX_X.md b/changelogs/changelog_X_XX_X.md index 524599b11..f8a09a535 100644 --- a/changelogs/changelog_X_XX_X.md +++ b/changelogs/changelog_X_XX_X.md @@ -1,4 +1,4 @@ -# v1.xx.x - yyyy-mm-dd +# v2.xx.x - yyyy-mm-dd This is an example file. The changes should go to changelog.md! diff --git a/compiler/aliases.nim b/compiler/aliases.nim index 40d6e272c..fa1167753 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -51,14 +51,16 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult = if compareTypes(a, b, dcEqIgnoreDistinct): return arYes case a.kind of tyObject: - if a[0] != nil: - result = isPartOfAux(a[0].skipTypes(skipPtrs), b, marker) + if a.baseClass != nil: + result = isPartOfAux(a.baseClass.skipTypes(skipPtrs), b, marker) if result == arNo: result = isPartOfAux(a.n, b, marker) of tyGenericInst, tyDistinct, tyAlias, tySink: - result = isPartOfAux(lastSon(a), b, marker) - of tyArray, tySet, tyTuple: - for i in 0..<a.len: - result = isPartOfAux(a[i], b, marker) + 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) if result == arYes: return else: discard diff --git a/compiler/ast.nim b/compiler/ast.nim index e25ce42dd..a342e1ea7 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -20,6 +20,9 @@ when defined(nimPreviewSlimSystem): export int128 +import nodekinds +export nodekinds + type TCallingConvention* = enum ccNimCall = "nimcall" # nimcall, also the default @@ -33,207 +36,12 @@ type ccThisCall = "thiscall" # thiscall (parameters are pushed right-to-left) ccClosure = "closure" # proc has a closure ccNoConvention = "noconv" # needed for generating proper C procs sometimes - -type - TNodeKind* = enum # order is extremely important, because ranges are used - # to check whether a node belongs to a certain class - nkNone, # unknown node kind: indicates an error - # Expressions: - # Atoms: - nkEmpty, # the node is empty - nkIdent, # node is an identifier - nkSym, # node is a symbol - nkType, # node is used for its typ field - - nkCharLit, # a character literal '' - nkIntLit, # an integer literal - nkInt8Lit, - nkInt16Lit, - nkInt32Lit, - nkInt64Lit, - nkUIntLit, # an unsigned integer literal - nkUInt8Lit, - nkUInt16Lit, - nkUInt32Lit, - nkUInt64Lit, - nkFloatLit, # a floating point literal - nkFloat32Lit, - nkFloat64Lit, - nkFloat128Lit, - nkStrLit, # a string literal "" - nkRStrLit, # a raw string literal r"" - nkTripleStrLit, # a triple string literal """ - nkNilLit, # the nil literal - # end of atoms - nkComesFrom, # "comes from" template/macro information for - # better stack trace generation - nkDotCall, # used to temporarily flag a nkCall node; - # this is used - # for transforming ``s.len`` to ``len(s)`` - - nkCommand, # a call like ``p 2, 4`` without parenthesis - nkCall, # a call like p(x, y) or an operation like +(a, b) - nkCallStrLit, # a call with a string literal - # x"abc" has two sons: nkIdent, nkRStrLit - # x"""abc""" has two sons: nkIdent, nkTripleStrLit - nkInfix, # a call like (a + b) - nkPrefix, # a call like !a - nkPostfix, # something like a! (also used for visibility) - nkHiddenCallConv, # an implicit type conversion via a type converter - - nkExprEqExpr, # a named parameter with equals: ''expr = expr'' - nkExprColonExpr, # a named parameter with colon: ''expr: expr'' - nkIdentDefs, # a definition like `a, b: typeDesc = expr` - # either typeDesc or expr may be nil; used in - # formal parameters, var statements, etc. - nkVarTuple, # a ``var (a, b) = expr`` construct - nkPar, # syntactic (); may be a tuple constructor - nkObjConstr, # object constructor: T(a: 1, b: 2) - nkCurly, # syntactic {} - nkCurlyExpr, # an expression like a{i} - nkBracket, # syntactic [] - nkBracketExpr, # an expression like a[i..j, k] - nkPragmaExpr, # an expression like a{.pragmas.} - nkRange, # an expression like i..j - nkDotExpr, # a.b - nkCheckedFieldExpr, # a.b, but b is a field that needs to be checked - nkDerefExpr, # a^ - nkIfExpr, # if as an expression - nkElifExpr, - nkElseExpr, - nkLambda, # lambda expression - nkDo, # lambda block appering as trailing proc param - nkAccQuoted, # `a` as a node - - nkTableConstr, # a table constructor {expr: expr} - nkBind, # ``bind expr`` node - nkClosedSymChoice, # symbol choice node; a list of nkSyms (closed) - nkOpenSymChoice, # symbol choice node; a list of nkSyms (open) - nkHiddenStdConv, # an implicit standard type conversion - nkHiddenSubConv, # an implicit type conversion from a subtype - # to a supertype - nkConv, # a type conversion - nkCast, # a type cast - nkStaticExpr, # a static expr - nkAddr, # a addr expression - nkHiddenAddr, # implicit address operator - nkHiddenDeref, # implicit ^ operator - nkObjDownConv, # down conversion between object types - nkObjUpConv, # up conversion between object types - nkChckRangeF, # range check for floats - nkChckRange64, # range check for 64 bit ints - nkChckRange, # range check for ints - nkStringToCString, # string to cstring - nkCStringToString, # cstring to string - # end of expressions - - nkAsgn, # a = b - nkFastAsgn, # internal node for a fast ``a = b`` - # (no string copy) - nkGenericParams, # generic parameters - nkFormalParams, # formal parameters - nkOfInherit, # inherited from symbol - - nkImportAs, # a 'as' b in an import statement - nkProcDef, # a proc - nkMethodDef, # a method - nkConverterDef, # a converter - nkMacroDef, # a macro - nkTemplateDef, # a template - nkIteratorDef, # an iterator - - nkOfBranch, # used inside case statements - # for (cond, action)-pairs - nkElifBranch, # used in if statements - nkExceptBranch, # an except section - nkElse, # an else part - nkAsmStmt, # an assembler block - nkPragma, # a pragma statement - nkPragmaBlock, # a pragma with a block - nkIfStmt, # an if statement - nkWhenStmt, # a when expression or statement - nkForStmt, # a for statement - nkParForStmt, # a parallel for statement - nkWhileStmt, # a while statement - nkCaseStmt, # a case statement - nkTypeSection, # a type section (consists of type definitions) - nkVarSection, # a var section - nkLetSection, # a let section - nkConstSection, # a const section - nkConstDef, # a const definition - nkTypeDef, # a type definition - nkYieldStmt, # the yield statement as a tree - nkDefer, # the 'defer' statement - nkTryStmt, # a try statement - nkFinally, # a finally section - nkRaiseStmt, # a raise statement - nkReturnStmt, # a return statement - nkBreakStmt, # a break statement - nkContinueStmt, # a continue statement - nkBlockStmt, # a block statement - nkStaticStmt, # a static statement - nkDiscardStmt, # a discard statement - nkStmtList, # a list of statements - nkImportStmt, # an import statement - nkImportExceptStmt, # an import x except a statement - nkExportStmt, # an export statement - nkExportExceptStmt, # an 'export except' statement - nkFromStmt, # a from * import statement - nkIncludeStmt, # an include statement - nkBindStmt, # a bind statement - nkMixinStmt, # a mixin statement - nkUsingStmt, # an using statement - nkCommentStmt, # a comment statement - nkStmtListExpr, # a statement list followed by an expr; this is used - # to allow powerful multi-line templates - nkBlockExpr, # a statement block ending in an expr; this is used - # to allow powerful multi-line templates that open a - # temporary scope - nkStmtListType, # a statement list ending in a type; for macros - nkBlockType, # a statement block ending in a type; for macros - # types as syntactic trees: - - nkWith, # distinct with `foo` - nkWithout, # distinct without `foo` - - nkTypeOfExpr, # type(1+2) - nkObjectTy, # object body - nkTupleTy, # tuple body - nkTupleClassTy, # tuple type class - nkTypeClassTy, # user-defined type class - nkStaticTy, # ``static[T]`` - nkRecList, # list of object parts - nkRecCase, # case section of object - nkRecWhen, # when section of object - nkRefTy, # ``ref T`` - nkPtrTy, # ``ptr T`` - nkVarTy, # ``var T`` - nkConstTy, # ``const T`` - nkOutTy, # ``out T`` - nkDistinctTy, # distinct type - nkProcTy, # proc type - nkIteratorTy, # iterator type - nkSinkAsgn, # '=sink(x, y)' - nkEnumTy, # enum body - nkEnumFieldDef, # `ident = expr` in an enumeration - nkArgList, # argument list - nkPattern, # a special pattern; used for matching - nkHiddenTryStmt, # a hidden try statement - nkClosure, # (prc, env)-pair (internally used for code gen) - nkGotoState, # used for the state machine (for iterators) - nkState, # give a label to a code section (for iterators) - nkBreakState, # special break statement for easier code generation - nkFuncDef, # a func - nkTupleConstr # a tuple constructor - nkError # erroneous AST node - nkModuleRef # for .rod file support: A (moduleId, itemId) pair - nkReplayAction # for .rod file support: A replay action - nkNilRodNode # for .rod file support: a 'nil' PNode + ccMember = "member" # proc is a (cpp) member TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 51 flags! + TSymFlag* = enum # 63 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -285,7 +93,7 @@ type sfNamedParamCall, # symbol needs named parameter call syntax in target # language; for interfacing with Objective C sfDiscardable, # returned value may be discarded implicitly - sfOverridden, # proc is overridden + sfOverridden, # proc is overridden sfCallsite # A flag for template symbols to tell the # compiler it should use line information from # the calling side of the macro, not from the @@ -318,24 +126,25 @@ type sfByCopy # param is marked as pass bycopy sfMember # proc is a C++ member of a type sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl + sfWasGenSym # symbol was 'gensym'ed + sfForceLift # variable has to be lifted into closure environment + + sfDirty # template is not hygienic (old styled template) module, + # compiled from a dirty-buffer + sfCustomPragma # symbol is custom pragma template + sfBase, # a base method + sfGoto # var is used for 'goto' code generation + sfAnon, # symbol name that was generated by the compiler + # the compiler will avoid printing such names + # in user messages. + sfAllUntyped # macro or template is immediately expanded in a generic context + sfTemplateRedefinition # symbol is a redefinition of an earlier template TSymFlags* = set[TSymFlag] const sfNoInit* = sfMainModule # don't generate code to init the variable - sfAllUntyped* = sfVolatile # macro or template is immediately expanded \ - # in a generic context - - sfDirty* = sfPure - # template is not hygienic (old styled template) - # module, compiled from a dirty-buffer - - sfAnon* = sfDiscardable - # symbol name that was generated by the compiler - # the compiler will avoid printing such names - # in user messages. - sfNoForward* = sfRegister # forward declarations are not required (per module) sfReorder* = sfForward @@ -344,12 +153,8 @@ const sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code sfExperimental* = sfOverridden # module uses the .experimental switch - sfGoto* = sfOverridden # var is used for 'goto' code generation sfWrittenTo* = sfBorrow # param is assigned to # currently unimplemented - sfBase* = sfDiscriminant - sfCustomPragma* = sfRegister # symbol is custom pragma template - sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition const @@ -409,7 +214,8 @@ type tyUncheckedArray # An array with boundaries [0,+∞] - tyProxy # used as errornous type (for idetools) + tyError # used as erroneous type (for idetools) + # as an erroneous node should match everything tyBuiltInTypeClass # Type such as the catch-all object, tuple, seq, etc @@ -434,9 +240,9 @@ type tyInferred # In the initial state `base` stores a type class constraining # the types that can be inferred. After a candidate type is - # selected, it's stored in `lastSon`. Between `base` and `lastSon` + # selected, it's stored in `last`. Between `base` and `last` # there may be 0, 2 or more types that were also considered as - # possible candidates in the inference process (i.e. lastSon will + # possible candidates in the inference process (i.e. last will # be updated to store a type best conforming to all candidates) tyAnd, tyOr, tyNot @@ -471,10 +277,6 @@ static: const tyPureObject* = tyTuple GcTypeKinds* = {tyRef, tySequence, tyString} - tyError* = tyProxy # as an errornous node should match everything - tyUnknown* = tyFromExpr - - tyUnknownTypes* = {tyError, tyFromExpr} tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass, tyUserTypeClass, tyUserTypeClassInst, @@ -520,6 +322,9 @@ type nfFirstWrite # this node is a first write nfHasComment # node has a comment nfSkipFieldChecking # node skips field visable checking + nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot + # because openSym experimental switch is disabled + # gives warning instead TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47) @@ -552,7 +357,7 @@ type tfIterator, # type is really an iterator, not a tyProc tfPartial, # type is declared as 'partial' tfNotNil, # type cannot be 'nil' - tfRequiresInit, # type constains a "not nil" constraint somewhere or + tfRequiresInit, # type contains a "not nil" constraint somewhere or # a `requiresInit` field, so the default zero init # is not appropriate tfNeedsFullInit, # object type marked with {.requiresInit.} @@ -640,6 +445,8 @@ const tfGcSafe* = tfThread tfObjHasKids* = tfEnumHasHoles tfReturnsNew* = tfInheritable + tfNonConstExpr* = tfExplicitCallConv + ## tyFromExpr where the expression shouldn't be evaluated as a static value skError* = skUnknown var @@ -679,7 +486,6 @@ type mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, mCharToStr, mBoolToStr, - mIntToStr, mInt64ToStr, mFloatToStr, # for compiling nimStdlibVersion < 1.5.1 (not bootstrapping) mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, @@ -696,7 +502,7 @@ type mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mEnsureMove, mWasMoved, mDup, mDestroy, mTrace, - mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset, + mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, mIterableType, @@ -749,7 +555,6 @@ const mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, mCharToStr, mBoolToStr, - mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, @@ -779,10 +584,6 @@ proc hash*(x: ItemId): Hash = type - TIdObj* {.acyclic.} = object of RootObj - itemId*: ItemId - PIdObj* = ref TIdObj - PNode* = ref TNode TNodeSeq* = seq[PNode] PType* = ref TType @@ -851,7 +652,7 @@ type storage*: TStorageLoc flags*: TLocFlags # location's flags lode*: PNode # Node where the location came from; can be faked - r*: Rope # rope value of location (code generators) + snippet*: Rope # C code snippet of location (code generators) # ---------------- end of backend information ------------------------------ @@ -885,7 +686,8 @@ type PScope* = ref TScope PLib* = ref TLib - TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym + TSym* {.acyclic.} = object # Keep in sync with PackedSym + itemId*: ItemId # proc and type instantiations are cached in the generic symbol case kind*: TSymKind of routineKinds: @@ -954,11 +756,12 @@ type attachedTrace, attachedDeepCopy - TType* {.acyclic.} = object of TIdObj # \ + TType* {.acyclic.} = object # \ # types are identical iff they have the # same id; there may be multiple copies of a type # in memory! # Keep in sync with PackedType + itemId*: ItemId kind*: TTypeKind # kind of type callConv*: TCallingConvention # for procs flags*: TTypeFlags # flags of the type @@ -990,24 +793,6 @@ type TPairSeq* = seq[TPair] - TIdPair* = object - key*: PIdObj - val*: RootRef - - TIdPairSeq* = seq[TIdPair] - TIdTable* = object # the same as table[PIdent] of PObject - counter*: int - data*: TIdPairSeq - - TIdNodePair* = object - key*: PIdObj - val*: PNode - - TIdNodePairSeq* = seq[TIdNodePair] - TIdNodeTable* = object # the same as table[PIdObj] of PNode - counter*: int - data*: TIdNodePairSeq - TNodePair* = object h*: Hash # because it is expensive to compute! key*: PNode @@ -1095,7 +880,8 @@ const nfIsRef, nfIsPtr, nfPreventCg, nfLL, nfFromTemplate, nfDefaultRefsParam, nfExecuteOnReload, nfLastRead, - nfFirstWrite, nfSkipFieldChecking} + nfFirstWrite, nfSkipFieldChecking, + nfDisabledOpenSym} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 @@ -1108,10 +894,8 @@ const nfAllFieldsSet* = nfBase2 - nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, - nkCommand, nkCallStrLit, nkHiddenCallConv} nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice, - nkClosedSymChoice} + nkClosedSymChoice, nkOpenSym} nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit} nkLiterals* = {nkCharLit..nkTripleStrLit} @@ -1139,12 +923,13 @@ proc getPIdent*(a: PNode): PIdent {.inline.} = of nkSym: a.sym.name of nkIdent: a.ident of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name + of nkOpenSym: getPIdent(a.sons[0]) else: nil const moduleShift = when defined(cpu32): 20 else: 24 -template id*(a: PIdObj): int = +template id*(a: PType | PSym): int = let x = a (x.itemId.module.int shl moduleShift) + x.itemId.item.int @@ -1196,9 +981,7 @@ proc isCallExpr*(n: PNode): bool = proc discardSons*(father: PNode) -type Indexable = PNode | PType - -proc len*(n: Indexable): int {.inline.} = +proc len*(n: PNode): int {.inline.} = result = n.sons.len proc safeLen*(n: PNode): int {.inline.} = @@ -1212,18 +995,31 @@ proc safeArrLen*(n: PNode): int {.inline.} = elif n.kind in {nkNone..nkFloat128Lit}: result = 0 else: result = n.len -proc add*(father, son: Indexable) = +proc add*(father, son: PNode) = + assert son != nil + father.sons.add(son) + +proc addAllowNil*(father, son: PNode) {.inline.} = + father.sons.add(son) + +template `[]`*(n: PNode, i: int): PNode = n.sons[i] +template `[]=`*(n: PNode, i: int; x: PNode) = n.sons[i] = x + +template `[]`*(n: PNode, i: BackwardsIndex): PNode = n[n.len - i.int] +template `[]=`*(n: PNode, i: BackwardsIndex; x: PNode) = n[n.len - i.int] = x + +proc add*(father, son: PType) = assert son != nil father.sons.add(son) -proc addAllowNil*(father, son: Indexable) {.inline.} = +proc addAllowNil*(father, son: PType) {.inline.} = father.sons.add(son) -template `[]`*(n: Indexable, i: int): Indexable = n.sons[i] -template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x +template `[]`*(n: PType, i: int): PType = n.sons[i] +template `[]=`*(n: PType, i: int; x: PType) = n.sons[i] = x -template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int] -template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x +template `[]`*(n: PType, i: BackwardsIndex): PType = n[n.len - i.int] +template `[]=`*(n: PType, i: BackwardsIndex; x: PType) = n[n.len - i.int] = x proc getDeclPragma*(n: PNode): PNode = ## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found. @@ -1261,8 +1057,11 @@ proc getDeclPragma*(n: PNode): PNode = proc extractPragma*(s: PSym): PNode = ## gets the pragma node of routine/type/var/let/const symbol `s` - if s.kind in routineKinds: - result = s.ast[pragmasPos] + if s.kind in routineKinds: # bug #24167 + if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty: + result = s.ast[pragmasPos] + else: + result = nil elif s.kind in {skType, skVar, skLet, skConst}: if s.ast != nil and s.ast.len > 0: if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1: @@ -1335,6 +1134,33 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = result.info = info result.typ = typ +proc newNode*(kind: TNodeKind, info: TLineInfo): PNode = + ## new node with line info, no type, and no children + newNodeImpl(info) + setIdMaybe() + +proc newAtom*(ident: PIdent, info: TLineInfo): PNode = + result = newNode(nkIdent, info) + result.ident = ident + +proc newAtom*(kind: TNodeKind, intVal: BiggestInt, info: TLineInfo): PNode = + result = newNode(kind, info) + result.intVal = intVal + +proc newAtom*(kind: TNodeKind, floatVal: BiggestFloat, info: TLineInfo): PNode = + result = newNode(kind, info) + result.floatVal = floatVal + +proc newAtom*(kind: TNodeKind; strVal: sink string; info: TLineInfo): PNode = + result = newNode(kind, info) + result.strVal = strVal + +proc newTree*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode = + result = newNodeI(kind, info) + if children.len > 0: + result.info = children[0].info + result.sons = @children + proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode = result = newNode(kind) if children.len > 0: @@ -1354,7 +1180,7 @@ proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[ result.sons = @children template previouslyInferred*(t: PType): PType = - if t.sons.len > 1: t.lastSon else: nil + if t.sons.len > 1: t.last else: nil when false: import tables, strutils @@ -1432,11 +1258,6 @@ proc copyStrTable*(dest: var TStrTable, src: TStrTable) = setLen(dest.data, src.data.len) for i in 0..high(src.data): dest.data[i] = src.data[i] -proc copyIdTable*(dest: var TIdTable, src: TIdTable) = - dest.counter = src.counter - newSeq(dest.data, src.data.len) - for i in 0..high(src.data): dest.data[i] = src.data[i] - proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) = dest.counter = src.counter setLen(dest.data, src.data.len) @@ -1466,6 +1287,9 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode = result.typ = sym.typ result.info = info +proc newOpenSym*(n: PNode): PNode {.inline.} = + result = newTreeI(nkOpenSym, n.info, n) + proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = result = newNode(kind) result.intVal = intVal @@ -1474,7 +1298,42 @@ proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode = result = newNode(kind) result.intVal = castToInt64(intVal) -proc lastSon*(n: Indexable): Indexable = n.sons[^1] +proc lastSon*(n: PNode): PNode {.inline.} = n.sons[^1] +template setLastSon*(n: PNode, s: PNode) = n.sons[^1] = s + +template firstSon*(n: PNode): PNode = n.sons[0] +template secondSon*(n: PNode): PNode = n.sons[1] + +template hasSon*(n: PNode): bool = n.len > 0 +template has2Sons*(n: PNode): bool = n.len > 1 + +proc replaceFirstSon*(n, newson: PNode) {.inline.} = + n.sons[0] = newson + +proc replaceSon*(n: PNode; i: int; newson: PNode) {.inline.} = + n.sons[i] = newson + +proc last*(n: PType): PType {.inline.} = n.sons[^1] + +proc elementType*(n: PType): PType {.inline.} = n.sons[^1] +proc skipModifier*(n: PType): PType {.inline.} = n.sons[^1] + +proc indexType*(n: PType): PType {.inline.} = n.sons[0] +proc baseClass*(n: PType): PType {.inline.} = n.sons[0] + +proc base*(t: PType): PType {.inline.} = + result = t.sons[0] + +proc returnType*(n: PType): PType {.inline.} = n.sons[0] +proc setReturnType*(n, r: PType) {.inline.} = n.sons[0] = r +proc setIndexType*(n, idx: PType) {.inline.} = n.sons[0] = idx + +proc firstParamType*(n: PType): PType {.inline.} = n.sons[1] +proc firstGenericParam*(n: PType): PType {.inline.} = n.sons[1] + +proc typeBodyImpl*(n: PType): PType {.inline.} = n.sons[^1] + +proc genericHead*(n: PType): PType {.inline.} = n.sons[0] proc skipTypes*(t: PType, kinds: TTypeKinds): PType = ## Used throughout the compiler code to test whether a type tree contains or @@ -1482,7 +1341,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType = ## last child nodes of a type tree need to be searched. This is a really hot ## path within the compiler! result = t - while result.kind in kinds: result = lastSon(result) + while result.kind in kinds: result = last(result) proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode = let kind = skipTypes(typ, abstractVarRange).kind @@ -1541,42 +1400,140 @@ proc `$`*(s: PSym): string = else: result = "<nil>" -iterator items*(t: PType): PType = +when false: + iterator items*(t: PType): PType = + for i in 0..<t.sons.len: yield t.sons[i] + + iterator pairs*(n: PType): tuple[i: int, n: PType] = + for i in 0..<n.sons.len: yield (i, n.sons[i]) + +when true: + proc len*(n: PType): int {.inline.} = + result = n.sons.len + +proc sameTupleLengths*(a, b: PType): bool {.inline.} = + result = a.sons.len == b.sons.len + +iterator tupleTypePairs*(a, b: PType): (int, PType, PType) = + for i in 0 ..< a.sons.len: + yield (i, a.sons[i], b.sons[i]) + +iterator underspecifiedPairs*(a, b: PType; start = 0; without = 0): (PType, PType) = + # XXX Figure out with what typekinds this is called. + for i in start ..< min(a.sons.len, b.sons.len) + without: + yield (a.sons[i], b.sons[i]) + +proc signatureLen*(t: PType): int {.inline.} = + result = t.sons.len + +proc paramsLen*(t: PType): int {.inline.} = + result = t.sons.len - 1 + +proc genericParamsLen*(t: PType): int {.inline.} = + assert t.kind == tyGenericInst + result = t.sons.len - 2 # without 'head' and 'body' + +proc genericInvocationParamsLen*(t: PType): int {.inline.} = + assert t.kind == tyGenericInvocation + result = t.sons.len - 1 # without 'head' + +proc kidsLen*(t: PType): int {.inline.} = + result = t.sons.len + +proc genericParamHasConstraints*(t: PType): bool {.inline.} = t.sons.len > 0 + +proc hasElementType*(t: PType): bool {.inline.} = t.sons.len > 0 +proc isEmptyTupleType*(t: PType): bool {.inline.} = t.sons.len == 0 +proc isSingletonTupleType*(t: PType): bool {.inline.} = t.sons.len == 1 + +proc genericConstraint*(t: PType): PType {.inline.} = t.sons[0] + +iterator genericInstParams*(t: PType): (bool, PType) = + for i in 1..<t.sons.len-1: + yield (i!=1, t.sons[i]) + +iterator genericInstParamPairs*(a, b: PType): (int, PType, PType) = + for i in 1..<min(a.sons.len, b.sons.len)-1: + yield (i-1, a.sons[i], b.sons[i]) + +iterator genericInvocationParams*(t: PType): (bool, PType) = + for i in 1..<t.sons.len: + yield (i!=1, t.sons[i]) + +iterator genericInvocationAndBodyElements*(a, b: PType): (PType, PType) = + for i in 1..<a.sons.len: + yield (a.sons[i], b.sons[i-1]) + +iterator genericInvocationParamPairs*(a, b: PType): (bool, PType, PType) = + for i in 1..<a.sons.len: + if i >= b.sons.len: + yield (false, nil, nil) + else: + yield (true, a.sons[i], b.sons[i]) + +iterator genericBodyParams*(t: PType): (int, PType) = + for i in 0..<t.sons.len-1: + yield (i, t.sons[i]) + +iterator userTypeClassInstParams*(t: PType): (bool, PType) = + for i in 1..<t.sons.len-1: + yield (i!=1, t.sons[i]) + +iterator ikids*(t: PType): (int, PType) = + for i in 0..<t.sons.len: yield (i, t.sons[i]) + +const + FirstParamAt* = 1 + FirstGenericParamAt* = 1 + +iterator paramTypes*(t: PType): (int, PType) = + for i in FirstParamAt..<t.sons.len: yield (i, t.sons[i]) + +iterator paramTypePairs*(a, b: PType): (PType, PType) = + for i in FirstParamAt..<a.sons.len: yield (a.sons[i], b.sons[i]) + +template paramTypeToNodeIndex*(x: int): int = x + +iterator kids*(t: PType): PType = for i in 0..<t.sons.len: yield t.sons[i] -iterator pairs*(n: PType): tuple[i: int, n: PType] = - for i in 0..<n.sons.len: yield (i, n.sons[i]) +iterator signature*(t: PType): PType = + # yields return type + parameter types + for i in 0..<t.sons.len: yield t.sons[i] -proc newType*(kind: TTypeKind, idgen: IdGenerator; owner: PSym, sons: seq[PType] = @[]): PType = +proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType = let id = nextTypeId idgen result = PType(kind: kind, owner: owner, size: defaultSize, align: defaultAlignment, itemId: id, - uniqueId: id, sons: sons) + uniqueId: id, sons: @[]) + if son != nil: result.sons.add son when false: if result.itemId.module == 55 and result.itemId.item == 2: echo "KNID ", kind writeStackTrace() -template newType*(kind: TTypeKind, id: IdGenerator; owner: PSym, parent: PType): PType = - newType(kind, id, owner, parent.sons) - -proc setSons*(dest: PType; sons: seq[PType]) {.inline.} = dest.sons = sons - -when false: - proc newType*(prev: PType, sons: seq[PType]): PType = - result = prev - result.sons = sons +proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons +proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son] +proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len) proc mergeLoc(a: var TLoc, b: TLoc) = if a.k == low(typeof(a.k)): a.k = b.k if a.storage == low(typeof(a.storage)): a.storage = b.storage a.flags.incl b.flags if a.lode == nil: a.lode = b.lode - if a.r == "": a.r = b.r + if a.snippet == "": a.snippet = b.snippet + +proc newSons*(father: PNode, length: int) = + setLen(father.sons, length) -proc newSons*(father: Indexable, length: int) = +proc newSons*(father: PType, length: int) = setLen(father.sons, length) +proc truncateInferredTypeCandidates*(t: PType) {.inline.} = + assert t.kind == tyInferred + if t.sons.len > 1: + setLen(t.sons, 1) + proc assignType*(dest, src: PType) = dest.kind = src.kind dest.flags = src.flags @@ -1592,8 +1549,8 @@ proc assignType*(dest, src: PType) = mergeLoc(dest.sym.loc, src.sym.loc) else: dest.sym = src.sym - newSons(dest, src.len) - for i in 0..<src.len: dest[i] = src[i] + newSons(dest, src.sons.len) + for i in 0..<src.sons.len: dest[i] = src[i] proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType = result = newType(t.kind, idgen, owner) @@ -1639,24 +1596,10 @@ proc initStrTable*(): TStrTable = result = TStrTable(counter: 0) newSeq(result.data, StartSize) -proc initIdTable*(): TIdTable = - result = TIdTable(counter: 0) - newSeq(result.data, StartSize) - -proc resetIdTable*(x: var TIdTable) = - x.counter = 0 - # clear and set to old initial size: - setLen(x.data, 0) - setLen(x.data, StartSize) - proc initObjectSet*(): TObjectSet = result = TObjectSet(counter: 0) newSeq(result.data, StartSize) -proc initIdNodeTable*(): TIdNodeTable = - result = TIdNodeTable(counter: 0) - newSeq(result.data, StartSize) - proc initNodeTable*(): TNodeTable = result = TNodeTable(counter: 0) newSeq(result.data, StartSize) @@ -1665,7 +1608,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType = result = t var i = maxIters while result.kind in kinds: - result = lastSon(result) + result = last(result) dec i if i == 0: return nil @@ -1673,8 +1616,8 @@ proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType = ## same as skipTypes but handles 'nil' result = t while result != nil and result.kind in kinds: - if result.len == 0: return nil - result = lastSon(result) + if result.sons.len == 0: return nil + result = last(result) proc isGCedMem*(t: PType): bool {.inline.} = result = t.kind in {tyString, tyRef, tySequence} or @@ -1941,13 +1884,15 @@ proc skipGenericOwner*(s: PSym): PSym = ## Generic instantiations are owned by their originating generic ## symbol. This proc skips such owners and goes straight to the owner ## of the generic itself (the module or the enclosing proc). - result = if s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule: + result = if s.kind == skModule: + s + elif s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule: s.owner.owner else: s.owner proc originatingModule*(s: PSym): PSym = - result = s.owner + result = s while result.kind != skModule: result = result.owner proc isRoutine*(s: PSym): bool {.inline.} = @@ -1993,23 +1938,21 @@ proc toVar*(typ: PType; kind: TTypeKind; idgen: IdGenerator): PType = ## returned. Otherwise ``typ`` is simply returned as-is. result = typ if typ.kind != kind: - result = newType(kind, idgen, typ.owner) - rawAddSon(result, typ) + result = newType(kind, idgen, typ.owner, typ) proc toRef*(typ: PType; idgen: IdGenerator): PType = ## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and ## returned. Otherwise ``typ`` is simply returned as-is. result = typ if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject: - result = newType(tyRef, idgen, typ.owner) - rawAddSon(result, typ) + result = newType(tyRef, idgen, typ.owner, typ) proc toObject*(typ: PType): PType = ## If ``typ`` is a tyRef then its immediate son is returned (which in many ## cases should be a ``tyObject``). ## Otherwise ``typ`` is simply returned as-is. let t = typ.skipTypes({tyAlias, tyGenericInst}) - if t.kind == tyRef: t.lastSon + if t.kind == tyRef: t.elementType else: typ proc toObjectFromRefPtrGeneric*(typ: PType): PType = @@ -2026,7 +1969,7 @@ proc toObjectFromRefPtrGeneric*(typ: PType): PType = result = typ while true: case result.kind - of tyGenericBody: result = result.lastSon + of tyGenericBody: result = result.last of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0] # automatic dereferencing is deep, refs #18298. else: break @@ -2039,11 +1982,7 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool = return false let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst}) - - if base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}: - result = true - else: - result = false + result = base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {} proc isInfixAs*(n: PNode): bool = return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == ord(wAs) @@ -2058,7 +1997,7 @@ proc findUnresolvedStatic*(n: PNode): PNode = return n if n.typ != nil and n.typ.kind == tyTypeDesc: let t = skipTypes(n.typ, {tyTypeDesc}) - if t.kind == tyGenericParam and t.len == 0: + if t.kind == tyGenericParam and not t.genericParamHasConstraints: return n for son in n: let n = son.findUnresolvedStatic @@ -2119,7 +2058,7 @@ proc newProcType*(info: TLineInfo; idgen: IdGenerator; owner: PSym): PType = result.n.add newNodeI(nkEffectList, info) proc addParam*(procType: PType; param: PSym) = - param.position = procType.len-1 + param.position = procType.sons.len-1 procType.n.add newSymNode(param) rawAddSon(procType, param.typ) @@ -2181,3 +2120,16 @@ const proc isTrue*(n: PNode): bool = n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or n.kind == nkIntLit and n.intVal != 0 + +type + TypeMapping* = Table[ItemId, PType] + SymMapping* = Table[ItemId, PSym] + +template idTableGet*(tab: typed; key: PSym | PType): untyped = tab.getOrDefault(key.itemId) +template idTablePut*(tab: typed; key, val: PSym | PType) = tab[key.itemId] = val + +template initSymMapping*(): Table[ItemId, PSym] = initTable[ItemId, PSym]() +template initTypeMapping*(): Table[ItemId, PType] = initTable[ItemId, PType]() + +template resetIdTable*(tab: Table[ItemId, PSym]) = tab.clear() +template resetIdTable*(tab: Table[ItemId, PType]) = tab.clear() diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index cd772f0f1..7a9892f78 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -12,24 +12,18 @@ # the data structures here are used in various places of the compiler. import - ast, options, lineinfos, ropes, idents, rodutils, + ast, astyaml, options, lineinfos, 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: @@ -37,15 +31,6 @@ 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) @@ -80,16 +65,6 @@ 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 @@ -245,170 +220,6 @@ 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" @@ -573,14 +384,12 @@ proc value(this: var DebugPrinter; value: PType) = this.key "n" this.value value.n - 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 + this.key "sons" + this.openBracket + for i, a in value.ikids: + if i > 0: this.comma + this.value a + this.closeBracket if value.n != nil: this.key "n" @@ -649,30 +458,33 @@ proc value(this: var DebugPrinter; value: PNode) = proc debug(n: PSym; conf: ConfigRef) = - var this: DebugPrinter - this.visited = initTable[pointer, int]() - this.renderSymType = true - this.useColor = not defined(windows) + var this = DebugPrinter( + visited: initTable[pointer, int](), + renderSymType: true, + useColor: not defined(windows) + ) this.value(n) echo($this.res) proc debug(n: PType; conf: ConfigRef) = - var this: DebugPrinter - this.visited = initTable[pointer, int]() - this.renderSymType = true - this.useColor = not defined(windows) + var this = DebugPrinter( + visited: initTable[pointer, int](), + renderSymType: true, + useColor: not defined(windows) + ) this.value(n) echo($this.res) proc debug(n: PNode; conf: ConfigRef) = - var this: DebugPrinter - this.visited = initTable[pointer, int]() - #this.renderSymType = true - this.useColor = not defined(windows) + var this = DebugPrinter( + visited: initTable[pointer, int](), + renderSymType: false, + useColor: not defined(windows) + ) this.value(n) echo($this.res) -proc nextTry(h, maxHash: Hash): Hash = +proc nextTry(h, maxHash: Hash): Hash {.inline.} = 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 @@ -895,125 +707,12 @@ proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym = result = nextIter(ti, tab) iterator items*(tab: TStrTable): PSym = - var it: TTabIter + var it: TTabIter = default(TTabIter) var s = initTabIter(it, tab) while s != nil: yield s s = nextIter(it, tab) -proc hasEmptySlot(data: TIdPairSeq): bool = - for h in 0..high(data): - if data[h].key == nil: - return true - result = false - -proc idTableRawGet(t: TIdTable, key: int): int = - var h: Hash - h = key and high(t.data) # start with real hash value - while t.data[h].key != nil: - if t.data[h].key.id == key: - return h - h = nextTry(h, high(t.data)) - result = - 1 - -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 = nil - -proc idTableGet(t: TIdTable, key: int): RootRef = - var index = idTableRawGet(t, key) - if index >= 0: result = t.data[index].val - else: result = nil - -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 - 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 idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) = - var - index: int - n: TIdPairSeq - index = idTableRawGet(t, key.id) - if index >= 0: - 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 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) - -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) - -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 newSeq(x.data, StartSize) @@ -1059,14 +758,6 @@ proc iiTablePut(t: var TIITable, key, val: int) = iiTableRawInsert(t.data, key, val) inc(t.counter) -proc isAddrNode*(n: PNode): bool = - case n.kind - of nkAddr, nkHiddenAddr: true - of nkCallKinds: - if n[0].kind == nkSym and n[0].sym.magic == mAddr: true - else: false - else: false - proc listSymbolNames*(symbols: openArray[PSym]): string = result = "" for sym in symbols: diff --git a/compiler/astmsgs.nim b/compiler/astmsgs.nim index a9027126a..aeeff1fd0 100644 --- a/compiler/astmsgs.nim +++ b/compiler/astmsgs.nim @@ -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[0].sym + result = t.genericHead.sym proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) = result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)] @@ -24,6 +24,12 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) = result.add " declared in " & toFileLineCol(conf, typ.sym.info) result.add "]" +proc addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) = + result.add " [$1" % typ.kind.toHumanStr + if typ.sym != nil: + result.add " declared in " & toFileLineCol(conf, typ.sym.info) + result.add "]" + proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) = if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ) diff --git a/compiler/astyaml.nim b/compiler/astyaml.nim new file mode 100644 index 000000000..b0fa2bfb2 --- /dev/null +++ b/compiler/astyaml.nim @@ -0,0 +1,154 @@ +# +# +# 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; indent, maxRecDepth: int) +proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; indent, maxRecDepth: int) +proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; indent, maxRecDepth: int) + +proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; 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) + + 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, 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, 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: $2", [istr]) + res.treeToYamlAux(conf, n.loc.lode, marker, indent + 1, maxRecDepth - 1) + +proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; 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) + res.addf("kind: $2", [istr, makeYamlString($n.kind)]) + res.addf("\n$1sym: ") + res.symToYamlAux(conf, n.sym, marker, indent + 1, maxRecDepth - 1) + res.addf("\n$1n: ") + res.treeToYamlAux(conf, n.n, marker, indent + 1, maxRecDepth - 1) + if card(n.flags) > 0: + res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)]) + res.addf("\n$1callconv: $2", [istr, makeYamlString($n.callConv)]) + res.addf("\n$1size: $2", [istr, $(n.size)]) + res.addf("\n$1align: $2", [istr, $(n.align)]) + if n.hasElementType: + res.addf("\n$1sons:") + for a in n.kids: + res.addf("\n - ") + res.typeToYamlAux(conf, a, marker, indent + 1, maxRecDepth - 1) + +proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; indent: int; + maxRecDepth: int) = + if n == nil: + res.add("null") + else: + var istr = spaces(indent * 4) + 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 .. nkInt64Lit: + res.addf("\n$1intVal: $2", [istr, $(n.intVal)]) + of nkFloatLit, nkFloat32Lit, nkFloat64Lit: + res.addf("\n$1floatVal: $2", [istr, n.floatVal.toStrMaxPrecision]) + of nkStrLit .. nkTripleStrLit: + res.addf("\n$1strVal: $2", [istr, makeYamlString(n.strVal)]) + of nkSym: + res.addf("\n$1sym: ", [istr]) + res.symToYamlAux(conf, n.sym, marker, 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, indent + 1, maxRecDepth - 1) + if n.typ != nil: + res.addf("\n$1typ: ", [istr]) + res.typeToYamlAux(conf, n.typ, marker, indent + 1, maxRecDepth) + +proc treeToYaml*(conf: ConfigRef; n: PNode; indent: int = 0; maxRecDepth: int = -1): string = + var marker = initIntSet() + result = newStringOfCap(1024) + result.treeToYamlAux(conf, n, marker, 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, 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, indent, maxRecDepth) diff --git a/compiler/backendpragmas.nim b/compiler/backendpragmas.nim deleted file mode 100644 index b18644810..000000000 --- a/compiler/backendpragmas.nim +++ /dev/null @@ -1,25 +0,0 @@ -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) diff --git a/compiler/cbuilder.nim b/compiler/cbuilder.nim new file mode 100644 index 000000000..bb1bdfe27 --- /dev/null +++ b/compiler/cbuilder.nim @@ -0,0 +1,174 @@ +type + Snippet = string + Builder = string + +template newBuilder(s: string): Builder = + s + +proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") = + obj.add('\t') + obj.add(typ) + obj.add(" ") + obj.add(name) + if isFlexArray: + obj.add("[SEQ_DECL_SIZE]") + if initializer.len != 0: + obj.add(initializer) + obj.add(";\n") + +proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") = + ## for fields based on an `skField` symbol + 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") + +type + BaseClassKind = enum + bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti + StructBuilderInfo = object + baseKind: BaseClassKind + preFieldsLen: int + +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 & "*" + +proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo = + result = StructBuilderInfo(baseKind: bcNone) + obj.add("struct ") + obj.add(name) + if baseType.len != 0: + if m.compileToCpp: + result.baseKind = bcCppInherit + else: + result.baseKind = bcSupField + if result.baseKind == bcCppInherit: + obj.add(" : public ") + obj.add(baseType) + obj.add(" ") + obj.add("{\n") + result.preFieldsLen = obj.len + if result.baseKind == bcSupField: + obj.addField(name = "Sup", typ = baseType) + +proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) = + if info.baseKind == bcNone and info.preFieldsLen == obj.len: + # no fields were added, add dummy field + obj.addField(name = "dummy", typ = "char") + obj.add("};\n") + +template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) = + ## for independent structs, not directly based on a Nim type + let info = startSimpleStruct(obj, m, name, baseType) + body + finishSimpleStruct(obj, m, info) + +proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo = + result = StructBuilderInfo(baseKind: bcNone) + if tfPacked in t.flags: + if hasAttribute in CC[m.config.cCompiler].props: + obj.add(structOrUnion(t)) + obj.add(" __attribute__((__packed__))") + else: + obj.add("#pragma pack(push, 1)\n") + obj.add(structOrUnion(t)) + else: + obj.add(structOrUnion(t)) + obj.add(" ") + obj.add(name) + if t.kind == tyObject: + if t.baseClass == nil: + if lacksMTypeField(t): + result.baseKind = bcNone + elif optTinyRtti in m.config.globalOptions: + result.baseKind = bcNoneTinyRtti + else: + result.baseKind = bcNoneRtti + elif m.compileToCpp: + result.baseKind = bcCppInherit + else: + result.baseKind = bcSupField + elif baseType.len != 0: + if m.compileToCpp: + result.baseKind = bcCppInherit + else: + result.baseKind = bcSupField + if result.baseKind == bcCppInherit: + obj.add(" : public ") + obj.add(baseType) + obj.add(" ") + obj.add("{\n") + result.preFieldsLen = obj.len + case result.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 t.itemId notin m.g.graph.memberProcsPerType and + t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and + t.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) + +proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) = + if info.baseKind == bcNone and info.preFieldsLen == obj.len and + t.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 t.flags and hasAttribute notin CC[m.config.cCompiler].props: + obj.add("#pragma pack(pop)\n") + +template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) = + ## for structs built directly from a Nim type + let info = startStruct(obj, m, typ, name, baseType) + body + finishStruct(obj, m, typ, info) + +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") diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index ad84be3f9..ac607e3ad 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -76,6 +76,23 @@ 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]) @@ -83,13 +100,17 @@ 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[0] != nil: + 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 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[0], needsInit=true) + if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true) elif d.k notin {locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: discard "resetLoc(p, d)" @@ -97,7 +118,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[0], needsInit=true) + var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true) pl.add(addrLoc(p.config, tmp)) pl.add(");\n") line(p, cpsStmts, pl) @@ -111,43 +132,56 @@ 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.r = pl + d.snippet = pl excl d.flags, lfSingleUse else: if d.k == locNone and p.splitDecls == 0: - d = getTempCpp(p, typ[0], pl) + d = getTempCpp(p, typ.returnType, pl) else: - if d.k == locNone: d = getTemp(p, typ[0]) + if d.k == locNone: d = getTemp(p, typ.returnType) var list = initLoc(locCall, d.lode, OnUnknown) - list.r = pl - genAssignment(p, d, list, {}) # no need for deep copying + list.snippet = pl + genAssignment(p, d, list, {needAssignCall}) # no need for deep copying if canRaise: raiseExit(p) elif isHarmlessStore(p, canRaise, d): - if d.k == locNone: d = getTemp(p, typ[0]) + var useTemp = false + if d.k == locNone: + useTemp = true + d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list = initLoc(locCall, d.lode, OnUnknown) - list.r = pl - genAssignment(p, d, list, {}) # no need for deep copying - if canRaise: raiseExit(p) + 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) else: - var tmp: TLoc = getTemp(p, typ[0], needsInit=true) + var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true) var list = initLoc(locCall, d.lode, OnUnknown) - list.r = pl - genAssignment(p, tmp, list, {}) # no need for deep copying - if canRaise: raiseExit(p) + 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) genAssignment(p, d, tmp, {}) else: pl.add(");\n") line(p, cpsStmts, pl) if canRaise: raiseExit(p) -proc genBoundsCheck(p: BProc; arr, a, b: TLoc) +proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) proc reifiedOpenArray(n: PNode): bool {.inline.} = var x = n - while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}: - x = x[0] + while true: + case x.kind + of {nkAddr, nkHiddenAddr, nkHiddenDeref}: + x = x[0] + of nkHiddenStdConv: + x = x[1] + else: + break if x.kind == nkSym and x.sym.kind == skParam: result = false else: @@ -157,12 +191,15 @@ 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) + genBoundsCheck(p, a, b, c, ty) if prepareForMutation: linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) - let ty = skipTypes(a.t, abstractVar+{tyPtr}) let dest = getTypeDesc(p.module, destType) let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)] case ty.kind @@ -218,7 +255,7 @@ 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[0]) + let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType) result.add x & ", " & y else: var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n) @@ -237,8 +274,7 @@ 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 - t.r = "(*$1)" % [a.rdLoc] + var t = TLoc(snippet: "(*$1)" % [a.rdLoc]) result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" % [a.rdLoc, lenExpr(p, t), dataField(p), dataFieldAccessor(p, "*" & a.rdLoc)] @@ -248,15 +284,14 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) = of tyArray: result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))] of tyPtr, tyRef: - case lastSon(a.t).kind + case elementType(a.t).kind of tyString, tySequence: - var t: TLoc - t.r = "(*$1)" % [a.rdLoc] + var t = TLoc(snippet: "(*$1)" % [a.rdLoc]) result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" % [a.rdLoc, lenExpr(p, t), dataField(p), dataFieldAccessor(p, "*" & a.rdLoc)] of tyArray: - result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))] + result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, elementType(a.t)))] else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) @@ -287,7 +322,7 @@ 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[0]) and + elif ccgIntroducedPtr(p.config, param, call[0].typ.returnType) and (optByRef notin param.options or not p.module.compileToCpp): a = initLocExpr(p, n) if n.kind in {nkCharLit..nkNilLit}: @@ -296,18 +331,33 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result) elif p.module.compileToCpp and param.typ.kind in {tyVar} and n.kind == nkHiddenAddr: - a = initLocExprSingleUse(p, n[0]) + # 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 # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still # means '*T'. See posix.nim for lots of examples that do that in the wild. let callee = call[0] if callee.kind == nkSym and {sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and - {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}: + {lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and + needsIndirect: addAddrLoc(p.config, a, result) else: addRdLoc(a, result) else: 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 @@ -352,7 +402,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, mReset: + mAddr, mNew, mNewFinalize, mWasMoved, mDestroy: getPotentialWrites(n[1], true, result) for i in 2..<n.len: getPotentialWrites(n[i], mutate, result) @@ -390,13 +440,15 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) = if not needTmp[i - 1]: needTmp[i - 1] = potentialAlias(n, potentialWrites) getPotentialWrites(ri[i], false, potentialWrites) - if ri[i].kind in {nkHiddenAddr, nkAddr}: - # Optimization: don't use a temp, if we would only take the address anyway - needTmp[i - 1] = false + when false: + # this optimization is wrong, see bug #23748 + if ri[i].kind in {nkHiddenAddr, nkAddr}: + # Optimization: don't use a temp, if we would only take the address anyway + needTmp[i - 1] = false var oldLen = result.len for i in 1..<ri.len: - if i < typ.len: + if i < typ.n.len: assert(typ.n[i].kind == nkSym) let paramType = typ.n[i] if not paramType.typ.isCompileTimeOnly: @@ -421,7 +473,6 @@ 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) @@ -444,7 +495,6 @@ 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) @@ -457,14 +507,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[0] != nil: + if typ.returnType != 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[0], needsInit=true) + d = getTemp(p, typ.returnType, needsInit=true) elif d.k notin {locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: discard "resetLoc(p, d)" @@ -472,29 +522,29 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = genCallPattern() if canRaise: raiseExit(p) else: - var tmp: TLoc = getTemp(p, typ[0], needsInit=true) + var tmp: TLoc = getTemp(p, typ.returnType, 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[0]) + if d.k == locNone: d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) if tfIterator in typ.flags: - list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] + list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] else: - list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc] + list.snippet = 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[0]) + var tmp: TLoc = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) if tfIterator in typ.flags: - list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] + list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] else: - list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc] + list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc] genAssignment(p, tmp, list, {}) if canRaise: raiseExit(p) genAssignment(p, d, tmp, {}) @@ -504,14 +554,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.len: + if i < typ.n.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 typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr: + elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr: if argsCounter > 0: result.add ", " genArgNoParam(p, ri[i][0], result) inc argsCounter @@ -586,7 +636,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.len + internalAssert p.config, i < typ.n.len assert(typ.n[i].kind == nkSym) # if the parameter is lying (tyVar) and thus we required an additional deref, # skip the deref: @@ -676,28 +726,27 @@ 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.r + let pat = $ri[0].sym.loc.snippet 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[0] != nil: + if typ.returnType != 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.r = pl + d.snippet = pl excl d.flags, lfSingleUse else: - if d.k == locNone: d = getTemp(p, typ[0]) + if d.k == locNone: d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) - list.r = pl + list.snippet = pl genAssignment(p, d, list, {}) # no need for deep copying else: pl.add(";\n") @@ -707,10 +756,9 @@ 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.r) + pl.add(op.snippet) 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) @@ -721,15 +769,14 @@ 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.r + let pat = $ri[0].sym.loc.snippet internalAssert p.config, pat.len > 0 var start = 3 if ' ' in pat: start = 1 - pl.add(op.r) + pl.add(op.snippet) if ri.len > 1: pl.add(": ") genArg(p, ri[1], typ.n[1].sym, ri, pl) @@ -738,13 +785,12 @@ 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.r) + pl.add(op.snippet) if ri.len > 2: pl.add(": ") genArg(p, ri[2], typ.n[2].sym, ri, pl) for i in start..<ri.len: - assert(typ.len == typ.n.len) - if i >= typ.len: + if i >= typ.n.len: internalError(p.config, ri.info, "varargs for objective C method?") assert(typ.n[i].kind == nkSym) var param = typ.n[i].sym @@ -752,29 +798,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[0] != nil: + if typ.returnType != 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[0], needsInit=true) + if d.k == locNone: d = getTemp(p, typ.returnType, 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[0], needsInit=true) + var tmp: TLoc = getTemp(p, typ.returnType, 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[0]) + if d.k == locNone: d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, ri, OnUnknown) - list.r = pl + list.snippet = pl genAssignment(p, d, list, {}) # no need for deep copying else: pl.add("];\n") diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 2612c5c12..545d43ae8 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -221,10 +221,11 @@ proc asgnComplexity(n: PNode): int = proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = assert field != "" - result.k = locField - result.storage = a.storage - result.lode = lodeTyp t - result.r = rdLoc(a) & "." & field + result = TLoc(k: locField, + storage: a.storage, + lode: lodeTyp t, + snippet: rdLoc(a) & "." & field + ) proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let newflags = @@ -235,8 +236,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: flags let t = skipTypes(dest.t, abstractInst).getUniqueType() - for i in 0..<t.len: - let t = t[i] + for i, t in t.ikids: let field = "Field$1" % [i.rope] genAssignment(p, optAsgnLoc(dest, t, field), optAsgnLoc(src, t, field), newflags) @@ -254,9 +254,9 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, case t.kind of nkSym: let field = t.sym - if field.loc.r == "": fillObjectFields(p.module, typ) - genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r), - optAsgnLoc(src, field.typ, field.loc.r), newflags) + if field.loc.snippet == "": fillObjectFields(p.module, typ) + genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.snippet), + optAsgnLoc(src, field.typ, field.loc.snippet), newflags) of nkRecList: for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ) else: discard @@ -285,15 +285,22 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfoV1(p.module, dest.t, dest.lode.info)]) -proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) = +proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) = assert d.k != locNone # getTemp(p, d.t, d) case a.t.skipTypes(abstractVar).kind of tyOpenArray, tyVarargs: if reifiedOpenArray(a.lode): - linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n", - [rdLoc(d), a.rdLoc]) + if needTempForOpenArray in flags: + var tmp: TLoc = getTemp(p, a.t) + linefmt(p, cpsStmts, "$2 = $1; $n", + [a.rdLoc, tmp.rdLoc]) + linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n", + [rdLoc(d), tmp.rdLoc]) + else: + linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n", + [rdLoc(d), a.rdLoc]) else: linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $2Len_0;$n", [rdLoc(d), a.rdLoc]) @@ -336,7 +343,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyString: if optSeqDestructors in p.config.globalOptions: genGenericAsgn(p, dest, src, flags) - elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest): + elif ({needToCopy, needToCopySinkParam} * flags == {} and src.storage != OnStatic) or canMove(p, src.lode, dest): genRefAssign(p, dest, src) else: if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): @@ -361,7 +368,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) of tyTuple: if containsGarbageCollectedRef(dest.t): - if dest.t.len <= 4: genOptAsgnTuple(p, dest, src, flags) + if dest.t.kidsLen <= 4: genOptAsgnTuple(p, dest, src, flags) else: genGenericAsgn(p, dest, src, flags) else: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) @@ -372,7 +379,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = elif not isObjLackingTypeField(ty): genGenericAsgn(p, dest, src, flags) elif containsGarbageCollectedRef(ty): - if ty[0].isNil and asgnComplexity(ty.n) <= 4: + if ty[0].isNil and asgnComplexity(ty.n) <= 4 and + needAssignCall notin flags: # calls might contain side effects discard getTypeDesc(p.module, ty) internalAssert p.config, ty.n != nil genOptAsgnObject(p, dest, src, flags, ty.n, ty) @@ -391,7 +399,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # open arrays are always on the stack - really? What if a sequence is # passed to an open array? if reifiedOpenArray(dest.lode): - genOpenArrayConv(p, dest, src) + genOpenArrayConv(p, dest, src, flags) elif containsGarbageCollectedRef(dest.t): linefmt(p, cpsStmts, # XXX: is this correct for arrays? "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", @@ -475,7 +483,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = if d.k != locNone: var a: TLoc = initLoc(locData, n, OnStatic) # need to generate an assignment here - a.r = r + a.snippet = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) else: @@ -483,13 +491,13 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = # the flags field! d.k = locData d.lode = n - d.r = r + d.snippet = r proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = if d.k != locNone: # need to generate an assignment here var a: TLoc = initLoc(locExpr, n, s) - a.r = r + a.snippet = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) else: @@ -497,7 +505,7 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = # the flags field! d.k = locExpr d.lode = n - d.r = r + d.snippet = r proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) = if d.k != locNone: internalError(p.config, e.info, "binaryStmt") @@ -582,7 +590,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # skipping 'range' is correct here as we'll generate a proper range check # later via 'chckRange' let t = e.typ.skipTypes(abstractRange) - if optOverflowCheck notin p.options: + if optOverflowCheck notin p.options or (m in {mSucc, mPred} and t.kind in {tyUInt..tyUInt64}): let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)] putIntoDest(p, d, e, res) else: @@ -604,7 +612,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if t.kind == tyInt64: prc64[m] else: prc[m]) putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) else: - let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)] + let res = "($1)(($2) $3 ($4))" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)] putIntoDest(p, d, e, res) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = @@ -748,7 +756,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = var a: TLoc var typ = e[0].typ if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass: - typ = typ.lastSon + typ = typ.last typ = typ.skipTypes(abstractInstOwned) if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr: d = initLocExprSingleUse(p, e[0][0]) @@ -801,10 +809,12 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}: var a: TLoc = initLocExpr(p, e[0]) - putIntoDest(p, d, e, "&" & a.r, a.storage) + putIntoDest(p, d, e, "&" & a.snippet, a.storage) #Message(e.info, warnUser, "HERE NEW &") elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ): expr(p, e[0], d) + # bug #19497 + d.lode = e else: var a: TLoc = initLocExpr(p, e[0]) putIntoDest(p, d, e, addrLoc(p.config, a), a.storage) @@ -853,7 +863,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = var a: TLoc = default(TLoc) if p.module.compileToCpp and e.kind == nkDotExpr and e[1].kind == nkSym and e[1].typ.kind == tyPtr: # special case for C++: we need to pull the type of the field as member and friends require the complete type. - let typ = e[1].typ[0] + let typ = e[1].typ.elementType if typ.itemId in p.module.g.graph.memberProcsPerType: discard getTypeDesc(p.module, typ) @@ -870,10 +880,10 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = else: var rtyp: PType = nil let field = lookupFieldAgain(p, ty, f, r, addr rtyp) - if field.loc.r == "" and rtyp != nil: fillObjectFields(p.module, rtyp) - if field.loc.r == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty)) + if field.loc.snippet == "" and rtyp != nil: fillObjectFields(p.module, rtyp) + if field.loc.snippet == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty)) r.add "." - r.add field.loc.r + r.add field.loc.snippet putIntoDest(p, d, e, r, a.storage) r.freeze @@ -892,10 +902,10 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = test = initLoc(locNone, it, OnStack) u = initLocExpr(p, it[1]) v = initLoc(locExpr, disc, OnUnknown) - v.r = newRopeAppender() - v.r.add obj - v.r.add(".") - v.r.add(disc.sym.loc.r) + v.snippet = newRopeAppender() + v.snippet.add obj + v.snippet.add(".") + v.snippet.add(disc.sym.loc.snippet) genInExprAux(p, it, u, v, test) var msg = "" if optDeclaredLocs in p.config.globalOptions: @@ -953,12 +963,12 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = var r = rdLoc(a) let f = e[0][1].sym let field = lookupFieldAgain(p, ty, f, r) - if field.loc.r == "": fillObjectFields(p.module, ty) - if field.loc.r == "": + if field.loc.snippet == "": fillObjectFields(p.module, ty) + if field.loc.snippet == "": internalError(p.config, e.info, "genCheckedRecordField") # generate the checks: genFieldCheck(p, e, r, field) r.add(".") - r.add field.loc.r + r.add field.loc.snippet putIntoDest(p, d, e[0], r, a.storage) r.freeze else: @@ -1012,8 +1022,8 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) -proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = - let ty = skipTypes(arr.t, abstractVarRange) +proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) = + let ty = arrTyp case ty.kind of tyOpenArray, tyVarargs: if reifiedOpenArray(arr.lode): @@ -1082,7 +1092,8 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = var b = initLocExpr(p, y) var ty = skipTypes(a.t, abstractVarRange) if ty.kind in {tyRef, tyPtr}: - ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: + ty = skipTypes(ty.elementType, abstractVarRange) + # emit range check: if optBoundsCheck in p.options: linefmt(p, cpsStmts, "if ($1 < 0 || $1 >= $2){ #raiseIndexError2($1,$2-1); ", @@ -1092,7 +1103,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: - a.r = ropecg(p.module, "(*$1)", [a.r]) + a.snippet = ropecg(p.module, "(*$1)", [a.snippet]) if lfPrepareForMutation in d.flags and ty.kind == tyString and optSeqDestructors in p.config.globalOptions: @@ -1102,7 +1113,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses) - if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) + if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.elementType, abstractVarRange) case ty.kind of tyUncheckedArray: genUncheckedArrayElem(p, n, n[0], n[1], d) of tyArray: genArrayElem(p, n, n[0], n[1], d) @@ -1158,9 +1169,9 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = var tmpB = initLocExprSingleUse(p, e[2]) tmpB.k = locExpr if m == mOr: - tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))" + tmpB.snippet = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))" else: - tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))" + tmpB.snippet = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))" if d.k == locNone: d = tmpB else: @@ -1263,7 +1274,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = lens.add(lenExpr(p, a)) lens.add(" + ") appends.add(ropecg(p.module, "#appendString($1, $2);$n", [strLoc(p, tmp), rdLoc(a)])) - linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.r, lens, L]) + linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.snippet, lens, L]) p.s(cpsStmts).add appends if d.k == locNone: d = tmp @@ -1309,7 +1320,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = [byRefLoc(p, dest), lens, L]) else: call = initLoc(locCall, e, OnHeap) - call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L]) + call.snippet = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L]) genAssignment(p, dest, call, {}) gcUsage(p.config, e) p.s(cpsStmts).add appends @@ -1324,12 +1335,12 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = var call = initLoc(locCall, e, OnHeap) if not p.module.compileToCpp: const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)" - call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), + call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a), getTypeDesc(p.module, e[1].typ), genTypeInfoV1(p.module, seqType, e.info)]) else: const seqAppendPattern = "($2) #incrSeqV3($1, $3)" - call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), + call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a), getTypeDesc(p.module, e[1].typ), genTypeInfoV1(p.module, seqType, e.info)]) # emit the write barrier if required, but we can always move here, so @@ -1339,19 +1350,11 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) var dest = initLoc(locExpr, e[2], OnHeap) var tmpL = getIntTemp(p) - lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)]) - dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)]) + lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.snippet, rdLoc(a), lenField(p)]) + dest.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.snippet, dataField(p)]) genAssignment(p, dest, b, {needToCopy}) gcUsage(p.config, e) -proc genReset(p: BProc, n: PNode) = - var a: TLoc = initLocExpr(p, n[1]) - specializeReset(p, a) - when false: - linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", - [addrLoc(p.config, a), - genTypeInfoV1(p.module, skipTypes(a.t, {tyVar}), n.info)]) - proc genDefault(p: BProc; n: PNode; d: var TLoc) = if d.k == locNone: d = getTemp(p, n.typ, needsInit=true) else: resetLoc(p, d) @@ -1362,16 +1365,16 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = var b: TLoc = initLoc(locExpr, a.lode, OnHeap) let refType = typ.skipTypes(abstractInstOwned) assert refType.kind == tyRef - let bt = refType.lastSon + let bt = refType.elementType if sizeExpr == "": sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)] if optTinyRtti in p.config.globalOptions: if needsInit: - b.r = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))", + b.snippet = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))", [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)]) else: - b.r = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))", + b.snippet = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))", [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)]) genAssignment(p, a, b, {}) else: @@ -1396,15 +1399,15 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = if p.config.selectedGC == gcGo: # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to # implement the write barrier - b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) + b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, a), b.rdLoc]) else: # use newObjRC1 as an optimization - b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) + b.snippet = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: - b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) + b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) genAssignment(p, a, b, {}) # set the object type: genObjectInit(p, cpsStmts, bt, a, constructRefObj) @@ -1430,18 +1433,18 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = if not lenIsZero: if p.config.selectedGC == gcGo: # we need the write barrier - call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), + call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), genTypeInfoV1(p.module, seqtype, dest.lode.info), length]) linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc]) else: - call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype), + call.snippet = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype), genTypeInfoV1(p.module, seqtype, dest.lode.info), length]) linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc]) else: if lenIsZero: - call.r = rope"NIM_NIL" + call.snippet = rope"NIM_NIL" else: - call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), + call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), genTypeInfoV1(p.module, seqtype, dest.lode.info), length]) genAssignment(p, dest, call, {}) @@ -1452,7 +1455,7 @@ proc genNewSeq(p: BProc, e: PNode) = let seqtype = skipTypes(e[1].typ, abstractVarRange) linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", [a.rdLoc, b.rdLoc, - getTypeDesc(p.module, seqtype.lastSon), + getTypeDesc(p.module, seqtype.elementType), getSeqPayloadType(p.module, seqtype)]) else: let lenIsZero = e[2].kind == nkIntLit and e[2].intVal == 0 @@ -1465,7 +1468,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = if optSeqDestructors in p.config.globalOptions: if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3));$n", - [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon), + [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.elementType), getSeqPayloadType(p.module, seqtype), ]) else: @@ -1484,9 +1487,13 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = if id == p.module.labels: # expression not found in the cache: inc(p.module.labels) - p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ", [getTypeDesc(p.module, t), d.r]) - genBracedInit(p, n, isConst = true, t, p.module.s[cfsData]) - p.module.s[cfsData].addf(";$n", []) + var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, t), d.snippet] + # bug #23627; when generating const object fields, it's likely that + # we need to generate type infos for the object, which may be an object with + # custom hooks. We need to generate potential consts in the hooks first. + genBracedInit(p, n, isConst = true, t, data) + data.addf(";$n", []) + p.module.s[cfsData].add data proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: @@ -1497,15 +1504,14 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) = - var tmp2: TLoc = default(TLoc) - tmp2.r = r - let field = lookupFieldAgain(p, ty, nField.sym, tmp2.r) - if field.loc.r == "": fillObjectFields(p.module, ty) - if field.loc.r == "": internalError(p.config, info, "genFieldObjConstr") + var tmp2 = TLoc(snippet: r) + let field = lookupFieldAgain(p, ty, nField.sym, tmp2.snippet) + if field.loc.snippet == "": fillObjectFields(p.module, ty) + if field.loc.snippet == "": internalError(p.config, info, "genFieldObjConstr") if check != nil and optFieldCheck in p.options: genFieldCheck(p, check, r, field) - tmp2.r.add(".") - tmp2.r.add(field.loc.r) + tmp2.snippet.add(".") + tmp2.snippet.add(field.loc.snippet) if useTemp: tmp2.k = locTemp tmp2.storage = if isRef: OnHeap else: OnStack @@ -1513,7 +1519,12 @@ proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, c tmp2.k = d.k tmp2.storage = if isRef: OnHeap else: d.storage tmp2.lode = val - expr(p, val, tmp2) + if nField.typ.skipTypes(abstractVar).kind in {tyOpenArray, tyVarargs}: + var tmp3 = getTemp(p, val.typ) + expr(p, val, tmp3) + genOpenArrayConv(p, tmp2, tmp3, {}) + else: + expr(p, val, tmp2) proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = # inheritance in C++ does not allow struct initialization so @@ -1544,7 +1555,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = r = rdLoc(tmp) if isRef: rawGenNew(p, tmp, "", needsInit = nfAllFieldsSet notin e.flags) - t = t.lastSon.skipTypes(abstractInstOwned) + t = t.elementType.skipTypes(abstractInstOwned) r = "(*$1)" % [r] gcUsage(p.config, e) elif needsZeroMem: @@ -1590,7 +1601,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = if optSeqDestructors in p.config.globalOptions: let seqtype = n.typ linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", - [rdLoc dest[], lit, getTypeDesc(p.module, seqtype.lastSon), + [rdLoc dest[], lit, getTypeDesc(p.module, seqtype.elementType), getSeqPayloadType(p.module, seqtype)]) else: # generate call to newSeq before adding the elements per hand: @@ -1599,7 +1610,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = arr = initLoc(locExpr, n[i], OnHeap) var lit = newRopeAppender() intLiteral(i, lit) - arr.r = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)]) + arr.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)]) arr.storage = OnHeap # we know that sequences are on the heap expr(p, n[i], arr) gcUsage(p.config, n) @@ -1623,7 +1634,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = if optSeqDestructors in p.config.globalOptions: let seqtype = n.typ linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", - [rdLoc d, L, getTypeDesc(p.module, seqtype.lastSon), + [rdLoc d, L, getTypeDesc(p.module, seqtype.elementType), getSeqPayloadType(p.module, seqtype)]) else: var lit = newRopeAppender() @@ -1635,19 +1646,19 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) var lit = newRopeAppender() intLiteral(i, lit) - elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)]) + elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)]) elem.storage = OnHeap # we know that sequences are on the heap arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage) - arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), lit]) + arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), lit]) genAssignment(p, elem, arr, {needToCopy}) else: var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) - linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, L]) + linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.snippet, L]) elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) - elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)]) + elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)]) elem.storage = OnHeap # we know that sequences are on the heap arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage) - arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)]) + arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)]) genAssignment(p, elem, arr, {needToCopy}) lineF(p, cpsStmts, "}$n", []) @@ -1663,11 +1674,11 @@ proc genNewFinalize(p: BProc, e: PNode) = b = initLoc(locExpr, a.lode, OnHeap) ti = genTypeInfo(p.config, p.module, refType, e.info) p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) - b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ + b.snippet = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ getTypeDesc(p.module, refType), - ti, getTypeDesc(p.module, skipTypes(refType.lastSon, abstractRange))]) + ti, getTypeDesc(p.module, skipTypes(refType.elementType, abstractRange))]) genAssignment(p, a, b, {}) # set the object type: - bt = skipTypes(refType.lastSon, abstractRange) + bt = skipTypes(refType.elementType, abstractRange) genObjectInit(p, cpsStmts, bt, a, constructRefObj) gcUsage(p.config, e) @@ -1699,12 +1710,12 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = if t.kind notin {tyVar, tyLent}: nilCheck = r if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: r = ropecg(p.module, "(*$1)", [r]) - t = skipTypes(t.lastSon, typedescInst+{tyOwned}) + t = skipTypes(t.elementType, typedescInst+{tyOwned}) discard getTypeDesc(p.module, t) if not p.module.compileToCpp: - while t.kind == tyObject and t[0] != nil: + while t.kind == tyObject and t.baseClass != nil: r.add(".Sup") - t = skipTypes(t[0], skipPtrs) + t = skipTypes(t.baseClass, skipPtrs) if isObjLackingTypeField(t): globalError(p.config, x.info, "no 'of' operator available for pure objects") @@ -1788,13 +1799,13 @@ proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope; result: var Rope; enforceV1 if t.kind notin {tyVar, tyLent}: nilCheck = derefs if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: derefs = "(*$1)" % [derefs] - t = skipTypes(t.lastSon, abstractInst) + t = skipTypes(t.elementType, abstractInst) result.add derefs discard getTypeDesc(p.module, t) if not p.module.compileToCpp: - while t.kind == tyObject and t[0] != nil: + while t.kind == tyObject and t.baseClass != nil: result.add(".Sup") - t = skipTypes(t[0], skipPtrs) + t = skipTypes(t.baseClass, skipPtrs) result.add ".m_type" if optTinyRtti in p.config.globalOptions and enforceV1: result.add "->typeInfoV1" @@ -1828,7 +1839,7 @@ proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc = initLocExpr(p, n[1]) - a.r = ropecg(p.module, frmt, [rdLoc(a)]) + a.snippet = ropecg(p.module, frmt, [rdLoc(a)]) a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR) if d.k == locNone: d = getTemp(p, n.typ) genAssignment(p, d, a, {}) @@ -1847,11 +1858,11 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var b = initLocExpr(p, a[2]) var c = initLocExpr(p, a[3]) if optBoundsCheck in p.options: - genBoundsCheck(p, m, b, c) + genBoundsCheck(p, m, b, c, skipTypes(m.t, abstractVarRange)) if op == mHigh: - putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)", [rdLoc(b), rdLoc(c)])) + putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)])) else: - putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)])) + putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1)+1)", [rdLoc(b), rdLoc(c)])) else: if not reifiedOpenArray(a): if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)") @@ -1882,8 +1893,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var a = initLocExpr(p, e[1]) var x = lenExpr(p, a) if op == mHigh: x = "($1-1)" % [x] - lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x]) - putIntoDest(p, d, e, tmp.r) + lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.snippet, x]) + putIntoDest(p, d, e, tmp.snippet) of tyArray: # YYY: length(sideeffect) is optimized away incorrectly? if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ))) @@ -1905,13 +1916,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = var call = initLoc(locCall, e, OnHeap) if not p.module.compileToCpp: const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)" - call.r = ropecg(p.module, setLenPattern, [ + call.snippet = ropecg(p.module, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)]) else: const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)" - call.r = ropecg(p.module, setLenPattern, [ + call.snippet = ropecg(p.module, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)]) @@ -1927,7 +1938,7 @@ proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = var b = initLocExpr(p, e[2]) var call = initLoc(locCall, e, OnHeap) - call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ + call.snippet = ropecg(p.module, "#setLengthStr($1, $2)", [ rdLoc(a), rdLoc(b)]) genAssignment(p, a, call, {}) gcUsage(p.config, e) @@ -2004,23 +2015,23 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = a = initLocExpr(p, ea) b = initLoc(locExpr, e, OnUnknown) if e[1].len > 0: - b.r = rope("(") + b.snippet = rope("(") for i in 0..<e[1].len: let it = e[1][i] if it.kind == nkRange: x = initLocExpr(p, it[0]) y = initLocExpr(p, it[1]) - b.r.addf("$1 >= $2 && $1 <= $3", + b.snippet.addf("$1 >= $2 && $1 <= $3", [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)]) else: x = initLocExpr(p, it) - b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) - if i < e[1].len - 1: b.r.add(" || ") - b.r.add(")") + b.snippet.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) + if i < e[1].len - 1: b.snippet.add(" || ") + b.snippet.add(")") else: # handle the case of an empty set - b.r = rope("0") - putIntoDest(p, d, e, b.r) + b.snippet = rope("0") + putIntoDest(p, d, e, b.snippet) else: assert(e[1].typ != nil) assert(e[2].typ != nil) @@ -2080,7 +2091,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n") of mCard: var a: TLoc = initLocExpr(p, e[1]) - putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [addrLoc(p.config, a), size])) + putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [rdCharLoc(a), size])) of mLtSet, mLeSet: i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter a = initLocExpr(p, e[1]) @@ -2158,7 +2169,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = inc(p.labels) var lbl = p.labels.rope var tmp: TLoc = default(TLoc) - tmp.r = "LOC$1.source" % [lbl] + tmp.snippet = "LOC$1.source" % [lbl] let destsize = getSize(p.config, destt) let srcsize = getSize(p.config, srct) @@ -2223,12 +2234,16 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = raiseInstr(p, p.s(cpsStmts)) linefmt p, cpsStmts, "}$n", [] - putIntoDest(p, d, n, "(($1) ($2))" % + if sameBackendTypeIgnoreRange(dest, n[0].typ): + # don't cast so an address can be taken for `var` conversions + putIntoDest(p, d, n, "($1)" % [rdCharLoc(a)], a.storage) + else: + putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) proc genConv(p: BProc, e: PNode, d: var TLoc) = let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink}) - if sameBackendType(destType, e[1].typ): + if sameBackendTypeIgnoreRange(destType, e[1].typ): expr(p, e[1], d) else: genSomeCast(p, e, d) @@ -2242,9 +2257,15 @@ proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc = initLocExpr(p, n[0]) - putIntoDest(p, d, n, - ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), - a.storage) + if p.module.compileToCpp: + # fixes for const qualifier; bug #12703; bug #19588 + putIntoDest(p, d, n, + ropecg(p.module, "#cstrToNimstr((NCSTRING) $1)", [rdLoc(a)]), + a.storage) + else: + putIntoDest(p, d, n, + ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), + a.storage) gcUsage(p.config, n) proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = @@ -2331,8 +2352,13 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = else: linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)]) else: - let flags = if not canMove(p, n[1], d): {needToCopy} else: {} - genAssignment(p, d, a, flags) + if n[1].kind == nkSym and isSinkParam(n[1].sym): + var tmp = getTemp(p, n[1].typ.skipTypes({tySink})) + genAssignment(p, tmp, a, {needToCopySinkParam}) + genAssignment(p, d, tmp, {}) + resetLoc(p, tmp) + else: + genAssignment(p, d, a, {}) resetLoc(p, a) proc genDestroy(p: BProc; n: PNode) = @@ -2355,7 +2381,7 @@ proc genDestroy(p: BProc; n: PNode) = linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" & "}$n", - [rdLoc(a), getTypeDesc(p.module, t.lastSon)]) + [rdLoc(a), getTypeDesc(p.module, t.elementType)]) else: discard "nothing to do" else: let t = n[1].typ.skipTypes(abstractVar) @@ -2366,7 +2392,7 @@ proc genDestroy(p: BProc; n: PNode) = proc genDispose(p: BProc; n: PNode) = when false: - let elemType = n[1].typ.skipTypes(abstractVar).lastSon + let elemType = n[1].typ.skipTypes(abstractVar).elementType var a: TLoc = initLocExpr(p, n[1].skipAddr) @@ -2381,7 +2407,7 @@ proc genDispose(p: BProc; n: PNode) = lineCg(p, cpsStmts, ["#nimDestroyAndDispose($#)", rdLoc(a)]) proc genSlice(p: BProc; e: PNode; d: var TLoc) = - let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon, + let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.elementType, prepareForMutation = e[1].kind == nkHiddenDeref and e[1].typ.skipTypes(abstractInst).kind == tyString and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}) @@ -2440,7 +2466,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var call = initLoc(locCall, e, OnHeap) var dest = initLocExpr(p, e[1]) var b = initLocExpr(p, e[2]) - call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) + call.snippet = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) genAssignment(p, dest, call, {}) of mAppendStrStr: genStrAppend(p, e, d) of mAppendSeqElem: @@ -2453,16 +2479,14 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mLeStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) <= 0)") of mLtStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) < 0)") of mIsNil: genIsNil(p, e, d) - of mIntToStr: genDollar(p, e, d, "#nimIntToStr($1)") - of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)") of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)") of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)") - of mFloatToStr: - if e[1].typ.skipTypes(abstractInst).kind == tyFloat32: - genDollar(p, e, d, "#nimFloat32ToStr($1)") + of mCStrToStr: + if p.module.compileToCpp: + # fixes for const qualifier; bug #12703; bug #19588 + genDollar(p, e, d, "#cstrToNimstr((NCSTRING) $1)") else: - genDollar(p, e, d, "#nimFloatToStr($1)") - of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") + genDollar(p, e, d, "#cstrToNimstr($1)") of mStrToStr, mUnown: expr(p, e[1], d) of generatedMagics: genCall(p, e, d) of mEnumToStr: @@ -2506,7 +2530,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let member = if t.kind == tyTuple: "Field" & rope(dotExpr[1].sym.position) - else: dotExpr[1].sym.loc.r + else: dotExpr[1].sym.loc.snippet putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member]) of mChr: genSomeCast(p, e, d) of mOrd: genOrd(p, e, d) @@ -2529,8 +2553,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # - not sure, and it wouldn't work if the symbol behind the magic isn't # somehow forward-declared from some other usage, but it is *possible* if lfNoDecl notin opr.loc.flags: - let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r) - assert prc != nil, $opr.loc.r + let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.snippet) + assert prc != nil, $opr.loc.snippet # HACK: # Explicitly add this proc as declared here so the cgsym call doesn't # add a forward declaration - without this we could end up with the same @@ -2543,14 +2567,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id) # Make the function behind the magic get actually generated - this will # not lead to a forward declaration! The genCall will lead to one. - cgsym(p.module, $opr.loc.r) + cgsym(p.module, $opr.loc.snippet) # make sure we have pointer-initialising code for hot code reloading if not wasDeclared and p.hcrOn: p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n", [mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)]) genCall(p, e, d) of mDefault, mZeroDefault: genDefault(p, e, d) - of mReset: genReset(p, e) of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError, mSlurp..mQuoteAst: @@ -2586,6 +2609,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mTrace: discard "no code to generate" of mEnsureMove: expr(p, e[1], d) + of mDup: + expr(p, e[1], d) else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind @@ -2671,7 +2696,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var it = n[i] if it.kind == nkExprColonExpr: it = it[1] rec = initLoc(locExpr, it, dest[].storage) - rec.r = "$1.Field$2" % [rdLoc(dest[]), rope(i)] + rec.snippet = "$1.Field$2" % [rdLoc(dest[]), rope(i)] rec.flags.incl(lfEnforceDeref) expr(p, it, rec) @@ -2721,12 +2746,12 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage) var lit = newRopeAppender() intLiteral(i, lit) - arr.r = "$1[$2]" % [rdLoc(d), lit] + arr.snippet = "$1[$2]" % [rdLoc(d), lit] expr(p, n[i], arr) proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) = requestConstImpl(p, sym) - assert((sym.loc.r != "") and (sym.loc.t != nil)) + assert((sym.loc.snippet != "") and (sym.loc.t != nil)) putLocIntoDest(p, d, sym.loc) template genStmtListExprImpl(exprOrStmt) {.dirty.} = @@ -2858,24 +2883,24 @@ proc genConstSetup(p: BProc; sym: PSym): bool = result = lfNoDecl notin sym.loc.flags proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) = - if sym.loc.r == "": + if sym.loc.snippet == "": if not genConstSetup(p, sym): return - assert(sym.loc.r != "", $sym.name.s & $sym.itemId) + assert(sym.loc.snippet != "", $sym.name.s & $sym.itemId) if m.hcrOn: - m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]); + m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]); m.initProc.procSec(cpsLocals).addf( - "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, + "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(q, sym)]) else: let headerDecl = "extern NIM_CONST $1 $2;$n" % - [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r] + [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet] m.s[cfsData].add(headerDecl) if sfExportc in sym.flags and p.module.g.generatedHeader != nil: p.module.g.generatedHeader.s[cfsData].add(headerDecl) proc genConstDefinition(q: BModule; p: BProc; sym: PSym) = # add a suffix for hcr - will later init the global pointer with this data - let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r + let actualConstName = if q.hcrOn: sym.loc.snippet & "_const" else: sym.loc.snippet var data = newRopeAppender() data.addf("N_LIB_PRIVATE NIM_CONST $1 $2 = ", [getTypeDesc(q, sym.typ), actualConstName]) @@ -2884,16 +2909,16 @@ proc genConstDefinition(q: BModule; p: BProc; sym: PSym) = q.s[cfsData].add data if q.hcrOn: # generate the global pointer with the real name - q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.r]) + q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.snippet]) # register it (but ignore the boolean result of hcrRegisterGlobal) q.initProc.procSec(cpsLocals).addf( "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n", - [getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)]) + [getModuleDllPath(q, sym), sym.loc.snippet, rdLoc(sym.loc)]) # always copy over the contents of the actual constant with the _const # suffix ==> this means that the constant is reloadable & updatable! q.initProc.procSec(cpsLocals).add(ropecg(q, "\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n", - [sym.loc.r, actualConstName, rdLoc(sym.loc)])) + [sym.loc.snippet, actualConstName, rdLoc(sym.loc)])) proc genConstStmt(p: BProc, n: PNode) = # This code is only used in the new DCE implementation. @@ -2933,7 +2958,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genProcPrototype(p.module, sym) else: genProc(p.module, sym) - if sym.loc.r == "" or sym.loc.lode == nil: + if sym.loc.snippet == "" or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: @@ -2943,7 +2968,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = putIntoDest(p, d, n, lit, OnStatic) elif useAliveDataFromDce in p.module.flags: genConstHeader(p.module, p.module, p, sym) - assert((sym.loc.r != "") and (sym.loc.t != nil)) + assert((sym.loc.snippet != "") and (sym.loc.t != nil)) putLocIntoDest(p, d, sym.loc) else: genComplexConst(p, sym, d) @@ -2958,14 +2983,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sfCompileTime in sym.flags: genSingleVar(p, sym, n, astdef(sym)) - if sym.loc.r == "" or sym.loc.t == nil: + if sym.loc.snippet == "" or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id if sfThread in sym.flags: accessThreadLocalVar(p, sym) if emulatedThreadVars(p.config): - putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r) + putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.snippet) else: putLocIntoDest(p, d, sym.loc) else: @@ -2973,17 +2998,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of skTemp: when false: # this is more harmful than helpful. - if sym.loc.r == "": + if sym.loc.snippet == "": # we now support undeclared 'skTemp' variables for easier # transformations in other parts of the compiler: assignLocalVar(p, n) - if sym.loc.r == "" or sym.loc.t == nil: + if sym.loc.snippet == "" or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) of skParam: - if sym.loc.r == "" or sym.loc.t == nil: + if sym.loc.snippet == "" or sym.loc.t == nil: # echo "FAILED FOR PRCO ", p.prc.name.s # debug p.prc.typ.n # echo renderTree(p.prc.ast, {renderIds}) @@ -3073,7 +3098,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkLambdaKinds: var sym = n[namePos].sym genProc(p.module, sym) - if sym.loc.r == "" or sym.loc.lode == nil: + if sym.loc.snippet == "" or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of nkClosure: genClosure(p, n, d) @@ -3104,7 +3129,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if ex.kind != nkEmpty: genLineDir(p, n) var a: TLoc = initLocExprSingleUse(p, ex) - line(p, cpsStmts, "(void)(" & a.r & ");\L") + line(p, cpsStmts, "(void)(" & a.snippet & ");\L") of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt, nkHiddenTryStmt: case p.config.exc @@ -3191,17 +3216,17 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = result.add "}" of tyTuple: result.add "{" - if p.vccAndC and t.len == 0: + if p.vccAndC and t.isEmptyTupleType: result.add "0" - for i in 0..<t.len: + for i, a in t.ikids: if i > 0: result.add ", " - getDefaultValue(p, t[i], info, result) + getDefaultValue(p, a, info, result) result.add "}" of tyArray: result.add "{" - for i in 0..<toInt(lengthOrd(p.config, t[0])): + for i in 0..<toInt(lengthOrd(p.config, t.indexType)): if i > 0: result.add ", " - getDefaultValue(p, t[1], info, result) + getDefaultValue(p, t.elementType, info, result) result.add "}" #result = rope"{}" of tyOpenArray, tyVarargs: @@ -3226,7 +3251,8 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, getNullValueAux(p, t, it, constOrNil, result, count, isConst, info) of nkRecCase: getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info) - if count > 0: result.add ", " + var res = "" + if count > 0: res.add ", " var branch = Zero if constOrNil != nil: ## find kind value, default is zero if not specified @@ -3240,18 +3266,21 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, break let selectedBranch = caseObjDefaultBranch(obj, branch) - result.add "{" + res.add "{" var countB = 0 let b = lastSon(obj[selectedBranch]) # designated initilization is the only way to init non first element of unions # branches are allowed to have no members (b.len == 0), in this case they don't need initializer if b.kind == nkRecList and not isEmptyCaseObjectBranch(b): - result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {" - getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info) - result.add "}" + res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {" + getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info) + res.add "}" elif b.kind == nkSym: - result.add "." & mangleRecFieldName(p.module, b.sym) & " = " - getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info) + res.add "." & mangleRecFieldName(p.module, b.sym) & " = " + getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info) + else: + return + result.add res result.add "}" of nkSym: @@ -3276,7 +3305,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, result: var Rope; count: var int; isConst: bool, info: TLineInfo) = - var base = t[0] + var base = t.baseClass let oldRes = result let oldcount = count if base != nil: @@ -3308,7 +3337,7 @@ proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Rope) = proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Rope) = result.add "{" if p.vccAndC and n.len == 0 and n.typ.kind == tyArray: - getDefaultValue(p, n.typ[1], n.info, result) + getDefaultValue(p, n.typ.elementType, n.info, result) for i in 0..<n.len: let it = n[i] if i > 0: result.add ",\n" @@ -3425,7 +3454,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul genConstSimpleList(p, n, isConst, data) let payload = getTempName(p.module) - let ctype = getTypeDesc(p.module, typ[0]) + let ctype = getTypeDesc(p.module, typ.elementType) let arrLen = n.len appcg(p.module, cfsStrData, "static $5 $1 $3[$2] = $4;$n", [ diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index 47b6a1e15..6caeb8084 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -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.r == "": fillObjectFields(p.module, typ) + if disc.loc.snippet == "": 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.r]) + lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet]) 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.r], disc.loc.t) + specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t) of nkSym: let field = n.sym if field.typ.kind == tyVoid: return - if field.loc.r == "": fillObjectFields(p.module, typ) + if field.loc.snippet == "": fillObjectFields(p.module, typ) if field.loc.t == nil: internalError(p.config, n.info, "specializeResetN()") - specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t) + specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t) else: internalError(p.config, n.info, "specializeResetN()") proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = @@ -54,24 +54,23 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = case typ.kind of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred, tySink, tyOwned: - specializeResetT(p, accessor, lastSon(typ)) + specializeResetT(p, accessor, skipModifier(typ)) of tyArray: - let arraySize = lengthOrd(p.config, typ[0]) + let arraySize = lengthOrd(p.config, typ.indexType) var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", - [i.r, arraySize]) - specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ[1]) + [i.snippet, arraySize]) + specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType) lineF(p, cpsStmts, "}$n", []) of tyObject: - for i in 0..<typ.len: - var x = typ[i] - if x != nil: x = x.skipTypes(skipPtrs) - specializeResetT(p, accessor.parentObj(p.module), x) + var x = typ.baseClass + 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 in 0..<typ.len: - specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), typ[i]) + for i, a in typ.ikids: + specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a) of tyString, tyRef, tySequence: lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor]) @@ -82,7 +81,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor]) else: lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor]) - of tyChar, tyBool, tyEnum, tyInt..tyUInt64: + of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64: lineCg(p, cpsStmts, "$1 = 0;$n", [accessor]) of tyCstring, tyPointer, tyPtr, tyVar, tyLent: lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor]) @@ -96,8 +95,8 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = else: raiseAssert "unexpected set type kind" of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, - tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs, - tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, + tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs, + tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable: discard diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index c3d44c1d3..883108f2c 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -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.r = "$1.Field$2" % [rdLoc(tup), rope(i)] + field.snippet = "$1.Field$2" % [rdLoc(tup), rope(i)] else: if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple") - field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)] + field.snippet = "$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.r, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp]) + [hcrCond, curr.loc.snippet, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp]) proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} = @@ -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.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)] + d.snippet = "(((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.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)] + d.snippet = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)] proc genGotoVar(p: BProc; value: PNode) = if value.kind notin {nkCharLit..nkUInt64Lit}: @@ -289,22 +289,32 @@ 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): string = +proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): 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) + result.add genCppInitializer(p.module, p, call[i][0].sym.typ, didGenTemp) 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 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) = - let params = genCppParamsForCtor(p, call) +proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope, didGenTemp: var bool) = + let params = genCppParamsForCtor(p, call, didGenTemp) if params.len == 0: decl = runtimeFormat("$#;\n", [decl]) else: @@ -331,7 +341,14 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = # v.owner.kind != skModule: targetProc = p.module.preInitProc if isCppCtorCall and not containsHiddenPointer(v.typ): - callGlobalVarCppCtor(targetProc, v, vn, value) + 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, {}) else: assignGlobalVar(targetProc, vn, valueAsRope) @@ -366,11 +383,15 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = var decl = localVarDecl(p, vn) var tmp: TLoc if isCppCtorCall: - genCppVarForCtor(p, value, decl) + var didGenTemp = false + genCppVarForCtor(p, value, decl, didGenTemp) line(p, cpsStmts, decl) else: tmp = initLocExprSingleUse(p, value) - lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc]) + if value.kind == nkEmpty: + lineF(p, cpsStmts, "$#;\n", [decl]) + else: + lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc]) return assignLocalVar(p, vn) initLocalVar(p, v, imm) @@ -384,7 +405,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.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) + [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) # nothing special left to do later on - let's avoid closing and reopening blocks forHcr = false @@ -393,7 +414,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.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) + [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) startBlock(targetProc) if value.kind != nkEmpty and valueAsRope.len == 0: genLineDir(targetProc, vn) @@ -734,6 +755,18 @@ 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 and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: # if the current try stmt have a finally block, @@ -763,10 +796,14 @@ proc genRaiseStmt(p: BProc, t: PNode) = var e = rdLoc(a) discard getTypeDesc(p.module, t[0].typ) var typ = skipTypes(t[0].typ, abstractPtrs) - # XXX For reasons that currently escape me, this is only required by the new - # C++ based exception handling: - if p.config.exc == excCpp: + case p.config.exc + of 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]) @@ -1038,8 +1075,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = inc(p.labels, 2) let etmp = p.labels - - lineCg(p, cpsStmts, "std::exception_ptr T$1_;$n", [etmp]) + #init on locals, fixes #23306 + lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp]) let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, false, 0.Natural)) @@ -1508,7 +1545,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.r) + res.add($sym.loc.snippet) of nkTypeOfExpr: res.add($getTypeDesc(p.module, it.typ)) else: @@ -1538,6 +1575,21 @@ 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 @@ -1575,9 +1627,9 @@ proc genPragma(p: BProc, n: PNode) = case whichPragma(it) of wEmit: genEmit(p, it) of wPush: - processPushBackendOption(p.optionsStack, p.options, n, i+1) + processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1) of wPop: - processPopBackendOption(p.optionsStack, p.options) + processPopBackendOption(p.config, p.optionsStack, p.options) else: discard diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index abf830b57..1f551f022 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -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.r]) + m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.snippet]) 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.r]) + m.s[cfsVars].addf(" $1;$n", [s.loc.snippet]) proc generateThreadLocalStorage(m: BModule) = if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags): diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index adad9df3e..ed4c79d9a 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -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.r == "": fillObjectFields(c.p.module, typ) + if disc.loc.snippet == "": 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.r]) + lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet]) 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.r == "": fillObjectFields(c.p.module, typ) + if field.loc.snippet == "": fillObjectFields(c.p.module, typ) if field.loc.t == nil: internalError(c.p.config, n.info, "genTraverseProc()") - genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t) + genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t) else: internalError(c.p.config, n.info, "genTraverseProc()") proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} = @@ -71,37 +71,36 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = case typ.kind of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred, tySink, tyOwned: - genTraverseProc(c, accessor, lastSon(typ)) + genTraverseProc(c, accessor, skipModifier(typ)) of tyArray: - let arraySize = lengthOrd(c.p.config, typ[0]) + let arraySize = lengthOrd(c.p.config, typ.indexType) 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.r, arraySize]) + [i.snippet, arraySize]) let oldLen = p.s(cpsStmts).len - genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ[1]) + genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType) 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: - 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) + var x = typ.baseClass + 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 in 0..<typ.len: - genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), typ[i]) + for i, a in typ.ikids: + genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a) 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.lastSon): + elif containsGarbageCollectedRef(typ.elementType): # 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]) @@ -118,15 +117,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: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt)) + var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt)) var oldCode = p.s(cpsStmts) freeze oldCode - var a: TLoc = TLoc(r: accessor) + var a = TLoc(snippet: accessor) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", - [i.r, lenExpr(c.p, a)]) + [i.snippet, lenExpr(c.p, a)]) let oldLen = p.s(cpsStmts).len - genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ[0]) + genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType) if p.s(cpsStmts).len == oldLen: # do not emit dummy long loops for faster debug builds: p.s(cpsStmts) = oldCode @@ -134,7 +133,6 @@ 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 @@ -147,18 +145,19 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope = lineF(p, cpsLocals, "$1 a;$n", [t]) lineF(p, cpsInit, "a = ($1)p;$n", [t]) - c.p = p - c.visitorFrmt = "op" # "#nimGCvisit((void*)$1, op);$n" + var c = TTraversalClosure(p: p, + visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n" + ) assert typ.kind != tyTypeDesc if typ.kind == tySequence: genTraverseProcSeq(c, "a".rope, typ) else: - if skipTypes(typ[0], typedescInst+{tyOwned}).kind == tyArray: + if skipTypes(typ.elementType, typedescInst+{tyOwned}).kind == tyArray: # C's arrays are broken beyond repair: - genTraverseProc(c, "a".rope, typ[0]) + genTraverseProc(c, "a".rope, typ.elementType) else: - genTraverseProc(c, "(*a)".rope, typ[0]) + genTraverseProc(c, "(*a)".rope, typ.elementType) let generatedProc = "$1 {$n$2$3$4}\n" % [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)] @@ -174,7 +173,6 @@ 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) @@ -183,8 +181,10 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope = accessThreadLocalVar(p, s) sLoc = "NimTV_->" & sLoc - c.visitorFrmt = "0" # "#nimGCvisit((void*)$1, 0);$n" - c.p = p + var c = TTraversalClosure(p: p, + visitorFrmt: "0" # "#nimGCvisit((void*)$1, 0);$n" + ) + let header = "static N_NIMCALL(void, $1)(void)" % [result] genTraverseProc(c, sLoc, s.loc.t) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 462b08a43..2c2556336 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -55,24 +55,38 @@ proc mangleField(m: BModule; name: PIdent): string = if isKeyword(name): result.add "_0" +proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string = + result = "_Z" # Common prefix in Itanium ABI + result.add encodeSym(m, s, makeUnique) + if s.typ.len > 1: #we dont care about the return param + for i in 1..<s.typ.len: + if s.typ[i].isNil: continue + result.add encodeType(m, s.typ[i]) + + if result in m.g.mangledPrcs: + result = mangleProc(m, s, true) + else: + m.g.mangledPrcs.incl(result) + proc fillBackendName(m: BModule; s: PSym) = - if s.loc.r == "": - var result = s.name.s.mangle.rope - result.add "__" - result.add m.g.graph.ifaces[s.itemId.module].uniqueName - result.add "_u" - result.addInt s.itemId.item # s.disamb # + if s.loc.snippet == "": + var result: Rope + if not m.compileToCpp and s.kind in routineKinds and optCDebug in m.g.config.globalOptions and + m.g.config.symbolFiles == disabledSf: + result = mangleProc(m, s, false).rope + else: + result = s.name.s.mangle.rope + result.add mangleProcNameExt(m.g.graph, s) if m.hcrOn: result.add '_' result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config)) - s.loc.r = result + s.loc.snippet = result writeMangledName(m.ndi, s, m.config) proc fillParamName(m: BModule; s: PSym) = - if s.loc.r == "": + if s.loc.snippet == "": var res = s.name.s.mangle - res.add "_p" - res.addInt s.position + res.add mangleParamExt(s) #res.add idOrSig(s, res, m.sigConflicts, m.config) # Take into account if HCR is on because of the following scenario: # if a module gets imported and it has some more importc symbols in it, @@ -90,13 +104,13 @@ proc fillParamName(m: BModule; s: PSym) = # and a function called in main or proxy uses `socket` as a parameter name. # That would lead to either needing to reload `proxy` or to overwrite the # executable file for the main module, which is running (or both!) -> error. - s.loc.r = res.rope + s.loc.snippet = res.rope writeMangledName(m.ndi, s, m.config) proc fillLocalName(p: BProc; s: PSym) = assert s.kind in skLocalVars+{skTemp} #assert sfGlobal notin s.flags - if s.loc.r == "": + if s.loc.snippet == "": var key = s.name.s.mangle let counter = p.sigConflicts.getOrDefault(key) var result = key.rope @@ -106,7 +120,7 @@ proc fillLocalName(p: BProc; s: PSym) = elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key): result.add "_" & rope(counter+1) p.sigConflicts.inc(key) - s.loc.r = result + s.loc.snippet = result if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config) proc scopeMangledParam(p: BProc; param: PSym) = @@ -133,23 +147,23 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = var t = typ while true: if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}: - return t.sym.loc.r + return t.sym.loc.snippet if t.kind in irrelevantForBackend: - t = t.lastSon + t = t.skipModifier else: break - let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.lastSon else: typ - if typ.loc.r == "": - typ.typeName(typ.loc.r) - typ.loc.r.add $sig + let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.elementType else: typ + if typ.loc.snippet == "": + typ.typeName(typ.loc.snippet) + typ.loc.snippet.add $sig else: when defined(debugSigHashes): # check consistency: var tn = newRopeAppender() typ.typeName(tn) - assert($typ.loc.r == $(tn & $sig)) - result = typ.loc.r + assert($typ.loc.snippet == $(tn & $sig)) + result = typ.loc.snippet if result == "": internalError(m.config, "getTypeName: " & $typ.kind) proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind = @@ -175,10 +189,10 @@ proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind = of tyObject, tyTuple: result = ctStruct of tyUserTypeClasses: doAssert typ.isResolvedUserTypeClass - return mapType(conf, typ.lastSon, isParam) + result = mapType(conf, typ.skipModifier, isParam) of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned: - result = mapType(conf, lastSon(typ), isParam) + result = mapType(conf, skipModifier(typ), isParam) of tyEnum: if firstOrd(conf, typ) < 0: result = ctInt32 @@ -189,9 +203,9 @@ proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind = of 4: result = ctInt32 of 8: result = ctInt64 else: result = ctInt32 - of tyRange: result = mapType(conf, typ[0], isParam) + of tyRange: result = mapType(conf, typ.elementType, isParam) of tyPtr, tyVar, tyLent, tyRef: - var base = skipTypes(typ.lastSon, typedescInst) + var base = skipTypes(typ.elementType, typedescInst) case base.kind of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctPtrToArray of tySet: @@ -206,7 +220,7 @@ proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind = of tyInt..tyUInt64: result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: - if typ.n != nil: result = mapType(conf, lastSon typ, isParam) + if typ.n != nil: result = mapType(conf, typ.skipModifier, isParam) else: result = ctVoid doAssert(false, "mapType: " & $typ.kind) @@ -231,11 +245,14 @@ proc isImportedCppType(t: PType): bool = proc isOrHasImportedCppType(typ: PType): bool = searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType) +proc hasNoInit(t: PType): bool = + result = t.sym != nil and sfNoInit in t.sym.flags + proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and - (typ[0] == nil) or isPureObject(typ)) + (typ.baseClass == nil) or isPureObject(typ)) proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool = # Arrays and sets cannot be returned by a C procedure, because C is @@ -257,9 +274,15 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool = {tyVar, tyLent, tyRef, tyPtr}) of ctStruct: let t = skipTypes(rettype, typedescInst) - if rettype.isImportedCppType or t.isImportedCppType: return false - result = containsGarbageCollectedRef(t) or - (t.kind == tyObject and not isObjLackingTypeField(t)) + if rettype.isImportedCppType or t.isImportedCppType or + (typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc}): + # prevents nrvo for cdecl procs; # bug #23401 + result = false + else: + result = containsGarbageCollectedRef(t) or + (t.kind == tyObject and not isObjLackingTypeField(t)) or + (getSize(conf, rettype) == szUnknownSize and (t.sym == nil or sfImportc notin t.sym.flags)) + else: result = false const @@ -267,7 +290,9 @@ const "N_STDCALL", "N_CDECL", "N_SAFECALL", "N_SYSCALL", # this is probably not correct for all platforms, # but one can #define it to what one wants - "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV"] + "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV", + "N_NOCONV" #ccMember is N_NOCONV + ] proc cacheGetType(tab: TypeCache; sig: SigHash): Rope = # returns nil if we need to declare this type @@ -296,7 +321,7 @@ proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) = proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope = if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone: useHeader(m, t.sym) - result = t.sym.loc.r + result = t.sym.loc.snippet else: result = rope(literal) @@ -324,14 +349,14 @@ proc getSimpleTypeDesc(m: BModule; typ: PType): Rope = of tyNil: result = typeNameOrLiteral(m, typ, "void*") of tyInt..tyUInt64: result = typeNameOrLiteral(m, typ, NumericalTypeToStr[typ.kind]) - of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ[0]) + of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.skipModifier) of tyStatic: - if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ) + if typ.n != nil: result = getSimpleTypeDesc(m, skipModifier typ) else: result = "" internalError(m.config, "tyStatic for getSimpleTypeDesc") of tyGenericInst, tyAlias, tySink, tyOwned: - result = getSimpleTypeDesc(m, lastSon typ) + result = getSimpleTypeDesc(m, skipModifier typ) else: result = "" if result != "" and typ.isImportedType(): @@ -351,13 +376,6 @@ proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope = result = getSimpleTypeDesc(m, typ) if result == "": result = cacheGetType(m.typeCache, sig) -proc structOrUnion(t: PType): Rope = - let cachedUnion = rope("union") - let cachedStruct = rope("struct") - let t = t.skipTypes({tyAlias, tySink}) - if tfUnion in t.flags: cachedUnion - else: cachedStruct - proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) = if m.compileToCpp: m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename] @@ -456,8 +474,8 @@ macro unrollChars(x: static openArray[char], name, body: untyped) = copy body ))) -proc multiFormat*(frmt: var string, chars : static openArray[char], args: openArray[seq[string]]) = - var res : string +proc multiFormat*(frmt: var string, chars: static openArray[char], args: openArray[seq[string]]) = + var res: string unrollChars(chars, c): res = "" let arg = args[find(chars, c)] @@ -499,15 +517,16 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params weakDep=false;) = let t = prc.typ let isCtor = sfConstructor in prc.flags - if isCtor or (name[0] == '~' and sfMember in prc.flags): #destructors cant have void + if isCtor or (name[0] == '~' and sfMember in prc.flags): + # destructors can't have void rettype = "" - elif t[0] == nil or isInvalidReturnType(m.config, t): + elif t.returnType == nil or isInvalidReturnType(m.config, t): rettype = "void" else: if rettype == "": - rettype = getTypeDescAux(m, t[0], check, dkResult) + rettype = getTypeDescAux(m, t.returnType, check, dkResult) else: - rettype = runtimeFormat(rettype.replace("'0", "$1"), [getTypeDescAux(m, t[0], check, dkResult)]) + rettype = runtimeFormat(rettype.replace("'0", "$1"), [getTypeDescAux(m, t.returnType, check, dkResult)]) var types, names, args: seq[string] = @[] if not isCtor: var this = t.n[1].sym @@ -515,10 +534,10 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params fillLoc(this.loc, locParam, t.n[1], this.paramStorageLoc) if this.typ.kind == tyPtr: - this.loc.r = "this" + this.loc.snippet = "this" else: - this.loc.r = "(*this)" - names.add this.loc.r + this.loc.snippet = "(*this)" + names.add this.loc.snippet types.add getTypeDescWeak(m, this.typ, check, dkParam) let firstParam = if isCtor: 1 else: 2 @@ -531,11 +550,11 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params descKind = dkRefGenericParam else: descKind = dkRefParam - var typ, name : string + var typ, name: string fillParamName(m, param) fillLoc(param.loc, locParam, t.n[i], param.paramStorageLoc) - if ccgIntroducedPtr(m.config, param, t[0]) and descKind == dkParam: + if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam: typ = getTypeDescWeak(m, param.typ, check, descKind) & "*" incl(param.loc.flags, lfIndirect) param.loc.storage = OnUnknown @@ -546,7 +565,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params if sfNoalias in param.flags: typ.add("NIM_NOALIAS ") - name = param.loc.r + name = param.loc.snippet types.add typ names.add name if sfCodegenDecl notin param.flags: @@ -573,10 +592,10 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope, check: var IntSet, declareEnvironment=true; weakDep=false;) = params = "(" - if t[0] == nil or isInvalidReturnType(m.config, t): + if t.returnType == nil or isInvalidReturnType(m.config, t): rettype = "void" else: - rettype = getTypeDescAux(m, t[0], check, dkResult) + rettype = getTypeDescAux(m, t.returnType, check, dkResult) for i in 1..<t.n.len: if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams") var param = t.n[i].sym @@ -592,7 +611,7 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope, fillLoc(param.loc, locParam, t.n[i], param.paramStorageLoc) var typ: Rope - if ccgIntroducedPtr(m.config, param, t[0]) and descKind == dkParam: + if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam: typ = (getTypeDescWeak(m, param.typ, check, descKind)) typ.add("*") incl(param.loc.flags, lfIndirect) @@ -606,24 +625,24 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope, typ.add("NIM_NOALIAS ") if sfCodegenDecl notin param.flags: params.add(typ) - params.add(param.loc.r) + params.add(param.loc.snippet) else: - params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.r]) + params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.snippet]) # declare the len field for open arrays: var arr = param.typ.skipTypes({tyGenericInst}) - if arr.kind in {tyVar, tyLent, tySink}: arr = arr.lastSon + if arr.kind in {tyVar, tyLent, tySink}: arr = arr.elementType var j = 0 while arr.kind in {tyOpenArray, tyVarargs}: # this fixes the 'sort' bug: if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown # need to pass hidden parameter: - params.addf(", NI $1Len_$2", [param.loc.r, j.rope]) + params.addf(", NI $1Len_$2", [param.loc.snippet, j.rope]) inc(j) arr = arr[0].skipTypes({tySink}) - if t[0] != nil and isInvalidReturnType(m.config, t): - var arr = t[0] + if t.returnType != nil and isInvalidReturnType(m.config, t): + var arr = t.returnType if params != "(": params.add(", ") - if mapReturnType(m.config, t[0]) != ctArray: + if mapReturnType(m.config, arr) != ctArray: if isHeaderFile in m.flags: # still generates types for `--header` params.add(getTypeDescAux(m, arr, check, dkResult)) @@ -645,7 +664,7 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope, proc mangleRecFieldName(m: BModule; field: PSym): Rope = if {sfImportc, sfExportc} * field.flags != {}: - result = field.loc.r + result = field.loc.snippet else: result = rope(mangleField(m, field.name)) if result == "": internalError(m.config, field.info, "mangleRecFieldName") @@ -657,9 +676,9 @@ proc hasCppCtor(m: BModule; typ: PType): bool = if sfConstructor in prc.flags: return true -proc genCppParamsForCtor(p: BProc; call: PNode): string +proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string -proc genCppInitializer(m: BModule, prc: BProc; typ: PType): string = +proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool): string = #To avoid creating a BProc per test when called inside a struct nil BProc is allowed result = "{}" if typ.itemId in m.g.graph.initializersPerType: @@ -668,13 +687,13 @@ proc genCppInitializer(m: BModule, prc: BProc; typ: PType): string = var p = prc if p == nil: p = BProc(module: m) - result = "{" & genCppParamsForCtor(p, call) & "}" + result = "{" & genCppParamsForCtor(p, call, didGenTemp) & "}" if prc == nil: assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks" proc genRecordFieldsAux(m: BModule; n: PNode, rectype: PType, - check: var IntSet; result: var Rope; unionPrefix = "") = + check: var IntSet; result: var Builder; unionPrefix = "") = case n.kind of nkRecList: for i in 0..<n.len: @@ -691,63 +710,51 @@ proc genRecordFieldsAux(m: BModule; n: PNode, let k = lastSon(n[i]) if k.kind != nkSym: let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i - var a = newRopeAppender() + var a = newBuilder("") genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".") - if a != "": - if tfPacked notin rectype.flags: - unionBody.add("struct {") - else: - if hasAttribute in CC[m.config.cCompiler].props: - unionBody.add("struct __attribute__((__packed__)){") - else: - unionBody.addf("#pragma pack(push, 1)$nstruct{", []) - unionBody.add(a) - unionBody.addf("} $1;$n", [structName]) - if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props: - unionBody.addf("#pragma pack(pop)$n", []) + if a.len != 0: + unionBody.addFieldWithStructType(m, rectype, structName): + unionBody.add(a) else: genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix) else: internalError(m.config, "genRecordFieldsAux(record case branch)") - if unionBody != "": - result.addf("union{\n$1};$n", [unionBody]) + if unionBody.len != 0: + result.addAnonUnion: + result.add(unionBody) of nkSym: let field = n.sym if field.typ.kind == tyVoid: return #assert(field.ast == nil) let sname = mangleRecFieldName(m, field) fillLoc(field.loc, locField, n, unionPrefix & sname, OnUnknown) - if field.alignment > 0: - result.addf "NIM_ALIGN($1) ", [rope(field.alignment)] # for importcpp'ed objects, we only need to set field.loc, but don't # have to recurse via 'getTypeDescAux'. And not doing so prevents problems # with heavily templatized C++ code: if not isImportedCppType(rectype): - let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: "" - let fieldType = field.loc.lode.typ.skipTypes(abstractInst) + var typ: Rope = "" + var isFlexArray = false + var initializer = "" if fieldType.kind == tyUncheckedArray: - result.addf("\t$1 $2[SEQ_DECL_SIZE];$n", - [getTypeDescAux(m, fieldType.elemType, check, dkField), sname]) + typ = getTypeDescAux(m, fieldType.elemType, check, dkField) + isFlexArray = true elif fieldType.kind == tySequence: # we need to use a weak dependency here for trecursive_table. - result.addf("\t$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, dkField), sname, noAlias]) - elif field.bitsize != 0: - result.addf("\t$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, rope($field.bitsize), noAlias]) + typ = getTypeDescWeak(m, field.loc.t, check, dkField) else: + typ = getTypeDescAux(m, field.loc.t, check, dkField) # don't use fieldType here because we need the # tyGenericInst for C++ template support let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags) if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)): - var initializer = genCppInitializer(m, nil, fieldType) - result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer]) - else: - result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias]) + var didGenTemp = false + initializer = genCppInitializer(m, nil, fieldType, didGenTemp) + result.addField(field, sname, typ, isFlexArray, initializer) else: internalError(m.config, n.info, "genRecordFieldsAux()") proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false) -proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope = - result = newRopeAppender() +proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) = genRecordFieldsAux(m, typ.n, typ, check, result) if typ.itemId in m.g.graph.memberProcsPerType: let procs = m.g.graph.memberProcsPerType[typ.itemId] @@ -762,95 +769,41 @@ proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope = genMemberProcHeader(m, prc, header, false, true) result.addf "$1;$n", [header] if isCtorGen and not isDefaultCtorGen: - var ch: IntSet + var ch: IntSet = default(IntSet) result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)] proc fillObjectFields*(m: BModule; typ: PType) = # sometimes generic objects are not consistently merged. We patch over # this fact here. var check = initIntSet() - discard getRecordFields(m, typ, check) + var ignored = newBuilder("") + addRecordFields(ignored, m, typ, check) proc mangleDynLibProc(sym: PSym): Rope -proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope, - check: var IntSet, hasField:var bool): Rope = - result = "" - if typ.kind == tyObject: - if typ[0] == nil: - if lacksMTypeField(typ): - appcg(m, result, " {$n", []) - else: - if optTinyRtti in m.config.globalOptions: - appcg(m, result, " {$n#TNimTypeV2* m_type;$n", []) - else: - appcg(m, result, " {$n#TNimType* m_type;$n", []) - hasField = true - elif m.compileToCpp: - appcg(m, result, " : public $1 {$n", [baseType]) - if typ.isException and m.config.exc == excCpp: - when false: - appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions - if typ.sym.magic == mException: - # Add cleanup destructor to Exception base class - appcg(m, result, "~$1();$n", [name]) - # define it out of the class body and into the procs section so we don't have to - # artificially forward-declare popCurrentExceptionEx (very VERY troublesome for HCR) - appcg(m, cfsProcs, "inline $1::~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name]) - hasField = true - else: - appcg(m, result, " {$n $1 Sup;$n", [baseType]) - hasField = true - else: - result.addf(" {$n", [name]) - proc getRecordDesc(m: BModule; typ: PType, name: Rope, check: var IntSet): Rope = # declare the record: - var hasField = false - var structOrUnion: string - if tfPacked in typ.flags: - if hasAttribute in CC[m.config.cCompiler].props: - structOrUnion = structOrUnion(typ) & " __attribute__((__packed__))" - else: - structOrUnion = "#pragma pack(push, 1)\L" & structOrUnion(typ) - else: - structOrUnion = structOrUnion(typ) var baseType: string = "" - if typ[0] != nil: - baseType = getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, dkField) + if typ.baseClass != nil: + baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField) if typ.sym == nil or sfCodegenDecl notin typ.sym.flags: - result = structOrUnion & " " & name - result.add(getRecordDescAux(m, typ, name, baseType, check, hasField)) - let desc = getRecordFields(m, typ, check) - if not hasField and typ.itemId notin m.g.graph.memberProcsPerType: - if desc == "": - result.add("\tchar dummy;\n") - elif typ.len == 1 and typ.n[0].kind == nkSym: - let field = typ.n[0].sym - let fieldType = field.typ.skipTypes(abstractInst) - if fieldType.kind == tyUncheckedArray: - result.add("\tchar dummy;\n") - result.add(desc) - else: - result.add(desc) - result.add("};\L") + result = newBuilder("") + result.addStruct(m, typ, name, baseType): + result.addRecordFields(m, typ, check) else: - let desc = getRecordFields(m, typ, check) + var desc = newBuilder("") + desc.addRecordFields(m, typ, check) result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType]) - if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props: - result.add "#pragma pack(pop)\L" proc getTupleDesc(m: BModule; typ: PType, name: Rope, check: var IntSet): Rope = - result = "$1 $2 {$n" % [structOrUnion(typ), name] - var desc: Rope = "" - for i in 0..<typ.len: - desc.addf("$1 Field$2;$n", - [getTypeDescAux(m, typ[i], check, dkField), rope(i)]) - if desc == "": result.add("char dummy;\L") - else: result.add(desc) - result.add("};\L") + result = newBuilder("") + result.addStruct(m, typ, name, ""): + for i, a in typ.ikids: + result.addField( + name = "Field" & $i, + typ = getTypeDescAux(m, a, check, dkField)) proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool = # A helper proc for handling cppimport patterns, involving numeric @@ -872,25 +825,25 @@ proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool = proc resolveStarsInCppType(typ: PType, idx, stars: int): PType = # Make sure the index refers to one of the generic params of the type. # XXX: we should catch this earlier and report it as a semantic error. - if idx >= typ.len: + if idx >= typ.kidsLen: raiseAssert "invalid apostrophe type parameter index" result = typ[idx] for i in 1..stars: - if result != nil and result.len > 0: - result = if result.kind == tyGenericInst: result[1] + if result != nil and result.kidsLen > 0: + result = if result.kind == tyGenericInst: result[FirstGenericParamAt] else: result.elemType proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKind): Rope = let sig = hashType(t, m.config) if kind == dkParam: - result = getTypeDescWeak(m, t[0], check, kind) & "*" + result = getTypeDescWeak(m, t.elementType, check, kind) & "*" else: result = cacheGetType(m.typeCache, sig) if result == "": result = getTypeName(m, t, sig) m.typeCache[sig] = result - let elemType = getTypeDescWeak(m, t[0], check, kind) + let elemType = getTypeDescWeak(m, t.elementType, check, kind) m.s[cfsTypes].addf("typedef struct {$n$2* Field0;$nNI Field1;$n} $1;$n", [result, elemType]) @@ -907,12 +860,11 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym) let sig = hashType(origTyp, m.config) - result = "" # todo move `result = getTypePre(m, t, sig)` here ? + result = getTypePre(m, t, sig) defer: # defer is the simplest in this case if isImportedType(t) and not m.typeABICache.containsOrIncl(sig): addAbiCheck(m, t, result) - result = getTypePre(m, t, sig) if result != "" and t.kind != tyOpenArray: excl(check, t.id) if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst: @@ -922,7 +874,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes of tyRef, tyPtr, tyVar, tyLent: var star = if t.kind in {tyVar} and tfVarIsPtr notin origTyp.flags and compileToCpp(m): "&" else: "*" - var et = origTyp.skipTypes(abstractInst).lastSon + var et = origTyp.skipTypes(abstractInst).elementType var etB = et.skipTypes(abstractInst) if mapType(m.config, t, kind == dkParam) == ctPtrToArray and (etB.kind != tyOpenArray or kind == dkParam): if etB.kind == tySet: @@ -1014,19 +966,15 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes assert(cacheGetType(m.typeCache, sig) == "") m.typeCache[sig] = result & seqStar(m) if not isImportedType(t): - if skipTypes(t[0], typedescInst).kind != tyEmpty: - const - cppSeq = "struct $2 : #TGenericSeq {$n" - cSeq = "struct $2 {$n" & - " #TGenericSeq Sup;$n" - if m.compileToCpp: - appcg(m, m.s[cfsSeqTypes], - cppSeq & " $1 data[SEQ_DECL_SIZE];$n" & - "};$n", [getTypeDescAux(m, t[0], check, kind), result]) - else: - appcg(m, m.s[cfsSeqTypes], - cSeq & " $1 data[SEQ_DECL_SIZE];$n" & - "};$n", [getTypeDescAux(m, t[0], check, kind), result]) + if skipTypes(t.elementType, typedescInst).kind != tyEmpty: + var struct = newBuilder("") + let baseType = cgsymValue(m, "TGenericSeq") + struct.addSimpleStruct(m, name = result, baseType = baseType): + struct.addField( + name = "data", + typ = getTypeDescAux(m, t.elementType, check, kind), + isFlexArray = true) + m.s[cfsSeqTypes].add struct else: result = rope("TGenericSeq") result.add(seqStar(m)) @@ -1034,7 +982,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes result = getTypeName(m, origTyp, sig) m.typeCache[sig] = result if not isImportedType(t): - let foo = getTypeDescAux(m, t[0], check, kind) + let foo = getTypeDescAux(m, t.elementType, check, kind) m.s[cfsTypes].addf("typedef $1 $2[1];$n", [foo, result]) of tyArray: var n: BiggestInt = toInt64(lengthOrd(m.config, t)) @@ -1042,9 +990,9 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes result = getTypeName(m, origTyp, sig) m.typeCache[sig] = result if not isImportedType(t): - let foo = getTypeDescAux(m, t[1], check, kind) + let e = getTypeDescAux(m, t.elementType, check, kind) m.s[cfsTypes].addf("typedef $1 $2[$3];$n", - [foo, result, rope(n)]) + [e, result, rope(n)]) of tyObject, tyTuple: let tt = origTyp.skipTypes({tyDistinct}) if isImportedCppType(t) and tt.kind == tyGenericInst: @@ -1079,9 +1027,9 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes result.add cppName.substr(chunkStart) else: result = cppNameAsRope & "<" - for i in 1..<tt.len-1: - if i > 1: result.add(" COMMA ") - addResultType(tt[i]) + for needsComma, a in tt.genericInstParams: + if needsComma: result.add(" COMMA ") + addResultType(a) result.add("> ") # always call for sideeffects: assert t.kind != tyTuple @@ -1112,8 +1060,8 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes of tySet: # Don't use the imported name as it may be scoped: 'Foo::SomeKind' result = rope("tySet_") - t.lastSon.typeName(result) - result.add $t.lastSon.hashType(m.config) + t.elementType.typeName(result) + result.add $t.elementType.hashType(m.config) m.typeCache[sig] = result if not isImportedType(t): let s = int(getSize(m.config, t)) @@ -1123,7 +1071,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes [result, rope(getSize(m.config, t))]) of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned, tyUserTypeClass, tyUserTypeClassInst, tyInferred: - result = getTypeDescAux(m, lastSon(t), check, kind) + result = getTypeDescAux(m, skipModifier(t), check, kind) else: internalError(m.config, "getTypeDescAux(" & $t.kind & ')') result = "" @@ -1175,12 +1123,15 @@ proc isReloadable(m: BModule; prc: PSym): bool = proc isNonReloadable(m: BModule; prc: PSym): bool = return m.hcrOn and sfNonReloadable in prc.flags -proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual: var bool; isCtor: bool, isFunctor=false) = +proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual, isStatic: var bool; isCtor: bool, isFunctor=false) = var afterParams: string = "" if scanf(val, "$*($*)$s$*", name, params, afterParams): if name.strip() == "operator" and params == "": #isFunctor? - parseVFunctionDecl(afterParams, name, params, retType, superCall, isFnConst, isOverride, isMemberVirtual, isCtor, true) + parseVFunctionDecl(afterParams, name, params, retType, superCall, isFnConst, isOverride, isMemberVirtual, isStatic, isCtor, true) return + if name.find("static ") > -1: + isStatic = true + name = name.replace("static ", "") isFnConst = afterParams.find("const") > -1 isOverride = afterParams.find("override") > -1 isMemberVirtual = name.find("virtual ") > -1 @@ -1195,7 +1146,7 @@ proc parseVFunctionDecl(val: string; name, params, retType, superCall: var strin params = "(" & params & ")" -proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl : bool = false) = +proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl: bool = false) = assert sfCppMember * prc.flags != {} let isCtor = sfConstructor in prc.flags var check = initIntSet() @@ -1204,17 +1155,17 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = var memberOp = "#." #only virtual var typ: PType if isCtor: - typ = prc.typ[0] + typ = prc.typ.returnType else: - typ = prc.typ[1] + typ = prc.typ.firstParamType if typ.kind == tyPtr: - typ = typ[0] + typ = typ.elementType memberOp = "#->" var typDesc = getTypeDescWeak(m, typ, check, dkParam) let asPtrStr = rope(if asPtr: "_PTR" else: "") var name, params, rettype, superCall: string = "" - var isFnConst, isOverride, isMemberVirtual: bool = false - parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isCtor) + var isFnConst, isOverride, isMemberVirtual, isStatic: bool = false + parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isStatic, isCtor) genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false) let isVirtual = sfVirtual in prc.flags or isMemberVirtual var fnConst, override: string = "" @@ -1223,6 +1174,8 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = if isFnConst: fnConst = " const" if isFwdDecl: + if isStatic: + result.add "static " if isVirtual: rettype = "virtual " & rettype if isOverride: @@ -1230,7 +1183,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = superCall = "" else: if not isCtor: - prc.loc.r = "$1$2(@)" % [memberOp, name] + prc.loc.snippet = "$1$2(@)" % [memberOp, name] elif superCall != "": superCall = " : " & superCall @@ -1251,7 +1204,7 @@ proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) # handle the 2 options for hotcodereloading codegen - function pointer # (instead of forward declaration) or header for function body with "_actual" postfix let asPtrStr = rope(if asPtr: "_PTR" else: "") - var name = prc.loc.r + var name = prc.loc.snippet if not asPtr and isReloadable(m, prc): name.add("_actual") # careful here! don't access ``prc.ast`` as that could reload large parts of @@ -1333,8 +1286,8 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; proc genTypeInfoAux(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) = var base: Rope - if typ.len > 0 and typ.lastSon != nil: - var x = typ.lastSon + if typ.hasElementType and typ.last != nil: + var x = typ.last if typ.kind == tyObject: x = x.skipTypes(skipPtrs) if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x): base = rope("0") @@ -1391,13 +1344,13 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope; var tmp = discriminatorTableName(m, typ, field) var L = lengthOrd(m.config, field.typ) assert L > 0 - if field.loc.r == "": fillObjectFields(m, typ) + if field.loc.snippet == "": fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 3;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" & - "$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.r, + "$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.snippet, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s), tmp, rope(L)]) @@ -1429,41 +1382,38 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope; # Do not produce code for void types if isEmptyType(field.typ): return if field.bitsize == 0: - if field.loc.r == "": fillObjectFields(m, typ) + if field.loc.snippet == "": fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 1;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n", [expr, getTypeDesc(m, origType, dkVar), - field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)]) + field.loc.snippet, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)]) else: internalError(m.config, n.info, "genObjectFields") proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) = - if typ.kind == tyObject: - if incompleteType(typ): - localError(m.config, info, "request for RTTI generation for incomplete object: " & - typeToString(typ)) - genTypeInfoAux(m, typ, origType, name, info) - else: - genTypeInfoAuxBase(m, typ, origType, name, rope("0"), info) + assert typ.kind == tyObject + if incompleteType(typ): + localError(m.config, info, "request for RTTI generation for incomplete object: " & + typeToString(typ)) + genTypeInfoAux(m, typ, origType, name, info) var tmp = getNimNode(m) - if not isImportedType(typ): + if (not isImportedType(typ)) or tfCompleteStruct in typ.flags: genObjectFields(m, typ, origType, typ.n, tmp, info) m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), tmp]) - var t = typ[0] + var t = typ.baseClass while t != nil: t = t.skipTypes(skipPtrs) t.flags.incl tfObjHasKids - t = t[0] + t = t.baseClass proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) = genTypeInfoAuxBase(m, typ, typ, name, rope("0"), info) var expr = getNimNode(m) - if typ.len > 0: - var tmp = getTempName(m) & "_" & $typ.len - genTNimNodeArray(m, tmp, rope(typ.len)) - for i in 0..<typ.len: - var a = typ[i] + if not typ.isEmptyTupleType: + var tmp = getTempName(m) & "_" & $typ.kidsLen + genTNimNodeArray(m, tmp, rope(typ.kidsLen)) + for i, a in typ.ikids: var tmp2 = getNimNode(m) m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(i), tmp2]) m.s[cfsTypeInit3].addf("$1.kind = 1;$n" & @@ -1472,10 +1422,10 @@ proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) "$1.name = \"Field$3\";$n", [tmp2, getTypeDesc(m, origType, dkVar), rope(i), genTypeInfoV1(m, a, info)]) m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", - [expr, rope(typ.len), tmp]) + [expr, rope(typ.kidsLen), tmp]) else: m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2;$n", - [expr, rope(typ.len)]) + [expr, rope(typ.kidsLen)]) m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), expr]) proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) = @@ -1520,14 +1470,14 @@ proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) = m.s[cfsTypeInit3].addf("$1.flags = 1<<2;$n", [tiNameForHcr(m, name)]) proc genSetInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) = - assert(typ[0] != nil) + assert(typ.elementType != nil) genTypeInfoAux(m, typ, typ, name, info) var tmp = getNimNode(m) m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 0;$n$3.node = &$1;$n", [tmp, rope(firstOrd(m.config, typ)), tiNameForHcr(m, name)]) proc genArrayInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) = - genTypeInfoAuxBase(m, typ, typ, name, genTypeInfoV1(m, typ[1], info), info) + genTypeInfoAuxBase(m, typ, typ, name, genTypeInfoV1(m, typ.elementType, info), info) proc fakeClosureType(m: BModule; owner: PSym): PType = # we generate the same RTTI as for a tuple[pointer, ref tuple[]] @@ -1543,7 +1493,7 @@ include ccgtrav proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = genProc(m, s) m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", - [result, s.loc.r]) + [result, s.loc.snippet]) proc declareNimType(m: BModule; name: string; str: Rope, module: int) = let nr = rope(name) @@ -1596,14 +1546,14 @@ proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TType n[paramsPos] = result.typ.n let body = newNodeI(nkStmtList, info) let castType = makePtrType(typ, idgen) - if theProc.typ[1].kind != tyVar: + if theProc.typ.firstParamType.kind != tyVar: body.add newTreeI(nkCall, info, newSymNode(theProc), newDeref(newTreeIT( nkCast, info, castType, newNodeIT(nkType, info, castType), newSymNode(dest) )) ) else: - let addrOf = newNodeIT(nkAddr, info, theProc.typ[1]) + let addrOf = newNodeIT(nkHiddenAddr, info, theProc.typ.firstParamType) addrOf.add newDeref(newTreeIT( nkCast, info, castType, newNodeIT(nkType, info, castType), newSymNode(dest) @@ -1631,10 +1581,10 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: let wrapper = generateRttiDestructor(m.g.graph, t, theProc.owner, attachedDestructor, theProc.info, m.idgen, theProc) genProc(m, wrapper) - result.add wrapper.loc.r + result.add wrapper.loc.snippet else: genProc(m, theProc) - result.add theProc.loc.r + result.add theProc.loc.snippet when false: if not canFormAcycle(m.g.graph, t) and op == attachedTrace: @@ -1682,7 +1632,7 @@ proc genVTable(seqs: seq[PSym]): string = result = "{" for i in 0..<seqs.len: if i > 0: result.add ", " - result.add "(void *) " & seqs[i].loc.r + result.add "(void *) " & seqs[i].loc.snippet result.add "}" proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) = @@ -1731,7 +1681,7 @@ proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLin m.s[cfsTypeInit3].add typeEntry - if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions: + if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions: discard genTypeInfoV1(m, t, info) proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) = @@ -1781,7 +1731,7 @@ proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineIn addf(typeEntry, ", .flags = $1};$n", [rope(flags)]) m.s[cfsVars].add typeEntry - if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions: + if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions: discard genTypeInfoV1(m, t, info) proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope = @@ -1827,7 +1777,7 @@ proc openArrayToTuple(m: BModule; t: PType): PType = result = newType(tyTuple, m.idgen, t.owner) let p = newType(tyPtr, m.idgen, t.owner) let a = newType(tyUncheckedArray, m.idgen, t.owner) - a.add t.lastSon + a.add t.elementType p.add a result.add p result.add getSysType(m.g.graph, t.owner.info, tyInt) @@ -1837,8 +1787,7 @@ proc typeToC(t: PType): string = ## to be unique. let s = typeToString(t) result = newStringOfCap(s.len) - for i in 0..<s.len: - let c = s[i] + for c in s: case c of 'a'..'z': result.add c @@ -1909,11 +1858,11 @@ proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope = of tyPointer, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64, tyVar, tyLent: genTypeInfoAuxBase(m, t, t, result, rope"0", info) of tyStatic: - if t.n != nil: result = genTypeInfoV1(m, lastSon t, info) + if t.n != nil: result = genTypeInfoV1(m, skipModifier t, info) else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')') of tyUserTypeClasses: internalAssert m.config, t.isResolvedUserTypeClass - return genTypeInfoV1(m, t.lastSon, info) + return genTypeInfoV1(m, t.skipModifier, info) of tyProc: if t.callConv != ccClosure: genTypeInfoAuxBase(m, t, t, result, rope"0", info) @@ -1972,8 +1921,11 @@ proc genTypeSection(m: BModule, n: PNode) = if len(n[i]) == 0: continue if n[i][0].kind != nkPragmaExpr: continue for p in 0..<n[i][0].len: - if (n[i][0][p].kind != nkSym): continue - if sfExportc in n[i][0][p].sym.flags: - discard getTypeDescAux(m, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind)) + if (n[i][0][p].kind notin {nkSym, nkPostfix}): continue + var s = n[i][0][p] + if s.kind == nkPostfix: + s = n[i][0][p][1] + if {sfExportc, sfCompilerProc} * s.sym.flags == {sfExportc}: + discard getTypeDescAux(m, s.typ, intSet, descKindFromSymKind(s.sym.kind)) if m.g.generatedHeader != nil: - discard getTypeDescAux(m.g.generatedHeader, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind)) + discard getTypeDescAux(m.g.generatedHeader, s.typ, intSet, descKindFromSymKind(s.sym.kind)) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index bfb429f82..c0e574186 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -11,9 +11,9 @@ import ast, types, msgs, wordrecg, - platform, trees, options, cgendata + platform, trees, options, cgendata, mangleutils -import std/[hashes, strutils] +import std/[hashes, strutils, formatfloat] when defined(nimPreviewSlimSystem): import std/assertions @@ -68,53 +68,6 @@ 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 @@ -126,10 +79,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 @@ -137,6 +90,9 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool = if s.typ.sym != nil and sfForward in s.typ.sym.flags: # forwarded objects are *always* passed by pointers for consistency! result = true + elif s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}: + # bug #23354: + result = false elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3): result = true # requested anyway elif (tfFinal in pt.flags) and (pt[0] == nil): @@ -153,3 +109,62 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool = result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or pt.kind == tySet and mapSetType(conf, pt) == ctArray) +proc encodeName*(name: string): string = + result = mangle(name) + result = $result.len & result + +proc makeUnique(m: BModule; s: PSym, name: string = ""): string = + result = if name == "": s.name.s else: name + result.add "__" + result.add m.g.graph.ifaces[s.itemId.module].uniqueName + result.add "_u" + result.add $s.itemId.item + +proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string = + #Module::Type + var name = s.name.s + if makeUnique: + name = makeUnique(m, s, name) + "N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E" + +proc encodeType*(m: BModule; t: PType): string = + result = "" + var kindName = ($t.kind)[2..^1] + kindName[0] = toLower($kindName[0])[0] + case t.kind + of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam: + result = encodeSym(m, t.sym) + of tyGenericInst, tyUserTypeClassInst, tyGenericBody: + result = encodeName(t[0].sym.name.s) + result.add "I" + for i in 1..<t.len - 1: + result.add encodeType(m, t[i]) + result.add "E" + of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc, + tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass: + result = + case t.kind: + of tySequence: encodeName("seq") + else: encodeName(kindName) + result.add "I" + for i in 0..<t.len: + let s = t[i] + if s.isNil: continue + result.add encodeType(m, s) + result.add "E" + of tyRange: + var val = "range_" + if t.n[0].typ.kind in {tyFloat..tyFloat128}: + val.addFloat t.n[0].floatVal + val.add "_" + val.addFloat t.n[1].floatVal + else: + val.add $t.n[0].intVal & "_" & $t.n[1].intVal + result = encodeName(val) + of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty: + result = encodeName(kindName) + of tyAlias, tyInferred, tyOwned: + result = encodeType(m, t.elementType) + else: + assert false, "encodeType " & $t.kind + diff --git a/compiler/cgen.nim b/compiler/cgen.nim index d22a6bdc2..091f5c842 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -15,7 +15,8 @@ import ccgutils, ropes, wordrecg, treetab, cgmeth, rodutils, renderer, cgendata, aliases, lowerings, ndi, lineinfos, pathutils, transf, - injectdestructors, astmsgs, modulepaths, backendpragmas + injectdestructors, astmsgs, modulepaths, pushpoppragmas, + mangleutils from expanddefaults import caseObjDefaultBranch @@ -32,6 +33,12 @@ 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`. @@ -65,8 +72,7 @@ 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, - r: "", flags: flags - ) + snippet: "", 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 @@ -74,7 +80,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.r == "": a.r = r + if a.snippet == "": a.snippet = r proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} = # fills the loc if it is not already initialized @@ -266,24 +272,28 @@ proc safeLineNm(info: TLineInfo): int = result = toLinenumber(info) if result < 0: result = 0 # negative numbers are not allowed in #line -proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) = +proc genPostprocessDir(field1, field2, field3: string): string = + result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd + +proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) = assert line >= 0 if optLineDir in conf.options and line > 0: - r.addf("\n#line $2 $1\n", - [rope(makeSingleLineCString(filename)), rope(line)]) + if fileIdx == InvalidFileIdx: + r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n")) + else: + r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n")) -proc genCLineDir(r: var Rope, filename: string, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) = +proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) = assert line >= 0 if optLineDir in p.config.options and line > 0: - if lastFileIndex == info.fileIndex: - r.addf("\n#line $1\n", [rope(line)]) + if fileIdx == InvalidFileIdx: + r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n")) else: - r.addf("\n#line $2 $1\n", - [rope(makeSingleLineCString(filename)), rope(line)]) + r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n")) proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) = if optLineDir in conf.options: - genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf) + genCLineDir(r, info.fileIndex, info.safeLineNm, conf) proc freshLineInfo(p: BProc; info: TLineInfo): bool = if p.lastLineInfo.line != info.line or @@ -298,7 +308,7 @@ proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) = if optLineDir in conf.options: let lastFileIndex = p.lastLineInfo.fileIndex if freshLineInfo(p, info): - genCLineDir(r, toFullPath(conf, info), info.safeLineNm, p, info, lastFileIndex) + genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex) proc genLineDir(p: BProc, t: PNode) = if p == p.module.preInitProc: return @@ -309,16 +319,11 @@ proc genLineDir(p: BProc, t: PNode) = let lastFileIndex = p.lastLineInfo.fileIndex let freshLine = freshLineInfo(p, t.info) if freshLine: - genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p, t.info, lastFileIndex) + genCLineDir(p.s(cpsStmts), t.info.fileIndex, line, p, t.info, lastFileIndex) if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx: if freshLine: - if lastFileIndex == t.info.fileIndex: - linefmt(p, cpsStmts, "nimln_($1);", - [line]) - else: - linefmt(p, cpsStmts, "nimlf_($1, $2);", - [line, quotedFilename(p.config, t.info)]) + line(p, cpsStmts, genPostprocessDir("nimln", $line, $t.info.fileIndex.int32)) proc accessThreadLocalVar(p: BProc, s: PSym) proc emulatedThreadVars(conf: ConfigRef): bool {.inline.} @@ -335,15 +340,15 @@ proc getTempName(m: BModule): Rope = proc rdLoc(a: TLoc): Rope = # 'read' location (deref if indirect) if lfIndirect in a.flags: - result = "(*" & a.r & ")" + result = "(*" & a.snippet & ")" else: - result = a.r + result = a.snippet proc addRdLoc(a: TLoc; result: var Rope) = if lfIndirect in a.flags: - result.add "(*" & a.r & ")" + result.add "(*" & a.snippet & ")" else: - result.add a.r + result.add a.snippet proc lenField(p: BProc): Rope {.inline.} = result = rope(if p.module.compileToCpp: "len" else: "Sup.len") @@ -368,6 +373,7 @@ proc dataField(p: BProc): Rope = proc genProcPrototype(m: BModule, sym: PSym) +include cbuilder include ccgliterals include ccgtypes @@ -380,22 +386,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.r & ")" + result.add "(&" & a.snippet & ")" else: - result.add a.r + result.add a.snippet proc addrLoc(conf: ConfigRef; a: TLoc): Rope = if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray: - result = "(&" & a.r & ")" + result = "(&" & a.snippet & ")" else: - result = a.r + result = a.snippet 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.r & ")" + result = "(&" & a.snippet & ")" else: - result = a.r + result = a.snippet proc rdCharLoc(a: TLoc): Rope = # read a location that may need a char-cast: @@ -406,6 +412,9 @@ proc rdCharLoc(a: TLoc): Rope = type TAssignmentFlag = enum needToCopy + needToCopySinkParam + needTempForOpenArray + needAssignCall TAssignmentFlags = set[TAssignmentFlag] proc genObjConstr(p: BProc, e: PNode, d: var TLoc) @@ -474,9 +483,12 @@ 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): return + if isImportedCppType(typ): + var didGenTemp = false + linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(loc), genCppInitializer(p.module, p, typ, didGenTemp)]) + return if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}: - assert loc.r != "" + assert loc.snippet != "" let atyp = skipTypes(loc.t, abstractInst) if atyp.kind in {tyVar, tyLent}: @@ -486,7 +498,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = elif not isComplexValueType(typ): if containsGcRef: var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) - nilLoc.r = rope("NIM_NIL") + nilLoc.snippet = rope("NIM_NIL") genRefAssign(p, loc, nilLoc) else: linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)]) @@ -524,13 +536,13 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = elif not isComplexValueType(typ): if containsGarbageCollectedRef(loc.t): var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) - nilLoc.r = rope("NIM_NIL") + nilLoc.snippet = 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): + if (not isTemp or containsGarbageCollectedRef(loc.t)) and not hasNoInit(loc.t): # don't use nimZeroMem for temporary values for performance if we can # avoid it: if not isOrHasImportedCppType(typ): @@ -552,13 +564,14 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = inc(p.labels) - result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, storage: OnStack, flags: {}) if p.module.compileToCpp and isOrHasImportedCppType(t): - linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r, - genCppInitializer(p.module, p, t)]) + var didGenTemp = false + linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.snippet, + genCppInitializer(p.module, p, t, didGenTemp)]) else: - linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r]) + linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.snippet]) constructLoc(p, result, not needsInit) when false: # XXX Introduce a compiler switch in order to detect these easily. @@ -571,16 +584,16 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc = inc(p.labels) - result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, storage: OnStack, flags: {}) - linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, dkVar), result.r, value]) + linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.snippet, value]) proc getIntTemp(p: BProc): TLoc = inc(p.labels) - result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, + result = TLoc(snippet: "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.r]) + linefmt(p, cpsLocals, "NI $1;$n", [result.snippet]) proc localVarDecl(p: BProc; n: PNode): Rope = result = "" @@ -602,9 +615,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.r) + result.add(s.loc.snippet) else: - result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r]) + result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet]) proc assignLocalVar(p: BProc, n: PNode) = #assert(s.loc.k == locNone) # not yet assigned @@ -613,7 +626,8 @@ 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): - decl.add genCppInitializer(p.module, p, n.typ) + var didGenTemp = false + decl.add genCppInitializer(p.module, p, n.typ, didGenTemp) decl.add ";" & nl line(p, cpsLocals, decl) @@ -644,22 +658,11 @@ 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.r, value]) + decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.snippet, value]) else: - decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) - -proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) + decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.snippet]) -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 genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool) proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = let s = n.sym @@ -673,7 +676,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.r = mangleDynLibProc(s) + s.loc.snippet = mangleDynLibProc(s) if value != "": internalError(p.config, n.info, ".dynlib variables cannot have a value") return @@ -704,19 +707,31 @@ 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.r]) + decl.addf(" $1;$n", [s.loc.snippet]) else: - decl.addf(" $1 = $2;$n", [s.loc.r, value]) + decl.addf(" $1 = $2;$n", [s.loc.snippet, value]) else: - decl.addf(" $1;$n", [s.loc.r]) + decl.addf(" $1;$n", [s.loc.snippet]) 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.r != "") + assert(s.loc.snippet != "") scopeMangledParam(p, s) proc fillProcLoc(m: BModule; n: PNode) = @@ -742,6 +757,7 @@ 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) @@ -768,15 +784,11 @@ $1define nimfr_(proc, file) \ TFrame FR_; \ FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_); - $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 nimln_(n) \ - FR_.line = n; +$1define nimln_(n) \ + FR_.line = n; - $1define nimlf_(n, file) \ - FR_.line = n; FR_.filename = file; +$1define nimlf_(n, file) \ + FR_.line = n; FR_.filename = file; """ if p.module.s[cfsFrameDefines].len == 0: @@ -840,7 +852,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = p.options.excl optStackTrace p.flags.incl nimErrorFlagDisabled var dest: TLoc = initLoc(locTemp, lib.path, OnStack) - dest.r = getTempName(m) + dest.snippet = getTempName(m) appcg(m, m.s[cfsDynLibInit],"$1 $2;$n", [getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)]) expr(p, lib.path, dest) @@ -858,7 +870,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.r is the external name! + # NOTE: sym.loc.snippet is the external name! result = rope(sym.name.s) else: result = rope(strutils.`%`("Dl_$1_", $sym.id)) @@ -866,10 +878,10 @@ proc mangleDynLibProc(sym: PSym): Rope = proc symInDynamicLib(m: BModule, sym: PSym) = var lib = sym.annex let isCall = isGetProcAddr(lib) - var extname = sym.loc.r + var extname = sym.loc.snippet if not isCall: loadDynamicLib(m, lib) var tmp = mangleDynLibProc(sym) - sym.loc.r = tmp # from now on we only need the internal name + sym.loc.snippet = tmp # from now on we only need the internal name sym.typ.sym = nil # generate a new name inc(m.labels, 2) if isCall: @@ -896,24 +908,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.r, getTypeDesc(m, sym.loc.t, dkVar)]) + m.s[cfsVars].addf("$2 $1;$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)]) proc varInDynamicLib(m: BModule, sym: PSym) = var lib = sym.annex - var extname = sym.loc.r + var extname = sym.loc.snippet loadDynamicLib(m, lib) incl(sym.loc.flags, lfIndirect) var tmp = mangleDynLibProc(sym) - sym.loc.r = tmp # from now on we only need the internal name + sym.loc.snippet = 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.r, getTypeDesc(m, sym.loc.t, dkVar)]) + [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)]) proc symInDynamicLibPartial(m: BModule, sym: PSym) = - sym.loc.r = mangleDynLibProc(sym) + sym.loc.snippet = mangleDynLibProc(sym) sym.typ.sym = nil # generate a new name proc cgsymImpl(m: BModule; sym: PSym) {.inline.} = @@ -936,7 +948,7 @@ proc cgsymValue(m: BModule, name: string): Rope = cgsymImpl m, sym else: rawMessage(m.config, errGenerated, "system module needs: " & name) - result = sym.loc.r + result = sym.loc.snippet if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}: result.addActualSuffixForHCR(m.module, sym) @@ -1032,7 +1044,7 @@ proc easyResultAsgn(n: PNode): PNode = type InitResultEnum = enum Unknown, InitSkippable, InitRequired -proc allPathsAsgnResult(n: PNode): InitResultEnum = +proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum = # Exceptions coming from calls don't have not be considered here: # # proc bar(): string = raise newException(...) @@ -1047,7 +1059,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum = # echo "a was not written to" # template allPathsInBranch(it) = - let a = allPathsAsgnResult(it) + let a = allPathsAsgnResult(p, it) case a of InitRequired: return InitRequired of InitSkippable: discard @@ -1059,14 +1071,20 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum = case n.kind of nkStmtList, nkStmtListExpr: for it in n: - result = allPathsAsgnResult(it) + result = allPathsAsgnResult(p, it) if result != Unknown: return result of nkAsgn, nkFastAsgn, nkSinkAsgn: if n[0].kind == nkSym and n[0].sym.kind == skResult: - if not containsResult(n[1]): result = InitSkippable + if not containsResult(n[1]): + if allPathsAsgnResult(p, n[1]) == InitRequired: + result = InitRequired + else: + result = InitSkippable else: result = InitRequired elif containsResult(n): result = InitRequired + else: + result = allPathsAsgnResult(p, n[1]) of nkReturnStmt: if n.len > 0: if n[0].kind == nkEmpty and result != InitSkippable: @@ -1075,7 +1093,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum = # initialized. This avoids cases like #9286 where this heuristic lead to # wrong code being generated. result = InitRequired - else: result = allPathsAsgnResult(n[0]) + else: result = allPathsAsgnResult(p, n[0]) of nkIfStmt, nkIfExpr: var exhaustive = false result = InitSkippable @@ -1101,9 +1119,9 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum = of nkWhileStmt: # some dubious code can assign the result in the 'while' # condition and that would be fine. Everything else isn't: - result = allPathsAsgnResult(n[0]) + result = allPathsAsgnResult(p, n[0]) if result == Unknown: - result = allPathsAsgnResult(n[1]) + result = allPathsAsgnResult(p, n[1]) # we cannot assume that the 'while' loop is really executed at least once: if result == InitSkippable: result = Unknown of harmless: @@ -1128,9 +1146,21 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum = allPathsInBranch(n[0]) for i in 1..<n.len: if n[i].kind == nkFinally: - result = allPathsAsgnResult(n[i].lastSon) + result = allPathsAsgnResult(p, n[i].lastSon) else: allPathsInBranch(n[i].lastSon) + of nkCallKinds: + if canRaiseDisp(p, n[0]): + result = InitRequired + else: + for i in 0..<n.safeLen: + allPathsInBranch(n[i]) + of nkRaiseStmt: + result = InitRequired + of nkChckRangeF, nkChckRange64, nkChckRange: + # TODO: more checks might need to be covered like overflow, indexDefect etc. + # bug #22852 + result = InitRequired else: for i in 0..<n.safeLen: allPathsInBranch(n[i]) @@ -1171,7 +1201,7 @@ proc genProcAux*(m: BModule, prc: PSym) = let tmpInfo = prc.info discard freshLineInfo(p, prc.info) - if sfPure notin prc.flags and prc.typ[0] != nil: + if sfPure notin prc.flags and prc.typ.returnType != nil: if resultPos >= prc.ast.len: internalError(m.config, prc.info, "proc has no result symbol") let resNode = prc.ast[resultPos] @@ -1185,9 +1215,9 @@ proc genProcAux*(m: BModule, prc: PSym) = else: # declare the result symbol: assignLocalVar(p, resNode) - assert(res.loc.r != "") + assert(res.loc.snippet != "") if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and - allPathsAsgnResult(procBody) == InitSkippable: + allPathsAsgnResult(p, procBody) == InitSkippable: # In an ideal world the codegen could rely on injectdestructors doing its job properly # and then the analysis step would not be required. discard "result init optimized out" @@ -1197,9 +1227,10 @@ 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[0]) + assignParam(p, res, prc.typ.returnType) # We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)' # to 'unsureAsgn(result, x)' # Sketch why this is correct: If 'result' points to a stack location @@ -1207,7 +1238,7 @@ proc genProcAux*(m: BModule, prc: PSym) = # global is either 'nil' or points to valid memory and so the RC operation # succeeds without touching not-initialized memory. if sfNoInit in prc.flags: discard - elif allPathsAsgnResult(procBody) == InitSkippable: discard + elif allPathsAsgnResult(p, procBody) == InitSkippable: discard else: resetLoc(p, res.loc) if skipTypes(res.typ, abstractInst).kind == tyArray: @@ -1217,7 +1248,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[0]) + assignParam(p, param, prc.typ.returnType) closureSetup(p, prc) genProcBody(p, procBody) @@ -1261,7 +1292,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.r, prc.loc.r & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)]) + [prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)]) proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = result = (sfCompileToCpp in m.module.flags and @@ -1321,7 +1352,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.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)]) + [prc.loc.snippet, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)]) else: symInDynamicLibPartial(m, prc) elif prc.typ.callConv == ccInline: @@ -1338,8 +1369,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.r = nil - # prc.loc.r = mangleName(m, prc) + # prc.loc.snippet = nil + # prc.loc.snippet = mangleName(m, prc) genProcPrototype(m, prc) genProcAux(m, prc) elif sfImportc notin prc.flags: @@ -1351,7 +1382,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.r, getProcTypeCast(m, prc), getModuleDllPath(m, prc)]) + [prc.loc.snippet, 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 @@ -1404,7 +1435,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.r != "") + assert(sym.loc.snippet != "") incl(m.declaredThings, sym.id) if sfThread in sym.flags: declareThreadVar(m, sym, true) @@ -1418,9 +1449,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.r]) + m.s[cfsVars].addf(" $1;$n", [sym.loc.snippet]) if m.hcrOn: m.initProc.procSec(cpsLocals).addf( - "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, + "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)]) proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} = @@ -1453,15 +1484,13 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope = ## Returns a mangled module name. - result = "" - result.add mangleModuleName(conf, filename).mangle + result = 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 = "" - result.add mangleModuleName(m.g.config, m.filename).mangle + result = mangleModuleName(m.g.config, m.filename).mangle proc getSomeInitName(m: BModule, suffix: string): Rope = if not m.hcrOn: @@ -1797,7 +1826,7 @@ proc genDatInitCode(m: BModule) = # we don't want to break into such init code - could happen if a line # directive from a function written by the user spills after itself - genCLineDir(prc, "generated_not_to_break_here", 999999, m.config) + genCLineDir(prc, InvalidFileIdx, 999999, m.config) for i in cfsTypeInit1..cfsDynLibInit: if m.s[i].len != 0: @@ -1819,11 +1848,11 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R var extname = prefix & sym var tmp = mangleDynLibProc(prc) - prc.loc.r = tmp + prc.loc.snippet = tmp prc.typ.sym = nil if not containsOrIncl(m.declaredThings, prc.id): - m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, dkVar)]) + m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.snippet, 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)] @@ -1838,7 +1867,7 @@ proc genInitCode(m: BModule) = [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname] # we don't want to break into such init code - could happen if a line # directive from a function written by the user spills after itself - genCLineDir(prc, "generated_not_to_break_here", 999999, m.config) + genCLineDir(prc, InvalidFileIdx, 999999, m.config) if m.typeNodes > 0: if m.hcrOn: appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName]) @@ -1908,9 +1937,6 @@ 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", []) @@ -1953,6 +1979,40 @@ proc genInitCode(m: BModule) = registerModuleToMain(m.g, m) +proc postprocessCode(conf: ConfigRef, r: var Rope) = + # find the first directive + var f = r.find(postprocessDirStart) + if f == -1: + return + + var + nimlnDirLastF = "" + + var res: Rope = r.substr(0, f - 1) + while f != -1: + var + e = r.find(postprocessDirEnd, f + 1) + dir = r.substr(f + 1, e - 1).split(postprocessDirSep) + case dir[0] + of "nimln": + if dir[2] == nimlnDirLastF: + res.add("nimln_(" & dir[1] & ");") + else: + res.add("nimlf_(" & dir[1] & ", " & quotedFilename(conf, dir[2].parseInt.FileIndex) & ");") + nimlnDirLastF = dir[2] + else: + raiseAssert "unexpected postprocess directive" + + # find the next directive + f = r.find(postprocessDirStart, e + 1) + # copy the code until the next directive + if f != -1: + res.add(r.substr(e + 1, f - 1)) + else: + res.add(r.substr(e + 1)) + + r = res + proc genModule(m: BModule, cfile: Cfile): Rope = var moduleIsEmpty = true @@ -1981,9 +2041,17 @@ proc genModule(m: BModule, cfile: Cfile): Rope = if m.config.cppCustomNamespace.len > 0: closeNamespaceNim(result) + if optLineDir in m.config.options: + var srcFileDefs = "" + for fi in 0..m.config.m.fileInfos.high: + srcFileDefs.add("#define FX_" & $fi & " " & makeSingleLineCString(toFullPath(m.config, fi.FileIndex)) & "\n") + result = srcFileDefs & result + if moduleIsEmpty: result = "" + postprocessCode(m.config, result) + proc initProcOptions(m: BModule): TOptions = let opts = m.config.options if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts @@ -2183,6 +2251,22 @@ proc updateCachedModule(m: BModule) = cf.flags = {CfileFlag.Cached} addFileToCompile(m.config, cf) +proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym = + let procname = getIdent(graph.cache, "NimDestroyGlobals") + result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info) + result.typ = newProcType(m.module.info, m.idgen, m.module.owner) + result.typ.callConv = ccCDecl + incl result.flags, sfExportc + result.loc.snippet = "NimDestroyGlobals" + 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: @@ -2194,6 +2278,13 @@ 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 diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 30d778bc2..5368e9dc7 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -88,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] + optionsStack*: seq[(TOptions, TNoteKinds)] 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 @@ -137,6 +137,7 @@ type # unconditionally... # nimtvDeps is VERY hard to cache because it's # not a list of IDs nor can it be made to be one. + mangledPrcs*: HashSet[string] TCGen = object of PPassContext # represents a C source file s*: TCFileSections # sections of the C file @@ -150,7 +151,7 @@ type typeABICache*: HashSet[SigHash] # cache for ABI checks; reusing typeCache # would be ideal but for some reason enums # don't seem to get cached so it'd generate - # 1 ABI check per occurence in code + # 1 ABI check per occurrence in code forwTypeCache*: TypeCache # cache for forward declarations of types declaredThings*: IntSet # things we have declared in this .c file declaredProtos*: IntSet # prototypes we have declared in this .c file diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 833bb6fe5..ca97d0494 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -69,21 +69,23 @@ type proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult = result = No if a.name.id != b.name.id: return - if a.typ.len != b.typ.len: + if a.typ.signatureLen != b.typ.signatureLen: return - for i in 1..<a.typ.len: - var aa = a.typ[i] - var bb = b.typ[i] + var i = 0 + for x, y in paramTypePairs(a.typ, b.typ): + inc i + var aa = x + var bb = y 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.lastSon - bb = bb.lastSon + aa = aa.elementType + bb = bb.elementType else: break - if sameType(a.typ[i], b.typ[i]): + if sameType(x, y): if aa.kind == tyObject and result != Invalid: result = Yes elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods): @@ -102,10 +104,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[0], b.typ[0], {IgnoreFlags}): - if b.typ[0] != nil and b.typ[0].kind == tyUntyped: + if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}): + if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped: # infer 'auto' from the base to make it consistent: - b.typ[0] = a.typ[0] + b.typ.setReturnType a.typ.returnType else: return No @@ -131,8 +133,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.r = "" - if s.typ[0] != nil: + disp.loc.snippet = "" + if s.typ.returnType != nil: if disp.ast.len > resultPos: disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen) else: @@ -157,9 +159,14 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) = proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) = var witness: PSym = nil - if s.typ[1].owner.getModule != s.getModule and vtables in g.config.features and not g.config.isDefined("nimInternalNonVtablesTesting"): + if s.typ.firstParamType.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[1].typeToString() & ")") + "` 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") + for i in 0..<g.methods.len: let disp = g.methods[i].dispatcher case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions) @@ -179,10 +186,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[1].skipTypes(skipPtrs).itemId notin g.bucketTable: - g.bucketTable[s.typ[1].skipTypes(skipPtrs).itemId] = 1 + if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable: + g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1 else: - g.bucketTable.inc(s.typ[1].skipTypes(skipPtrs).itemId) + g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId) g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen))) #echo "adding ", s.info if witness != nil: @@ -203,7 +210,7 @@ proc relevantCol*(methods: seq[PSym], col: int): bool = proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int = result = 0 - for col in 1..<a.typ.len: + for col in FirstParamAt..<a.typ.signatureLen: if contains(relevantCols, col): var aa = skipTypes(a.typ[col], skipPtrs) var bb = skipTypes(b.typ[col], skipPtrs) @@ -233,13 +240,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.len + var paramLen = base.typ.signatureLen 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 1..<paramLen: + for col in FirstParamAt..<paramLen: if contains(relevantCols, col): let param = base.typ.n[col].sym if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: @@ -248,7 +255,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 1..<paramLen: + for col in FirstParamAt..<paramLen: if contains(relevantCols, col): var isn = newNodeIT(nkCall, base.info, boolType) isn.add newSymNode(iss) @@ -263,7 +270,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; cond = a else: cond = isn - let retTyp = base.typ[0] + let retTyp = base.typ.returnType let call = newNodeIT(nkCall, base.info, retTyp) call.add newSymNode(curr) for col in 1..<paramLen: @@ -292,7 +299,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 1..<g.methods[bucket].methods[0].typ.len: + for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen: 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 diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 4e4523601..8bdd04ca7 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -18,7 +18,8 @@ # dec a # # Should be transformed to: -# STATE0: +# case :state +# of 0: # if a > 0: # echo "hi" # :state = 1 # Next state @@ -26,19 +27,14 @@ # else: # :state = 2 # Next state # break :stateLoop # Proceed to the next state -# STATE1: +# of 1: # dec a # :state = 0 # Next state # break :stateLoop # Proceed to the next state -# STATE2: +# of 2: # :state = -1 # End of execution - -# The transformation should play well with lambdalifting, however depending -# on situation, it can be called either before or after lambdalifting -# transformation. As such we behave slightly differently, when accessing -# iterator state, or using temp variables. If lambdalifting did not happen, -# we just create local variables, so that they will be lifted further on. -# Otherwise, we utilize existing env, created by lambdalifting. +# else: +# return # Lambdalifting treats :state variable specially, it should always end up # as the first field in env. Currently C codegen depends on this behavior. @@ -104,12 +100,13 @@ # Is transformed to (yields are left in place for example simplicity, # in reality the code is subdivided even more, as described above): # -# STATE0: # Try +# case :state +# of 0: # Try # yield 0 # raise ... # :state = 2 # What would happen should we not raise # break :stateLoop -# STATE1: # Except +# of 1: # Except # yield 1 # :tmpResult = 3 # Return # :unrollFinally = true # Return @@ -117,7 +114,7 @@ # break :stateLoop # :state = 2 # What would happen should we not return # break :stateLoop -# STATE2: # Finally +# of 2: # Finally # yield 2 # if :unrollFinally: # This node is created by `newEndFinallyNode` # if :curExc.isNil: @@ -130,6 +127,8 @@ # raise # state = -1 # Goto next state. In this case we just exit # break :stateLoop +# else: +# return import ast, msgs, idents, @@ -145,12 +144,11 @@ type Ctx = object g: ModuleGraph fn: PSym - stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting tmpResultSym: PSym # Used when we return, but finally has to interfere unrollFinallySym: PSym # Indicates that we're unrolling finally states (either exception happened or premature return) curExcSym: PSym # Current exception - states: seq[PNode] # The resulting states. Every state is an nkState node. + states: seq[tuple[label: int, body: PNode]] # The resulting states. blockLevel: int # Temp used to transform break and continue stmts stateLoopLabel: PSym # Label to break on, when jumping between states. exitStateIdx: int # index of the last state @@ -162,15 +160,23 @@ type nearestFinally: int # Index of the nearest finally block. For try/except it # is their finally. For finally it is parent finally. Otherwise -1 idgen: IdGenerator + varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states + stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting + # remove if -d:nimOptIters is default, treating it as always nil + nimOptItersEnabled: bool # tracks if -d:nimOptIters is enabled + # should be default when issues are fixed, see #24094 const nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt, nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs + emptyStateLabel = -1 + localNotSeen = -1 + localRequiresLifting = -2 proc newStateAccess(ctx: var Ctx): PNode = if ctx.stateVarSym.isNil: result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), - getStateField(ctx.g, ctx.fn), ctx.fn.info) + getStateField(ctx.g, ctx.fn), ctx.fn.info) else: result = newSymNode(ctx.stateVarSym) @@ -187,7 +193,8 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode = proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.idgen, ctx.fn, ctx.fn.info) result.typ = typ - assert(not typ.isNil) + result.flags.incl sfNoInit + assert(not typ.isNil, "Env var needs a type") if not ctx.stateVarSym.isNil: # We haven't gone through labmda lifting yet, so just create a local var, @@ -198,7 +205,7 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = else: let envParam = getEnvParam(ctx.fn) # let obj = envParam.typ.lastSon - result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache, ctx.idgen) + result = addUniqueField(envParam.typ.elementType, result, ctx.g.cache, ctx.idgen) proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode = if ctx.stateVarSym.isNil: @@ -206,9 +213,12 @@ proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode = else: result = newSymNode(s) +proc newTempVarAccess(ctx: Ctx, s: PSym): PNode = + result = newSymNode(s, ctx.fn.info) + proc newTmpResultAccess(ctx: var Ctx): PNode = if ctx.tmpResultSym.isNil: - ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ[0]) + ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ.returnType) ctx.newEnvVarAccess(ctx.tmpResultSym) proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode = @@ -228,10 +238,7 @@ proc newState(ctx: var Ctx, n, gotoOut: PNode): int = result = ctx.states.len let resLit = ctx.g.newIntLit(n.info, result) - let s = newNodeI(nkState, n.info) - s.add(resLit) - s.add(n) - ctx.states.add(s) + ctx.states.add((result, n)) ctx.exceptionTable.add(ctx.curExcHandlingState) if not gotoOut.isNil: @@ -250,9 +257,26 @@ proc addGotoOut(n: PNode, gotoOut: PNode): PNode = if result.len == 0 or result[^1].kind != nkGotoState: result.add(gotoOut) -proc newTempVar(ctx: var Ctx, typ: PType): PSym = - result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ) +proc newTempVarDef(ctx: Ctx, s: PSym, initialValue: PNode): PNode = + var v = initialValue + if v == nil: + v = ctx.g.emptyNode + newTree(nkVarSection, newTree(nkIdentDefs, newSymNode(s), ctx.g.emptyNode, v)) + +proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode + +proc newTempVar(ctx: var Ctx, typ: PType, parent: PNode, initialValue: PNode = nil): PSym = + if ctx.nimOptItersEnabled: + result = newSym(skVar, getIdent(ctx.g.cache, ":tmpSlLower" & $ctx.tempVarId), ctx.idgen, ctx.fn, ctx.fn.info) + else: + result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ) inc ctx.tempVarId + result.typ = typ + assert(not typ.isNil, "Temp var needs a type") + if ctx.nimOptItersEnabled: + parent.add(ctx.newTempVarDef(result, initialValue)) + elif initialValue != nil: + parent.add(ctx.newEnvVarAsgn(result, initialValue)) proc hasYields(n: PNode): bool = # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt. @@ -263,8 +287,8 @@ proc hasYields(n: PNode): bool = result = false else: result = false - for c in n: - if c.hasYields: + for i in ord(n.kind == nkCast)..<n.len: + if n[i].hasYields: result = true break @@ -413,7 +437,7 @@ proc hasYieldsInExpressions(n: PNode): bool = proc exprToStmtList(n: PNode): tuple[s, res: PNode] = assert(n.kind == nkStmtListExpr) - result.s = newNodeI(nkStmtList, n.info) + result = (newNodeI(nkStmtList, n.info), nil) result.s.sons = @[] var n = n @@ -424,8 +448,15 @@ proc exprToStmtList(n: PNode): tuple[s, res: PNode] = result.res = n +proc newTempVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = + if isEmptyType(v.typ): + result = v + else: + result = newTree(nkFastAsgn, ctx.newTempVarAccess(s), v) + result.info = v.info proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = + # unused with -d:nimOptIters if isEmptyType(v.typ): result = v else: @@ -433,10 +464,13 @@ proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = result.info = v.info proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) = + var input = input if input.kind == nkStmtListExpr: let (st, res) = exprToStmtList(input) output.add(st) - output.add(ctx.newEnvVarAsgn(sym, res)) + input = res + if ctx.nimOptItersEnabled: + output.add(ctx.newTempVarAsgn(sym, input)) else: output.add(ctx.newEnvVarAsgn(sym, input)) @@ -448,6 +482,16 @@ proc newNotCall(g: ModuleGraph; e: PNode): PNode = result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e) result.typ = g.getSysType(e.info, tyBool) +proc boolLit(g: ModuleGraph; info: TLineInfo; value: bool): PNode = + result = newIntLit(g, info, ord value) + result.typ = getSysType(g, info, tyBool) + +proc captureVar(c: var Ctx, s: PSym) = + if c.varStates.getOrDefault(s.itemId) != localRequiresLifting: + c.varStates[s.itemId] = localRequiresLifting # Mark this variable for lifting + let e = getEnvParam(c.fn) + discard addField(e.typ.elementType, s, c.g.cache, c.idgen) + proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = result = n case n.kind @@ -504,9 +548,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = var tmp: PSym = nil let isExpr = not isEmptyType(n.typ) if isExpr: - tmp = ctx.newTempVar(n.typ) result = newNodeI(nkStmtListExpr, n.info) result.typ = n.typ + tmp = ctx.newTempVar(n.typ, result) else: result = newNodeI(nkStmtList, n.info) @@ -557,7 +601,11 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = else: internalError(ctx.g.config, "lowerStmtListExpr(nkIf): " & $branch.kind) - if isExpr: result.add(ctx.newEnvVarAccess(tmp)) + if isExpr: + if ctx.nimOptItersEnabled: + result.add(ctx.newTempVarAccess(tmp)) + else: + result.add(ctx.newEnvVarAccess(tmp)) of nkTryStmt, nkHiddenTryStmt: var ns = false @@ -571,7 +619,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = if isExpr: result = newNodeI(nkStmtListExpr, n.info) result.typ = n.typ - let tmp = ctx.newTempVar(n.typ) + let tmp = ctx.newTempVar(n.typ, result) n[0] = ctx.convertExprBodyToAsgn(n[0], tmp) for i in 1..<n.len: @@ -587,7 +635,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = else: internalError(ctx.g.config, "lowerStmtListExpr(nkTryStmt): " & $branch.kind) result.add(n) - result.add(ctx.newEnvVarAccess(tmp)) + if ctx.nimOptItersEnabled: + result.add(ctx.newTempVarAccess(tmp)) + else: + result.add(ctx.newEnvVarAccess(tmp)) of nkCaseStmt: var ns = false @@ -600,9 +651,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let isExpr = not isEmptyType(n.typ) if isExpr: - let tmp = ctx.newTempVar(n.typ) result = newNodeI(nkStmtListExpr, n.info) result.typ = n.typ + let tmp = ctx.newTempVar(n.typ, result) if n[0].kind == nkStmtListExpr: let (st, ex) = exprToStmtList(n[0]) @@ -619,7 +670,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = else: internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind) result.add(n) - result.add(ctx.newEnvVarAccess(tmp)) + if ctx.nimOptItersEnabled: + result.add(ctx.newTempVarAccess(tmp)) + else: + result.add(ctx.newEnvVarAccess(tmp)) elif n[0].kind == nkStmtListExpr: result = newNodeI(nkStmtList, n.info) let (st, ex) = exprToStmtList(n[0]) @@ -649,10 +703,14 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = result.add(st) cond = ex - let tmp = ctx.newTempVar(cond.typ) - result.add(ctx.newEnvVarAsgn(tmp, cond)) + let tmp = ctx.newTempVar(cond.typ, result, cond) + # result.add(ctx.newTempVarAsgn(tmp, cond)) - var check = ctx.newEnvVarAccess(tmp) + var check: PNode + if ctx.nimOptItersEnabled: + check = ctx.newTempVarAccess(tmp) + else: + check = ctx.newEnvVarAccess(tmp) if n[0].sym.magic == mOr: check = ctx.g.newNotCall(check) @@ -662,12 +720,18 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let (st, ex) = exprToStmtList(cond) ifBody.add(st) cond = ex - ifBody.add(ctx.newEnvVarAsgn(tmp, cond)) + if ctx.nimOptItersEnabled: + ifBody.add(ctx.newTempVarAsgn(tmp, cond)) + else: + ifBody.add(ctx.newEnvVarAsgn(tmp, cond)) let ifBranch = newTree(nkElifBranch, check, ifBody) let ifNode = newTree(nkIfStmt, ifBranch) result.add(ifNode) - result.add(ctx.newEnvVarAccess(tmp)) + if ctx.nimOptItersEnabled: + result.add(ctx.newTempVarAccess(tmp)) + else: + result.add(ctx.newEnvVarAccess(tmp)) else: for i in 0..<n.len: if n[i].kind == nkStmtListExpr: @@ -676,9 +740,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = n[i] = ex if n[i].kind in nkCallKinds: # XXX: This should better be some sort of side effect tracking - let tmp = ctx.newTempVar(n[i].typ) - result.add(ctx.newEnvVarAsgn(tmp, n[i])) - n[i] = ctx.newEnvVarAccess(tmp) + let tmp = ctx.newTempVar(n[i].typ, result, n[i]) + # result.add(ctx.newTempVarAsgn(tmp, n[i])) + if ctx.nimOptItersEnabled: + n[i] = ctx.newTempVarAccess(tmp) + else: + n[i] = ctx.newEnvVarAccess(tmp) result.add(n) @@ -694,6 +761,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let (st, ex) = exprToStmtList(c[^1]) result.add(st) c[^1] = ex + for i in 0 .. c.len - 3: + if c[i].kind == nkSym: + let s = c[i].sym + if sfForceLift in s.flags: + ctx.captureVar(s) + result.add(varSect) of nkDiscardStmt, nkReturnStmt, nkRaiseStmt: @@ -779,7 +852,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let check = newTree(nkIfStmt, branch) let newBody = newTree(nkStmtList, st, check, n[1]) - n[0] = newSymNode(ctx.g.getSysSym(n[0].info, "true")) + n[0] = ctx.g.boolLit(n[0].info, true) n[1] = newBody of nkDotExpr, nkCheckedFieldExpr: @@ -831,7 +904,7 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode = let retStmt = if ctx.nearestFinally == 0: # last finally, we can return - let retValue = if ctx.fn.typ[0].isNil: + let retValue = if ctx.fn.typ.returnType.isNil: ctx.g.emptyNode else: newTree(nkFastAsgn, @@ -1133,10 +1206,10 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int = let label = stateIdx if label == ctx.exitStateIdx: break var newLabel = label - if label == -1: + if label == emptyStateLabel: newLabel = ctx.exitStateIdx else: - let fs = skipStmtList(ctx, ctx.states[label][1]) + let fs = skipStmtList(ctx, ctx.states[label].body) if fs.kind == nkGotoState: newLabel = fs[0].intVal.int if label == newLabel: break @@ -1145,7 +1218,7 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int = if maxJumps == 0: assert(false, "Internal error") - result = ctx.states[stateIdx][0].intVal.int + result = ctx.states[stateIdx].label proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode= result = n @@ -1263,11 +1336,10 @@ proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} = proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = # while true: # block :stateLoop: - # gotoState :state # local vars decl (if needed) # body # Might get wrapped in try-except let loopBody = newNodeI(nkStmtList, n.info) - result = newTree(nkWhileStmt, newSymNode(ctx.g.getSysSym(n.info, "true")), loopBody) + result = newTree(nkWhileStmt, ctx.g.boolLit(n.info, true), loopBody) result.info = n.info let localVars = newNodeI(nkStmtList, n.info) @@ -1282,11 +1354,7 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = let blockStmt = newNodeI(nkBlockStmt, n.info) blockStmt.add(newSymNode(ctx.stateLoopLabel)) - let gs = newNodeI(nkGotoState, n.info) - gs.add(ctx.newStateAccess()) - gs.add(ctx.g.newIntLit(n.info, ctx.states.len - 1)) - - var blockBody = newTree(nkStmtList, gs, localVars, n) + var blockBody = newTree(nkStmtList, localVars, n) if ctx.hasExceptions: blockBody = ctx.wrapIntoTryExcept(blockBody) @@ -1299,29 +1367,28 @@ proc deleteEmptyStates(ctx: var Ctx) = # Apply new state indexes and mark unused states with -1 var iValid = 0 - for i, s in ctx.states: - let body = skipStmtList(ctx, s[1]) + for i, s in ctx.states.mpairs: + let body = skipStmtList(ctx, s.body) if body.kind == nkGotoState and i != ctx.states.len - 1 and i != 0: # This is an empty state. Mark with -1. - s[0].intVal = -1 + s.label = emptyStateLabel else: - s[0].intVal = iValid + s.label = iValid inc iValid for i, s in ctx.states: - let body = skipStmtList(ctx, s[1]) + let body = skipStmtList(ctx, s.body) if body.kind != nkGotoState or i == 0: - discard ctx.skipThroughEmptyStates(s) + discard ctx.skipThroughEmptyStates(s.body) let excHandlState = ctx.exceptionTable[i] if excHandlState < 0: ctx.exceptionTable[i] = -ctx.skipEmptyStates(-excHandlState) elif excHandlState != 0: ctx.exceptionTable[i] = ctx.skipEmptyStates(excHandlState) - var i = 0 + var i = 1 # ignore the entry and the exit while i < ctx.states.len - 1: - let fs = skipStmtList(ctx, ctx.states[i][1]) - if fs.kind == nkGotoState and i != 0: + if ctx.states[i].label == emptyStateLabel: ctx.states.delete(i) ctx.exceptionTable.delete(i) else: @@ -1430,18 +1497,77 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode = for i in 0 ..< n.len: result[i] = preprocess(c, n[i]) +proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) = + case n.kind + of nkSym: + let s = n.sym + if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn: + let vs = c.varStates.getOrDefault(s.itemId, localNotSeen) + if vs == localNotSeen: # First seing this variable + c.varStates[s.itemId] = stateIdx + elif vs == localRequiresLifting: + discard # Sym already marked + elif vs != stateIdx: + c.captureVar(s) + of nkReturnStmt: + if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: + # we have a `result = result` expression produced by the closure + # transform, let's not touch the LHS in order to make the lifting pass + # correct when `result` is lifted + detectCapturedVars(c, n[0][1], stateIdx) + else: + detectCapturedVars(c, n[0], stateIdx) + else: + for i in 0 ..< n.safeLen: + detectCapturedVars(c, n[i], stateIdx) + +proc detectCapturedVars(c: var Ctx) = + for i, s in c.states: + detectCapturedVars(c, s.body, i) + +proc liftLocals(c: var Ctx, n: PNode): PNode = + result = n + case n.kind + of nkSym: + let s = n.sym + if c.varStates.getOrDefault(s.itemId) == localRequiresLifting: + # lift + let e = getEnvParam(c.fn) + let field = getFieldFromObj(e.typ.elementType, s) + assert(field != nil) + result = rawIndirectAccess(newSymNode(e), field, n.info) + # elif c.varStates.getOrDefault(s.itemId, localNotSeen) != localNotSeen: + # echo "Not lifting ", s.name.s + + of nkReturnStmt: + if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: + # we have a `result = result` expression produced by the closure + # transform, let's not touch the LHS in order to make the lifting pass + # correct when `result` is lifted + n[0][1] = liftLocals(c, n[0][1]) + else: + n[0] = liftLocals(c, n[0]) + else: + for i in 0 ..< n.safeLen: + n[i] = liftLocals(c, n[i]) + proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode = - var ctx: Ctx - ctx.g = g - ctx.fn = fn - ctx.idgen = idgen + var ctx = Ctx(g: g, fn: fn, idgen: idgen, + # should be default when issues are fixed, see #24094: + nimOptItersEnabled: isDefined(g.config, "nimOptIters")) if getEnvParam(fn).isNil: - # Lambda lifting was not done yet. Use temporary :state sym, which will - # be handled specially by lambda lifting. Local temp vars (if needed) - # should follow the same logic. - ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info) - ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen) + if ctx.nimOptItersEnabled: + # The transformation should always happen after at least partial lambdalifting + # is performed, so that the closure iter environment is always created upfront. + doAssert(false, "Env param not created before iter transformation") + else: + # Lambda lifting was not done yet. Use temporary :state sym, which will + # be handled specially by lambda lifting. Local temp vars (if needed) + # should follow the same logic. + ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info) + ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen) + ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info) var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen) var n = preprocess(pc, n.toStmtList) @@ -1463,17 +1589,23 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: # Optimize empty states away ctx.deleteEmptyStates() - # Make new body by concatenating the list of states - result = newNodeI(nkStmtList, n.info) + let caseDispatcher = newTreeI(nkCaseStmt, n.info, + ctx.newStateAccess()) + + if ctx.nimOptItersEnabled: + # Lamdalifting will not touch our locals, it is our responsibility to lift those that + # need it. + detectCapturedVars(ctx) + for s in ctx.states: - assert(s.len == 2) - let body = s[1] - s.sons.del(1) - result.add(s) - result.add(body) + let body = ctx.transformStateAssignments(s.body) + caseDispatcher.add newTreeI(nkOfBranch, body.info, g.newIntLit(body.info, s.label), body) + + caseDispatcher.add newTreeI(nkElse, n.info, newTreeI(nkReturnStmt, n.info, g.emptyNode)) - result = ctx.transformStateAssignments(result) - result = ctx.wrapIntoStateLoop(result) + result = wrapIntoStateLoop(ctx, caseDispatcher) + if ctx.nimOptItersEnabled: + result = liftLocals(ctx, result) when false: echo "TRANSFORM TO STATES: " @@ -1482,3 +1614,5 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: echo "exception table:" for i, e in ctx.exceptionTable: echo i, " -> ", e + + echo "ENV: ", renderTree(getEnvParam(fn).typ.elementType.n) diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index 031ad755e..e51248639 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -57,6 +57,11 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi if conf.cmd == cmdNimscript: incl(conf.globalOptions, optWasNimscript) loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files + # restores `conf.notes` after loading config files + # because it has overwrites the notes when compiling the system module which + # is a foreign module compared to the project + if conf.cmd in cmdBackends: + conf.notes = conf.mainPackageNotes if not self.suggestMode: let scriptFile = conf.projectFull.changeFileExt("nims") diff --git a/compiler/commands.nim b/compiler/commands.nim index 176b73044..cbf915ca6 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -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] +import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils] import msgs, options, nversion, condsyms, extccomp, platform, wordrecg, nimblecmd, lineinfos, pathutils @@ -248,6 +248,7 @@ 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) @@ -303,6 +304,12 @@ 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) @@ -462,7 +469,6 @@ 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 @@ -500,7 +506,6 @@ 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) = diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 037060417..d48bacdc5 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -96,7 +96,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred} case f.kind of tyAlias: - result = matchType(c, f.lastSon, a, m) + result = matchType(c, f.skipModifier, a, m) of tyTypeDesc: if isSelf(f): #let oldLen = m.inferred.len @@ -105,18 +105,19 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = #m.inferred.setLen oldLen #echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation) else: - 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 + if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType: + if f.hasElementType: + result = matchType(c, f.elementType, a.elementType, m) + else: + result = true # both lack it else: result = false of tyGenericInvocation: result = false - if a.kind == tyGenericInst and a[0].kind == tyGenericBody: - if sameType(f[0], a[0]) and f.len == a.len-1: - for i in 1 ..< f.len: + if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody: + if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1: + for i in FirstGenericParamAt ..< f.kidsLen: if not matchType(c, f[i], a[i], m): return false return true of tyGenericParam: @@ -126,17 +127,17 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = else: let old = existingBinding(m, f) if old == nil: - if f.len > 0 and f[0].kind != tyNone: + if f.hasElementType and f.elementType.kind != tyNone: # also check the generic's constraints: let oldLen = m.inferred.len - result = matchType(c, f[0], a, m) + result = matchType(c, f.elementType, a, m) m.inferred.setLen oldLen if result: when logBindings: echo "A adding ", f, " ", ak m.inferred.add((f, ak)) elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}: when logBindings: echo "B adding ", f, " ", lastSon ak - m.inferred.add((f, lastSon ak)) + m.inferred.add((f, last ak)) result = true else: when logBindings: echo "C adding ", f, " ", ak @@ -155,9 +156,9 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = # 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[0], a[0], m) + result = matchType(c, f.elementType, a.elementType, m) elif m.magic == mArrPut: - result = matchType(c, f[0], a, m) + result = matchType(c, f.elementType, a, m) else: result = false of tyEnum, tyObject, tyDistinct: @@ -167,7 +168,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = 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.len > 0 and ak[0].kind == tyOrdinal) + (ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal) of tyConcept: let oldLen = m.inferred.len let oldPotentialImplementation = m.potentialImplementation @@ -178,10 +179,11 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = m.inferred.setLen oldLen of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr, tyGenericInst: + # ^ XXX Rewrite this logic, it's more complex than it needs to be. result = false let ak = a.skipTypes(ignorableForArgType - {f.kind}) - if ak.kind == f.kind and f.len == ak.len: - for i in 0..<ak.len: + if ak.kind == f.kind and f.kidsLen == ak.kidsLen: + for i in 0..<ak.kidsLen: if not matchType(c, f[i], ak[i], m): return false return true of tyOr: @@ -190,30 +192,30 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = # say the concept requires 'int|float|string' if the potentialImplementation # says 'int|string' that is good enough. var covered = 0 - for i in 0..<f.len: - for j in 0..<a.len: + for ff in f.kids: + for aa in a.kids: let oldLenB = m.inferred.len - let r = matchType(c, f[i], a[j], m) + let r = matchType(c, ff, aa, m) if r: inc covered break m.inferred.setLen oldLenB - result = covered >= a.len + result = covered >= a.kidsLen if not result: m.inferred.setLen oldLen else: result = false - for i in 0..<f.len: - result = matchType(c, f[i], a, m) + for ff in f.kids: + result = matchType(c, ff, a, m) if result: break # and remember the binding! m.inferred.setLen oldLen of tyNot: if a.kind == tyNot: - result = matchType(c, f[0], a[0], m) + result = matchType(c, f.elementType, a.elementType, m) else: let oldLen = m.inferred.len - result = not matchType(c, f[0], a, m) + result = not matchType(c, f.elementType, a, m) m.inferred.setLen oldLen of tyAnything: result = true @@ -252,7 +254,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool = m.inferred.setLen oldLen return false - if not matchReturnType(c, n[0].sym.typ[0], candidate.typ[0], m): + if not matchReturnType(c, n[0].sym.typ.returnType, candidate.typ.returnType, m): m.inferred.setLen oldLen return false @@ -307,9 +309,9 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool = # error was reported earlier. result = false -proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invocation: PType): bool = +proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool = ## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but - ## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fullfill the + ## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the ## concept's requirements. If so, we return true and fill the 'bindings' with pairs of ## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.) ## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the @@ -334,8 +336,8 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invo # 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: + if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1: # bind even more generic parameters assert invocation.kind == tyGenericInvocation - for i in 1 ..< invocation.len: + for i in FirstGenericParamAt ..< invocation.kidsLen: bindings.idTablePut(invocation[i], arg[i]) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 5865e5310..5043fc5d4 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -157,6 +157,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimAllowNonVarDestructor") defineSymbol("nimHasQuirky") defineSymbol("nimHasEnsureMove") + defineSymbol("nimHasNoReturnError") defineSymbol("nimUseStrictDefs") defineSymbol("nimHasNolineTooLong") @@ -165,3 +166,6 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasWarnStdPrefix") defineSymbol("nimHasVtables") + defineSymbol("nimHasGenericsOpenSym2") + defineSymbol("nimHasGenericsOpenSym3") + defineSymbol("nimHasJsNoLambdaLifting") diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 1511628dd..5534d07e7 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -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,14 +181,6 @@ 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 @@ -213,7 +205,6 @@ proc genAndOr(c: var Con; n: PNode) = # fork lab1 # asgn dest, b # lab1: - # join F1 c.gen(n[1]) forkT: c.gen(n[2]) @@ -324,7 +315,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) @@ -380,7 +371,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.len and isOutParam(t[i]): + if t != nil and i < t.signatureLen 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: @@ -390,7 +381,6 @@ 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: diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 29eeced9b..8e5f5e4e7 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -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.targetPath = string(conf.outDir / outDirPath) + result = (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 @@ -831,7 +831,7 @@ proc getName(n: PNode): string = result = "`" for i in 0..<n.len: result.add(getName(n[i])) result = "`" - of nkOpenSymChoice, nkClosedSymChoice: + of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: result = getName(n[0]) else: result = "" @@ -849,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: + of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: result = getNameIdent(cache, n[0]) else: result = nil @@ -863,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: + of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: result = getRstName(n[0]) else: result = nil @@ -1000,8 +1000,9 @@ 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.name = baseName.nimIdentNormalize - result.symKind = k.toHumanStr + result = LangSymbol(name: baseName.nimIdentNormalize, + symKind: k.toHumanStr + ) if k in routineKinds: var paramTypes: seq[string] = @[] @@ -1030,7 +1031,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}) + renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix}) var kind = tkEof while true: getNextTok(r, kind, literal) @@ -1058,7 +1059,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}) + renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix}) while true: getNextTok(r, kind, literal) if kind == tkEof: @@ -1085,6 +1086,9 @@ 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( @@ -1095,10 +1099,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)) - let renderFlags = - if nonExports: {renderNoBody, renderNoComments, renderDocComments, renderSyms, - renderExpandUsing, renderNonExportedFields} - else: {renderNoBody, renderNoComments, renderDocComments, renderSyms, renderExpandUsing} + var renderFlags = {renderNoBody, renderNoComments, renderDocComments, + renderSyms, renderExpandUsing, renderNoPostfix} + if nonExports: + renderFlags.incl renderNonExportedFields nodeToHighlightedHtml(d, n, result, renderFlags, symbolOrIdEnc) let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int) @@ -1121,18 +1125,19 @@ 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 nameNode.kind == nkSym: + if k in routineKinds and symNameNode.kind == nkSym: let att = attachToType(d, nameNode.sym) if att != nil: attype = esc(d.target, att.name.s) - elif k == skType and nameNode.kind == nkSym and nameNode.sym.typ.kind in {tyEnum, tyBool}: - let etyp = nameNode.sym.typ + elif k == skType and symNameNode.kind == nkSym and + symNameNode.sym.typ.kind in {tyEnum, tyBool}: + let etyp = symNameNode.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 = nameNode.sym.name.s & '.' & plain, + term = plain, linkTitle = symNameNode.sym.name.s & '.' & plain, linkDesc = xmltree.escape(getPlainDocstring(e).docstringSummary), line = n.info.line.int) @@ -1153,8 +1158,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 nameNode.kind == nkSym: - d.types.strTableAdd nameNode.sym + if k == skType and symNameNode.kind == nkSym: + d.types.strTableAdd symNameNode.sym proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): JsonItem = if not isVisible(d, nameNode): return @@ -1162,12 +1167,14 @@ 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} + renderFlags = {renderNoBody, renderNoComments, renderDocComments, + renderExpandUsing, renderNoPostfix} if nonExports: renderFlags.incl renderNonExportedFields r = initTokRender(n, renderFlags) - result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int, + result = JsonItem(json: %{ "name": %name, "type": %($k), "line": %n.info.line.int, "col": %n.info.col} + ) if comm != nil: result.rst = comm result.rstField = "description" @@ -1199,8 +1206,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): var param = %{"name": %($genericParam)} if genericParam.sym.typ.len > 0: param["types"] = newJArray() - for kind in genericParam.sym.typ: - param["types"].add %($kind) + param["types"] = %($genericParam.sym.typ.elementType) result.json["signature"]["genericParams"].add param if optGenIndex in d.conf.globalOptions: genItem(d, n, nameNode, k, kForceExport) @@ -1400,7 +1406,8 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags for it in n: traceDeps(d, it) of nkExportStmt: for it in n: - if it.kind == nkSym: + # bug #23051; don't generate documentation for exported symbols again + if it.kind == nkSym and sfExported notin it.sym.flags: if d.module != nil and d.module == it.sym.owner: generateDoc(d, it.sym.ast, orig, config, kForceExport) elif it.sym.ast != nil: diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim index b8d0480c7..dc516d2e5 100644 --- a/compiler/enumtostr.nim +++ b/compiler/enumtostr.nim @@ -63,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.len > 0: - result = searchObjCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field) + if result == nil and t.baseClass != nil: + result = searchObjCase(t.baseClass.skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field) doAssert result != nil proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym = diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index ab26ca1bb..9871c81af 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -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[0]) + result = mapType(conf, t.skipModifier) 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.lastSon) + result = sizeof(pointer) + packSize(conf, v[0], typ.elementType) of tyDistinct, tyGenericInst, tyAlias, tySink: - result = packSize(conf, v, typ[0]) + result = packSize(conf, v, typ.skipModifier) 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[1]) + result = v.len * packSize(conf, v[0], typ.elementType) 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.lastSon, res +! sizeof(pointer)) + pack(conf, v[0], typ.elementType, res +! sizeof(pointer)) dec packRecCheck awr(pointer, res +! sizeof(pointer)) of tyArray: - let baseSize = getSize(conf, typ[1]) + let baseSize = getSize(conf, typ.elementType) for i in 0..<v.len: - pack(conf, v[i], typ[1], res +! i * baseSize) + pack(conf, v[i], typ.elementType, res +! i * baseSize) of tyObject, tyTuple: packObject(conf, v, typ, res) of tyNil: discard of tyDistinct, tyGenericInst, tyAlias, tySink: - pack(conf, v, typ[0], res) + pack(conf, v, typ.skipModifier, res) else: globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ)) @@ -304,9 +304,9 @@ proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode = result = n if result.kind != nkBracket: globalError(conf, n.info, "cannot map value from FFI") - let baseSize = getSize(conf, typ[1]) + let baseSize = getSize(conf, typ.elementType) for i in 0..<result.len: - result[i] = unpack(conf, x +! i * baseSize, typ[1], result[i]) + result[i] = unpack(conf, x +! i * baseSize, typ.elementType, result[i]) proc canonNodeKind(k: TNodeKind): TNodeKind = case k @@ -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.lastSon, n[0]) + n[0] = unpack(conf, p, typ.elementType, 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.lastSon, n) + result = unpack(conf, x, typ.skipModifier, n) else: # XXX what to do with 'array' here? result = nil @@ -434,7 +434,7 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode = proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode = internalAssert conf, call[0].kind == nkPtrLit - var cif: TCif + var cif: TCif = default(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[0]), sig) != OK: + mapType(conf, typ.returnType), 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[0]): pointer(nil) - else: alloc(getSize(conf, typ[0]).int) + let retVal = if isEmptyType(typ.returnType): pointer(nil) + else: alloc(getSize(conf, typ.returnType).int) libffi.call(cif, fn, retVal, args) if retVal.isNil: result = newNode(nkEmpty) else: - result = unpack(conf, retVal, typ[0], nil) + result = unpack(conf, retVal, typ.returnType, nil) result.info = call.info if retVal != nil: dealloc retVal @@ -474,7 +474,7 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType, info: TLineInfo): PNode = internalAssert conf, fn.kind == nkPtrLit - var cif: TCif + var cif: TCif = default(TCif) var sig: ParamList = default(ParamList) for i in 0..len-1: var aTyp = args[i+start].typ diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 5ebb4fa55..77c136d63 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -17,8 +17,8 @@ type owner, genSymOwner: PSym instLines: bool # use the instantiation lines numbers isDeclarative: bool - mapping: TIdTable # every gensym'ed symbol needs to be mapped to some - # new symbol + mapping: SymMapping # every gensym'ed symbol needs to be mapped to some + # new symbol config: ConfigRef ic: IdentCache instID: int @@ -33,6 +33,19 @@ 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) @@ -44,10 +57,10 @@ 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.len + s.position - 1] + handleParam actual[s.owner.typ.signatureLen + s.position - 1] else: internalAssert c.config, sfGenSym in s.flags or s.kind == skType - var x = PSym(idTableGet(c.mapping, s)) + var x = idTableGet(c.mapping, s) if x == nil: x = copySym(s, c.idgen) # sem'check needs to set the owner properly later, see bug #9476 @@ -56,6 +69,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = # internalAssert c.config, false idTablePut(c.mapping, s, x) if sfGenSym in s.flags: + # TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID) result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID), if c.instLines: actual.info else: templ.info) else: @@ -116,7 +130,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.len-1 + expectedRegularParams = s.typ.paramsLen givenRegularParams = totalParams - genericParams if givenRegularParams < 0: givenRegularParams = 0 @@ -182,14 +196,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 - ctx.owner = tmpl - ctx.genSymOwner = genSymOwner - ctx.config = conf - ctx.ic = ic - ctx.mapping = initIdTable() - ctx.instID = instID[] - ctx.idgen = idgen + var ctx = TemplCtx(owner: tmpl, + genSymOwner: genSymOwner, + config: conf, + ic: ic, + mapping: initSymMapping(), + instID: instID[], + idgen: idgen + ) let body = tmpl.ast[bodyPos] #echo "instantion of ", renderTree(body, {renderIds}) diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim index 988433278..c520d8849 100644 --- a/compiler/expanddefaults.nim +++ b/compiler/expanddefaults.nim @@ -55,8 +55,8 @@ proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) = discard proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) = - if t[0] != nil: - expandDefaultObj(t[0], info, res) + if t.baseClass != nil: + expandDefaultObj(t.baseClass, 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(t[0], info) + result = expandDefault(skipModifier t, info) of tyVoid: result = newZero(t, info, nkEmpty) of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned: - result = expandDefault(t.lastSon, info) + result = expandDefault(t.skipModifier, info) of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic: - if t.len > 0: - result = expandDefault(t.lastSon, info) + if t.hasElementType: + result = expandDefault(t.skipModifier, 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[1], info) + result.add expandDefault(t.elementType, info) of tyPtr, tyRef, tyProc, tyPointer, tyCstring: result = newZero(t, info, nkNilLit) of tyVar, tyLent: - let e = t.lastSon + let e = t.elementType 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(t.lastSon, info, nkNilLit) + result = newZero(e, 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: + for it in t.kids: 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, tyProxy, tyBuiltInTypeClass, + tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: result = newZero(t, info, nkEmpty) # bug indicator diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 3deab0b74..ce25da773 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -19,7 +19,7 @@ import std/[os, osproc, streams, sequtils, times, strtabs, json, jsonutils, suga import std / strutils except addf when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, assertions] import ../dist/checksums/src/checksums/sha1 @@ -172,6 +172,22 @@ 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" @@ -285,7 +301,9 @@ const envcc(), icl(), icc(), - clangcl()] + clangcl(), + hipcc(), + nvcc()] hExt* = ".h" @@ -319,7 +337,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, backendNir: discard + of backendC: discard of backendInvalid: # during parsing of cfg files; we don't know the backend yet, no point in # guessing wrong thing @@ -982,10 +1000,11 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile = # works out of the box with `hashMainCompilationParams`. result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json") -const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes +const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes type BuildCache = object cacheVersion: string outputFile: string + outputLastModificationTime: string compile: seq[(string, string)] link: seq[string] linkcmd: string @@ -999,7 +1018,7 @@ type BuildCache = object depfiles: seq[(string, string)] nimexe: string -proc writeJsonBuildInstructions*(conf: ConfigRef) = +proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) = var linkFiles = collect(for it in conf.externalToLink: var it = it if conf.noAbsolutePaths: it = it.extractFilename @@ -1020,11 +1039,17 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = currentDir: getCurrentDir()) if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"): bcache.cmdline = conf.commandLine - bcache.depfiles = collect(for it in conf.m.fileInfos: + for it in conf.m.fileInfos: let path = it.fullPath.string if isAbsolute(path): # TODO: else? - (path, $secureHashFile(path))) + if path in deps: + bcache.depfiles.add (path, deps[path]) + else: # backup for configs etc. + bcache.depfiles.add (path, $secureHashFile(path)) + bcache.nimexe = hashNimExe() + if fileExists(bcache.outputFile): + bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile) conf.jsonBuildFile = conf.jsonBuildInstructionsFile conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty) @@ -1045,6 +1070,8 @@ 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) @@ -1061,7 +1088,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 diff --git a/compiler/guards.nim b/compiler/guards.nim index 87dbfdf6f..bbb239867 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -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: diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 0085ea748..8e81633ef 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -16,7 +16,7 @@ from std/os import removeFile, isAbsolute import ../../dist/checksums/src/checksums/sha1 -import ".." / nir / nirlineinfos +import iclineinfos when defined(nimPreviewSlimSystem): import std/[syncio, assertions, formatfloat] @@ -59,8 +59,8 @@ type emittedTypeInfo*: seq[string] backendFlags*: set[ModuleBackendFlag] - syms*: seq[PackedSym] - types*: seq[PackedType] + syms*: OrderedTable[int32, PackedSym] + types*: OrderedTable[int32, 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(kind: t.kind, flags: t.flags, callConv: t.callConv, + var p = PackedType(id: t.uniqueId.item, 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 items t: + for kid in kids t: p.types.add kid.storeType(c, m) c.addMissing t.sym p.sym = t.sym.safeItemId(c, m) @@ -383,10 +383,9 @@ 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.kind = l.kind - result.generated = l.generated - result.isOverridden = l.isOverridden - result.name = toLitId($l.name, m) + result = PackedLib(kind: l.kind, generated: l.generated, + isOverridden: l.isOverridden, name: toLitId($l.name, m) + ) storeNode(result, l, path) proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId = @@ -397,12 +396,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(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic, + var p = PackedSym(id: s.itemId.item, 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)) @@ -415,7 +414,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.r, m) + p.externalName = toLitId(s.loc.snippet, m) p.locFlags = s.loc.flags c.addMissing s.typ p.typ = s.typ.storeType(c, m) @@ -434,11 +433,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, + info = info, flags = n.flags, typeId = storeTypeLater(n.typ, c, m)) else: ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total - info = info) + info = info, flags = n.flags) ir.addNode(kind = nkNone, info = info, operand = toLitId(n.sym.itemId.module.FileIndex, c, m).int32) ir.addNode(kind = nkNone, info = info, @@ -614,6 +613,10 @@ 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 @@ -646,8 +649,8 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef loadTabSection topLevelSection, m.topLevel loadTabSection bodiesSection, m.bodies - loadSeqSection symsSection, m.syms - loadSeqSection typesSection, m.types + loadTableSection symsSection, m.syms + loadTableSection typesSection, m.types loadSeqSection typeInstCacheSection, m.typeInstCache loadSeqSection procInstCacheSection, m.procInstCache @@ -692,6 +695,10 @@ 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 @@ -715,9 +722,9 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac storeTabSection topLevelSection, m.topLevel storeTabSection bodiesSection, m.bodies - storeSeqSection symsSection, m.syms + storeTableSection symsSection, m.syms - storeSeqSection typesSection, m.types + storeTableSection typesSection, m.types storeSeqSection typeInstCacheSection, m.typeInstCache storeSeqSection procInstCacheSection, m.procInstCache @@ -768,8 +775,8 @@ type status*: ModuleStatus symsInit, typesInit, loadedButAliveSetChanged*: bool fromDisk*: PackedModule - syms: seq[PSym] # indexed by itemId - types: seq[PType] + syms: OrderedTable[int32, PSym] # indexed by itemId + types: OrderedTable[int32, PType] module*: PSym # the one true module symbol. iface, ifaceHidden: Table[PIdent, seq[PackedItemId]] # PackedItemId so that it works with reexported symbols too @@ -830,7 +837,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId]) of nkSym: result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[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: @@ -843,7 +851,8 @@ 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) @@ -936,7 +945,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; result.owner = loadSym(c, g, si, s.owner) let externalName = g[si].fromDisk.strings[s.externalName] if externalName != "": - result.loc.r = rope externalName + result.loc.snippet = externalName result.loc.flags = s.locFlags result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom) @@ -960,11 +969,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[s.item] == nil: + if g[si].syms.getOrDefault(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: @@ -1011,11 +1020,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[t.item] == nil: + if g[si].types.getOrDefault(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 @@ -1154,10 +1163,7 @@ proc loadProcBody*(config: ConfigRef, cache: IdentCache; proc loadTypeFromId*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: int; id: PackedItemId): PType = bench g.loadType: - if id.item < g[module].types.len: - result = g[module].types[id.item] - else: - result = nil + result = g[module].types.getOrDefault(id.item) if result == nil: var decoder = PackedDecoder( lastModule: int32(-1), @@ -1170,10 +1176,7 @@ proc loadTypeFromId*(config: ConfigRef, cache: IdentCache; proc loadSymFromId*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: int; id: PackedItemId): PSym = bench g.loadSym: - if id.item < g[module].syms.len: - result = g[module].syms[id.item] - else: - result = nil + result = g[module].syms.getOrDefault(id.item) if result == nil: var decoder = PackedDecoder( lastModule: int32(-1), @@ -1189,19 +1192,6 @@ 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 diff --git a/compiler/nir/nirlineinfos.nim b/compiler/ic/iclineinfos.nim index f11ef7c42..74a7d971b 100644 --- a/compiler/nir/nirlineinfos.nim +++ b/compiler/ic/iclineinfos.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf +# (c) Copyright 2024 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim index d78e56847..3e8ea2503 100644 --- a/compiler/ic/integrity.nim +++ b/compiler/ic/integrity.nim @@ -10,7 +10,7 @@ ## Integrity checking for a set of .rod files. ## The set must cover a complete Nim project. -import std/sets +import std/[sets, tables] 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 i in 0..high(m.syms): - checkLocalSym c, int32(i) + for _, v in pairs(m.syms): + checkLocalSym c, v.id 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 diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index da8e7e597..39037b94f 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -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 +import std/[sets, tables] 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 ".." / nir / nirlineinfos +import iclineinfos import packed_ast, bitabs, ic type diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 8971d72be..a39bb7adf 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -16,7 +16,7 @@ import std/[hashes, tables, strtabs] import bitabs, rodfiles import ".." / [ast, options] -import ".." / nir / nirlineinfos +import iclineinfos when defined(nimPreviewSlimSystem): import std/assertions @@ -47,6 +47,7 @@ type path*: NodeId PackedSym* = object + id*: int32 kind*: TSymKind name*: LitId typ*: PackedItemId @@ -71,6 +72,7 @@ type instantiatedFrom*: PackedItemId PackedType* = object + id*: int32 kind*: TTypeKind callConv*: TCallingConvention #nodekind*: TNodeKind @@ -124,7 +126,7 @@ proc `==`*(a, b: NodePos): bool {.borrow.} proc `==`*(a, b: NodeId): bool {.borrow.} proc newTreeFrom*(old: PackedTree): PackedTree = - result.nodes = @[] + result = PackedTree(nodes: @[]) when false: result.sh = old.sh proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) = diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 5eef3874a..ac995dd2e 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -19,6 +19,8 @@ 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 @@ -170,6 +172,18 @@ 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 @@ -211,6 +225,19 @@ 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 diff --git a/compiler/importer.nim b/compiler/importer.nim index a8de1e8bc..ffb7e0305 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -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 + var it: ModuleIter = default(ModuleIter) var e = initModuleIter(it, c.graph, fromMod, s.name) while e != nil: if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3") @@ -246,12 +246,14 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool) result = createModuleAliasImpl(realModule.name) if importHidden: result.options.incl optImportHidden - c.unusedImports.add((result, n.info)) + let moduleIdent = if n.kind == nkInfix: n[^1] else: n + c.unusedImports.add((result, moduleIdent.info)) c.importModuleMap[result.id] = realModule.id c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] = - var ret: typeof(result) + result = (nil, false) + var ret = default(typeof(result)) proc processPragma(n2: PNode): PNode = let (result2, kws) = splitPragmas(c, n2) result = result2 @@ -306,7 +308,15 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = 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) - suggestSym(c.graph, n.info, result, c.graph.usageSym, false) + + proc suggestMod(n: PNode; s: PSym) = + if n.kind == nkImportAs: + suggestMod(n[0], realModule) + elif n.kind == nkInfix: + suggestMod(n[2], s) + else: + suggestSym(c.graph, n.info, s, c.graph.usageSym, false) + suggestMod(n, result) importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) else: diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 7a64790c2..3dcc364a3 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -163,7 +163,8 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool = result = false proc isLastRead(n: PNode; c: var Con; s: var Scope): bool = - if not hasDestructor(c, n.typ): return true + # bug #23354; an object type could have a non-trival assignements when it is passed to a sink parameter + if not hasDestructor(c, n.typ) and (n.typ.kind != tyObject or isTrival(getAttachedOp(c.graph, n.typ, attachedAsgn))): return true let m = skipConvDfa(n) result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or @@ -221,7 +222,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.len > 1 and op.typ[1].kind != tyVar: + if op.typ != nil and op.typ.signatureLen > 1 and op.typ.firstParamType.kind != tyVar: addrExp = dest else: addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) @@ -299,7 +300,7 @@ proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFla proc isCriticalLink(dest: PNode): bool {.inline.} = #[ Lins's idea that only "critical" links can introduce a cycle. This is - critical for the performance gurantees that we strive for: If you + critical for the performance guarantees that we strive for: If you traverse a data structure, no tracing will be performed at all. ORC is about this promise: The GC only touches the memory that the mutator touches too. @@ -316,9 +317,10 @@ proc isCriticalLink(dest: PNode): bool {.inline.} = ]# result = dest.kind != nkSym -proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) = - if c.graph.config.selectedGC == gcOrc: - let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}) +proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) = + if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags: + # add cyclic flag, but not to sink calls, which IsExplicitSink generates + let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct}) if cyclicType(c.graph, t): result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest)) @@ -411,7 +413,10 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = # generate: (let tmp = v; reset(v); tmp) if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0: - assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) + assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) or + (n.typ.kind == tyPtr and n.sym.typ.kind == tyRef) + # bug #23505; transformed by `transf`: addr (deref ref) -> ptr + # we know it's really a pointer; so here we assign it directly result = copyTree(n) else: result = newNodeIT(nkStmtListExpr, n.info, n.typ) @@ -442,24 +447,25 @@ proc isCapturedVar(n: PNode): bool = proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) - let tmp = c.getTemp(s, n.typ, n.info) - if hasDestructor(c, n.typ): - let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink}) + let nTyp = n.typ.skipTypes(tyUserTypeClasses) + let tmp = c.getTemp(s, nTyp, n.info) + if hasDestructor(c, nTyp): + let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink}) let op = getAttachedOp(c.graph, typ, attachedDup) if op != nil and tfHasOwned notin typ.flags: if sfError in op.flags: - c.checkForErrorPragma(n.typ, n, "=dup") + c.checkForErrorPragma(nTyp, n, "=dup") else: let copyOp = getAttachedOp(c.graph, typ, attachedAsgn) if copyOp != nil and sfError in copyOp.flags and sfOverridden notin op.flags: - c.checkForErrorPragma(n.typ, n, "=dup", inferredFromCopy = true) + c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true) let src = p(n, c, s, normal) var newCall = newTreeIT(nkCall, src.info, src.typ, newSymNode(op), src) - c.finishCopy(newCall, n, isFromSink = true) + c.finishCopy(newCall, n, {}, isFromSink = true) result.add newTreeI(nkFastAsgn, src.info, tmp, newCall @@ -468,9 +474,9 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result.add c.genWasMoved(tmp) var m = c.genCopy(tmp, n, {}) m.add p(n, c, s, normal) - c.finishCopy(m, n, isFromSink = true) + c.finishCopy(m, n, {}, isFromSink = true) result.add m - if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: + if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, ("passing '$1' to a sink parameter introduces an implicit copy; " & "if possible, rearrange your program's control flow to prevent it") % $n) @@ -479,8 +485,8 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = ("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n) else: if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: - assert(not containsManagedMemory(n.typ)) - if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: + assert(not containsManagedMemory(nTyp)) + if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter") result.add newTree(nkAsgn, tmp, p(n, c, s, normal)) # Since we know somebody will take over the produced copy, there is @@ -489,14 +495,14 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = proc isDangerousSeq(t: PType): bool {.inline.} = let t = t.skipTypes(abstractInst) - result = t.kind == tySequence and tfHasOwned notin t[0].flags + result = t.kind == tySequence and tfHasOwned notin t.elementType.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: + of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast: result = containsConstSeq(n[1]) of nkObjConstr, nkClosure: for i in 1..<n.len: @@ -594,7 +600,9 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt # tricky because you would have to intercept moveOrCopy at a certain point let tmp = c.getTemp(s.parent[], ret.typ, ret.info) tmp.sym.flags = tmpFlags - let cpy = if hasDestructor(c, ret.typ): + let cpy = if hasDestructor(c, ret.typ) and + ret.typ.kind notin {tyOpenArray, tyVarargs}: + # bug #23247 we don't own the data, so it's harmful to destroy it s.parent[].final.add c.genDestroy(tmp) moveOrCopy(tmp, ret, c, s, {IsDecl}) else: @@ -646,7 +654,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 it[^1].typ.isEmptyType or willProduceStmt: + branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt: processScope(c, ofScope, maybeVoid(it[^1], ofScope)) else: processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags) @@ -694,7 +702,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 it[^1].typ.isEmptyType or willProduceStmt: + branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt: processScope(c, branchScope, maybeVoid(it[^1], branchScope)) else: processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags) @@ -754,7 +762,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode = let tmp = c.getTemp(s, n[0].typ, n.info) var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn) m.add p(n[0], c, s, normal) - c.finishCopy(m, n[0], isFromSink = false) + c.finishCopy(m, n[0], {}, isFromSink = false) result = newTree(nkStmtList, c.genWasMoved(tmp), m) var toDisarm = n[0] if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon @@ -769,6 +777,18 @@ 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) + 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}: @@ -810,6 +830,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing elif n.kind in {nkObjDownConv, nkObjUpConv}: result = copyTree(n) result[0] = p(n[0], c, s, sinkArg) + elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}: + result = copyTree(n) + result[1] = p(n[1], c, s, sinkArg) elif n.typ == nil: # 'raise X' can be part of a 'case' expression. Deal with it here: result = p(n, c, s, normal) @@ -853,15 +876,14 @@ 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: + if field != nil and (sfCursor in field.flags or field.typ.kind in {tyOpenArray, tyVarargs}): + # don't sink fields with openarray types 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 (hasDestructor(c, t) and - getAttachedOp(c.graph, t, attachedDestructor) != nil and - sfOverridden in getAttachedOp(c.graph, t, attachedDestructor).flags)): + if mode == normal and (isRefConstr or hasCustomDestructor(c, t)): result = ensureDestruction(result, n, c, s) of nkCallKinds: if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove: @@ -876,8 +898,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing elif c.inSpawn > 0: c.inSpawn.dec - let parameters = n[0].typ - let L = if parameters != nil: parameters.len else: 0 + # 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 when false: var isDangerous = false @@ -907,7 +930,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing result[0] = p(n[0], c, s, normal) if canRaise(n[0]): s.needsTry = true if mode == normal: - result = ensureDestruction(result, n, c, s) + if result.typ != nil and result.typ.kind notin {tyOpenArray, tyVarargs}: + # Returns of openarray types shouldn't be destroyed + # bug #19435; # bug #23247 + result = ensureDestruction(result, n, c, s) of nkDiscardStmt: # Small optimization result = shallowCopy(n) if n[0].kind != nkEmpty: @@ -1148,7 +1174,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy result = c.genCopy(dest, ri, flags) dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + c.finishCopy(result, dest, flags, isFromSink = false) of nkBracket: # array constructor if ri.len > 0 and isDangerousSeq(ri.typ): @@ -1156,7 +1182,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy result = c.genCopy(dest, ri, flags) dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + c.finishCopy(result, dest, flags, isFromSink = false) else: result = c.genSink(s, dest, p(ri, c, s, consumed), flags) of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: @@ -1166,9 +1192,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 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): + elif ri.sym.kind != skParam and + 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) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) @@ -1177,7 +1203,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy result = c.genCopy(dest, ri, flags) dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + c.finishCopy(result, dest, flags, isFromSink = false) of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast: result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags) of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: @@ -1197,7 +1223,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy result = c.genCopy(dest, ri, flags) dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + c.finishCopy(result, dest, flags, isFromSink = false) when false: proc computeUninit(c: var Con) = diff --git a/compiler/installer.ini b/compiler/installer.ini index d12127bde..54a35dbee 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -6,11 +6,11 @@ Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64 + linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64 macosx: i386;amd64;powerpc64;arm64 solaris: i386;amd64;sparc;sparc64 freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el - netbsd: i386;amd64 + netbsd: i386;amd64;arm64 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" +pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css;doc/nimdoc.cls" diff --git a/compiler/int128.nim b/compiler/int128.nim index ca06a3a97..74e581cd5 100644 --- a/compiler/int128.nim +++ b/compiler/int128.nim @@ -34,6 +34,7 @@ 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: @@ -208,30 +209,35 @@ 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 @@ -258,6 +264,7 @@ 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 @@ -281,6 +288,7 @@ 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) @@ -313,6 +321,7 @@ 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) @@ -335,6 +344,7 @@ 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) @@ -373,6 +383,8 @@ 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) @@ -539,24 +551,28 @@ 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 diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index b11d64a6b..17fbde29e 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -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.len > 0 and ret[0] != nil: - result = canAlias(arg, ret[0], marker) + if not result and ret.baseClass != nil: + result = canAlias(arg, ret.baseClass, marker) else: result = true of tyTuple: result = false - for i in 0..<ret.len: - result = canAlias(arg, ret[i], marker) + for r in ret.kids: + result = canAlias(arg, r, marker) if result: break of tyArray, tySequence, tyDistinct, tyGenericInst, tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef: - result = canAlias(arg, ret.lastSon, marker) + result = canAlias(arg, ret.skipModifier, marker) of tyProc: result = ret.callConv == ccClosure else: @@ -119,14 +119,16 @@ proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult = if result != NotFound: return result case t.kind of tyObject: - if t[0] != nil: - result = containsDangerousRefAux(t[0].skipTypes(skipPtrs), marker) + if t.baseClass != nil: + result = containsDangerousRefAux(t.baseClass.skipTypes(skipPtrs), marker) if result == NotFound: result = containsDangerousRefAux(t.n, marker) of tyGenericInst, tyDistinct, tyAlias, tySink: - result = containsDangerousRefAux(lastSon(t), marker) - of tyArray, tySet, tyTuple, tySequence: - for i in 0..<t.len: - result = containsDangerousRefAux(t[i], marker) + 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) if result == Found: return result else: discard diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 7c636af8b..713944def 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -31,9 +31,10 @@ implements the required case distinction. import ast, trees, magicsys, options, nversion, msgs, idents, types, - ropes, ccgutils, wordrecg, renderer, + ropes, wordrecg, renderer, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, - transf, injectdestructors, sourcemap, astmsgs, backendpragmas + transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas, + mangleutils import pipelineutils @@ -102,29 +103,33 @@ type prc: PSym globals, locals, body: Rope options: TOptions - optionsStack: seq[TOptions] + optionsStack: seq[(TOptions, TNoteKinds)] module: BModule g: PGlobals - generatedParamCopies: IntSet beforeRetNeeded: bool unique: int # for temp identifier generation blocks: seq[TBlock] extraIndent: int - up: PProc # up the call chain; required for closure support - declaredGlobals: IntSet previousFileName: string # For frameInfo inside templates. + # legacy: generatedParamCopies and up fields are used for jsNoLambdaLifting + generatedParamCopies: IntSet + up: PProc # up the call chain; required for closure support template config*(p: PProc): ConfigRef = p.module.config proc indentLine(p: PProc, r: Rope): Rope = var p = p - var ind = 0 - while true: - inc ind, p.blocks.len + p.extraIndent - if p.up == nil or p.up.prc != p.prc.owner: - break - p = p.up - result = repeat(' ', ind*2) & r + if jsNoLambdaLifting in p.config.legacyFeatures: + var ind = 0 + while true: + inc ind, p.blocks.len + p.extraIndent + if p.up == nil or p.up.prc != p.prc.owner: + break + p = p.up + result = repeat(' ', ind*2) & r + else: + let ind = p.blocks.len + p.extraIndent + result = repeat(' ', ind*2) & r template line(p: PProc, added: string) = p.body.add(indentLine(p, rope(added))) @@ -175,10 +180,6 @@ proc initProcOptions(module: BModule): TOptions = proc newInitProc(globals: PGlobals, module: BModule): PProc = result = newProc(globals, module, nil, initProcOptions(module)) -proc declareGlobal(p: PProc; id: int; r: Rope) = - if p.prc != nil and not p.declaredGlobals.containsOrIncl(id): - p.locals.addf("global $1;$n", [r]) - const MappedToObject = {tyObject, tyArray, tyTuple, tyOpenArray, tySet, tyVarargs} @@ -187,16 +188,16 @@ proc mapType(typ: PType): TJSTypeKind = let t = skipTypes(typ, abstractInst) case t.kind of tyVar, tyRef, tyPtr: - if skipTypes(t.lastSon, abstractInst).kind in MappedToObject: + if skipTypes(t.elementType, abstractInst).kind in MappedToObject: result = etyObject else: result = etyBaseIndex of tyPointer: # treat a tyPointer like a typed pointer to an array of bytes result = etyBaseIndex - of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent: + of tyRange, tyDistinct, tyOrdinal, tyError, tyLent: # tyLent is no-op as JS has pass-by-reference semantics - result = mapType(t[0]) + result = mapType(skipModifier t) of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt of tyBool: result = etyBool of tyFloat..tyFloat128: result = etyFloat @@ -212,9 +213,9 @@ proc mapType(typ: PType): TJSTypeKind = result = etyNone of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst, tySink, tyOwned: - result = mapType(typ.lastSon) + result = mapType(typ.skipModifier) of tyStatic: - if t.n != nil: result = mapType(lastSon t) + if t.n != nil: result = mapType(skipModifier t) else: result = etyNone of tyProc: result = etyProc of tyCstring: result = etyString @@ -245,7 +246,7 @@ proc mangleName(m: BModule, s: PSym): Rope = for chr in name: if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}: return false - result = s.loc.r + result = s.loc.snippet if result == "": if s.kind == skField and s.name.s.validJsName: result = rope(s.name.s) @@ -269,10 +270,14 @@ proc mangleName(m: BModule, s: PSym): Rope = # When hot reloading is enabled, we must ensure that the names # of functions and types will be preserved across rebuilds: result.add(idOrSig(s, m.module.name.s, m.sigConflicts, m.config)) + elif s.kind == skParam: + result.add mangleParamExt(s) + elif s.kind in routineKinds: + result.add mangleProcNameExt(m.graph, s) else: result.add("_") result.add(rope(s.id)) - s.loc.r = result + s.loc.snippet = result proc escapeJSString(s: string): string = result = newStringOfCap(s.len + s.len shr 2) @@ -464,9 +469,6 @@ const # magic checked op; magic unchecked op; mUnaryMinusF64: ["", ""], mCharToStr: ["nimCharToStr", "nimCharToStr"], mBoolToStr: ["nimBoolToStr", "nimBoolToStr"], - mIntToStr: ["cstrToNimstr", "cstrToNimstr"], - mInt64ToStr: ["cstrToNimstr", "cstrToNimstr"], - mFloatToStr: ["cstrToNimstr", "cstrToNimstr"], mCStrToStr: ["cstrToNimstr", "cstrToNimstr"], mStrToStr: ["", ""]] @@ -516,7 +518,7 @@ proc maybeMakeTempAssignable(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rop let (m1, tmp1) = maybeMakeTemp(p, n[0], address) let typ = skipTypes(n[0].typ, abstractPtrs) if typ.kind == tyArray: - first = firstOrd(p.config, typ[0]) + first = firstOrd(p.config, typ.indexType) if optBoundsCheck in p.options: useMagic(p, "chckIndx") if first == 0: # save a couple chars @@ -609,6 +611,17 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = r.res = frmt % [a, tmp] r.kind = resExpr +proc genBreakState(p: PProc, n: PNode, r: var TCompRes) = + var a: TCompRes = default(TCompRes) + # mangle `:state` properly somehow + if n.kind == nkClosure: + gen(p, n[1], a) + r.res = "(($1).HEX3Astate < 0)" % [rdLoc(a)] + else: + gen(p, n, a) + r.res = "((($1.ClE_0).HEX3Astate) < 0)" % [rdLoc(a)] + r.kind = resExpr + proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = var x, y: TCompRes = default(TCompRes) @@ -788,7 +801,13 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mEqProc: applyFormat("($1 == $2)", "($1 == $2)") of mUnaryMinusI: applyFormat("negInt($1)", "-($1)") of mUnaryMinusI64: applyFormat("negInt64($1)", "-($1)") - of mAbsI: applyFormat("absInt($1)", "Math.abs($1)") + of mAbsI: + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + useMagic(p, "absInt64") + applyFormat("absInt64($1)", "absInt64($1)") + else: + applyFormat("absInt($1)", "Math.abs($1)") of mNot: applyFormat("!($1)", "!($1)") of mUnaryPlusI: applyFormat("+($1)", "+($1)") of mBitnotI: @@ -805,8 +824,6 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mUnaryMinusF64: applyFormat("-($1)", "-($1)") of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)") of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)") - of mIntToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") - of mInt64ToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)") of mStrToStr, mUnown, mIsolate, mFinished: applyFormat("$1", "$1") else: @@ -827,7 +844,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = arithAux(p, n, r, op) of mModI: arithAux(p, n, r, op) - of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr: + of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr: arithAux(p, n, r, op) of mEqRef: if mapType(n[1].typ) != etyBaseIndex: @@ -837,6 +854,11 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = gen(p, n[1], x) gen(p, n[2], y) r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res] + of mEqProc: + if skipTypes(n[1].typ, abstractInst).callConv == ccClosure: + binaryExpr(p, n, r, "cmpClosures", "cmpClosures($1, $2)") + else: + arithAux(p, n, r, op) else: arithAux(p, n, r, op) r.kind = resExpr @@ -926,7 +948,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = p.body.add("++excHandler;\L") var tmpFramePtr = rope"F" lineF(p, "try {$n", []) - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n[0], a) moveInto(p, a, r) var generalCatchBranchExists = false @@ -970,7 +992,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = # if isJsObject(throwObj.typ): if isImportedException(throwObj.typ, p.config): orExpr.addf("lastJSError instanceof $1", - [throwObj.typ.sym.loc.r]) + [throwObj.typ.sym.loc.snippet]) else: orExpr.addf("isObj(lastJSError.m_type, $1)", [genTypeInfo(p, throwObj.typ)]) @@ -980,8 +1002,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = # If some branch requires a local alias introduce it here. This is needed # since JS cannot do ``catch x as y``. if excAlias != nil: - excAlias.sym.loc.r = mangleName(p.module, excAlias.sym) - lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r) + excAlias.sym.loc.snippet = mangleName(p.module, excAlias.sym) + lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.snippet) gen(p, n[i][^1], a) moveInto(p, a, r) lineF(p, "}$n", []) @@ -1019,7 +1041,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = a, b, cond, stmt: TCompRes = default(TCompRes) genLineDir(p, n) gen(p, n[0], cond) - let typeKind = skipTypes(n[0].typ, abstractVar).kind + let typeKind = skipTypes(n[0].typ, abstractVar+{tyRange}).kind var transferRange = false let anyString = typeKind in {tyString, tyCstring} case typeKind @@ -1087,14 +1109,19 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = lineF(p, "break;$n", []) of nkElse: if transferRange: - lineF(p, "else{$n", []) + if n.len == 2: # a dangling else for a case statement + transferRange = false + lineF(p, "switch ($1) {$n", [cond.rdLoc]) + lineF(p, "default: $n", []) + else: + lineF(p, "else{$n", []) else: lineF(p, "default: $n", []) p.nested: gen(p, it[0], stmt) moveInto(p, stmt, r) if transferRange: - lineF(p, "}$n", []) + lineF(p, "}$n", []) else: lineF(p, "break;$n", []) else: internalError(p.config, it.info, "jsgen.genCaseStmt") @@ -1136,10 +1163,14 @@ proc genBreakStmt(p: PProc, n: PNode) = p.blocks[idx].id = abs(p.blocks[idx].id) # label is used lineF(p, "break Label$1;$n", [rope(p.blocks[idx].id)]) -proc genAsmOrEmitStmt(p: PProc, n: PNode) = +proc genAsmOrEmitStmt(p: PProc, n: PNode; isAsmStmt = false) = genLineDir(p, n) p.body.add p.indentLine("") - for i in 0..<n.len: + let offset = + if isAsmStmt: 1 # first son is pragmas + else: 0 + + for i in offset..<n.len: let it = n[i] case it.kind of nkStrLit..nkTripleStrLit: @@ -1194,8 +1225,17 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) = lineF(p, "}$n", []) line(p, repeat('}', toClose) & "\L") -proc generateHeader(p: PProc, typ: PType): Rope = +proc generateHeader(p: PProc, prc: PSym): Rope = result = "" + let typ = prc.typ + if jsNoLambdaLifting notin p.config.legacyFeatures: + if typ.callConv == ccClosure: + # we treat Env as the `this` parameter of the function + # to keep it simple + let env = prc.ast[paramsPos].lastSon + assert env.kind == nkSym, "env is missing" + env.sym.loc.snippet = "this" + for i in 1..<typ.n.len: assert(typ.n[i].kind == nkSym) var param = typ.n[i].sym @@ -1228,9 +1268,10 @@ const proc needsNoCopy(p: PProc; y: PNode): bool = return y.kind in nodeKindsNeedNoCopy or - ((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and + ((mapType(y.typ) != etyBaseIndex or + (jsNoLambdaLifting in p.config.legacyFeatures and y.kind == nkSym and y.sym.kind == skParam)) and (skipTypes(y.typ, abstractInst).kind in - {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned} + IntegralTypes)) + {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned, tyOpenArray} + IntegralTypes)) proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes = default(TCompRes) @@ -1257,7 +1298,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = lineF(p, "$1 = nimCopy(null, $2, $3);$n", [a.rdLoc, b.res, genTypeInfo(p, y.typ)]) of etyObject: - if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + if x.typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") @@ -1312,19 +1353,10 @@ proc genFastAsgn(p: PProc, n: PNode) = genAsgnAux(p, n[0], n[1], noCopyNeeded=noCopy) proc genSwap(p: PProc, n: PNode) = - var a, b: TCompRes = default(TCompRes) - gen(p, n[1], a) - gen(p, n[2], b) - var tmp = p.getTemp(false) - if mapType(p, skipTypes(n[1].typ, abstractVar)) == etyBaseIndex: - let tmp2 = p.getTemp(false) - if a.typ != etyBaseIndex or b.typ != etyBaseIndex: - internalError(p.config, n.info, "genSwap") - lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n", - [tmp, a.address, b.address]) - tmp = tmp2 - lineF(p, "var $1 = $2; $2 = $3; $3 = $1;", - [tmp, a.res, b.res]) + let stmtList = lowerSwap(p.module.graph, n, p.module.idgen, if p.prc != nil: p.prc else: p.module.module) + assert stmtList.kind == nkStmtList + for i in 0..<stmtList.len: + genStmt(p, stmtList[i]) proc getFieldPosition(p: PProc; f: PNode): int = case f.kind @@ -1344,8 +1376,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = else: if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr") var f = b[1].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) - r.res = makeJSString($f.loc.r) + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) + r.res = makeJSString($f.loc.snippet) internalAssert p.config, a.typ != etyBaseIndex r.address = a.res r.kind = resExpr @@ -1372,8 +1404,8 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = else: if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess") var f = n[1].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) - r.res = "$1.$2" % [r.res, f.loc.r] + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) + r.res = "$1.$2" % [r.res, f.loc.snippet] mkTemp(1) r.kind = resExpr @@ -1393,11 +1425,11 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = # Field symbol var field = accessExpr[1].sym internalAssert p.config, field.kind == skField - if field.loc.r == "": field.loc.r = mangleName(p.module, field) + if field.loc.snippet == "": field.loc.snippet = mangleName(p.module, field) # Discriminant symbol let disc = checkExpr[2].sym internalAssert p.config, disc.kind == skField - if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc) + if disc.loc.snippet == "": disc.loc.snippet = mangleName(p.module, disc) var setx: TCompRes = default(TCompRes) gen(p, checkExpr[1], setx) @@ -1414,16 +1446,16 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen let msg = genFieldDefect(p.config, field.name.s, disc) lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n", - setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===", + setx.res, tmp, disc.loc.snippet, if negCheck: "!==" else: "===", makeJSString(msg), genTypeInfo(p, disc.typ)) if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex: r.typ = etyBaseIndex - r.res = makeJSString($field.loc.r) + r.res = makeJSString($field.loc.snippet) r.address = tmp else: r.typ = etyNone - r.res = "$1.$2" % [tmp, field.loc.r] + r.res = "$1.$2" % [tmp, field.loc.snippet] r.kind = resExpr proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = @@ -1439,7 +1471,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = r.address = x var typ = skipTypes(m[0].typ, abstractPtrs) if typ.kind == tyArray: - first = firstOrd(p.config, typ[0]) + first = firstOrd(p.config, typ.indexType) if optBoundsCheck in p.options: useMagic(p, "chckIndx") if first == 0: # save a couple chars @@ -1454,8 +1486,8 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = - var ty = skipTypes(n[0].typ, abstractVarRange) - if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.lastSon, abstractVarRange) + var ty = skipTypes(n[0].typ, abstractVarRange+tyUserTypeClasses) + if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.elementType, abstractVarRange) case ty.kind of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: genArrayAddr(p, n, r) @@ -1490,10 +1522,10 @@ template isIndirect(x: PSym): bool = proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = let s = n.sym - if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3") + if s.loc.snippet == "": internalError(p.config, n.info, "genAddr: 3") case s.kind of skParam: - r.res = s.loc.r + r.res = s.loc.snippet r.address = "" r.typ = etyNone of skVar, skLet, skResult: @@ -1507,15 +1539,15 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = # make addr() a no-op: r.typ = etyNone if isIndirect(s): - r.res = s.loc.r & "[0]" + r.res = s.loc.snippet & "[0]" else: - r.res = s.loc.r + r.res = s.loc.snippet r.address = "" elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: # for ease of code generation, we do not distinguish between # sfAddrTaken and sfGlobal. r.typ = etyBaseIndex - r.address = s.loc.r + r.address = s.loc.snippet r.res = rope("0") else: # 'var openArray' for instance produces an 'addr' but this is harmless: @@ -1542,7 +1574,7 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = if ty.kind in MappedToObject: gen(p, n[0], r) else: - let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind + let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange+tyUserTypeClasses).kind case kindOfIndexedExpr of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: genArrayAddr(p, n[0], r) @@ -1588,12 +1620,15 @@ proc attachProc(p: PProc; s: PSym) = proc genProcForSymIfNeeded(p: PProc, s: PSym) = if not p.g.generatedSyms.containsOrIncl(s.id): - let newp = genProc(p, s) - var owner = p - while owner != nil and owner.prc != s.owner: - owner = owner.up - if owner != nil: owner.locals.add(newp) - else: attachProc(p, newp, s) + if jsNoLambdaLifting in p.config.legacyFeatures: + let newp = genProc(p, s) + var owner = p + while owner != nil and owner.prc != s.owner: + owner = owner.up + if owner != nil: owner.locals.add(newp) + else: attachProc(p, newp, s) + else: + attachProc(p, s) proc genCopyForParamIfNeeded(p: PProc, n: PNode) = let s = n.sym @@ -1605,7 +1640,7 @@ proc genCopyForParamIfNeeded(p: PProc, n: PNode) = internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s) if owner.prc == s.owner: if not owner.generatedParamCopies.containsOrIncl(s.id): - let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)] + let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.snippet, genTypeInfo(p, s.typ)] owner.locals.add(owner.indentLine(copy)) return owner = owner.up @@ -1616,40 +1651,40 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = var s = n.sym case s.kind of skVar, skLet, skParam, skTemp, skResult, skForVar: - if s.loc.r == "": + if s.loc.snippet == "": internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) if sfCompileTime in s.flags: genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info)) - if s.kind == skParam: + if jsNoLambdaLifting in p.config.legacyFeatures and s.kind == skParam: genCopyForParamIfNeeded(p, n) let k = mapType(p, s.typ) if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: if isIndirect(s): - r.address = "$1[0][0]" % [s.loc.r] - r.res = "$1[0][1]" % [s.loc.r] + r.address = "$1[0][0]" % [s.loc.snippet] + r.res = "$1[0][1]" % [s.loc.snippet] else: - r.address = "$1[0]" % [s.loc.r] - r.res = "$1[1]" % [s.loc.r] + r.address = "$1[0]" % [s.loc.snippet] + r.res = "$1[1]" % [s.loc.snippet] else: - r.address = s.loc.r - r.res = s.loc.r & "_Idx" + r.address = s.loc.snippet + r.res = s.loc.snippet & "_Idx" elif isIndirect(s): - r.res = "$1[0]" % [s.loc.r] + r.res = "$1[0]" % [s.loc.snippet] else: - r.res = s.loc.r + r.res = s.loc.snippet of skConst: genConstant(p, s) - if s.loc.r == "": + if s.loc.snippet == "": internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) - r.res = s.loc.r - of skProc, skFunc, skConverter, skMethod: + r.res = s.loc.snippet + of skProc, skFunc, skConverter, skMethod, skIterator: if sfCompileTime in s.flags: localError(p.config, n.info, "request to generate code for .compileTime proc: " & s.name.s) discard mangleName(p.module, s) - r.res = s.loc.r + r.res = s.loc.snippet if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or {sfImportc, sfInfixCall} * s.flags != {}: discard @@ -1661,13 +1696,13 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = else: genProcForSymIfNeeded(p, s) else: - if s.loc.r == "": + if s.loc.snippet == "": internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) if mapType(p, s.typ) == etyBaseIndex: - r.address = s.loc.r - r.res = s.loc.r & "_Idx" + r.address = s.loc.snippet + r.res = s.loc.snippet & "_Idx" else: - r.res = s.loc.r + r.res = s.loc.snippet r.kind = resVal proc genDeref(p: PProc, n: PNode, r: var TCompRes) = @@ -1809,9 +1844,9 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = # don't call '$' here for efficiency: let f = n[0].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) if sfInfixCall in f.flags: - let pat = $n[0].sym.loc.r + let pat = $n[0].sym.loc.snippet internalAssert p.config, pat.len > 0 if pat.contains({'#', '(', '@'}): var typ = skipTypes(n[0].typ, abstractInst) @@ -1889,7 +1924,7 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v while t != nil: t = t.skipTypes(skipPtrs) createRecordVarAux(p, t.n, excludedFieldIDs, output) - t = t[0] + t = t.baseClass proc arrayTypeForElemType(conf: ConfigRef; typ: PType): string = let typ = typ.skipTypes(abstractRange) @@ -1926,7 +1961,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar: result = putToSeq("0", indirect) of tyInt, tyUInt: - if $t.sym.loc.r == "bigint": + if $t.sym.loc.snippet == "bigint": result = putToSeq("0n", indirect) else: result = putToSeq("0", indirect) @@ -1938,7 +1973,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = of tyFloat..tyFloat128: result = putToSeq("0.0", indirect) of tyRange, tyGenericInst, tyAlias, tySink, tyOwned, tyLent: - result = createVar(p, lastSon(typ), indirect) + result = createVar(p, skipModifier(typ), indirect) of tySet: result = putToSeq("{}", indirect) of tyBool: @@ -1986,11 +2021,11 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("null", indirect) of tySequence, tyString: result = putToSeq("[]", indirect) - of tyCstring, tyProc: + of tyCstring, tyProc, tyOpenArray: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: - result = createVar(p, lastSon t, indirect) + result = createVar(p, skipModifier t, indirect) else: internalError(p.config, "createVar: " & $t.kind) result = "" @@ -2038,7 +2073,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = gen(p, n, a) case mapType(p, v.typ) of etyObject, etySeq: - if needsNoCopy(p, n): + if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n): s = a.res else: useMagic(p, "nimCopy") @@ -2048,33 +2083,44 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = if a.typ == etyBaseIndex: if targetBaseIndex: line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n", - [returnType, v.loc.r, a.address, a.res])) + [returnType, v.loc.snippet, a.address, a.res])) else: if isIndirect(v): line(p, runtimeFormat(varCode & " = [[$3, $4]];$n", - [returnType, v.loc.r, a.address, a.res])) + [returnType, v.loc.snippet, a.address, a.res])) else: line(p, runtimeFormat(varCode & " = [$3, $4];$n", - [returnType, v.loc.r, a.address, a.res])) + [returnType, v.loc.snippet, a.address, a.res])) else: if targetBaseIndex: let tmp = p.getTemp lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n", - [tmp, a.res, v.loc.r]) + [tmp, a.res, v.loc.snippet]) else: - line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res])) + line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, a.res])) return else: s = a.res if isIndirect(v): - line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s])) + line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.snippet, s])) else: - line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s])) + line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, s])) if useReloadingGuard or useGlobalPragmas: dec p.extraIndent lineF(p, "}$n") +proc genClosureVar(p: PProc, n: PNode) = + # assert n[2].kind != nkEmpty + # TODO: fixme transform `var env.x` into `var env.x = default()` after + # the order of transf and lambdalifting is fixed + if n[2].kind != nkEmpty: + genAsgnAux(p, n[0], n[2], false) + else: + var a: TCompRes = default(TCompRes) + gen(p, n[0], a) + line(p, runtimeFormat("$1 = $2;$n", [rdLoc(a), createVar(p, n[0].typ, false)])) + proc genVarStmt(p: PProc, n: PNode) = for i in 0..<n.len: var a = n[i] @@ -2084,15 +2130,17 @@ proc genVarStmt(p: PProc, n: PNode) = genStmt(p, unpacked) else: assert(a.kind == nkIdentDefs) - assert(a[0].kind == nkSym) - var v = a[0].sym - if lfNoDecl notin v.loc.flags and sfImportc notin v.flags: - genLineDir(p, a) - if sfCompileTime notin v.flags: - genVarInit(p, v, a[2]) - else: - # lazy emit, done when it's actually used. - if v.ast == nil: v.ast = a[2] + if a[0].kind == nkSym: + var v = a[0].sym + if lfNoDecl notin v.loc.flags and sfImportc notin v.flags: + genLineDir(p, a) + if sfCompileTime notin v.flags: + genVarInit(p, v, a[2]) + else: + # lazy emit, done when it's actually used. + if v.ast == nil: v.ast = a[2] + else: # closure + genClosureVar(p, a) proc genConstant(p: PProc, c: PSym) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): @@ -2139,20 +2187,20 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = if skipTypes(n[1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1].concat(" % [a.res]) else: - r.res.add("($1 || []).concat(" % [a.res]) + r.res.add("($1).concat(" % [a.res]) for i in 2..<n.len - 1: gen(p, n[i], a) if skipTypes(n[i].typ, abstractVarRange).kind == tyChar: r.res.add("[$1]," % [a.res]) else: - r.res.add("$1 || []," % [a.res]) + r.res.add("$1," % [a.res]) gen(p, n[^1], a) if skipTypes(n[^1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1])" % [a.res]) else: - r.res.add("$1 || [])" % [a.res]) + r.res.add("$1)" % [a.res]) proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = "") = useMagic(p, magic) @@ -2219,16 +2267,17 @@ proc genDefault(p: PProc, n: PNode; r: var TCompRes) = r.res = createVar(p, n.typ, indirect = false) r.kind = resExpr -proc genReset(p: PProc, n: PNode) = +proc genWasMoved(p: PProc, n: PNode) = + # TODO: it should be done by nir var x: TCompRes = default(TCompRes) - useMagic(p, "genericReset") gen(p, n[1], x) if x.typ == etyBaseIndex: lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res]) else: - let (a, tmp) = maybeMakeTempAssignable(p, n[1], x) - lineF(p, "$1 = genericReset($3, $2);$n", [a, - genTypeInfo(p, n[1].typ), tmp]) + var y: TCompRes = default(TCompRes) + genDefault(p, n[1], y) + let (a, _) = maybeMakeTempAssignable(p, n[1], x) + lineF(p, "$1 = $2;$n", [a, y.rdLoc]) proc genMove(p: PProc; n: PNode; r: var TCompRes) = var a: TCompRes = default(TCompRes) @@ -2236,7 +2285,7 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) = r.res = p.getTemp() gen(p, n[1], a) lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc]) - genReset(p, n) + genWasMoved(p, n) #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) proc genDup(p: PProc; n: PNode; r: var TCompRes) = @@ -2417,7 +2466,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]") of mOf: genOf(p, n, r) of mDefault, mZeroDefault: genDefault(p, n, r) - of mReset, mWasMoved: genReset(p, n) + of mWasMoved: genWasMoved(p, n) of mEcho: genEcho(p, n, r) of mNLen..mNError, mSlurp, mStaticExec: localError(p.config, n.info, errXMustBeCompileTime % n[0].sym.name.s) @@ -2530,17 +2579,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = let val = it[1] gen(p, val, a) var f = it[0].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id) let typ = val.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: - initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res]) + initList.addf("$#: [$#, $#]", [f.loc.snippet, a.address, a.res]) else: if not needsNoCopy(p, val): useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] - initList.addf("$#: $#", [f.loc.r, a.res]) + initList.addf("$#: $#", [f.loc.snippet, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) r.res = ("{$1}") % [initList] @@ -2599,7 +2648,13 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = let src = skipTypes(n[0].typ, abstractVarRange) let dest = skipTypes(n.typ, abstractVarRange) if optRangeCheck notin p.options: - return + if optJsBigInt64 in p.config.globalOptions and + dest.kind in {tyUInt..tyUInt32, tyInt..tyInt32} and + src.kind in {tyInt64, tyUInt64}: + # conversions to Number are kept + r.res = "Number($1)" % [r.res] + else: + discard elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures: if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res] @@ -2698,8 +2753,8 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = var returnStmt: Rope = "" var resultAsgn: Rope = "" var name = mangleName(p.module, prc) - let header = generateHeader(p, prc.typ) - if prc.typ[0] != nil and sfPure notin prc.flags: + let header = generateHeader(p, prc) + if prc.typ.returnType != nil and sfPure notin prc.flags: resultSym = prc.ast[resultPos].sym let mname = mangleName(p.module, resultSym) # otherwise uses "fat pointers" @@ -2780,9 +2835,9 @@ proc genPragma(p: PProc, n: PNode) = case whichPragma(it) of wEmit: genAsmOrEmitStmt(p, it[1]) of wPush: - processPushBackendOption(p.optionsStack, p.options, n, i+1) + processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1) of wPop: - processPopBackendOption(p.optionsStack, p.options) + processPopBackendOption(p.config, p.optionsStack, p.options) else: discard proc genCast(p: PProc, n: PNode, r: var TCompRes) = @@ -2917,7 +2972,18 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genInfixCall(p, n, r) else: genCall(p, n, r) - of nkClosure: gen(p, n[0], r) + of nkClosure: + if jsNoLambdaLifting in p.config.legacyFeatures: + gen(p, n[0], r) + else: + let tmp = getTemp(p) + var a: TCompRes = default(TCompRes) + var b: TCompRes = default(TCompRes) + gen(p, n[0], a) + gen(p, n[1], b) + lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc]) + r.res = tmp + r.kind = resVal of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) of nkPar, nkTupleConstr: genTupleConstr(p, n, r) @@ -2948,7 +3014,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkLambdaKinds: let s = n[namePos].sym discard mangleName(p.module, s) - r.res = s.loc.r + r.res = s.loc.snippet if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard elif not p.g.generatedSyms.containsOrIncl(s.id): p.locals.add(genProc(p, s)) @@ -2980,17 +3046,16 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if n[0].kind != nkEmpty: genLineDir(p, n) gen(p, n[0], r) - r.res = "var _ = " & r.res - of nkAsmStmt: genAsmOrEmitStmt(p, n) + r.res = "(" & r.res & ")" + of nkAsmStmt: + warningDeprecated(p.config, n.info, "'asm' for the JS target is deprecated, use the 'emit' pragma") + genAsmOrEmitStmt(p, n, true) of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection, nkCommentStmt, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt, + nkFromStmt, nkTemplateDef, nkMacroDef, nkIteratorDef, nkStaticStmt, nkMixinStmt, nkBindStmt: discard - of nkIteratorDef: - if n[0].sym.typ.callConv == TCallingConvention.ccClosure: - globalError(p.config, n.info, "Closure iterators are not supported by JS backend!") of nkPragma: genPragma(p, n) of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: var s = n[namePos].sym @@ -2998,7 +3063,9 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genSym(p, n[namePos], r) r.res = "" of nkGotoState, nkState: - globalError(p.config, n.info, "First class iterators not implemented") + globalError(p.config, n.info, "not implemented") + of nkBreakState: + genBreakState(p, n[0], r) of nkPragmaBlock: gen(p, n.lastSon, r) of nkComesFrom: discard "XXX to implement for better stack traces" @@ -3105,15 +3172,6 @@ proc wholeCode(graph: ModuleGraph; m: BModule): Rope = result = globals.typeInfo & globals.constants & globals.code -proc getClassName(t: PType): Rope = - var s = t.sym - if s.isNil or sfAnon in s.flags: - s = skipTypes(t, abstractPtrs).sym - if s.isNil or sfAnon in s.flags: - doAssert(false, "cannot retrieve class name") - if s.loc.r != "": result = s.loc.r - else: result = rope(s.name.s) - proc finalJSCodeGen*(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = ## Finalize JS code generation of a Nim module. ## Param `n` may contain nodes returned from the last module close call. diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 4b4ca9fe7..d980f9989 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -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[0] != nil + tfInheritable in t.flags or t.baseClass != 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[0] != nil): + if (typ.kind == tyObject) and (typ.baseClass != nil): p.g.typeInfo.addf("$1.base = $2;$n", - [name, genTypeInfo(p, typ[0].skipTypes(skipPtrs))]) + [name, genTypeInfo(p, typ.baseClass.skipTypes(skipPtrs))]) proc genTupleFields(p: PProc, typ: PType): Rope = var s: Rope = "" @@ -117,9 +117,9 @@ 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[0] != nil: + if typ.baseClass != nil: p.g.typeInfo.addf("$1.base = $2;$n", - [name, genTypeInfo(p, typ[0])]) + [name, genTypeInfo(p, typ.baseClass)]) proc genTypeInfo(p: PProc, typ: PType): Rope = let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned}) @@ -127,7 +127,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope = if containsOrIncl(p.g.typeInfoGenerated, t.id): return case t.kind of tyDistinct: - result = genTypeInfo(p, t[0]) + result = genTypeInfo(p, t.skipModifier) 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.lastSon)]) + [result, genTypeInfo(p, t.elementType)]) 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[1])]) + [result, genTypeInfo(p, t.elementType)]) 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, lastSon t) + if t.n != nil: result = genTypeInfo(p, skipModifier t) else: internalError(p.config, "genTypeInfo(" & $t.kind & ')') else: internalError(p.config, "genTypeInfo(" & $t.kind & ')') diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 9345ac114..54cdfc5bc 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -145,11 +145,13 @@ 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) - rawAddField(result, createStateField(g, owner, idgen)) + if owner.isIterator or not isDefined(g.config, "nimOptIters"): + rawAddField(result, createStateField(g, owner, idgen)) proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym = if resultPos < iter.ast.len: @@ -157,7 +159,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[0] + result.typ = iter.typ.returnType incl(result.flags, sfUsed) iter.ast.add newSymNode(result) @@ -172,26 +174,22 @@ proc addHiddenParam(routine: PSym, param: PSym) = assert sfFromGeneric in param.flags #echo "produced environment: ", param.id, " for ", routine.id -proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym = +proc getEnvParam*(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 @@ -231,22 +229,20 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf prc.flags.incl sfInjectDestructors proc interestingIterVar(s: PSym): bool {.inline.} = + # unused with -d:nimOptIters # XXX optimization: Only lift the variable if it lives across # yield/return boundaries! This can potentially speed up # closure iterators quite a bit. result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags -template isIterator*(owner: PSym): bool = - owner.kind == skIterator and owner.typ.callConv == ccClosure - -proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} = +template liftingHarmful(conf: ConfigRef; owner: PSym): bool = ## lambda lifting can be harmful for JS-like code generators. let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro - result = conf.backend == backendJs and not isCompileTime + jsNoLambdaLifting in conf.legacyFeatures and 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.lastSon, info, idgen) + createTypeBoundOps(g, nil, refType.elementType, info, idgen) createTypeBoundOps(g, nil, refType, info, idgen) if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions: owner.flags.incl sfInjectDestructors @@ -264,8 +260,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN let iter = n.sym assert iter.isIterator - result = newNodeIT(nkStmtListExpr, n.info, n.typ) - + result = newNodeIT(nkStmtListExpr, n.info, iter.typ) let hp = getHiddenParam(g, iter) var env: PNode if owner.isIterator: @@ -287,6 +282,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN result.add makeClosure(g, idgen, iter, env, n.info) proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode = + # unused with -d:nimOptIters let envParam = getHiddenParam(g, owner) let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr}) let field = addField(obj, s, g.cache, idgen) @@ -304,8 +300,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = localError(g.config, n.info, ("'$1' is of type <$2> which cannot be captured as it would violate memory" & " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." & - " Consider using a <ref $2> which can be captured.") % - [s.name.s, typeToString(s.typ), g.config$s.info]) + " Consider using a <ref T> which can be captured.") % + [s.name.s, typeToString(s.typ.skipTypes({tyVar})), 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]) @@ -342,9 +338,13 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym; info: TLineInfo): PType = result = c.ownerToType.getOrDefault(owner.id) if result.isNil: - result = newType(tyRef, c.idgen, owner) - let obj = createEnvObj(c.graph, c.idgen, owner, info) - rawAddSon(result, obj) + let env = getEnvParam(owner) + if env.isNil or not owner.isIterator or not isDefined(c.graph.config, "nimOptIters"): + result = newType(tyRef, c.idgen, owner) + let obj = createEnvObj(c.graph, c.idgen, owner, info) + rawAddSon(result, obj) + else: + result = env.typ c.ownerToType[owner.id] = result proc asOwnedRef(c: var DetectionPass; t: PType): PType = @@ -460,13 +460,14 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if owner.isIterator: c.somethingToDo = true addClosureParam(c, owner, n.info) - if interestingIterVar(s): + if not isDefined(c.graph.config, "nimOptIters") and interestingIterVar(s): if not c.capturedVars.contains(s.id): if not c.inTypeOf: c.capturedVars.incl(s.id) let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr}) if s.name.id == getIdent(c.graph.cache, ":state").id: + obj.n[0].sym.flags.incl sfNoInit obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item) else: discard addField(obj, s, c.graph.cache, c.idgen) @@ -551,8 +552,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: @@ -560,6 +561,7 @@ 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 @@ -571,7 +573,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[0], v) + addUniqueField(it.typ.elementType, v) result = indirectAccess(newSymNode(it), v, v.info) else: result = newSymNode(v) @@ -772,7 +774,7 @@ 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): + elif owner.isIterator and not isDefined(d.graph.config, "nimOptIters") and interestingIterVar(s): result = accessViaEnvParam(d.graph, n, owner) else: result = accessViaEnvVar(n, owner, d, c) @@ -882,19 +884,18 @@ 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 ( + if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and 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 and isDefined(g.config, "nimOptIters"): + var d = initDetectionPass(g, fn, idgen) + addClosureParam(d, fn, body.info) else: var d = initDetectionPass(g, fn, idgen) detectCapturedVars(body, fn, d) @@ -993,12 +994,15 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym): # gather vars in a tuple: var v2 = newNodeI(nkLetSection, body.info) var vpart = newNodeI(if body.len == 3: nkIdentDefs else: nkVarTuple, body.info) - for i in 0..<body.len-2: - if body[i].kind == nkSym: - body[i].sym.transitionToLet() - vpart.add body[i] + if body.len == 3 and body[0].kind == nkVarTuple: + vpart = body[0] # fixes for (i,j) in walk() # bug #15924 + else: + for i in 0..<body.len-2: + if body[i].kind == nkSym: + body[i].sym.transitionToLet() + vpart.add body[i] - vpart.add newNodeI(nkEmpty, body.info) # no explicit type + vpart.add newNodeI(nkEmpty, body.info) # no explicit type if not env.isNil: call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info) vpart.add call diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 7cff98b11..0121b1185 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -9,7 +9,7 @@ ## Layouter for nimpretty. -import idents, lexer, lineinfos, llstream, options, msgs, strutils, pathutils +import idents, lexer, ast, lineinfos, llstream, options, msgs, strutils, pathutils const MinLineLen = 15 @@ -243,23 +243,28 @@ proc renderTokens*(em: var Emitter): string = return content -proc writeOut*(em: Emitter, content: string) = +type + FinalCheck = proc (content: string; origAst: PNode): bool {.nimcall.} + +proc writeOut*(em: Emitter; content: string; origAst: PNode; check: FinalCheck) = ## 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) -proc closeEmitter*(em: var Emitter) = + 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) = ## Renders emitter tokens and write to a file let content = renderTokens(em) - em.writeOut(content) + em.writeOut(content, origAst, check) proc wr(em: var Emitter; x: string; lt: LayoutToken) = em.tokens.add x diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 1ef0ce879..ad5dd560c 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -300,8 +300,7 @@ 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 - t.literal = "" + var t = Token(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 diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 36d9d5b1a..9ff5c0a9d 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -40,7 +40,7 @@ template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink) proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; - info: TLineInfo; idgen: IdGenerator): PSym + info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo; idgen: IdGenerator) @@ -56,10 +56,10 @@ proc destructorOverridden(g: ModuleGraph; t: PType): bool = op != nil and sfOverridden in op.flags proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = - for i in 0..<t.len: + for i, a in t.ikids: let lit = lowerings.newIntLit(c.g, x.info, i) - let b = if c.kind == attachedTrace: y else: y.at(lit, t[i]) - fillBody(c, t[i], body, x.at(lit, t[i]), b) + let b = if c.kind == attachedTrace: y else: y.at(lit, a) + fillBody(c, a, body, x.at(lit, a), b) proc dotField(x: PNode, f: PSym): PNode = result = newNodeI(nkDotExpr, x.info, 2) @@ -139,9 +139,9 @@ 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[0]) + var destroy = newNodeIT(nkCall, x.info, op.typ.returnType) destroy.add(newSymNode(op)) - if op.typ[1].kind != tyVar: + if op.typ.firstParamType.kind != tyVar: destroy.add x else: destroy.add genAddr(c, x) @@ -153,11 +153,11 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = result = destroy proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = - result = newNodeIT(nkCall, x.info, op.typ[0]) + result = newNodeIT(nkCall, x.info, op.typ.returnType) result.add(newSymNode(op)) result.add genAddr(c, x) -proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) = +proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) = case n.kind of nkSym: if c.filterDiscriminator != nil: return @@ -167,6 +167,8 @@ 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: @@ -205,7 +207,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) branch[^1] = newNodeI(nkStmtList, c.info) fillBodyObj(c, n[i].lastSon, branch[^1], x, y, - enforceDefaultOp = localEnforceDefaultOp) + enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn) if branch[^1].len == 0: inc emptyBranches caseStmt.add(branch) if emptyBranches != n.len-1: @@ -216,20 +218,29 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false) c.filterDiscriminator = oldfilterDiscriminator of nkRecList: - for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp) + for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved) else: illFormedAstLocal(n, c.g.config) proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) = - if t.len > 0 and t[0] != nil: - fillBody(c, skipTypes(t[0], abstractPtrs), body, x, y) + 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) 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.len > 0 and obj[0] != nil: - obj = skipTypes(obj[0], abstractPtrs) + while obj.baseClass != nil: + obj = skipTypes(obj.baseClass, abstractPtrs) hasCase = hasCase or isCaseObj(obj.n) if hasCase and c.kind in {attachedAsgn, attachedDeepCopy}: @@ -279,6 +290,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) = c.kind = attachedDestructor fillBodyObjTImpl(c, t, body, blob, y) c.kind = prevKind + else: fillBodyObjTImpl(c, t, body, x, y) @@ -288,7 +300,7 @@ proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode = proc getCycleParam(c: TLiftCtx): PNode = assert c.kind in {attachedAsgn, attachedDup} - if c.fn.typ.len == 4: + if c.fn.typ.len == 3 + ord(c.kind == attachedAsgn): result = c.fn.typ.n.lastSon assert result.kind == nkSym assert result.sym.name.s == "cyclic" @@ -302,27 +314,35 @@ 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[1].kind == tyVar: + if op.typ.firstParamType.kind == tyVar: result.add genAddr(c, x) else: result.add x if y != nil: result.add y - if op.typ.len == 4: + if op.typ.signatureLen == 4: assert y != nil - if c.fn.typ.len == 4: + if c.fn.typ.signatureLen == 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[0]) + result = newNodeIT(nkCall, x.info, op.typ.returnType) 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)) @@ -545,7 +565,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.lastSon + let elemType = t.elementType 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: @@ -656,7 +676,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc cyclicType*(g: ModuleGraph, t: PType): bool = case t.kind - of tyRef: result = types.canFormAcycle(g, t.lastSon) + of tyRef: result = types.canFormAcycle(g, t.elementType) of tyProc: result = t.callConv == ccClosure else: result = false @@ -681,7 +701,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = ]# var actions = newNodeI(nkStmtList, c.info) - let elemType = t.lastSon + let elemType = t.elementType createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen) let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(c.g, elemType) @@ -851,7 +871,7 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var actions = newNodeI(nkStmtList, c.info) - let elemType = t.lastSon + let elemType = t.skipModifier #fillBody(c, elemType, actions, genDeref(x), genDeref(y)) #var disposeCall = genBuiltin(c, mDispose, "dispose", x) @@ -1017,7 +1037,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[0], body, x, y) + fillBody(c, t.elementType, body, x, y) of tyTuple: fillBodyTup(c, t, body, x, y) of tyVarargs, tyOpenArray: @@ -1026,7 +1046,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = else: discard "cannot copy openArray" - of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, + of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped, tyTypeDesc, tyGenericInvocation, tyForward, tyStatic: @@ -1034,16 +1054,18 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = discard of tyOrdinal, tyRange, tyInferred, tyGenericInst, tyAlias, tySink: - fillBody(c, lastSon(t), body, x, y) + fillBody(c, skipModifier(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[0] + let baseType = typ.elementType if getAttachedOp(g, baseType, kind) == nil: - discard produceSym(g, c, baseType, kind, info, idgen) + # TODO: fixme `isDistinct` is a fix for #23552; remove it after + # `-d:nimPreviewNonVarDestructor` becomes the default + discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true) result = getAttachedOp(g, baseType, kind) setAttachedOp(g, idgen.module, typ, kind, result) @@ -1082,7 +1104,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache incl result.flags, sfGeneratedOp proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp; - info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym = + info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym = if kind == attachedDup: return symDupPrototype(g, typ, owner, kind, info, idgen) @@ -1093,7 +1115,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp idgen, result, info) if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and - ((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or typ.kind in {tyRef, tyString, tySequence}): + ((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)): dest.typ = typ else: dest.typ = makeVarType(typ.owner, typ, idgen) @@ -1135,19 +1157,19 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newAsgnStmt(xx, yy) proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; - info: TLineInfo; idgen: IdGenerator): PSym = + info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym = if typ.kind == tyDistinct: return produceSymDistinctType(g, c, typ, kind, info, idgen) result = getAttachedOp(g, typ, kind) if result == nil: - result = symPrototype(g, typ, typ.owner, kind, info, idgen) + result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct) var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen, fn: result) let dest = if kind == attachedDup: result.ast[resultPos].sym else: result.typ.n[1].sym - let d = if result.typ[1].kind == tyVar: newDeref(newSymNode(dest)) else: newSymNode(dest) + let d = if result.typ.firstParamType.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) @@ -1160,7 +1182,7 @@ 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[1].kind == tyVar: d[0] else: d) + result.ast[bodyPos].add newOpCall(a, op, if op.typ.firstParamType.kind == tyVar: d[0] else: d) result.ast[bodyPos].add newAsgnStmt(d, src) else: var tk: TTypeKind @@ -1179,7 +1201,12 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; # bug #19205: Do not forget to also copy the hidden type field: genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src) - if not a.canRaise: incl result.flags, sfNeverRaises + if not a.canRaise: + incl result.flags, sfNeverRaises + result.ast[pragmasPos] = newNodeI(nkPragma, info) + result.ast[pragmasPos].add newTree(nkExprColonExpr, + newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info)) + completePartialOp(g, idgen.module, typ, kind, result) @@ -1239,7 +1266,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I else: localError(g.config, info, "unresolved generic parameter") -proc isTrival(s: PSym): bool {.inline.} = +proc isTrival*(s: PSym): bool {.inline.} = s == nil or (s.ast != nil and s.ast[bodyPos].len == 0) proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo; diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index d21825be7..94a483299 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -92,8 +92,10 @@ type warnStmtListLambda = "StmtListLambda", warnBareExcept = "BareExcept", warnImplicitDefaultValue = "ImplicitDefaultValue", + warnIgnoredSymbolInjection = "IgnoredSymbolInjection", warnStdPrefix = "StdPrefix" warnUser = "User", + warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", @@ -196,8 +198,10 @@ 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", warnUser: "$1", + warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output", @@ -264,6 +268,7 @@ 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 diff --git a/compiler/llstream.nim b/compiler/llstream.nim index fa223b373..cc8148483 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -68,6 +68,7 @@ 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") diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 1a60de7e5..d8fcf73e0 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -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: + of nkSymChoices, nkOpenSym: if x[0].kind == nkSym: id.add(x[0].sym.name.s) else: @@ -63,6 +63,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent = result = n[0].sym.name else: handleError(n, origin) + of nkOpenSym: + result = considerQuotedIdent(c, n[0], origin) else: handleError(n, origin) @@ -137,7 +139,7 @@ proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; return result iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym = - var ti: ModuleIter + var ti: ModuleIter = default(ModuleIter) var candidate = initIdentIter(ti, marked, im, name, g) while candidate != nil: yield candidate @@ -150,7 +152,7 @@ iterator importedItems*(c: PContext; name: PIdent): PSym = yield s proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] = - var ti: TIdentIter + var ti: TIdentIter = default(TIdentIter) result = @[] var res = initIdentIter(ti, c.pureEnumFields, name) while res != nil: @@ -222,7 +224,7 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} = proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = result = @[] for scope in allScopes(c.currentScope): - var ti: TIdentIter + var ti: TIdentIter = default(TIdentIter) var candidate = initIdentIter(ti, scope.symbols, s) while candidate != nil: if candidate.kind in filter: @@ -240,7 +242,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy result = @[] block outer: for scope in allScopes(c.currentScope): - var ti: TIdentIter + var ti: TIdentIter = default(TIdentIter) var candidate = initIdentIter(ti, scope.symbols, s) while candidate != nil: if candidate.kind in filter: @@ -256,11 +258,23 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy if s.kind in filter: result.add s +proc cmpScopes*(ctx: PContext, s: PSym): int = + # Do not return a negative number + if s.originatingModule == ctx.module: + result = 2 + var owner = s + while true: + owner = owner.skipGenericOwner + if owner.kind == skModule: break + inc result + else: + result = 1 + proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool = result = false block outer: for scope in allScopes(c.currentScope): - var ti: TIdentIter + var ti: TIdentIter = default(TIdentIter) var candidate = initIdentIter(ti, scope.symbols, s) var scopeHasCandidate = false while candidate != nil: @@ -291,8 +305,16 @@ proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): boo # imports had a candidate but wasn't ambiguous return false -proc errorSym*(c: PContext, n: PNode): PSym = +proc errorSym*(c: PContext, ident: PIdent, info: TLineInfo): PSym = ## creates an error symbol to avoid cascading errors (for IDE support) + result = newSym(skError, ident, c.idgen, getCurrOwner(c), info, {}) + result.typ = errorType(c) + incl(result.flags, sfDiscardable) + # pretend it's from the top level scope to prevent cascading errors: + if c.config.cmd != cmdInteractive and c.compilesContextId == 0: + c.moduleScope.addSym(result) + +proc errorSym*(c: PContext, n: PNode): PSym = var m = n # ensure that 'considerQuotedIdent' can't fail: if m.kind == nkDotExpr: m = m[1] @@ -300,12 +322,7 @@ proc errorSym*(c: PContext, n: PNode): PSym = considerQuotedIdent(c, m) else: getIdent(c.cache, "err:" & renderTree(m)) - result = newSym(skError, ident, c.idgen, getCurrOwner(c), n.info, {}) - result.typ = errorType(c) - incl(result.flags, sfDiscardable) - # pretend it's from the top level scope to prevent cascading errors: - if c.config.cmd != cmdInteractive and c.compilesContextId == 0: - c.moduleScope.addSym(result) + result = errorSym(c, ident, n.info) type TOverloadIterMode* = enum @@ -332,7 +349,7 @@ proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string = proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # check if all symbols have been used and defined: - var it: TTabIter + var it: TTabIter = default(TTabIter) var s = initTabIter(it, scope.symbols) var missingImpls = 0 var unusedSyms: seq[tuple[sym: PSym, key: string]] = @[] @@ -487,7 +504,7 @@ proc mustFixSpelling(c: PContext): bool {.inline.} = result = c.config.spellSuggestMax != 0 and c.compilesContextId == 0 # don't slowdown inside compiles() -proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) = +proc fixSpelling(c: PContext, ident: PIdent, result: var string) = ## when we cannot find the identifier, suggest nearby spellings var list = initHeapQueue[SpellCandidate]() let name0 = ident.s.nimIdentNormalize @@ -543,33 +560,47 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PS amb = false proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = - var amb: bool + var amb: bool = false discard errorUseQualifier(c, info, s, amb) -proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") = - var err = "ambiguous identifier: '" & candidates[0].name.s & "'" +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 & "'" var i = 0 for candidate in candidates: - 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) + 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) inc i - localError(c.config, info, errGenerated, err) -proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) = +proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) = + localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates)) + +proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string = 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 - errorUseQualifier(c, info, candidates, prefix) + result = ambiguousIdentifierMsg(candidates, prefix, indent) + +proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) = + localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices)) 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 & "'" & extra + err = "undeclared identifier: '" & name & "'" + if "`gensym" in name: + err.add "; if declared in a template, this identifier may be inconsistently marked inject or gensym" + if extra.len != 0: + err.add extra if c.recursiveDep.len > 0: err.add "\nThis might be caused by a recursive module dependency:\n" err.add c.recursiveDep @@ -577,11 +608,11 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extr c.recursiveDep = "" localError(c.config, info, errGenerated, err) -proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym = +proc errorUndeclaredIdentifierHint*(c: PContext; ident: PIdent; info: TLineInfo): PSym = var extra = "" - if c.mustFixSpelling: fixSpelling(c, n, ident, extra) - errorUndeclaredIdentifier(c, n.info, ident.s, extra) - result = errorSym(c, n) + if c.mustFixSpelling: fixSpelling(c, ident, extra) + errorUndeclaredIdentifier(c, info, ident.s, extra) + result = errorSym(c, ident, info) proc lookUp*(c: PContext, n: PNode): PSym = # Looks up a symbol. Generates an error in case of nil. @@ -589,13 +620,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, n.ident) + if result == nil: result = errorUndeclaredIdentifierHint(c, n.ident, n.info) of nkSym: result = n.sym of nkAccQuoted: var ident = considerQuotedIdent(c, n) result = searchInScopes(c, ident, amb) - if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident) + if result == nil: result = errorUndeclaredIdentifierHint(c, ident, n.info) else: internalError(c.config, n.info, "lookUp") return nil @@ -609,16 +640,30 @@ 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 = searchInScopesFilterBy(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 = searchInScopesFilterBy(c, ident, allExceptModule) + let candidates = lookUpCandidates(c, ident, allExceptModule) if candidates.len > 0: result = candidates[0] amb = candidates.len > 1 @@ -626,46 +671,47 @@ 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, n, ident) + result = errorUndeclaredIdentifierHint(c, ident, n.info) elif checkAmbiguity in flags and result != nil and amb: result = errorUseQualifier(c, n.info, result, amb) c.isAmbiguous = amb of nkSym: result = n.sym + of nkOpenSym: + result = qualifiedLookUp(c, n[0], flags) of nkDotExpr: result = nil var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule}) if m != nil and m.kind == skModule: var ident: PIdent = nil - if n[1].kind == nkIdent: - ident = n[1].ident - elif n[1].kind == nkAccQuoted: + if 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: - result = strTableGet(c.topLevelScope.symbols, ident) + 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 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 = someSym(c.graph, m, ident) + result = someSymAmb(c.graph, m, ident, amb) + if amb: c.isAmbiguous = true if result == nil and checkUndeclared in flags: - result = errorUndeclaredIdentifierHint(c, n[1], ident) + result = errorUndeclaredIdentifierHint(c, ident, n[1].info) elif n[1].kind == nkSym: result = n[1].sym if result.owner != nil and result.owner != m and checkUndeclared in flags: # dotExpr in templates can end up here - result = errorUndeclaredIdentifierHint(c, n[1], considerQuotedIdent(c, n[1])) + result = errorUndeclaredIdentifierHint(c, result.name, n[1].info) elif checkUndeclared in flags and n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}: localError(c.config, n[1].info, "identifier expected, but got: " & @@ -677,6 +723,10 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = if result != nil and result.kind == skStub: loadStub(result) proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = + if n.kind == nkOpenSym: + # maybe the logic in semexprs should be mirrored here instead + # for now it only seems this is called for `pickSym` in `getTypeIdent` + return initOverloadIter(o, c, n[0]) o.importIdx = -1 o.marked = initIntSet() case n.kind diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index f8ae67f41..2c9c4cb32 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem): import std/assertions proc newDeref*(n: PNode): PNode {.inline.} = - result = newNodeIT(nkHiddenDeref, n.info, n.typ[0]) + result = newNodeIT(nkHiddenDeref, n.info, n.typ.elementType) result.add n proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode = @@ -255,14 +255,14 @@ proc newDotExpr*(obj, b: PSym): PNode = 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)[0] + deref.typ = a.typ.skipTypes(abstractInst).elementType 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[0] + t = t.baseClass if t == nil: break t = t.skipTypes(skipPtrs) #if field == nil: @@ -278,7 +278,7 @@ proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode = 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)[0] + deref.typ = a.typ.skipTypes(abstractInst).elementType 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[0] + t = t.baseClass if t == nil: break t = t.skipTypes(skipPtrs) #if field == nil: @@ -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[0] + t = t.baseClass if t == nil: break t = t.skipTypes(skipPtrs) @@ -325,7 +325,7 @@ proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode = proc genDeref*(n: PNode; k = nkHiddenDeref): PNode = result = newNodeIT(k, n.info, - n.typ.skipTypes(abstractInst)[0]) + n.typ.skipTypes(abstractInst).elementType) result.add n proc callCodegenProc*(g: ModuleGraph; name: string; @@ -344,7 +344,7 @@ proc callCodegenProc*(g: ModuleGraph; name: string; if optionalArgs != nil: for i in 1..<optionalArgs.len-2: result.add optionalArgs[i] - result.typ = sym.typ[0] + result.typ = sym.typ.returnType proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode = result = nkIntLit.newIntNode(value) diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index b365a3a19..1ec6b9a69 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -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[0] != nil and r.typ[0].kind == tyInt: return r + if r.typ.returnType != nil and r.typ.returnType.kind == tyInt: return r result = r if result != nil: return result localError(g.config, info, "system module needs: " & name) diff --git a/compiler/main.nim b/compiler/main.nim index 742530c07..4c52317cf 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -22,8 +22,6 @@ import modules, modulegraphs, lineinfos, pathutils, vmprofiler -# ensure NIR compiles: -import nir / nir when defined(nimPreviewSlimSystem): import std/[syncio, assertions] @@ -48,9 +46,6 @@ 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. @@ -155,32 +150,12 @@ proc commandCompileToC(graph: ModuleGraph) = extccomp.callCCompiler(conf) # for now we do not support writing out a .json file with the build instructions when HCR is on if not conf.hcrOn: - extccomp.writeJsonBuildInstructions(conf) + extccomp.writeJsonBuildInstructions(conf, graph.cachedFiles) if optGenScript in graph.config.globalOptions: writeDepsFile(graph) if optGenCDeps in graph.config.globalOptions: 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) @@ -197,16 +172,13 @@ proc commandCompileToJS(graph: ModuleGraph) = if optGenScript in conf.globalOptions: writeDepsFile(graph) -proc commandInteractive(graph: ModuleGraph; useNir: bool) = +proc commandInteractive(graph: ModuleGraph) = graph.config.setErrorMaxHighMaybe initDefines(graph.config.symbols) - if useNir: - defineSymbol(graph.config.symbols, "noSignalHandler") - else: - defineSymbol(graph.config.symbols, "nimscript") + defineSymbol(graph.config.symbols, "nimscript") # note: seems redundant with -d:nimHasLibFFI when hasFFI: defineSymbol(graph.config.symbols, "nimffi") - setPipeLinePass(graph, if useNir: NirReplPass else: InterpreterPass) + setPipeLinePass(graph, InterpreterPass) compilePipelineSystemModule(graph) if graph.config.commandArgs.len > 0: discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) @@ -222,7 +194,7 @@ proc commandScan(cache: IdentCache, config: ConfigRef) = var stream = llStreamOpen(f, fmRead) if stream != nil: var - L: Lexer + L: Lexer = default(Lexer) tok: Token = default(Token) openLexer(L, f, stream, cache, config) while true: @@ -293,8 +265,6 @@ 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() = @@ -305,7 +275,6 @@ 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) = @@ -326,7 +295,8 @@ 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 - doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee + if not ret.string.isAbsolute: # `AbsoluteDir` is not a real guarantee + rawMessage(conf, errCannotOpenFile, ret.string & "/") if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex}: ret = ret / htmldocsDir conf.outDir = ret @@ -443,7 +413,7 @@ proc mainCommand*(graph: ModuleGraph) = wantMainModule(conf) commandView(graph) #msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") - of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir")) + of cmdInteractive: commandInteractive(graph) of cmdNimscript: if conf.projectIsCmd or conf.projectIsStdin: discard elif not fileExists(conf.projectFull): diff --git a/compiler/mangleutils.nim b/compiler/mangleutils.nim new file mode 100644 index 000000000..2ae954518 --- /dev/null +++ b/compiler/mangleutils.nim @@ -0,0 +1,59 @@ +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 # diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 95fa193dc..77762d23a 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -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] +import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils] import ../dist/checksums/src/checksums/md5 -import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages +import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb import ic / [packed_ast, ic] @@ -55,11 +55,6 @@ type concreteTypes*: seq[FullId] inst*: PInstantiation - SymInfoPair* = object - sym*: PSym - info*: TLineInfo - isDecl*: bool - PipelinePass* = enum NonePass SemPass @@ -67,8 +62,6 @@ type CgenPass EvalPass InterpreterPass - NirPass - NirReplPass GenDependPass Docgen2TexPass Docgen2JsonPass @@ -108,7 +101,7 @@ type doStopCompile*: proc(): bool {.closure.} usageSym*: PSym # for nimsuggest owners*: seq[PSym] - suggestSymbols*: Table[FileIndex, seq[SymInfoPair]] + suggestSymbols*: SuggestSymbolDatabase suggestErrors*: Table[FileIndex, seq[Suggest]] methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization! bucketTable*: CountTable[ItemId] @@ -140,6 +133,8 @@ type idgen*: IdGenerator operators*: Operators + cachedFiles*: StringTableRef + TPassContext* = object of RootObj # the pass's context idgen*: IdGenerator PPassContext* = ref TPassContext @@ -262,7 +257,7 @@ proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym = iterator allSyms*(g: ModuleGraph; m: PSym): PSym = let importHidden = optImportHidden in m.options if isCachedModule(g, m): - var rodIt: RodIter + var rodIt: RodIter = default(RodIter) var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden) while r != nil: yield r @@ -279,11 +274,30 @@ 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 + var mi: ModuleIter = default(ModuleIter) var r = initModuleIter(mi, g, g.systemModule, name) while r != nil: yield r @@ -314,7 +328,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) @@ -457,6 +471,49 @@ 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 @@ -468,7 +525,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, FileIndex(m.position)))) + uniqueName: rope(uniqueModuleName(g.config, m))) initStrTables(g, m) proc registerModuleById*(g: ModuleGraph; m: FileIndex) = @@ -477,19 +534,21 @@ proc registerModuleById*(g: ModuleGraph; m: FileIndex) = proc initOperators*(g: ModuleGraph): Operators = # These are safe for IC. # Public because it's used by DrNim. - 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) + 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) + ) proc initModuleGraphFields(result: ModuleGraph) = # A module ID of -1 means that the symbol is not attached to a module at all, @@ -502,7 +561,7 @@ proc initModuleGraphFields(result: ModuleGraph) = result.importStack = @[] result.inclToMod = initTable[FileIndex, FileIndex]() result.owners = @[] - result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]() + result.suggestSymbols = initTable[FileIndex, SuggestFileSymbolDatabase]() result.suggestErrors = initTable[FileIndex, seq[Suggest]]() result.methods = @[] result.compilerprocs = initStrTable() @@ -516,6 +575,7 @@ proc initModuleGraphFields(result: ModuleGraph) = result.symBodyHashes = initTable[int, SigHash]() result.operators = initOperators(result) result.emittedTypeInfo = initTable[string, FileIndex]() + result.cachedFiles = newStringTable() proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result = ModuleGraph() @@ -617,7 +677,6 @@ 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) = @@ -680,8 +739,6 @@ 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") @@ -707,16 +764,14 @@ func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool = ## Check if symbol belongs to the 'stdlib' package. sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId -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, @[]) +proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): SuggestFileSymbolDatabase = + result = graph.suggestSymbols.getOrDefault(fileIdx, newSuggestFileSymbolDatabase(fileIdx, optIdeExceptionInlayHints in graph.config.globalOptions)) + doAssert(result.fileIndex == fileIdx) iterator suggestSymbolsIter*(g: ModuleGraph): SymInfoPair = for xs in g.suggestSymbols.values: - for x in xs: - yield x + for i in xs.lineInfo.low..xs.lineInfo.high: + yield xs.getSymInfoPair(i) iterator suggestErrorsIter*(g: ModuleGraph): Suggest = for xs in g.suggestErrors.values: diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index c29ed6793..c9e6060e5 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -38,11 +38,18 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = localError(n.info, "only '/' supported with $package notation") result = "" else: - let modname = getModuleName(conf, n[2]) - # hacky way to implement 'x / y /../ z': - result = getModuleName(conf, n1) - result.add renderTree(n0, {renderNoComments}).replace(" ") - result.add modname + if n0.kind in nkIdentKinds: + let ident = n0.getPIdent + if ident != nil and ident.s[0] == '/': + let modname = getModuleName(conf, n[2]) + # hacky way to implement 'x / y /../ z': + result = getModuleName(conf, n1) + result.add renderTree(n0, {renderNoComments}).replace(" ") + result.add modname + else: + result = "" + else: + result = "" of nkPrefix: when false: if n[0].kind == nkIdent and n[0].ident.s == "$": diff --git a/compiler/modules.nim b/compiler/modules.nim index 0aa1c8930..6e2af8bcc 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -14,6 +14,9 @@ import idents, lexer, syntaxes, modulegraphs, lineinfos, pathutils +import ../dist/checksums/src/checksums/sha1 +import std/strtabs + proc resetSystemArtifacts*(g: ModuleGraph) = magicsys.resetSysTypes(g) @@ -42,6 +45,8 @@ proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode = result = syntaxes.parseFile(fileIdx, graph.cache, graph.config) graph.addDep(s, fileIdx) graph.addIncludeDep(s.position.FileIndex, fileIdx) + let path = toFullPath(graph.config, fileIdx) + graph.cachedFiles[path] = $secureHashFile(path) proc wantMainModule*(conf: ConfigRef) = if conf.projectFull.isEmpty: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 5c30acff3..c49ca8c9b 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -61,14 +61,12 @@ proc makeCString*(s: string): Rope = result.add('\"') proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo = - result.fullPath = fullPath - #shallow(result.fullPath) - result.projPath = projPath - #shallow(result.projPath) - result.shortName = fullPath.extractFilename + result = TFileInfo(fullPath: fullPath, projPath: projPath, + shortName: fullPath.extractFilename, + quotedFullName: fullPath.string.makeCString, + lines: @[] + ) result.quotedName = result.shortName.makeCString - result.quotedFullName = fullPath.string.makeCString - result.lines = @[] when defined(nimpretty): if not result.fullPath.isEmpty: try: @@ -125,18 +123,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 + var dummy: bool = false result = fileInfoIdx(conf, filename, dummy) proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile; isKnownFile: var bool): FileIndex = fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), isKnownFile) proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex = - var dummy: bool + var dummy: bool = false fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy) proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = - result.fileIndex = fileInfoIdx + result = TLineInfo(fileIndex: fileInfoIdx) if line < int high(uint16): result.line = uint16(line) else: @@ -431,7 +429,8 @@ To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 f proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) = if msg in fatalMsgs: if conf.cmd == cmdIdeTools: log(s) - quit(conf, msg) + if conf.cmd != cmdIdeTools or msg != errFatal: + quit(conf, msg) if msg >= errMin and msg <= errMax or (msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg): inc(conf.errorCounter) @@ -439,7 +438,11 @@ proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, if conf.errorCounter >= conf.errorMax: # only really quit when we're not in the new 'nim check --def' mode: if conf.ideCmd == ideNone: - quit(conf, msg) + when defined(nimsuggest): + #we need to inform the user that something went wrong when initializing NimSuggest + raiseRecoverableError(s) + else: + quit(conf, msg) elif eh == doAbort and conf.cmd != cmdIdeTools: quit(conf, msg) elif eh == doRaise: @@ -557,9 +560,10 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, ignoreMsg = not conf.hasHint(msg) if not ignoreMsg and msg in conf.warningAsErrors: title = ErrorTitle + color = ErrorColor else: title = HintTitle - color = HintColor + color = HintColor inc(conf.hintCounter) let s = if isRaw: arg else: getMessageStr(msg, arg) @@ -625,7 +629,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 == cmdIdeTools and conf.structuredErrorHook.isNil: return + if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return writeContext(conf, info) liMessage(conf, info, errInternal, errMsg, doAbort, info2) @@ -647,13 +651,16 @@ template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraM let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName liMessage(conf, info, msg, m, doNothing, instLoc()) -proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = - if i.fileIndex.int32 < 0: +proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope = + if fi.int32 < 0: result = makeCString "???" elif optExcessiveStackTrace in conf.globalOptions: - result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName + result = conf.m.fileInfos[fi.int32].quotedFullName else: - result = conf.m.fileInfos[i.fileIndex.int32].quotedName + result = conf.m.fileInfos[fi.int32].quotedName + +proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = + quotedFilename(conf, i.fileIndex) template listMsg(title, r) = msgWriteln(conf, title, {msgNoUnitSep}) @@ -662,31 +669,6 @@ 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" diff --git a/compiler/ndi.nim b/compiler/ndi.nim index a9d9cfe79..cc18ab39f 100644 --- a/compiler/ndi.nim +++ b/compiler/ndi.nim @@ -29,7 +29,7 @@ proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) = 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.writeRope(s.loc.snippet) f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf) template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) = diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim index 2a6de8733..7e0efc34b 100644 --- a/compiler/nilcheck.nim +++ b/compiler/nilcheck.nim @@ -498,7 +498,7 @@ proc checkCall(n, ctx, map): Check = # check args and handle possible mutations var isNew = false - result.map = map + result = Check(map: map) for i, child in n: discard check(child, ctx, map) @@ -507,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[0].kind == tyRef: + if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ.elementType.kind == tyRef: if not isNew: result.map = newNilMap(map) isNew = true @@ -753,6 +753,7 @@ 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 @@ -825,7 +826,7 @@ proc checkFor(n, ctx, map): Check = var check2 = check(n.sons[2], ctx, m) var map2 = check2.map - result.map = ctx.union(map0, m) + result = Check(map: ctx.union(map0, m)) result.map = ctx.union(result.map, map2) result.nilability = Safe @@ -853,7 +854,7 @@ proc checkWhile(n, ctx, map): Check = var check2 = check(n.sons[1], ctx, m) var map2 = check2.map - result.map = ctx.union(map0, map1) + result = Check(map: ctx.union(map0, map1)) result.map = ctx.union(result.map, map2) result.nilability = Safe @@ -899,7 +900,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.map = newNilMap(map) + result = Check(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) @@ -947,7 +948,7 @@ proc checkCase(n, ctx, map): Check = # c2 # also a == true is a , a == false is not a let base = n[0] - result.map = map.copyMap() + result = Check(map: map.copyMap()) result.nilability = Safe var a: PNode = nil for child in n: @@ -1219,7 +1220,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.map = map + result = Check(map: map) if n.kind in {nkObjConstr, nkTupleConstr}: # TODO deeper nested elements? # A(field: B()) # @@ -1246,10 +1247,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: - result.map = map + of nkVarSection, nkLetSection: + result = Check(map: map) for child in n: - result = checkAsgn(child[0], child[2], ctx, result.map) + result = checkAsgn(child[0].skipPragmaExpr, child[2], ctx, result.map) of nkForStmt: result = checkFor(n, ctx, map) of nkCaseStmt: @@ -1273,8 +1274,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = else: var elementMap = map.copyMap() - var elementCheck: Check - elementCheck.map = elementMap + var elementCheck = Check(map: elementMap) for element in n: elementCheck = check(element, ctx, elementCheck.map) @@ -1367,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[0].isNil and s.typ[0].kind == tyRef: Nil else: Safe, TResult, s.ast.info) + map.store(context, resultExprIndex, if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef: Nil else: Safe, TResult, s.ast.info) # echo "checking ", s.name.s, " ", filename @@ -1383,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[0].isNil and s.typ[0].kind == tyRef and tfNotNil in s.typ[0].flags: + if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef and tfNotNil in s.typ.returnType.flags: checkResult(s.ast, context, res.map) diff --git a/compiler/nim.nim b/compiler/nim.nim index 184303f8e..005f11a58 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -116,8 +116,9 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = conf.backend = backendC if conf.selectedGC == gcUnselected: - if conf.backend in {backendC, backendCpp, backendObjc, backendNir} or - (conf.cmd == cmdInteractive and isDefined(conf, "nir")): + if conf.backend in {backendC, backendCpp, backendObjc} or + (conf.cmd in cmdDocLike and conf.backend != backendJs) or + conf.cmd == cmdGendepend: initOrcDefines(conf) mainCommand(graph) diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 4b6e22bc9..a5324ea76 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -79,6 +79,8 @@ 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 = "-#" diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 1e7d62b4d..5417cd1e9 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -223,7 +223,7 @@ proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache; stream = llStreamOpen(filename, fmRead) if stream != nil: openLexer(L, filename, stream, cache, config) - tok.tokType = tkEof # to avoid a pointless warning + tok = Token(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) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index e98de7e62..0833cfeb3 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -11,8 +11,8 @@ import ast, modules, condsyms, options, llstream, lineinfos, vm, - vmdef, modulegraphs, idents, os, pathutils, - scriptconfig, std/[compilesettings, tables] + vmdef, modulegraphs, idents, pathutils, + scriptconfig, std/[compilesettings, tables, os] 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 + var it: ModuleIter = default(ModuleIter) var s = initModuleIter(it, i.graph, i.mainModule, n) result = nil while s != nil: diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 59a542a85..7edf55278 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -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[0]) + first = firstOrd(conf, s.typ.elementType) bitSetInit(result, int(getSize(conf, s.typ))) for i in 0..<s.len: if s[i].kind == nkRange: diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim deleted file mode 100644 index 51dfcdb09..000000000 --- a/compiler/nir/ast2ir.nim +++ /dev/null @@ -1,2645 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import std / [assertions, tables, sets] -import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys, - modulegraphs, renderer, transf, bitsets, trees, nimsets, - expanddefaults] -from ".." / lowerings import lowerSwap, lowerTupleUnpacking -from ".." / pathutils import customPath -import .. / ic / bitabs - -import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir, nirfiles - -when defined(nimCompilerStacktraceHints): - import std/stackframes - -type - ModuleCon* = ref object - nirm*: ref NirModule - types: TypesCon - module*: PSym - graph*: ModuleGraph - nativeIntId, nativeUIntId: TypeId - strPayloadId: (TypeId, TypeId) - idgen: IdGenerator - processedProcs, pendingProcsAsSet: HashSet[ItemId] - pendingProcs: seq[PSym] # procs we still need to generate code for - pendingVarsAsSet: HashSet[ItemId] - pendingVars: seq[PSym] - noModularity*: bool - inProc: int - toSymId: Table[ItemId, SymId] - symIdCounter: int32 - - ProcCon* = object - config*: ConfigRef - lit: Literals - lastFileKey: FileIndex - lastFileVal: LitId - labelGen: int - exitLabel: LabelId - #code*: Tree - blocks: seq[(PSym, LabelId)] - sm: SlotManager - idgen: IdGenerator - m: ModuleCon - prc: PSym - options: TOptions - -template code(c: ProcCon): Tree = c.m.nirm.code - -proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym; - nirm: ref NirModule): ModuleCon = - #let lit = Literals() # must be shared - result = ModuleCon(graph: graph, types: initTypesCon(config), nirm: nirm, - idgen: idgen, module: module) - case config.target.intSize - of 2: - result.nativeIntId = Int16Id - result.nativeUIntId = UInt16Id - of 4: - result.nativeIntId = Int32Id - result.nativeUIntId = UInt16Id - else: - result.nativeIntId = Int64Id - result.nativeUIntId = UInt16Id - result.strPayloadId = strPayloadPtrType(result.types, result.nirm.types) - nirm.namespace = nirm.lit.strings.getOrIncl(customPath(toFullPath(config, module.info))) - nirm.intbits = uint32(config.target.intSize * 8) - -proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon = - result = ProcCon(m: m, sm: initSlotManager({}), prc: prc, config: config, - lit: m.nirm.lit, idgen: m.idgen, - options: if prc != nil: prc.options - else: config.options) - result.exitLabel = newLabel(result.labelGen) - -proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo = - var val: LitId - if c.lastFileKey == i.fileIndex: - val = c.lastFileVal - else: - val = c.lit.strings.getOrIncl(toFullPath(c.config, i.fileIndex)) - # remember the entry: - c.lastFileKey = i.fileIndex - c.lastFileVal = val - result = pack(c.m.nirm.man, val, int32 i.line, int32 i.col) - -proc bestEffort(c: ProcCon): TLineInfo = - if c.prc != nil: - c.prc.info - else: - c.m.module.info - -proc popBlock(c: var ProcCon; oldLen: int) = - c.blocks.setLen(oldLen) - -template withBlock(labl: PSym; info: PackedLineInfo; asmLabl: LabelId; body: untyped) {.dirty.} = - var oldLen {.gensym.} = c.blocks.len - c.blocks.add (labl, asmLabl) - body - popBlock(c, oldLen) - -type - GenFlag = enum - gfAddrOf # load the address of the expression - gfToOutParam # the expression is passed to an `out` parameter - GenFlags = set[GenFlag] - -proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) - -proc genScope(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = - openScope c.sm - gen c, n, d, flags - closeScope c.sm - -proc freeTemp(c: var ProcCon; tmp: Value) = - let s = extractTemp(tmp) - if s != SymId(-1): - freeTemp(c.sm, s) - -proc freeTemps(c: var ProcCon; tmps: openArray[Value]) = - for t in tmps: freeTemp(c, t) - -proc typeToIr(m: ModuleCon; t: PType): TypeId = - typeToIr(m.types, m.nirm.types, t) - -proc allocTemp(c: var ProcCon; t: TypeId): SymId = - if c.m.noModularity: - result = allocTemp(c.sm, t, c.m.symIdCounter) - else: - result = allocTemp(c.sm, t, c.idgen.symId) - -const - ListSymId = -1 - -proc toSymId(c: var ProcCon; s: PSym): SymId = - if c.m.noModularity: - result = c.m.toSymId.getOrDefault(s.itemId, SymId(-1)) - if result.int < 0: - inc c.m.symIdCounter - result = SymId(c.m.symIdCounter) - c.m.toSymId[s.itemId] = result - when ListSymId != -1: - if result.int == ListSymId or s.name.s == "echoBinSafe": - echo result.int, " is ", s.name.s, " ", c.m.graph.config $ s.info, " ", s.flags - writeStackTrace() - else: - result = SymId(s.itemId.item) - -proc getTemp(c: var ProcCon; n: PNode): Value = - let info = toLineInfo(c, n.info) - let t = typeToIr(c.m, n.typ) - let tmp = allocTemp(c, t) - c.code.addSummon info, tmp, t - result = localToValue(info, tmp) - -proc getTemp(c: var ProcCon; t: TypeId; info: PackedLineInfo): Value = - let tmp = allocTemp(c, t) - c.code.addSummon info, tmp, t - result = localToValue(info, tmp) - -proc gen(c: var ProcCon; n: PNode; flags: GenFlags = {}) = - var tmp = default(Value) - gen(c, n, tmp, flags) - freeTemp c, tmp - -proc genScope(c: var ProcCon; n: PNode; flags: GenFlags = {}) = - openScope c.sm - gen c, n, flags - closeScope c.sm - -proc genx(c: var ProcCon; n: PNode; flags: GenFlags = {}): Value = - result = default(Value) - gen(c, n, result, flags) - assert Tree(result).len > 0, $n - -proc clearDest(c: var ProcCon; n: PNode; d: var Value) {.inline.} = - when false: - if n.typ.isNil or n.typ.kind == tyVoid: - let s = extractTemp(d) - if s != SymId(-1): - freeLoc(c.sm, s) - -proc isNotOpr(n: PNode): bool = - n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot - -proc jmpBack(c: var ProcCon; n: PNode; lab: LabelId) = - c.code.gotoLabel toLineInfo(c, n.info), GotoLoop, lab - -type - JmpKind = enum opcFJmp, opcTJmp - -proc xjmp(c: var ProcCon; n: PNode; jk: JmpKind; v: Value): LabelId = - result = newLabel(c.labelGen) - let info = toLineInfo(c, n.info) - buildTyped c.code, info, Select, Bool8Id: - c.code.copyTree Tree(v) - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, jk == opcTJmp) - c.code.gotoLabel info, Goto, result - -proc patch(c: var ProcCon; n: PNode; L: LabelId) = - addLabel c.code, toLineInfo(c, n.info), Label, L - -proc genWhile(c: var ProcCon; n: PNode) = - # lab1: - # cond, tmp - # fjmp tmp, lab2 - # body - # jmp lab1 - # lab2: - let info = toLineInfo(c, n.info) - let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) - withBlock(nil, info, lab1): - if isTrue(n[0]): - c.gen(n[1]) - c.jmpBack(n, lab1) - elif isNotOpr(n[0]): - var tmp = c.genx(n[0][1]) - let lab2 = c.xjmp(n, opcTJmp, tmp) - c.freeTemp(tmp) - c.gen(n[1]) - c.jmpBack(n, lab1) - c.patch(n, lab2) - else: - var tmp = c.genx(n[0]) - let lab2 = c.xjmp(n, opcFJmp, tmp) - c.freeTemp(tmp) - c.gen(n[1]) - c.jmpBack(n, lab1) - c.patch(n, lab2) - -proc genBlock(c: var ProcCon; n: PNode; d: var Value) = - openScope c.sm - let info = toLineInfo(c, n.info) - let lab1 = newLabel(c.labelGen) - - withBlock(n[0].sym, info, lab1): - c.gen(n[1], d) - - c.code.addLabel(info, Label, lab1) - closeScope c.sm - c.clearDest(n, d) - -proc jumpTo(c: var ProcCon; n: PNode; L: LabelId) = - c.code.addLabel(toLineInfo(c, n.info), Goto, L) - -proc genBreak(c: var ProcCon; n: PNode) = - if n[0].kind == nkSym: - for i in countdown(c.blocks.len-1, 0): - if c.blocks[i][0] == n[0].sym: - c.jumpTo n, c.blocks[i][1] - return - localError(c.config, n.info, "NIR problem: cannot find 'break' target") - else: - c.jumpTo n, c.blocks[c.blocks.high][1] - -proc genIf(c: var ProcCon; n: PNode; d: var Value) = - # if (!expr1) goto lab1; - # thenPart - # goto LEnd - # lab1: - # if (!expr2) goto lab2; - # thenPart2 - # goto LEnd - # lab2: - # elsePart - # Lend: - if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) - var ending = newLabel(c.labelGen) - for i in 0..<n.len: - var it = n[i] - if it.len == 2: - let info = toLineInfo(c, it[0].info) - var elsePos: LabelId - if isNotOpr(it[0]): - let tmp = c.genx(it[0][1]) - elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true - c.freeTemp tmp - else: - let tmp = c.genx(it[0]) - elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false - c.freeTemp tmp - c.clearDest(n, d) - if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `d` - c.genScope(it[1]) - else: - c.genScope(it[1], d) # then part - if i < n.len-1: - c.jumpTo it[1], ending - c.patch(it, elsePos) - else: - c.clearDest(n, d) - if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `d` - c.genScope(it[0]) - else: - c.genScope(it[0], d) - c.patch(n, ending) - c.clearDest(n, d) - -proc tempToDest(c: var ProcCon; n: PNode; d: var Value; tmp: Value) = - if isEmpty(d): - d = tmp - else: - let info = toLineInfo(c, n.info) - buildTyped c.code, info, Asgn, typeToIr(c.m, n.typ): - c.code.copyTree d - c.code.copyTree tmp - freeTemp(c, tmp) - -proc genAndOr(c: var ProcCon; n: PNode; opc: JmpKind; d: var Value) = - # asgn d, a - # tjmp|fjmp lab1 - # asgn d, b - # lab1: - var tmp = getTemp(c, n) - c.gen(n[1], tmp) - let lab1 = c.xjmp(n, opc, tmp) - c.gen(n[2], tmp) - c.patch(n, lab1) - tempToDest c, n, d, tmp - -proc unused(c: var ProcCon; n: PNode; x: Value) {.inline.} = - if hasValue(x): - #debug(n) - localError(c.config, n.info, "not unused") - -proc caseValue(c: var ProcCon; n: PNode) = - let info = toLineInfo(c, n.info) - build c.code, info, SelectValue: - let x = genx(c, n) - c.code.copyTree x - freeTemp(c, x) - -proc caseRange(c: var ProcCon; n: PNode) = - let info = toLineInfo(c, n.info) - build c.code, info, SelectRange: - let x = genx(c, n[0]) - let y = genx(c, n[1]) - c.code.copyTree x - c.code.copyTree y - freeTemp(c, y) - freeTemp(c, x) - -proc addUseCodegenProc(c: var ProcCon; dest: var Tree; name: string; info: PackedLineInfo) = - let cp = getCompilerProc(c.m.graph, name) - let theProc = c.genx newSymNode(cp) - copyTree c.code, theProc - -template buildCond(useNegation: bool; cond: typed; body: untyped) = - let lab = newLabel(c.labelGen) - buildTyped c.code, info, Select, Bool8Id: - c.code.copyTree cond - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, useNegation) - c.code.gotoLabel info, Goto, lab - - body - c.code.addLabel info, Label, lab - -template buildIf(cond: typed; body: untyped) = - buildCond false, cond, body - -template buildIfNot(cond: typed; body: untyped) = - buildCond true, cond, body - -template buildIfThenElse(cond: typed; then, otherwise: untyped) = - let lelse = newLabel(c.labelGen) - let lend = newLabel(c.labelGen) - buildTyped c.code, info, Select, Bool8Id: - c.code.copyTree cond - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, false) - c.code.gotoLabel info, Goto, lelse - - then() - c.code.gotoLabel info, Goto, lend - c.code.addLabel info, Label, lelse - otherwise() - c.code.addLabel info, Label, lend - -include stringcases - -proc genCase(c: var ProcCon; n: PNode; d: var Value) = - if not isEmptyType(n.typ): - if isEmpty(d): d = getTemp(c, n) - else: - unused(c, n, d) - - if n[0].typ.skipTypes(abstractInst).kind == tyString: - genStringCase(c, n, d) - return - - var sections = newSeqOfCap[LabelId](n.len-1) - let ending = newLabel(c.labelGen) - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[0]) - buildTyped c.code, info, Select, typeToIr(c.m, n[0].typ): - c.code.copyTree tmp - for i in 1..<n.len: - let section = newLabel(c.labelGen) - sections.add section - let it = n[i] - let itinfo = toLineInfo(c, it.info) - build c.code, itinfo, SelectPair: - build c.code, itinfo, SelectList: - for j in 0..<it.len-1: - if it[j].kind == nkRange: - caseRange c, it[j] - else: - caseValue c, it[j] - c.code.addLabel itinfo, Goto, section - c.freeTemp tmp - for i in 1..<n.len: - let it = n[i] - let itinfo = toLineInfo(c, it.info) - c.code.addLabel itinfo, Label, sections[i-1] - c.gen it.lastSon - if i != n.len-1: - c.code.addLabel itinfo, Goto, ending - c.code.addLabel info, Label, ending - -proc rawCall(c: var ProcCon; info: PackedLineInfo; opc: Opcode; t: TypeId; args: var openArray[Value]) = - buildTyped c.code, info, opc, t: - if opc in {CheckedCall, CheckedIndirectCall}: - c.code.addLabel info, CheckedGoto, c.exitLabel - for a in mitems(args): - c.code.copyTree a - freeTemp c, a - -proc canRaiseDisp(c: ProcCon; n: PNode): bool = - # we assume things like sysFatal cannot raise themselves - if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: - result = false - elif optPanics in c.config.globalOptions or - (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and - sfSystemRaisesDefect notin n.sym.flags): - # we know we can be strict: - result = canRaise(n) - else: - # we have to be *very* conservative: - result = canRaiseConservative(n) - -proc genCall(c: var ProcCon; n: PNode; d: var Value) = - let canRaise = canRaiseDisp(c, n[0]) - - let opc = if n[0].kind == nkSym and n[0].sym.kind in routineKinds: - (if canRaise: CheckedCall else: Call) - else: - (if canRaise: CheckedIndirectCall else: IndirectCall) - let info = toLineInfo(c, n.info) - - # In the IR we cannot nest calls. Thus we use two passes: - var args: seq[Value] = @[] - var t = n[0].typ - if t != nil: t = t.skipTypes(abstractInst) - args.add genx(c, n[0]) - for i in 1..<n.len: - if t != nil and i < t.len: - if isCompileTimeOnly(t[i]): discard - elif isOutParam(t[i]): args.add genx(c, n[i], {gfToOutParam}) - else: args.add genx(c, n[i]) - else: - args.add genx(c, n[i]) - - let tb = typeToIr(c.m, n.typ) - if not isEmptyType(n.typ): - if isEmpty(d): d = getTemp(c, n) - # XXX Handle problematic aliasing here: `a = f_canRaise(a)`. - buildTyped c.code, info, Asgn, tb: - c.code.copyTree d - rawCall c, info, opc, tb, args - else: - rawCall c, info, opc, tb, args - freeTemps c, args - -proc genRaise(c: var ProcCon; n: PNode) = - let info = toLineInfo(c, n.info) - let tb = typeToIr(c.m, n[0].typ) - - let d = genx(c, n[0]) - buildTyped c.code, info, SetExc, tb: - c.code.copyTree d - c.freeTemp(d) - c.code.addLabel info, Goto, c.exitLabel - -proc genReturn(c: var ProcCon; n: PNode) = - if n[0].kind != nkEmpty: - gen(c, n[0]) - # XXX Block leave actions? - let info = toLineInfo(c, n.info) - c.code.addLabel info, Goto, c.exitLabel - -proc genTry(c: var ProcCon; n: PNode; d: var Value) = - if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) - var endings: seq[LabelId] = @[] - let ehPos = newLabel(c.labelGen) - let oldExitLab = c.exitLabel - c.exitLabel = ehPos - if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `d` - c.gen(n[0]) - else: - c.gen(n[0], d) - c.clearDest(n, d) - - # Add a jump past the exception handling code - let jumpToFinally = newLabel(c.labelGen) - c.jumpTo n, jumpToFinally - # This signals where the body ends and where the exception handling begins - c.patch(n, ehPos) - c.exitLabel = oldExitLab - for i in 1..<n.len: - let it = n[i] - if it.kind != nkFinally: - # first opcExcept contains the end label of the 'except' block: - let endExcept = newLabel(c.labelGen) - for j in 0..<it.len - 1: - assert(it[j].kind == nkType) - let typ = it[j].typ.skipTypes(abstractPtrs-{tyTypeDesc}) - let itinfo = toLineInfo(c, it[j].info) - build c.code, itinfo, TestExc: - c.code.addTyped itinfo, typeToIr(c.m, typ) - if it.len == 1: - let itinfo = toLineInfo(c, it.info) - build c.code, itinfo, TestExc: - c.code.addTyped itinfo, VoidId - let body = it.lastSon - if isEmptyType(body.typ): # maybe noreturn call, don't touch `d` - c.gen(body) - else: - c.gen(body, d) - c.clearDest(n, d) - if i < n.len: - endings.add newLabel(c.labelGen) - c.patch(it, endExcept) - let fin = lastSon(n) - # we always generate an 'opcFinally' as that pops the safepoint - # from the stack if no exception is raised in the body. - c.patch(fin, jumpToFinally) - #c.gABx(fin, opcFinally, 0, 0) - for endPos in endings: c.patch(n, endPos) - if fin.kind == nkFinally: - c.gen(fin[0]) - c.clearDest(n, d) - #c.gABx(fin, opcFinallyEnd, 0, 0) - -template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar -proc isGlobal(n: PNode): bool = n.kind == nkSym and isGlobal(n.sym) - -proc genField(c: var ProcCon; n: PNode; d: var Value) = - var pos: int - if n.kind != nkSym or n.sym.kind != skField: - localError(c.config, n.info, "no field symbol") - pos = 0 - else: - pos = n.sym.position - d.addImmediateVal toLineInfo(c, n.info), pos - -proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) = - let info = toLineInfo(c, n.info) - if arr.skipTypes(abstractInst).kind == tyArray and - (let offset = firstOrd(c.config, arr); offset != Zero): - let x = c.genx(n) - buildTyped d, info, Sub, c.m.nativeIntId: - copyTree d.Tree, x - d.addImmediateVal toLineInfo(c, n.info), toInt(offset) - else: - c.gen(n, d) - if optBoundsCheck in c.options: - let idx = move d - build d, info, CheckedIndex: - d.Tree.addLabel info, CheckedGoto, c.exitLabel - copyTree d.Tree, idx - let x = toInt64 lengthOrd(c.config, arr) - d.addIntVal c.lit.numbers, info, c.m.nativeIntId, x - -proc rawGenNew(c: var ProcCon; d: Value; refType: PType; ninfo: TLineInfo; needsInit: bool) = - assert refType.kind == tyRef - let baseType = refType.lastSon - - let info = toLineInfo(c, ninfo) - let codegenProc = magicsys.getCompilerProc(c.m.graph, - if needsInit: "nimNewObj" else: "nimNewObjUninit") - let refTypeIr = typeToIr(c.m, refType) - buildTyped c.code, info, Asgn, refTypeIr: - copyTree c.code, d - buildTyped c.code, info, Cast, refTypeIr: - buildTyped c.code, info, Call, VoidPtrId: - let theProc = c.genx newSymNode(codegenProc, ninfo) - copyTree c.code, theProc - c.code.addImmediateVal info, int(getSize(c.config, baseType)) - c.code.addImmediateVal info, int(getAlign(c.config, baseType)) - -proc genNew(c: var ProcCon; n: PNode; needsInit: bool) = - # If in doubt, always follow the blueprint of the C code generator for `mm:orc`. - let refType = n[1].typ.skipTypes(abstractInstOwned) - let d = genx(c, n[1]) - rawGenNew c, d, refType, n.info, needsInit - freeTemp c, d - -proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let seqtype = skipTypes(n.typ, abstractVarRange) - let baseType = seqtype.lastSon - var a = c.genx(n[1]) - if isEmpty(d): d = getTemp(c, n) - # $1.len = 0 - buildTyped c.code, info, Asgn, c.m.nativeIntId: - buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype): - copyTree c.code, d - c.code.addImmediateVal info, 0 - c.code.addImmediateVal info, 0 - # $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3)) - let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0] - buildTyped c.code, info, Asgn, payloadPtr: - # $1.p - buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype): - copyTree c.code, d - c.code.addImmediateVal info, 1 - # ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3)) - buildTyped c.code, info, Cast, payloadPtr: - buildTyped c.code, info, Call, VoidPtrId: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayloadUninit") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - copyTree c.code, a - c.code.addImmediateVal info, int(getSize(c.config, baseType)) - c.code.addImmediateVal info, int(getAlign(c.config, baseType)) - freeTemp c, a - -proc genNewSeqPayload(c: var ProcCon; info: PackedLineInfo; d, b: Value; seqtype: PType) = - let baseType = seqtype.lastSon - # $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3)) - let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0] - - # $1.len = $2 - buildTyped c.code, info, Asgn, c.m.nativeIntId: - buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype): - copyTree c.code, d - c.code.addImmediateVal info, 0 - copyTree c.code, b - - buildTyped c.code, info, Asgn, payloadPtr: - # $1.p - buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype): - copyTree c.code, d - c.code.addImmediateVal info, 1 - # ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3)) - buildTyped c.code, info, Cast, payloadPtr: - buildTyped c.code, info, Call, VoidPtrId: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayload") - let theProc = c.genx newSymNode(codegenProc) - copyTree c.code, theProc - copyTree c.code, b - c.code.addImmediateVal info, int(getSize(c.config, baseType)) - c.code.addImmediateVal info, int(getAlign(c.config, baseType)) - -proc genNewSeq(c: var ProcCon; n: PNode) = - let info = toLineInfo(c, n.info) - let seqtype = skipTypes(n[1].typ, abstractVarRange) - var d = c.genx(n[1]) - var b = c.genx(n[2]) - - genNewSeqPayload(c, info, d, b, seqtype) - - freeTemp c, b - freeTemp c, d - -template intoDest*(d: var Value; info: PackedLineInfo; typ: TypeId; body: untyped) = - if typ == VoidId: - body(c.code) - elif isEmpty(d): - body(Tree(d)) - else: - buildTyped c.code, info, Asgn, typ: - copyTree c.code, d - body(c.code) - -template valueIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) = - if isEmpty(d): - body(Tree d) - else: - buildTyped c.code, info, Asgn, typeToIr(c.m, typ): - copyTree c.code, d - body(c.code) - -template constrIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) = - var tmp = default(Value) - body(Tree tmp) - if isEmpty(d): - d = tmp - else: - buildTyped c.code, info, Asgn, typeToIr(c.m, typ): - copyTree c.code, d - copyTree c.code, tmp - -proc genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let tmp2 = c.genx(n[2]) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, opc, t: - if opc in {CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedMod}: - target.addLabel info, CheckedGoto, c.exitLabel - copyTree target, tmp - copyTree target, tmp2 - intoDest d, info, t, body - c.freeTemp(tmp) - c.freeTemp(tmp2) - -proc genCmpOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let tmp2 = c.genx(n[2]) - let t = typeToIr(c.m, n[1].typ) - template body(target) = - buildTyped target, info, opc, t: - copyTree target, tmp - copyTree target, tmp2 - intoDest d, info, Bool8Id, body - c.freeTemp(tmp) - c.freeTemp(tmp2) - -proc genUnaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, opc, t: - copyTree target, tmp - intoDest d, info, t, body - c.freeTemp(tmp) - -proc genIncDec(c: var ProcCon; n: PNode; opc: Opcode) = - let info = toLineInfo(c, n.info) - let t = typeToIr(c.m, skipTypes(n[1].typ, abstractVar)) - - let d = c.genx(n[1]) - let tmp = c.genx(n[2]) - # we produce code like: i = i + 1 - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - buildTyped c.code, info, opc, t: - if opc in {CheckedAdd, CheckedSub}: - c.code.addLabel info, CheckedGoto, c.exitLabel - copyTree c.code, d - copyTree c.code, tmp - c.freeTemp(tmp) - #c.genNarrow(n[1], d) - c.freeTemp(d) - -proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) = - #echo c.m.graph.config $ n.info, " ", n - let info = toLineInfo(c, n.info) - var a = n[1] - #if a.kind == nkHiddenAddr: a = a[0] - var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses) - case typ.kind - of tyOpenArray, tyVarargs: - let xa = c.genx(a) - template body(target) = - buildTyped target, info, FieldAt, typeToIr(c.m, typ): - copyTree target, xa - target.addImmediateVal info, 1 # (p, len)-pair so len is at index 1 - intoDest d, info, c.m.nativeIntId, body - - of tyCstring: - let xa = c.genx(a) - if isEmpty(d): d = getTemp(c, n) - buildTyped c.code, info, Call, c.m.nativeIntId: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCStrLen") - assert codegenProc != nil - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - copyTree c.code, xa - - of tyString, tySequence: - let xa = c.genx(a) - - if typ.kind == tySequence: - # we go through a temporary here because people write bullshit code. - if isEmpty(d): d = getTemp(c, n) - - template body(target) = - buildTyped target, info, FieldAt, typeToIr(c.m, typ): - copyTree target, xa - target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 - intoDest d, info, c.m.nativeIntId, body - - of tyArray: - template body(target) = - target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) - intoDest d, info, c.m.nativeIntId, body - else: internalError(c.config, n.info, "genArrayLen()") - -proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, Sub, t: - # Little hack: This works because we know that `0.0` is all 0 bits: - target.addIntVal(c.lit.numbers, info, t, 0) - copyTree target, tmp - intoDest d, info, t, body - c.freeTemp(tmp) - -proc genHigh(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let t = typeToIr(c.m, n.typ) - var x = default(Value) - genArrayLen(c, n, x) - template body(target) = - buildTyped target, info, Sub, t: - copyTree target, x - target.addIntVal(c.lit.numbers, info, t, 1) - intoDest d, info, t, body - c.freeTemp x - -proc genBinaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string) = - let info = toLineInfo(c, n.info) - let xa = c.genx(n[1]) - let xb = c.genx(n[2]) - if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) - - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, Call, t: - let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc) - #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree target, theProc - copyTree target, xa - copyTree target, xb - - intoDest d, info, t, body - c.freeTemp xb - c.freeTemp xa - -proc genUnaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string; argAt = 1) = - let info = toLineInfo(c, n.info) - let xa = c.genx(n[argAt]) - if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) - - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, Call, t: - let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc) - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree target, theProc - copyTree target, xa - - intoDest d, info, t, body - c.freeTemp xa - -proc genEnumToStr(c: var ProcCon; n: PNode; d: var Value) = - let t = n[1].typ.skipTypes(abstractInst+{tyRange}) - let toStrProc = getToStringProc(c.m.graph, t) - # XXX need to modify this logic for IC. - var nb = copyTree(n) - nb[0] = newSymNode(toStrProc) - gen(c, nb, d) - -proc genOf(c: var ProcCon; n: PNode; d: var Value) = - genUnaryOp c, n, d, TestOf - -template sizeOfLikeMsg(name): string = - "'" & name & "' requires '.importc' types to be '.completeStruct'" - -proc genIsNil(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[1]) - let t = typeToIr(c.m, n[1].typ) - template body(target) = - buildTyped target, info, Eq, t: - copyTree target, tmp - addNilVal target, info, t - intoDest d, info, Bool8Id, body - c.freeTemp(tmp) - -proc fewCmps(conf: ConfigRef; s: PNode): bool = - # this function estimates whether it is better to emit code - # for constructing the set or generating a bunch of comparisons directly - if s.kind != nkCurly: - result = false - elif (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags): - result = false # it is better to emit the set generation code - elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}: - result = true # better not emit the set if int is basetype! - else: - result = s.len <= 8 # 8 seems to be a good value - -proc genInBitset(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - - let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ) - let setType = typeToIr(c.m, n[1].typ) - let mask = - case t - of UInt8Id: 7 - of UInt16Id: 15 - of UInt32Id: 31 - else: 63 - let expansion = if t == UInt64Id: UInt64Id else: c.m.nativeUIntId - # "(($1 &(1U<<((NU)($2)&7U)))!=0)" - or - - # "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)" - - template body(target) = - buildTyped target, info, BoolNot, Bool8Id: - buildTyped target, info, Eq, t: - buildTyped target, info, BitAnd, t: - if c.m.nirm.types[setType].kind != ArrayTy: - copyTree target, a - else: - buildTyped target, info, ArrayAt, setType: - copyTree target, a - buildTyped target, info, BitShr, t: - buildTyped target, info, Cast, expansion: - copyTree target, b - addIntVal target, c.lit.numbers, info, expansion, 3 - - buildTyped target, info, BitShl, t: - addIntVal target, c.lit.numbers, info, t, 1 - buildTyped target, info, BitAnd, t: - buildTyped target, info, Cast, expansion: - copyTree target, b - addIntVal target, c.lit.numbers, info, expansion, mask - addIntVal target, c.lit.numbers, info, t, 0 - intoDest d, info, t, body - - c.freeTemp(b) - c.freeTemp(a) - -proc genInSet(c: var ProcCon; n: PNode; d: var Value) = - let g {.cursor.} = c.m.graph - if n[1].kind == nkCurly and fewCmps(g.config, n[1]): - # a set constructor but not a constant set: - # do not emit the set, but generate a bunch of comparisons; and if we do - # so, we skip the unnecessary range check: This is a semantical extension - # that code now relies on. :-/ XXX - let elem = if n[2].kind in {nkChckRange, nkChckRange64}: n[2][0] - else: n[2] - let curly = n[1] - var ex: PNode = nil - for it in curly: - var test: PNode - if it.kind == nkRange: - test = newTree(nkCall, g.operators.opAnd.newSymNode, - newTree(nkCall, g.operators.opLe.newSymNode, it[0], elem), # a <= elem - newTree(nkCall, g.operators.opLe.newSymNode, elem, it[1]) - ) - else: - test = newTree(nkCall, g.operators.opEq.newSymNode, elem, it) - test.typ = getSysType(g, it.info, tyBool) - - if ex == nil: ex = test - else: ex = newTree(nkCall, g.operators.opOr.newSymNode, ex, test) - - if ex == nil: - let info = toLineInfo(c, n.info) - template body(target) = - boolVal target, c.lit.numbers, info, false - intoDest d, info, Bool8Id, body - else: - gen c, ex, d - else: - genInBitset c, n, d - -proc genCard(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let t = typeToIr(c.m, n.typ) - - let setType = typeToIr(c.m, n[1].typ) - if isEmpty(d): d = getTemp(c, n) - - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - buildTyped c.code, info, Call, t: - if c.m.nirm.types[setType].kind == ArrayTy: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "cardSet") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType): - copyTree c.code, a - c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) - elif t == UInt64Id: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits64") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - copyTree c.code, a - else: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits32") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - buildTyped c.code, info, Cast, UInt32Id: - copyTree c.code, a - freeTemp c, a - -proc genEqSet(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - let t = typeToIr(c.m, n.typ) - - let setType = typeToIr(c.m, n[1].typ) - - if c.m.nirm.types[setType].kind == ArrayTy: - if isEmpty(d): d = getTemp(c, n) - - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - buildTyped c.code, info, Eq, t: - buildTyped c.code, info, Call, t: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCmpMem") - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType): - copyTree c.code, a - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType): - copyTree c.code, b - c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - - else: - template body(target) = - buildTyped target, info, Eq, setType: - copyTree target, a - copyTree target, b - intoDest d, info, Bool8Id, body - - freeTemp c, b - freeTemp c, a - -proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (SymId, LabelId, LabelId) = - let tmp = allocTemp(c, c.m.nativeIntId) - c.code.addSummon info, tmp, c.m.nativeIntId - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, tmp - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, first - let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) - result = (tmp, lab1, newLabel(c.labelGen)) - - buildTyped c.code, info, Select, Bool8Id: - buildTyped c.code, info, Lt, c.m.nativeIntId: - c.code.addSymUse info, tmp - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, last - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, false) - c.code.gotoLabel info, Goto, result[2] - -proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): (SymId, LabelId, LabelId) = - let tmp = allocTemp(c, c.m.nativeIntId) - c.code.addSummon info, tmp, c.m.nativeIntId - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, tmp - copyTree c.code, first - let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) - result = (tmp, lab1, newLabel(c.labelGen)) - - buildTyped c.code, info, Select, Bool8Id: - buildTyped c.code, info, Le, c.m.nativeIntId: - c.code.addSymUse info, tmp - copyTree c.code, last - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, false) - c.code.gotoLabel info, Goto, result[2] - -proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId) = - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, s - buildTyped c.code, info, Add, c.m.nativeIntId: - c.code.addSymUse info, s - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 - c.code.addLabel info, GotoLoop, back - c.code.addLabel info, Label, exit - freeTemp(c.sm, s) - -proc genLeSet(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - let t = typeToIr(c.m, n.typ) - - let setType = typeToIr(c.m, n[1].typ) - - if c.m.nirm.types[setType].kind == ArrayTy: - let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ) - if isEmpty(d): d = getTemp(c, n) - # "for ($1 = 0; $1 < $2; $1++):" - # " $3 = (($4[$1] & ~ $5[$1]) == 0)" - # " if (!$3) break;" - let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) - buildTyped c.code, info, Asgn, Bool8Id: - copyTree c.code, d - buildTyped c.code, info, Eq, elemType: - buildTyped c.code, info, BitAnd, elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - c.code.addSymUse info, idx - buildTyped c.code, info, BitNot, elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, b - c.code.addSymUse info, idx - c.code.addIntVal c.lit.numbers, info, elemType, 0 - - # if !$3: break - buildTyped c.code, info, Select, Bool8Id: - c.code.copyTree d - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, false) - c.code.gotoLabel info, Goto, endLabel - - endLoop(c, info, idx, backLabel, endLabel) - else: - # "(($1 & ~ $2)==0)" - template body(target) = - buildTyped target, info, Eq, setType: - buildTyped target, info, BitAnd, setType: - copyTree target, a - buildTyped target, info, BitNot, setType: - copyTree target, b - target.addIntVal c.lit.numbers, info, setType, 0 - - intoDest d, info, Bool8Id, body - - freeTemp c, b - freeTemp c, a - -proc genLtSet(c: var ProcCon; n: PNode; d: var Value) = - localError(c.m.graph.config, n.info, "`<` for sets not implemented") - -proc genBinarySet(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - let t = typeToIr(c.m, n.typ) - - let setType = typeToIr(c.m, n[1].typ) - - if c.m.nirm.types[setType].kind == ArrayTy: - let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ) - if isEmpty(d): d = getTemp(c, n) - # "for ($1 = 0; $1 < $2; $1++):" - # " $3 = (($4[$1] & ~ $5[$1]) == 0)" - # " if (!$3) break;" - let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) - buildTyped c.code, info, Asgn, elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - c.code.addSymUse info, idx - buildTyped c.code, info, (if m == mPlusSet: BitOr else: BitAnd), elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - c.code.addSymUse info, idx - if m == mMinusSet: - buildTyped c.code, info, BitNot, elemType: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, b - c.code.addSymUse info, idx - else: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, b - c.code.addSymUse info, idx - - endLoop(c, info, idx, backLabel, endLabel) - else: - # "(($1 & ~ $2)==0)" - template body(target) = - buildTyped target, info, (if m == mPlusSet: BitOr else: BitAnd), setType: - copyTree target, a - if m == mMinusSet: - buildTyped target, info, BitNot, setType: - copyTree target, b - else: - copyTree target, b - - intoDest d, info, setType, body - - freeTemp c, b - freeTemp c, a - -proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) = - let info = toLineInfo(c, n.info) - let a = c.genx(n[1]) - let b = c.genx(n[2]) - - let setType = typeToIr(c.m, n[1].typ) - - let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ) - let mask = - case t - of UInt8Id: 7 - of UInt16Id: 15 - of UInt32Id: 31 - else: 63 - - buildTyped c.code, info, Asgn, setType: - if c.m.nirm.types[setType].kind == ArrayTy: - if m == mIncl: - # $1[(NU)($2)>>3] |=(1U<<($2&7U)) - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, b - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitOr, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, b - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, b - c.code.addIntVal c.lit.numbers, info, t, 7 - else: - # $1[(NU)($2)>>3] &= ~(1U<<($2&7U)) - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, b - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitAnd, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, a - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, b - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitNot, t: - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, b - c.code.addIntVal c.lit.numbers, info, t, 7 - - else: - copyTree c.code, a - if m == mIncl: - # $1 |= ((NU8)1)<<(($2) & 7) - buildTyped c.code, info, BitOr, setType: - copyTree c.code, a - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, b - c.code.addIntVal c.lit.numbers, info, t, mask - else: - # $1 &= ~(((NU8)1) << (($2) & 7)) - buildTyped c.code, info, BitAnd, setType: - copyTree c.code, a - buildTyped c.code, info, BitNot, t: - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, b - c.code.addIntVal c.lit.numbers, info, t, mask - freeTemp c, b - freeTemp c, a - -proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = - # example: { a..b, c, d, e, f..g } - # we have to emit an expression of the form: - # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); - # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); - let info = toLineInfo(c, n.info) - let setType = typeToIr(c.m, n.typ) - let size = int(getSize(c.config, n.typ)) - let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ) - let mask = - case t - of UInt8Id: 7 - of UInt16Id: 15 - of UInt32Id: 31 - else: 63 - - if isEmpty(d): d = getTemp(c, n) - if c.m.nirm.types[setType].kind != ArrayTy: - buildTyped c.code, info, Asgn, setType: - copyTree c.code, d - c.code.addIntVal c.lit.numbers, info, t, 0 - - for it in n: - if it.kind == nkRange: - let a = genx(c, it[0]) - let b = genx(c, it[1]) - let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) - buildTyped c.code, info, Asgn, setType: - copyTree c.code, d - buildTyped c.code, info, BitAnd, setType: - copyTree c.code, d - buildTyped c.code, info, BitNot, t: - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - c.code.addSymUse info, idx - c.code.addIntVal c.lit.numbers, info, t, mask - - endLoop(c, info, idx, backLabel, endLabel) - freeTemp c, b - freeTemp c, a - - else: - let a = genx(c, it) - buildTyped c.code, info, Asgn, setType: - copyTree c.code, d - buildTyped c.code, info, BitAnd, setType: - copyTree c.code, d - buildTyped c.code, info, BitNot, t: - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, a - c.code.addIntVal c.lit.numbers, info, t, mask - freeTemp c, a - - else: - # init loop: - let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, size) - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - c.code.addIntVal c.lit.numbers, info, t, 0 - endLoop(c, info, idx, backLabel, endLabel) - - # incl elements: - for it in n: - if it.kind == nkRange: - let a = genx(c, it[0]) - let b = genx(c, it[1]) - let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) - - buildTyped c.code, info, Asgn, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - c.code.addSymUse info, idx - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitOr, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - c.code.addSymUse info, idx - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - c.code.addSymUse info, idx - c.code.addIntVal c.lit.numbers, info, t, 7 - - endLoop(c, info, idx, backLabel, endLabel) - freeTemp c, b - freeTemp c, a - - else: - let a = genx(c, it) - # $1[(NU)($2)>>3] |=(1U<<($2&7U)) - buildTyped c.code, info, Asgn, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, a - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitOr, t: - buildTyped c.code, info, ArrayAt, setType: - copyTree c.code, d - buildTyped c.code, info, BitShr, t: - buildTyped c.code, info, Cast, c.m.nativeUIntId: - copyTree c.code, a - addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3 - buildTyped c.code, info, BitShl, t: - c.code.addIntVal c.lit.numbers, info, t, 1 - buildTyped c.code, info, BitAnd, t: - copyTree c.code, a - c.code.addIntVal c.lit.numbers, info, t, 7 - freeTemp c, a - -proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) = - if isDeepConstExpr(n): - let info = toLineInfo(c, n.info) - let setType = typeToIr(c.m, n.typ) - let size = int(getSize(c.config, n.typ)) - let cs = toBitSet(c.config, n) - - if c.m.nirm.types[setType].kind != ArrayTy: - template body(target) = - target.addIntVal c.lit.numbers, info, setType, cast[BiggestInt](bitSetToWord(cs, size)) - intoDest d, info, setType, body - else: - let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ) - template body(target) = - buildTyped target, info, ArrayConstr, setType: - for i in 0..high(cs): - target.addIntVal c.lit.numbers, info, t, int64 cs[i] - intoDest d, info, setType, body - else: - genSetConstrDyn c, n, d - -proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - # <Nim code> - # s = "Hello " & name & ", how do you feel?" & 'z' - # - # <generated code> - # { - # string tmp0; - # ... - # tmp0 = rawNewString(6 + 17 + 1 + s2->len); - # // we cannot generate s = rawNewString(...) here, because - # // ``s`` may be used on the right side of the expression - # appendString(tmp0, strlit_1); - # appendString(tmp0, name); - # appendString(tmp0, strlit_2); - # appendChar(tmp0, 'z'); - # asgn(s, tmp0); - # } - var args: seq[Value] = @[] - var argsRuntimeLen: seq[Value] = @[] - - var precomputedLen = 0 - for i in 1 ..< n.len: - let it = n[i] - args.add genx(c, it) - if skipTypes(it.typ, abstractVarRange).kind == tyChar: - inc precomputedLen - elif it.kind in {nkStrLit..nkTripleStrLit}: - inc precomputedLen, it.strVal.len - else: - argsRuntimeLen.add args[^1] - - # generate length computation: - var tmpLen = allocTemp(c, c.m.nativeIntId) - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, tmpLen - c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, precomputedLen - for a in mitems(argsRuntimeLen): - buildTyped c.code, info, Asgn, c.m.nativeIntId: - c.code.addSymUse info, tmpLen - buildTyped c.code, info, CheckedAdd, c.m.nativeIntId: - c.code.addLabel info, CheckedGoto, c.exitLabel - c.code.addSymUse info, tmpLen - buildTyped c.code, info, FieldAt, typeToIr(c.m, n.typ): - copyTree c.code, a - c.code.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 - - var tmpStr = getTemp(c, n) - # ^ because of aliasing, we always go through a temporary - let t = typeToIr(c.m, n.typ) - buildTyped c.code, info, Asgn, t: - copyTree c.code, tmpStr - buildTyped c.code, info, Call, t: - let codegenProc = magicsys.getCompilerProc(c.m.graph, "rawNewString") - #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - c.code.addSymUse info, tmpLen - freeTemp c.sm, tmpLen - - for i in 1 ..< n.len: - let it = n[i] - let isChar = skipTypes(it.typ, abstractVarRange).kind == tyChar - buildTyped c.code, info, Call, VoidId: - let codegenProc = magicsys.getCompilerProc(c.m.graph, - (if isChar: "appendChar" else: "appendString")) - #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, t): - copyTree c.code, tmpStr - copyTree c.code, args[i-1] - freeTemp c, args[i-1] - - if isEmpty(d): - d = tmpStr - else: - # XXX Test that this does not cause memory leaks! - buildTyped c.code, info, Asgn, t: - copyTree c.code, d - copyTree c.code, tmpStr - -proc genDefault(c: var ProcCon; n: PNode; d: var Value) = - let m = expandDefault(n.typ, n.info) - gen c, m, d - -proc genWasMoved(c: var ProcCon; n: PNode) = - let n1 = n[1].skipAddr - # XXX We need a way to replicate this logic or better yet a better - # solution for injectdestructors.nim: - #if c.withinBlockLeaveActions > 0 and notYetAlive(n1): - var d = c.genx(n1) - assert not isEmpty(d) - let m = expandDefault(n1.typ, n1.info) - gen c, m, d - -proc genMove(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - let n1 = n[1].skipAddr - var a = c.genx(n1) - if n.len == 4: - # generated by liftdestructors: - let src = c.genx(n[2]) - # if ($1.p == $2.p) goto lab1 - let lab1 = newLabel(c.labelGen) - - let n1t = typeToIr(c.m, n1.typ) - let payloadType = seqPayloadPtrType(c.m.types, c.m.nirm.types, n1.typ)[0] - buildTyped c.code, info, Select, Bool8Id: - buildTyped c.code, info, Eq, payloadType: - buildTyped c.code, info, FieldAt, n1t: - copyTree c.code, a - c.code.addImmediateVal info, 1 # (len, p)-pair - buildTyped c.code, info, FieldAt, n1t: - copyTree c.code, src - c.code.addImmediateVal info, 1 # (len, p)-pair - - build c.code, info, SelectPair: - build c.code, info, SelectValue: - c.code.boolVal(c.lit.numbers, info, true) - c.code.gotoLabel info, Goto, lab1 - - gen(c, n[3]) - c.patch n, lab1 - - buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ): - copyTree c.code, a - copyTree c.code, src - - else: - if isEmpty(d): d = getTemp(c, n) - buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ): - copyTree c.code, d - copyTree c.code, a - var op = getAttachedOp(c.m.graph, n.typ, attachedWasMoved) - if op == nil or skipTypes(n1.typ, abstractVar+{tyStatic}).kind in {tyOpenArray, tyVarargs}: - let m = expandDefault(n1.typ, n1.info) - gen c, m, a - else: - var opB = c.genx(newSymNode(op)) - buildTyped c.code, info, Call, typeToIr(c.m, n.typ): - copyTree c.code, opB - buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, typeToIr(c.m, n1.typ)): - copyTree c.code, a - -template fieldAt(x: Value; i: int; t: TypeId): Tree = - var result = default(Tree) - buildTyped result, info, FieldAt, t: - copyTree result, x - result.addImmediateVal info, i - result - -template eqNil(x: Tree; t: TypeId): Tree = - var result = default(Tree) - buildTyped result, info, Eq, t: - copyTree result, x - result.addNilVal info, t - result - -template eqZero(x: Tree): Tree = - var result = default(Tree) - buildTyped result, info, Eq, c.m.nativeIntId: - copyTree result, x - result.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - result - -template bitOp(x: Tree; opc: Opcode; y: int): Tree = - var result = default(Tree) - buildTyped result, info, opc, c.m.nativeIntId: - copyTree result, x - result.addIntVal c.lit.numbers, info, c.m.nativeIntId, y - result - -proc genDestroySeq(c: var ProcCon; n: PNode; t: PType) = - let info = toLineInfo(c, n.info) - let strLitFlag = 1 shl (c.m.graph.config.target.intSize * 8 - 2) # see also NIM_STRLIT_FLAG - - let x = c.genx(n[1]) - let baseType = t.lastSon - - let seqType = typeToIr(c.m, t) - let p = fieldAt(x, 0, seqType) - - # if $1.p != nil and ($1.p.cap and NIM_STRLIT_FLAG) == 0: - # alignedDealloc($1.p, NIM_ALIGNOF($2)) - buildIfNot p.eqNil(seqType): - buildIf fieldAt(Value(p), 0, seqPayloadPtrType(c.m.types, c.m.nirm.types, t)[0]).bitOp(BitAnd, 0).eqZero(): - let codegenProc = getCompilerProc(c.m.graph, "alignedDealloc") - buildTyped c.code, info, Call, VoidId: - let theProc = c.genx newSymNode(codegenProc, n.info) - copyTree c.code, theProc - copyTree c.code, p - c.code.addImmediateVal info, int(getAlign(c.config, baseType)) - - freeTemp c, x - -proc genDestroy(c: var ProcCon; n: PNode) = - let t = n[1].typ.skipTypes(abstractInst) - case t.kind - of tyString: - var unused = default(Value) - genUnaryCp(c, n, unused, "nimDestroyStrV1") - of tySequence: - genDestroySeq(c, n, t) - else: discard "nothing to do" - -type - IndexFor = enum - ForSeq, ForStr, ForOpenArray, ForArray - -proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PType): Value = - if optBoundsCheck in c.options: - let info = toLineInfo(c, n.info) - result = default(Value) - let idx = genx(c, n) - build result, info, CheckedIndex: - result.Tree.addLabel info, CheckedGoto, c.exitLabel - copyTree result.Tree, idx - case kind - of ForSeq, ForStr: - buildTyped result, info, FieldAt, typeToIr(c.m, arr): - copyTree result.Tree, a - result.addImmediateVal info, 0 # (len, p)-pair - of ForOpenArray: - buildTyped result, info, FieldAt, typeToIr(c.m, arr): - copyTree result.Tree, a - result.addImmediateVal info, 1 # (p, len)-pair - of ForArray: - let x = toInt64 lengthOrd(c.config, arr) - result.addIntVal c.lit.numbers, info, c.m.nativeIntId, x - freeTemp c, idx - else: - result = genx(c, n) - -proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo; - x: Value; n: PNode; arrType: PType) = - let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.lastSon)) - case arrType.kind - of tyString, tySequence: - let checkKind = if arrType.kind == tyString: ForStr else: ForSeq - let pay = if checkKind == ForStr: c.m.strPayloadId - else: seqPayloadPtrType(c.m.types, c.m.nirm.types, arrType) - - let y = genIndexCheck(c, n[2], x, checkKind, arrType) - let z = genIndexCheck(c, n[3], x, checkKind, arrType) - - buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ): - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, DerefArrayAt, pay[1]: - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, x - target.addImmediateVal info, 1 # (len, p)-pair - copyTree target, y - - # len: - target.addImmediateVal info, 1 - buildTyped target, info, Add, c.m.nativeIntId: - buildTyped target, info, Sub, c.m.nativeIntId: - copyTree target, z - copyTree target, y - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 - - freeTemp c, z - freeTemp c, y - of tyArray: - # XXX This evaluates the index check for `y` twice. - # This check is also still insufficient for non-zero based arrays. - let y = genIndexCheck(c, n[2], x, ForArray, arrType) - let z = genIndexCheck(c, n[3], x, ForArray, arrType) - - buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ): - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, ArrayAt, typeToIr(c.m, arrType): - copyTree target, x - copyTree target, y - - target.addImmediateVal info, 1 - buildTyped target, info, Add, c.m.nativeIntId: - buildTyped target, info, Sub, c.m.nativeIntId: - copyTree target, z - copyTree target, y - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 - - freeTemp c, z - freeTemp c, y - of tyOpenArray: - # XXX This evaluates the index check for `y` twice. - let y = genIndexCheck(c, n[2], x, ForOpenArray, arrType) - let z = genIndexCheck(c, n[3], x, ForOpenArray, arrType) - let pay = openArrayPayloadType(c.m.types, c.m.nirm.types, arrType) - - buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ): - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, DerefArrayAt, pay: - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, x - target.addImmediateVal info, 0 # (p, len)-pair - copyTree target, y - - target.addImmediateVal info, 1 - buildTyped target, info, Add, c.m.nativeIntId: - buildTyped target, info, Sub, c.m.nativeIntId: - copyTree target, z - copyTree target, y - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1 - - freeTemp c, z - freeTemp c, y - else: - raiseAssert "addSliceFields: " & typeToString(arrType) - -proc genSlice(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - - let x = c.genx(n[1]) - - let arrType = n[1].typ.skipTypes(abstractVar) - - template body(target) = - c.addSliceFields target, info, x, n, arrType - - valueIntoDest c, info, d, arrType, body - freeTemp c, x - -proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = - case m - of mAnd: c.genAndOr(n, opcFJmp, d) - of mOr: c.genAndOr(n, opcTJmp, d) - of mPred, mSubI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedSub else: Sub) - of mSucc, mAddI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedAdd else: Add) - of mInc: - unused(c, n, d) - c.genIncDec(n, if optOverflowCheck in c.options: CheckedAdd else: Add) - of mDec: - unused(c, n, d) - c.genIncDec(n, if optOverflowCheck in c.options: CheckedSub else: Sub) - of mOrd, mChr, mUnown: - c.gen(n[1], d) - of generatedMagics: - genCall(c, n, d) - of mNew, mNewFinalize: - unused(c, n, d) - c.genNew(n, needsInit = true) - of mNewSeq: - unused(c, n, d) - c.genNewSeq(n) - of mNewSeqOfCap: c.genNewSeqOfCap(n, d) - of mNewString, mNewStringOfCap, mExit: c.genCall(n, d) - of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr: - genArrayLen(c, n, d) - of mMulI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMul else: Mul) - of mDivI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedDiv else: Div) - of mModI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMod else: Mod) - of mAddF64: genBinaryOp(c, n, d, Add) - of mSubF64: genBinaryOp(c, n, d, Sub) - of mMulF64: genBinaryOp(c, n, d, Mul) - of mDivF64: genBinaryOp(c, n, d, Div) - of mShrI: genBinaryOp(c, n, d, BitShr) - of mShlI: genBinaryOp(c, n, d, BitShl) - of mAshrI: genBinaryOp(c, n, d, BitShr) - of mBitandI: genBinaryOp(c, n, d, BitAnd) - of mBitorI: genBinaryOp(c, n, d, BitOr) - of mBitxorI: genBinaryOp(c, n, d, BitXor) - of mAddU: genBinaryOp(c, n, d, Add) - of mSubU: genBinaryOp(c, n, d, Sub) - of mMulU: genBinaryOp(c, n, d, Mul) - of mDivU: genBinaryOp(c, n, d, Div) - of mModU: genBinaryOp(c, n, d, Mod) - of mEqI, mEqB, mEqEnum, mEqCh: - genCmpOp(c, n, d, Eq) - of mLeI, mLeEnum, mLeCh, mLeB: - genCmpOp(c, n, d, Le) - of mLtI, mLtEnum, mLtCh, mLtB: - genCmpOp(c, n, d, Lt) - of mEqF64: genCmpOp(c, n, d, Eq) - of mLeF64: genCmpOp(c, n, d, Le) - of mLtF64: genCmpOp(c, n, d, Lt) - of mLePtr, mLeU: genCmpOp(c, n, d, Le) - of mLtPtr, mLtU: genCmpOp(c, n, d, Lt) - of mEqProc, mEqRef: - genCmpOp(c, n, d, Eq) - of mXor: genBinaryOp(c, n, d, BitXor) - of mNot: genUnaryOp(c, n, d, BoolNot) - of mUnaryMinusI, mUnaryMinusI64: - genUnaryMinus(c, n, d) - #genNarrow(c, n, d) - of mUnaryMinusF64: genUnaryMinus(c, n, d) - of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], d) - of mBitnotI: - genUnaryOp(c, n, d, BitNot) - when false: - # XXX genNarrowU modified, do not narrow signed types - let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) - let size = getSize(c.config, t) - if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): - c.gABC(n, opcNarrowU, d, TRegister(size*8)) - of mStrToStr, mEnsureMove: c.gen n[1], d - of mIntToStr: genUnaryCp(c, n, d, "nimIntToStr") - of mInt64ToStr: genUnaryCp(c, n, d, "nimInt64ToStr") - of mBoolToStr: genUnaryCp(c, n, d, "nimBoolToStr") - of mCharToStr: genUnaryCp(c, n, d, "nimCharToStr") - of mFloatToStr: - if n[1].typ.skipTypes(abstractInst).kind == tyFloat32: - genUnaryCp(c, n, d, "nimFloat32ToStr") - else: - genUnaryCp(c, n, d, "nimFloatToStr") - of mCStrToStr: genUnaryCp(c, n, d, "cstrToNimstr") - of mEnumToStr: genEnumToStr(c, n, d) - - of mEqStr: genBinaryCp(c, n, d, "eqStrings") - of mEqCString: genCall(c, n, d) - of mLeStr: genBinaryCp(c, n, d, "leStrings") - of mLtStr: genBinaryCp(c, n, d, "ltStrings") - - of mSetLengthStr: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genBinaryCp(c, nb, d, "setLengthStrV2") - - of mSetLengthSeq: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genCall(c, nb, d) - - of mSwap: - unused(c, n, d) - c.gen(lowerSwap(c.m.graph, n, c.m.idgen, - if c.prc == nil: c.m.module else: c.prc), d) - of mParseBiggestFloat: - genCall c, n, d - of mHigh: - c.genHigh n, d - - of mEcho: - unused(c, n, d) - genUnaryCp c, n, d, "echoBinSafe" - - of mAppendStrCh: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genBinaryCp(c, nb, d, "nimAddCharV1") - of mMinI, mMaxI, mAbsI, mDotDot: - c.genCall(n, d) - of mSizeOf: - localError(c.config, n.info, sizeOfLikeMsg("sizeof")) - of mAlignOf: - localError(c.config, n.info, sizeOfLikeMsg("alignof")) - of mOffsetOf: - localError(c.config, n.info, sizeOfLikeMsg("offsetof")) - of mRunnableExamples: - discard "just ignore any call to runnableExamples" - of mOf: genOf(c, n, d) - of mAppendStrStr: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genBinaryCp(c, nb, d, "nimAddStrV1") - of mAppendSeqElem: - unused(c, n, d) - let nb = copyTree(n) - nb[1] = makeAddr(nb[1], c.m.idgen) - genCall(c, nb, d) - of mIsNil: genIsNil(c, n, d) - of mInSet: genInSet(c, n, d) - of mCard: genCard(c, n, d) - of mEqSet: genEqSet(c, n, d) - of mLeSet: genLeSet(c, n, d) - of mLtSet: genLtSet(c, n, d) - of mMulSet: genBinarySet(c, n, d, m) - of mPlusSet: genBinarySet(c, n, d, m) - of mMinusSet: genBinarySet(c, n, d, m) - of mIncl, mExcl: - unused(c, n, d) - genInclExcl(c, n, m) - of mConStrStr: genStrConcat(c, n, d) - of mDefault, mZeroDefault: - genDefault c, n, d - of mMove: genMove(c, n, d) - of mWasMoved, mReset: - unused(c, n, d) - genWasMoved(c, n) - of mDestroy: genDestroy(c, n) - #of mAccessEnv: unaryExpr(d, n, d, "$1.ClE_0") - #of mAccessTypeField: genAccessTypeField(c, n, d) - of mSlice: genSlice(c, n, d) - of mTrace: discard "no code to generate" - else: - # mGCref, mGCunref: unused by ORC - globalError(c.config, n.info, "cannot generate code for: " & $m) - -proc canElimAddr(n: PNode; idgen: IdGenerator): PNode = - result = nil - case n[0].kind - of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: - var m = n[0][0] - if m.kind in {nkDerefExpr, nkHiddenDeref}: - # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) - result = copyNode(n[0]) - result.add m[0] - if n.typ.skipTypes(abstractVar).kind != tyOpenArray: - result.typ = n.typ - elif n.typ.skipTypes(abstractInst).kind in {tyVar}: - result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: - var m = n[0][1] - if m.kind in {nkDerefExpr, nkHiddenDeref}: - # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) - result = copyNode(n[0]) - result.add n[0][0] - result.add m[0] - if n.typ.skipTypes(abstractVar).kind != tyOpenArray: - result.typ = n.typ - elif n.typ.skipTypes(abstractInst).kind in {tyVar}: - result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) - else: - if n[0].kind in {nkDerefExpr, nkHiddenDeref}: - # addr ( deref ( x )) --> x - result = n[0][0] - -proc genAddr(c: var ProcCon; n: PNode; d: var Value, flags: GenFlags) = - if (let m = canElimAddr(n, c.m.idgen); m != nil): - gen(c, m, d, flags) - return - - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[0], flags) - template body(target) = - buildTyped target, info, AddrOf, typeToIr(c.m, n.typ): - copyTree target, tmp - - valueIntoDest c, info, d, n.typ, body - freeTemp c, tmp - -proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = - let info = toLineInfo(c, n.info) - let tmp = c.genx(n[0], flags) - template body(target) = - buildTyped target, info, Load, typeToIr(c.m, n.typ): - copyTree target, tmp - - valueIntoDest c, info, d, n.typ, body - freeTemp c, tmp - -proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; tmp: Value; typ: PType) = - let arrType = typ.skipTypes(abstractVar) - let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.lastSon)) - case arrType.kind - of tyString: - let t = typeToIr(c.m, typ) - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]: - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, tmp - target.addImmediateVal info, 1 # (len, p)-pair - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - # len: - target.addImmediateVal info, 1 - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, tmp - target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 - - of tySequence: - let t = typeToIr(c.m, typ) - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, typ)[1]: - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, tmp - target.addImmediateVal info, 1 # (len, p)-pair - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - # len: - target.addImmediateVal info, 1 - buildTyped target, info, FieldAt, typeToIr(c.m, arrType): - copyTree target, tmp - target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 - - of tyArray: - let t = typeToIr(c.m, arrType) - target.addImmediateVal info, 0 - buildTyped target, info, AddrOf, elemType: - buildTyped target, info, ArrayAt, t: - copyTree target, tmp - target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0 - target.addImmediateVal info, 1 - target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, arrType)) - else: - raiseAssert "addAddrOfFirstElem: " & typeToString(typ) - -proc genToOpenArrayConv(c: var ProcCon; arg: PNode; d: var Value; flags: GenFlags; destType: PType) = - let info = toLineInfo(c, arg.info) - let tmp = c.genx(arg, flags) - let arrType = destType.skipTypes(abstractVar) - template body(target) = - buildTyped target, info, ObjConstr, typeToIr(c.m, arrType): - c.addAddrOfFirstElem target, info, tmp, arg.typ - - valueIntoDest c, info, d, arrType, body - freeTemp c, tmp - -proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: Opcode) = - let targetType = n.typ.skipTypes({tyDistinct}) - let argType = arg.typ.skipTypes({tyDistinct}) - - if sameBackendType(targetType, argType) or ( - argType.kind == tyProc and targetType.kind == argType.kind): - # don't do anything for lambda lifting conversions: - gen c, arg, d - return - - if opc != Cast and targetType.skipTypes({tyVar, tyLent}).kind in {tyOpenArray, tyVarargs} and - argType.skipTypes({tyVar, tyLent}).kind notin {tyOpenArray, tyVarargs}: - genToOpenArrayConv c, arg, d, flags, n.typ - return - - let info = toLineInfo(c, n.info) - let tmp = c.genx(arg, flags) - template body(target) = - buildTyped target, info, opc, typeToIr(c.m, n.typ): - if opc == CheckedObjConv: - target.addLabel info, CheckedGoto, c.exitLabel - copyTree target, tmp - - valueIntoDest c, info, d, n.typ, body - freeTemp c, tmp - -proc genObjOrTupleConstr(c: var ProcCon; n: PNode; d: var Value; t: PType) = - # XXX x = (x.old, 22) produces wrong code ... stupid self assignments - let info = toLineInfo(c, n.info) - template body(target) = - buildTyped target, info, ObjConstr, typeToIr(c.m, t): - for i in ord(n.kind == nkObjConstr)..<n.len: - let it = n[i] - if it.kind == nkExprColonExpr: - genField(c, it[0], Value target) - let tmp = c.genx(it[1]) - copyTree target, tmp - c.freeTemp(tmp) - else: - let tmp = c.genx(it) - target.addImmediateVal info, i - copyTree target, tmp - c.freeTemp(tmp) - - if isException(t): - target.addImmediateVal info, 1 # "name" field is at position after the "parent". See system.nim - target.addStrVal c.lit.strings, info, t.skipTypes(abstractInst).sym.name.s - - constrIntoDest c, info, d, t, body - -proc genRefObjConstr(c: var ProcCon; n: PNode; d: var Value) = - if isEmpty(d): d = getTemp(c, n) - let info = toLineInfo(c, n.info) - let refType = n.typ.skipTypes(abstractInstOwned) - let objType = refType.lastSon - - rawGenNew(c, d, refType, n.info, needsInit = nfAllFieldsSet notin n.flags) - var deref = default(Value) - deref.buildTyped info, Load, typeToIr(c.m, objType): - deref.Tree.copyTree d - genObjOrTupleConstr c, n, deref, objType - -proc genSeqConstr(c: var ProcCon; n: PNode; d: var Value) = - if isEmpty(d): d = getTemp(c, n) - - let info = toLineInfo(c, n.info) - let seqtype = skipTypes(n.typ, abstractVarRange) - let baseType = seqtype.lastSon - - var b = default(Value) - b.addIntVal c.lit.numbers, info, c.m.nativeIntId, n.len - - genNewSeqPayload(c, info, d, b, seqtype) - - for i in 0..<n.len: - var dd = default(Value) - buildTyped dd, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[1]: - buildTyped dd, info, FieldAt, typeToIr(c.m, seqtype): - copyTree Tree(dd), d - dd.addImmediateVal info, 1 # (len, p)-pair - dd.addIntVal c.lit.numbers, info, c.m.nativeIntId, i - gen(c, n[i], dd) - - freeTemp c, d - -proc genArrayConstr(c: var ProcCon; n: PNode, d: var Value) = - let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc}) - if seqType.kind == tySequence: - genSeqConstr(c, n, d) - return - - let info = toLineInfo(c, n.info) - template body(target) = - buildTyped target, info, ArrayConstr, typeToIr(c.m, n.typ): - for i in 0..<n.len: - let tmp = c.genx(n[i]) - copyTree target, tmp - c.freeTemp(tmp) - - constrIntoDest c, info, d, n.typ, body - -proc genAsgn2(c: var ProcCon; a, b: PNode) = - assert a != nil - assert b != nil - var d = c.genx(a) - c.gen b, d - -proc irModule(c: var ProcCon; owner: PSym): string = - #if owner == c.m.module: "" else: - customPath(toFullPath(c.config, owner.info)) - -proc fromForeignModule(c: ProcCon; s: PSym): bool {.inline.} = - result = ast.originatingModule(s) != c.m.module and not c.m.noModularity - -proc genForeignVar(c: var ProcCon; s: PSym) = - var opc: Opcode - if s.kind == skConst: - opc = SummonConst - elif sfThread in s.flags: - opc = SummonThreadLocal - else: - assert sfGlobal in s.flags - opc = SummonGlobal - let t = typeToIr(c.m, s.typ) - let info = toLineInfo(c, s.info) - build c.code, info, ForeignDecl: - buildTyped c.code, info, opc, t: - build c.code, info, ModuleSymUse: - c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s)) - c.code.addImmediateVal info, s.itemId.item.int - -proc genVarSection(c: var ProcCon; n: PNode) = - for a in n: - if a.kind == nkCommentStmt: continue - #assert(a[0].kind == nkSym) can happen for transformed vars - if a.kind == nkVarTuple: - c.gen(lowerTupleUnpacking(c.m.graph, a, c.m.idgen, c.prc)) - else: - var vn = a[0] - if vn.kind == nkPragmaExpr: vn = vn[0] - if vn.kind == nkSym: - let s = vn.sym - if s.kind == skConst: - if dontInlineConstant(n, s.astdef): - let symId = toSymId(c, s) - c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s) - let val = c.genx(s.astdef) - let info = toLineInfo(c, a.info) - buildTyped c.code, info, SummonConst, typeToIr(c.m, s.typ): - c.code.addSymDef info, symId - c.code.copyTree val - freeTemp c, val - else: - var opc: Opcode - if sfThread in s.flags: - opc = SummonThreadLocal - elif sfGlobal in s.flags: - opc = SummonGlobal - else: - opc = Summon - #assert t.int >= 0, typeToString(s.typ) & (c.config $ n.info) - let symId = toSymId(c, s) - c.code.addSummon toLineInfo(c, a.info), symId, typeToIr(c.m, s.typ), opc - c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s) - if a[2].kind != nkEmpty: - genAsgn2(c, vn, a[2]) - else: - if a[2].kind == nkEmpty: - genAsgn2(c, vn, expandDefault(vn.typ, vn.info)) - else: - genAsgn2(c, vn, a[2]) - -proc genAsgn(c: var ProcCon; n: PNode) = - var d = c.genx(n[0]) - c.gen n[1], d - -proc convStrToCStr(c: var ProcCon; n: PNode; d: var Value) = - genUnaryCp(c, n, d, "nimToCStringConv", argAt = 0) - -proc convCStrToStr(c: var ProcCon; n: PNode; d: var Value) = - genUnaryCp(c, n, d, "cstrToNimstr", argAt = 0) - -proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = - let info = toLineInfo(c, n.info) - let s = n.sym - if fromForeignModule(c, s): - if s.kind in {skVar, skConst, skLet} and not c.m.pendingVarsAsSet.containsOrIncl(s.itemId): - c.m.pendingVars.add s - - template body(target) = - build target, info, ModuleSymUse: - target.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s)) - target.addImmediateVal info, s.itemId.item.int - - valueIntoDest c, info, d, s.typ, body - else: - template body(target) = - target.addSymUse info, toSymId(c, s) - valueIntoDest c, info, d, s.typ, body - -proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = - let s = n.sym - case s.kind - of skConst: - if dontInlineConstant(n, s.astdef): - genRdVar(c, n, d, flags) - else: - gen(c, s.astdef, d, flags) - of skVar, skForVar, skTemp, skLet, skResult, skParam: - genRdVar(c, n, d, flags) - of skProc, skFunc, skConverter, skMethod, skIterator: - if not c.m.noModularity: - # anon and generic procs have no AST so we need to remember not to forget - # to emit these: - if not c.m.processedProcs.contains(s.itemId): - if not c.m.pendingProcsAsSet.containsOrIncl(s.itemId): - c.m.pendingProcs.add s - genRdVar(c, n, d, flags) - of skEnumField: - let info = toLineInfo(c, n.info) - template body(target) = - target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), s.position - valueIntoDest c, info, d, n.typ, body - else: - localError(c.config, n.info, "cannot generate code for: " & s.name.s) - -proc genNumericLit(c: var ProcCon; n: PNode; d: var Value; bits: int64) = - let info = toLineInfo(c, n.info) - template body(target) = - target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), bits - valueIntoDest c, info, d, n.typ, body - -proc genStringLit(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - template body(target) = - target.addStrVal c.lit.strings, info, n.strVal - valueIntoDest c, info, d, n.typ, body - -proc genNilLit(c: var ProcCon; n: PNode; d: var Value) = - let info = toLineInfo(c, n.info) - template body(target) = - target.addNilVal info, typeToIr(c.m, n.typ) - valueIntoDest c, info, d, n.typ, body - -proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) = - if optRangeCheck in c.options: - let info = toLineInfo(c, n.info) - let tmp = c.genx n[0] - let a = c.genx n[1] - let b = c.genx n[2] - template body(target) = - buildTyped target, info, CheckedRange, typeToIr(c.m, n.typ): - target.addLabel info, CheckedGoto, c.exitLabel - copyTree target, tmp - copyTree target, a - copyTree target, b - valueIntoDest c, info, d, n.typ, body - freeTemp c, tmp - freeTemp c, a - freeTemp c, b - else: - gen c, n[0], d - -proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = - let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}) - let arrayKind = arrayType.kind - let info = toLineInfo(c, n.info) - case arrayKind - of tyString: - let a = genx(c, n[0], flags) - let b = genIndexCheck(c, n[1], a, ForStr, arrayType) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]: - buildTyped target, info, FieldAt, typeToIr(c.m, arrayType): - copyTree target, a - target.addImmediateVal info, 1 # (len, p)-pair - copyTree target, b - intoDest d, info, t, body - freeTemp c, b - freeTemp c, a - - of tyCstring, tyPtr, tyUncheckedArray: - let a = genx(c, n[0], flags) - let b = genx(c, n[1]) - template body(target) = - buildTyped target, info, DerefArrayAt, typeToIr(c.m, arrayType): - copyTree target, a - copyTree target, b - valueIntoDest c, info, d, n.typ, body - - freeTemp c, b - freeTemp c, a - of tyTuple: - let a = genx(c, n[0], flags) - let b = int n[1].intVal - template body(target) = - buildTyped target, info, FieldAt, typeToIr(c.m, arrayType): - copyTree target, a - target.addImmediateVal info, b - valueIntoDest c, info, d, n.typ, body - - freeTemp c, a - of tyOpenArray, tyVarargs: - let a = genx(c, n[0], flags) - let b = genIndexCheck(c, n[1], a, ForOpenArray, arrayType) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, DerefArrayAt, openArrayPayloadType(c.m.types, c.m.nirm.types, n[0].typ): - buildTyped target, info, FieldAt, typeToIr(c.m, arrayType): - copyTree target, a - target.addImmediateVal info, 0 # (p, len)-pair - copyTree target, b - intoDest d, info, t, body - - freeTemp c, b - freeTemp c, a - of tyArray: - let a = genx(c, n[0], flags) - var b = default(Value) - genIndex(c, n[1], n[0].typ, b) - - template body(target) = - buildTyped target, info, ArrayAt, typeToIr(c.m, arrayType): - copyTree target, a - copyTree target, b - valueIntoDest c, info, d, n.typ, body - freeTemp c, b - freeTemp c, a - of tySequence: - let a = genx(c, n[0], flags) - let b = genIndexCheck(c, n[1], a, ForSeq, arrayType) - let t = typeToIr(c.m, n.typ) - template body(target) = - buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, n[0].typ)[1]: - buildTyped target, info, FieldAt, t: - copyTree target, a - target.addImmediateVal info, 1 # (len, p)-pair - copyTree target, b - intoDest d, info, t, body - freeTemp c, b - freeTemp c, a - else: - localError c.config, n.info, "invalid type for nkBracketExpr: " & $arrayKind - -proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = - let info = toLineInfo(c, n.info) - - var n0 = n[0] - var opc = FieldAt - if n0.kind == nkDotExpr: - # obj[].a --> DerefFieldAt instead of FieldAt: - n0 = n[0] - opc = DerefFieldAt - - let a = genx(c, n0, flags) - - template body(target) = - buildTyped target, info, opc, typeToIr(c.m, n0.typ): - copyTree target, a - genField c, n[1], Value(target) - - valueIntoDest c, info, d, n.typ, body - freeTemp c, a - -proc genParams(c: var ProcCon; params: PNode; prc: PSym): PSym = - result = nil - if params.len > 0 and resultPos < prc.ast.len: - let resNode = prc.ast[resultPos] - result = resNode.sym # get result symbol - c.code.addSummon toLineInfo(c, result.info), toSymId(c, result), - typeToIr(c.m, result.typ), SummonResult - elif prc.typ.len > 0 and not isEmptyType(prc.typ[0]) and not isCompileTimeOnly(prc.typ[0]): - # happens for procs without bodies: - let t = typeToIr(c.m, prc.typ[0]) - let tmp = allocTemp(c, t) - c.code.addSummon toLineInfo(c, params.info), tmp, t, SummonResult - - for i in 1..<params.len: - let s = params[i].sym - if not isCompileTimeOnly(s.typ): - let t = typeToIr(c.m, s.typ) - assert t.int != -1, typeToString(s.typ) - let symId = toSymId(c, s) - c.code.addSummon toLineInfo(c, params[i].info), symId, t, SummonParam - c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s) - -proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) = - template ann(s: untyped) = c.code.addPragmaId info, s - case callConv - of ccNimCall, ccFastCall, ccClosure: ann FastCall - of ccStdCall: ann StdCall - of ccCDecl: ann CDeclCall - of ccSafeCall: ann SafeCall - of ccSysCall: ann SysCall - of ccInline: ann InlineCall - of ccNoInline: ann NoinlineCall - of ccThisCall: ann ThisCall - of ccNoConvention: ann NoCall - -proc genProc(cOuter: var ProcCon; prc: PSym) = - if prc.magic notin generatedMagics: return - if cOuter.m.processedProcs.containsOrIncl(prc.itemId): - return - #assert cOuter.m.inProc == 0, " in nested proc! " & prc.name.s - if cOuter.m.inProc > 0: - if not cOuter.m.pendingProcsAsSet.containsOrIncl(prc.itemId): - cOuter.m.pendingProcs.add prc - return - inc cOuter.m.inProc - - var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config) - let body = - if not fromForeignModule(c, prc): - transformBody(c.m.graph, c.m.idgen, prc, {useCache, keepOpenArrayConversions}) - else: - nil - - let info = toLineInfo(c, prc.info) - build c.code, info, (if body != nil: ProcDecl else: ForeignProcDecl): - if body != nil: - let symId = toSymId(c, prc) - addSymDef c.code, info, symId - c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(prc.name.s) - else: - build c.code, info, ModuleSymUse: - c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(prc)) - c.code.addImmediateVal info, prc.itemId.item.int - addCallConv c, info, prc.typ.callConv - if sfCompilerProc in prc.flags: - build c.code, info, PragmaPair: - c.code.addPragmaId info, CoreName - c.code.addStrVal c.lit.strings, info, prc.name.s - if {sfImportc, sfExportc} * prc.flags != {}: - build c.code, info, PragmaPair: - c.code.addPragmaId info, ExternName - c.code.addStrVal c.lit.strings, info, prc.loc.r - if sfImportc in prc.flags: - if lfHeader in prc. loc.flags: - assert(prc. annex != nil) - let str = getStr(prc. annex.path) - build c.code, info, PragmaPair: - c.code.addPragmaId info, HeaderImport - c.code.addStrVal c.lit.strings, info, str - elif lfDynamicLib in prc. loc.flags: - assert(prc. annex != nil) - let str = getStr(prc. annex.path) - build c.code, info, PragmaPair: - c.code.addPragmaId info, DllImport - c.code.addStrVal c.lit.strings, info, str - elif sfExportc in prc.flags: - if lfDynamicLib in prc. loc.flags: - c.code.addPragmaId info, DllExport - else: - c.code.addPragmaId info, ObjExport - - let resultSym = genParams(c, prc.typ.n, prc) - if body != nil: - gen(c, body) - patch c, body, c.exitLabel - if resultSym != nil: - build c.code, info, Ret: - c.code.addSymUse info, toSymId(c, resultSym) - else: - build c.code, info, Ret: - c.code.addNop info - - #copyTree cOuter.code, c.code - dec cOuter.m.inProc - -proc genProc(cOuter: var ProcCon; n: PNode) = - if n.len == 0 or n[namePos].kind != nkSym: return - let prc = n[namePos].sym - if isGenericRoutineStrict(prc) or isCompileTimeProc(prc) or sfForward in prc.flags: return - genProc cOuter, prc - -proc genClosureCall(c: var ProcCon; n: PNode; d: var Value) = - let typ = skipTypes(n[0].typ, abstractInstOwned) - if tfIterator in typ.flags: - const PatIter = "$1.ClP_0($3, $1.ClE_0)" # we know the env exists - - else: - const PatProc = "$1.ClE_0? $1.ClP_0($3, $1.ClE_0):(($4)($1.ClP_0))($2)" - - -proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) = - if n[0].typ != nil and n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure: - # XXX genClosureCall p, n, d - genCall c, n, d - else: - genCall c, n, d - -proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = - when defined(nimCompilerStacktraceHints): - setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags - case n.kind - of nkSym: genSym(c, n, d, flags) - of nkCallKinds: - if n[0].kind == nkSym: - let s = n[0].sym - if s.magic != mNone: - genMagic(c, n, d, s.magic) - elif s.kind == skMethod: - localError(c.config, n.info, "cannot call method " & s.name.s & - " at compile time") - else: - genComplexCall(c, n, d) - else: - genComplexCall(c, n, d) - of nkCharLit..nkInt64Lit, nkUIntLit..nkUInt64Lit: - genNumericLit(c, n, d, n.intVal) - of nkFloatLit..nkFloat128Lit: - genNumericLit(c, n, d, cast[int64](n.floatVal)) - of nkStrLit..nkTripleStrLit: - genStringLit(c, n, d) - of nkNilLit: - if not n.typ.isEmptyType: genNilLit(c, n, d) - else: unused(c, n, d) - of nkAsgn, nkFastAsgn, nkSinkAsgn: - unused(c, n, d) - genAsgn(c, n) - of nkDotExpr: genObjAccess(c, n, d, flags) - of nkCheckedFieldExpr: genObjAccess(c, n[0], d, flags) - of nkBracketExpr: genArrAccess(c, n, d, flags) - of nkDerefExpr, nkHiddenDeref: genDeref(c, n, d, flags) - of nkAddr, nkHiddenAddr: genAddr(c, n, d, flags) - of nkIfStmt, nkIfExpr: genIf(c, n, d) - of nkWhenStmt: - # This is "when nimvm" node. Chose the first branch. - gen(c, n[0][1], d) - of nkCaseStmt: genCase(c, n, d) - of nkWhileStmt: - unused(c, n, d) - genWhile(c, n) - of nkBlockExpr, nkBlockStmt: genBlock(c, n, d) - of nkReturnStmt: genReturn(c, n) - of nkRaiseStmt: genRaise(c, n) - of nkBreakStmt: genBreak(c, n) - of nkTryStmt, nkHiddenTryStmt: genTry(c, n, d) - of nkStmtList: - #unused(c, n, d) - # XXX Fix this bug properly, lexim triggers it - for x in n: gen(c, x) - of nkStmtListExpr: - for i in 0..<n.len-1: gen(c, n[i]) - gen(c, n[^1], d, flags) - of nkPragmaBlock: - gen(c, n.lastSon, d, flags) - of nkDiscardStmt: - unused(c, n, d) - gen(c, n[0], d) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: - genConv(c, n, n[1], d, flags, NumberConv) # misnomer? - of nkObjDownConv: - genConv(c, n, n[0], d, flags, ObjConv) - of nkObjUpConv: - genConv(c, n, n[0], d, flags, CheckedObjConv) - of nkVarSection, nkLetSection, nkConstSection: - unused(c, n, d) - genVarSection(c, n) - of nkLambdaKinds: - #let s = n[namePos].sym - #discard genProc(c, s) - gen(c, newSymNode(n[namePos].sym), d) - of nkChckRangeF, nkChckRange64, nkChckRange: - genRangeCheck(c, n, d) - of declarativeDefs - {nkIteratorDef}: - unused(c, n, d) - genProc(c, n) - of nkEmpty, nkCommentStmt, nkTypeSection, nkPragma, - nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, - nkMixinStmt, nkBindStmt, nkMacroDef, nkIteratorDef: - unused(c, n, d) - of nkStringToCString: convStrToCStr(c, n, d) - of nkCStringToString: convCStrToStr(c, n, d) - of nkBracket: genArrayConstr(c, n, d) - of nkCurly: genSetConstr(c, n, d) - of nkObjConstr: - if n.typ.skipTypes(abstractInstOwned).kind == tyRef: - genRefObjConstr(c, n, d) - else: - genObjOrTupleConstr(c, n, d, n.typ) - of nkPar, nkClosure, nkTupleConstr: - genObjOrTupleConstr(c, n, d, n.typ) - of nkCast: - genConv(c, n, n[1], d, flags, Cast) - of nkComesFrom: - discard "XXX to implement for better stack traces" - #of nkState: genState(c, n) - #of nkGotoState: genGotoState(c, n) - #of nkBreakState: genBreakState(c, n, d) - else: - localError(c.config, n.info, "cannot generate IR code for " & $n) - -proc genPendingProcs(c: var ProcCon) = - while c.m.pendingProcs.len > 0 or c.m.pendingVars.len > 0: - let procs = move(c.m.pendingProcs) - for v in procs: - genProc(c, v) - let vars = move(c.m.pendingVars) - for v in vars: - genForeignVar(c, v) - -proc genStmt*(c: var ProcCon; n: PNode): NodePos = - result = NodePos c.code.len - var d = default(Value) - c.gen(n, d) - unused c, n, d - genPendingProcs c - -proc genExpr*(c: var ProcCon; n: PNode, requiresValue = true): int = - result = c.code.len - var d = default(Value) - c.gen(n, d) - genPendingProcs c - if isEmpty d: - if requiresValue: - globalError(c.config, n.info, "VM problem: d register is not set") diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim deleted file mode 100644 index 01f9c4c9a..000000000 --- a/compiler/nir/cir.nim +++ /dev/null @@ -1,983 +0,0 @@ -# -# -# 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 diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim deleted file mode 100644 index 6f7077fb0..000000000 --- a/compiler/nir/nir.nim +++ /dev/null @@ -1,105 +0,0 @@ -# -# -# 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) diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim deleted file mode 100644 index a2cf69988..000000000 --- a/compiler/nir/nirc.nim +++ /dev/null @@ -1,52 +0,0 @@ -# -# -# 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() diff --git a/compiler/nir/nirfiles.nim b/compiler/nir/nirfiles.nim deleted file mode 100644 index cd5a79f06..000000000 --- a/compiler/nir/nirfiles.nim +++ /dev/null @@ -1,83 +0,0 @@ -# -# -# 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) diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim deleted file mode 100644 index 6cffc1a89..000000000 --- a/compiler/nir/nirinsts.nim +++ /dev/null @@ -1,582 +0,0 @@ -# -# -# 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 diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim deleted file mode 100644 index a01e7a633..000000000 --- a/compiler/nir/nirslots.nim +++ /dev/null @@ -1,104 +0,0 @@ -# -# -# 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 diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim deleted file mode 100644 index a79bf6d01..000000000 --- a/compiler/nir/nirtypes.nim +++ /dev/null @@ -1,475 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Type system for NIR. Close to C's type system but without its quirks. - -import std / [assertions, hashes] -import .. / ic / [bitabs, rodfiles] - -type - NirTypeKind* = enum - VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, - IntVal, SizeVal, AlignVal, OffsetVal, - AnnotationVal, - ObjectTy, - UnionTy, - VarargsTy, # the `...` in a C prototype; also the last "atom" - APtrTy, # pointer to aliasable memory - UPtrTy, # pointer to unique/unaliasable memory - AArrayPtrTy, # pointer to array of aliasable memory - UArrayPtrTy, # pointer to array of unique/unaliasable memory - ArrayTy, - LastArrayTy, # array of unspecified size as a last field inside an object - ProcTy, - ObjectDecl, - UnionDecl, - FieldDecl - -const - TypeKindBits = 8'u32 - TypeKindMask = (1'u32 shl TypeKindBits) - 1'u32 - -type - TypeNode* = object # 4 bytes - x: uint32 - -template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask) -template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits) - -proc integralBits*(n: TypeNode): int {.inline.} = - # Number of bits in the IntTy, etc. Only valid for integral types. - assert n.kind in {IntTy, UIntTy, FloatTy, BoolTy, CharTy} - result = int(n.operand) - -template toX(k: NirTypeKind; operand: uint32): uint32 = - uint32(k) or (operand shl TypeKindBits) - -template toX(k: NirTypeKind; operand: LitId): uint32 = - uint32(k) or (operand.uint32 shl TypeKindBits) - -type - TypeId* = distinct int - -proc `==`*(a, b: TypeId): bool {.borrow.} -proc hash*(a: TypeId): Hash {.borrow.} - -type - Literals* = ref object - strings*: BiTable[string] - numbers*: BiTable[int64] - - TypeGraph* = object - nodes: seq[TypeNode] - lit: Literals - -const - VoidId* = TypeId 0 - Bool8Id* = TypeId 1 - Char8Id* = TypeId 2 - Int8Id* = TypeId 3 - Int16Id* = TypeId 4 - Int32Id* = TypeId 5 - Int64Id* = TypeId 6 - UInt8Id* = TypeId 7 - UInt16Id* = TypeId 8 - UInt32Id* = TypeId 9 - UInt64Id* = TypeId 10 - Float32Id* = TypeId 11 - Float64Id* = TypeId 12 - VoidPtrId* = TypeId 13 - LastBuiltinId* = 13 - -proc initTypeGraph*(lit: Literals): TypeGraph = - result = TypeGraph(nodes: @[ - TypeNode(x: toX(VoidTy, 0'u32)), - TypeNode(x: toX(BoolTy, 8'u32)), - TypeNode(x: toX(CharTy, 8'u32)), - TypeNode(x: toX(IntTy, 8'u32)), - TypeNode(x: toX(IntTy, 16'u32)), - TypeNode(x: toX(IntTy, 32'u32)), - TypeNode(x: toX(IntTy, 64'u32)), - TypeNode(x: toX(UIntTy, 8'u32)), - TypeNode(x: toX(UIntTy, 16'u32)), - TypeNode(x: toX(UIntTy, 32'u32)), - TypeNode(x: toX(UIntTy, 64'u32)), - TypeNode(x: toX(FloatTy, 32'u32)), - TypeNode(x: toX(FloatTy, 64'u32)), - TypeNode(x: toX(APtrTy, 2'u32)), - TypeNode(x: toX(VoidTy, 0'u32)) - ], lit: lit) - assert result.nodes.len == LastBuiltinId+2 - -type - TypePatchPos* = distinct int - -const - InvalidTypePatchPos* = TypePatchPos(-1) - LastAtomicValue = VarargsTy - -proc isValid(p: TypePatchPos): bool {.inline.} = p.int != -1 - -proc prepare(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = - result = TypePatchPos tree.nodes.len - tree.nodes.add TypeNode(x: toX(kind, 1'u32)) - -proc isAtom(tree: TypeGraph; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue -proc isAtom(tree: TypeGraph; pos: TypeId): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue - -proc patch(tree: var TypeGraph; pos: TypePatchPos) = - 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)) - -proc len*(tree: TypeGraph): int {.inline.} = tree.nodes.len - -template rawSpan(n: TypeNode): int = int(operand(n)) - -proc nextChild(tree: TypeGraph; 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 - -iterator sons*(tree: TypeGraph; n: TypeId): TypeId = - var pos = n.int - assert tree.nodes[pos].kind > LastAtomicValue - let last = pos + tree.nodes[pos].rawSpan - inc pos - while pos < last: - yield TypeId pos - nextChild tree, pos - -template `[]`*(t: TypeGraph; n: TypeId): TypeNode = t.nodes[n.int] - -proc elementType*(tree: TypeGraph; n: TypeId): TypeId {.inline.} = - assert tree[n].kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ArrayTy, LastArrayTy} - result = TypeId(n.int+1) - -proc litId*(n: TypeNode): LitId {.inline.} = - assert n.kind in {NameVal, IntVal, SizeVal, AlignVal, OffsetVal, AnnotationVal, ObjectTy, UnionTy} - result = LitId(n.operand) - -proc kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind - -proc span(tree: TypeGraph; pos: int): int {.inline.} = - if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) - -proc sons2(tree: TypeGraph; n: TypeId): (TypeId, TypeId) = - assert(not isAtom(tree, n.int)) - let a = n.int+1 - let b = a + span(tree, a) - result = (TypeId a, TypeId b) - -proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) = - assert(not isAtom(tree, n.int)) - let a = n.int+1 - let b = a + span(tree, a) - let c = b + span(tree, b) - result = (TypeId a, TypeId b, TypeId c) - -proc arrayName*(tree: TypeGraph; n: TypeId): TypeId {.inline.} = - assert tree[n].kind == ArrayTy - let (_, _, c) = sons3(tree, n) - result = c - -proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt = - assert tree[n].kind == ArrayTy - let (_, b) = sons2(tree, n) - result = tree.lit.numbers[LitId tree[b].operand] - -proc returnType*(tree: TypeGraph; n: TypeId): (TypeId, TypeId) = - # Returns the positions of the return type + calling convention. - var pos = n.int - assert tree.nodes[pos].kind == ProcTy - let a = n.int+1 - let b = a + span(tree, a) - result = (TypeId b, TypeId a) # not a typo, order is reversed - -iterator params*(tree: TypeGraph; n: TypeId): TypeId = - var pos = n.int - assert tree.nodes[pos].kind == ProcTy - let last = pos + tree.nodes[pos].rawSpan - inc pos - nextChild tree, pos - nextChild tree, pos - while pos < last: - yield TypeId pos - nextChild tree, pos - -proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = - assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, - ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl, - FieldDecl} - result = prepare(tree, kind) - -template typeInvariant(p: TypePatchPos) = - when false: - if tree[TypeId(p)].kind == FieldDecl: - var k = 0 - for ch in sons(tree, TypeId(p)): - inc k - assert k > 2, "damn! " & $k - -proc sealType*(tree: var TypeGraph; p: TypePatchPos) = - patch tree, p - typeInvariant(p) - -proc finishType*(tree: var TypeGraph; p: TypePatchPos): TypeId = - # Search for an existing instance of this type in - # order to reduce memory consumption: - patch tree, p - typeInvariant(p) - - let s = span(tree, p.int) - var i = 0 - while i < p.int: - if tree.nodes[i].x == tree.nodes[p.int].x: - var isMatch = true - for j in 1..<s: - if tree.nodes[j+i].x == tree.nodes[j+p.int].x: - discard "still a match" - else: - isMatch = false - break - if isMatch: - if p.int+s == tree.len: - setLen tree.nodes, p.int - return TypeId(i) - nextChild tree, i - result = TypeId(p) - -proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId = - assert kind in {ObjectTy, UnionTy} - let content = TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name))) - for i in 0..<tree.len: - if tree.nodes[i].x == content.x: - return TypeId(i) - result = TypeId tree.nodes.len - tree.nodes.add content - -proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) = - assert kind in {ObjectTy, UnionTy} - tree.nodes.add TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name))) - -proc getTypeTag*(tree: TypeGraph; t: TypeId): string = - assert tree[t].kind in {ObjectTy, UnionTy} - result = tree.lit.strings[LitId tree[t].operand] - -proc addVarargs*(tree: var TypeGraph) = - tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32)) - -proc getFloat128Type*(tree: var TypeGraph): TypeId = - result = TypeId tree.nodes.len - tree.nodes.add TypeNode(x: toX(FloatTy, 128'u32)) - -proc addBuiltinType*(g: var TypeGraph; id: TypeId) = - g.nodes.add g[id] - -template firstSon*(n: TypeId): TypeId = TypeId(n.int+1) - -proc addType*(g: var TypeGraph; t: TypeId) = - # We cannot simply copy `*Decl` nodes. We have to introduce `*Ty` nodes instead: - if g[t].kind in {ObjectDecl, UnionDecl}: - assert g[t.firstSon].kind == NameVal - let name = LitId g[t.firstSon].operand - if g[t].kind == ObjectDecl: - g.nodes.add TypeNode(x: toX(ObjectTy, name)) - else: - g.nodes.add TypeNode(x: toX(UnionTy, name)) - else: - let pos = t.int - let L = span(g, pos) - let d = g.nodes.len - g.nodes.setLen(d + L) - assert L > 0 - for i in 0..<L: - g.nodes[d+i] = g.nodes[pos+i] - -proc addArrayLen*(g: var TypeGraph; len: int64) = - g.nodes.add TypeNode(x: toX(IntVal, g.lit.numbers.getOrIncl(len))) - -proc addSize*(g: var TypeGraph; s: int64) = - g.nodes.add TypeNode(x: toX(SizeVal, g.lit.numbers.getOrIncl(s))) - -proc addOffset*(g: var TypeGraph; offset: int64) = - g.nodes.add TypeNode(x: toX(OffsetVal, g.lit.numbers.getOrIncl(offset))) - -proc addAlign*(g: var TypeGraph; a: int64) = - g.nodes.add TypeNode(x: toX(AlignVal, g.lit.numbers.getOrIncl(a))) - -proc addName*(g: var TypeGraph; name: string) = - g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name))) - -proc addAnnotation*(g: var TypeGraph; name: string) = - g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name))) - -proc addField*(g: var TypeGraph; name: string; typ: TypeId; offset: int64) = - let f = g.openType FieldDecl - g.addType typ - g.addOffset offset - g.addName name - sealType(g, f) - -proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = - let f = g.openType APtrTy - g.addType t - result = finishType(g, f) - -proc arrayPtrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = - let f = g.openType AArrayPtrTy - g.addType t - result = finishType(g, f) - -proc store*(r: var RodFile; g: TypeGraph) = - storeSeq r, g.nodes - -proc load*(r: var RodFile; g: var TypeGraph) = - loadSeq r, g.nodes - -proc toString*(dest: var string; g: TypeGraph; i: TypeId) = - case g[i].kind - of VoidTy: dest.add "void" - of IntTy: - dest.add "i" - dest.addInt g[i].operand - of UIntTy: - dest.add "u" - dest.addInt g[i].operand - of FloatTy: - dest.add "f" - dest.addInt g[i].operand - of BoolTy: - dest.add "b" - dest.addInt g[i].operand - of CharTy: - dest.add "c" - dest.addInt g[i].operand - of NameVal, AnnotationVal: - dest.add g.lit.strings[LitId g[i].operand] - of IntVal, SizeVal, AlignVal, OffsetVal: - dest.add $g[i].kind - dest.add ' ' - dest.add $g.lit.numbers[LitId g[i].operand] - of VarargsTy: - dest.add "..." - of APtrTy: - dest.add "aptr[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of UPtrTy: - dest.add "uptr[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of AArrayPtrTy: - dest.add "aArrayPtr[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of UArrayPtrTy: - dest.add "uArrayPtr[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of ArrayTy: - dest.add "Array[" - let (elems, len, name) = g.sons3(i) - toString(dest, g, elems) - dest.add ", " - toString(dest, g, len) - dest.add ", " - toString(dest, g, name) - dest.add "]" - of LastArrayTy: - # array of unspecified size as a last field inside an object - dest.add "LastArrayTy[" - toString(dest, g, g.elementType(i)) - dest.add "]" - of ObjectTy: - dest.add "object " - dest.add g.lit.strings[LitId g[i].operand] - of UnionTy: - dest.add "union " - dest.add g.lit.strings[LitId g[i].operand] - of ProcTy: - dest.add "proc[" - for t in sons(g, i): - dest.add ' ' - toString(dest, g, t) - dest.add "]" - of ObjectDecl: - dest.add "object[" - for t in sons(g, i): - toString(dest, g, t) - dest.add '\n' - dest.add "]" - of UnionDecl: - dest.add "union[" - for t in sons(g, i): - toString(dest, g, t) - dest.add '\n' - dest.add "]" - of FieldDecl: - dest.add "field[" - for t in sons(g, i): - toString(dest, g, t) - dest.add ' ' - dest.add "]" - - when false: - let (typ, offset, name) = g.sons3(i) - toString(dest, g, typ) - dest.add ' ' - toString(dest, g, offset) - dest.add ' ' - toString(dest, g, name) - -proc toString*(dest: var string; g: TypeGraph) = - var i = 0 - while i < g.len: - dest.add "T<" - dest.addInt i - dest.add "> " - toString(dest, g, TypeId i) - dest.add '\n' - nextChild g, i - -iterator allTypes*(g: TypeGraph; start = 0): TypeId = - var i = start - while i < g.len: - yield TypeId i - nextChild g, i - -iterator allTypesIncludingInner*(g: TypeGraph; start = 0): TypeId = - var i = start - while i < g.len: - yield TypeId i - inc i - -proc `$`(g: TypeGraph): string = - result = "" - toString(result, g) - -when isMainModule: - var g = initTypeGraph(Literals()) - - let a = g.openType ArrayTy - g.addBuiltinType Int8Id - g.addArrayLen 5 - g.addName "SomeArray" - let finalArrayType = finishType(g, a) - - let obj = g.openType ObjectDecl - g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl("MyType"))) - - g.addField "p", finalArrayType, 0 - sealType(g, obj) - - echo g diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim deleted file mode 100644 index faa7a1fb7..000000000 --- a/compiler/nir/nirvm.nim +++ /dev/null @@ -1,1175 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -##[ NIR is a little too high level to interpret it efficiently. Thus -we compute `addresses` for SymIds, labels and offsets for object fields -in a preprocessing step. - -We also split the instruction stream into separate (code, debug) seqs while -we're at it. -]## - -import std / [syncio, assertions, tables, intsets] -import ".." / ic / bitabs -import nirinsts, nirtypes, nirfiles, nirlineinfos - -type - OpcodeM = enum - ImmediateValM, - IntValM, - StrValM, - LoadLocalM, # with local ID - LoadGlobalM, - LoadProcM, - TypedM, # with type ID - PragmaIdM, # with Pragma ID, possible values: see PragmaKey enum - NilValM, - AllocLocals, - SummonParamM, - GotoM, - CheckedGotoM, # last atom - - ArrayConstrM, - ObjConstrM, - RetM, - YldM, - - SelectM, - SelectPairM, # ((values...), Label) - SelectListM, # (values...) - SelectValueM, # (value) - SelectRangeM, # (valueA..valueB) - - AddrOfM, - ArrayAtM, # (elemSize, addr(a), i) - DerefArrayAtM, - FieldAtM, # addr(obj.field) - DerefFieldAtM, - - LoadM, # a[] - AsgnM, # a = b - StoreM, # a[] = b - SetExcM, - TestExcM, - - CheckedRangeM, - CheckedIndexM, - - CallM, - CheckedAddM, # with overflow checking etc. - CheckedSubM, - CheckedMulM, - CheckedDivM, - CheckedModM, - AddM, - SubM, - MulM, - DivM, - ModM, - BitShlM, - BitShrM, - BitAndM, - BitOrM, - BitXorM, - BitNotM, - BoolNotM, - EqM, - LeM, - LtM, - CastM, - NumberConvM, - CheckedObjConvM, - ObjConvM, - TestOfM, - ProcDeclM, - PragmaPairM - -const - LastAtomicValue = CheckedGotoM - - OpcodeBits = 8'u32 - OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32 - -type - Instr = distinct uint32 - -template kind(n: Instr): OpcodeM = OpcodeM(n.uint32 and OpcodeMask) -template operand(n: Instr): uint32 = (n.uint32 shr OpcodeBits) - -template toIns(k: OpcodeM; operand: uint32): Instr = - Instr(uint32(k) or (operand shl OpcodeBits)) - -template toIns(k: OpcodeM; operand: LitId): Instr = - Instr(uint32(k) or (operand.uint32 shl OpcodeBits)) - -type - NimStrPayloadVM = object - cap: int - data: UncheckedArray[char] - NimStringVM = object - len: int - p: ptr NimStrPayloadVM - -const - GlobalsSize = 1024*24 - -type - PatchPos = distinct int - CodePos = distinct int - - Bytecode* = object - code: seq[Instr] - debug: seq[PackedLineInfo] - m: ref NirModule - procs: Table[SymId, CodePos] - globals: Table[SymId, (uint32, int)] - strings: Table[LitId, NimStringVM] - globalData: pointer - globalsAddr: uint32 - typeImpls: Table[string, TypeId] - offsets: Table[TypeId, seq[(int, TypeId)]] - sizes: Table[TypeId, (int, int)] # (size, alignment) - oldTypeLen: int - procUsagesToPatch: Table[SymId, seq[CodePos]] - interactive*: bool - - Universe* = object ## all units: For interpretation we need that - units: seq[Bytecode] - unitNames: Table[string, int] - current: int - -proc initBytecode*(m: ref NirModule): Bytecode = Bytecode(m: m, globalData: alloc0(GlobalsSize)) - -proc debug(bc: Bytecode; t: TypeId) = - var buf = "" - toString buf, bc.m.types, t - echo buf - -proc debug(bc: Bytecode; info: PackedLineInfo) = - let (litId, line, col) = bc.m.man.unpack(info) - echo bc.m.lit.strings[litId], ":", line, ":", col - -proc debug(bc: Bytecode; t: Tree; n: NodePos) = - var buf = "" - toString(t, n, bc.m.lit.strings, bc.m.lit.numbers, bc.m.symnames, buf) - echo buf - -template `[]`(t: seq[Instr]; n: CodePos): Instr = t[n.int] - -proc traverseObject(b: var Bytecode; t, offsetKey: TypeId) = - var size = -1 - var align = -1 - for x in sons(b.m.types, t): - case b.m.types[x].kind - of FieldDecl: - var offset = -1 - for y in sons(b.m.types, x): - if b.m.types[y].kind == OffsetVal: - offset = int(b.m.lit.numbers[b.m.types[y].litId]) - break - b.offsets.mgetOrPut(offsetKey, @[]).add (offset, x.firstSon) - of SizeVal: - size = int(b.m.lit.numbers[b.m.types[x].litId]) - of AlignVal: - align = int(b.m.lit.numbers[b.m.types[x].litId]) - of ObjectTy: - # inheritance - let impl = b.typeImpls.getOrDefault(b.m.lit.strings[b.m.types[x].litId]) - assert impl.int > 0 - traverseObject b, impl, offsetKey - else: discard - if t == offsetKey: - b.sizes[t] = (size, align) - -proc computeSize(b: var Bytecode; t: TypeId): (int, int) = - case b.m.types[t].kind - of ObjectDecl, UnionDecl: - result = b.sizes[t] - of ObjectTy, UnionTy: - let impl = b.typeImpls[b.m.lit.strings[b.m.types[t].litId]] - result = computeSize(b, impl) - of IntTy, UIntTy, FloatTy, BoolTy, CharTy: - let s = b.m.types[t].integralBits div 8 - result = (s, s) - of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ProcTy: - result = (sizeof(pointer), sizeof(pointer)) - of ArrayTy: - let e = elementType(b.m.types, t) - let n = arrayLen(b.m.types, t) - let inner = computeSize(b, e) - result = (inner[0] * n.int, inner[1]) - else: - result = (0, 0) - -proc computeElemSize(b: var Bytecode; t: TypeId): int = - case b.m.types[t].kind - of ArrayTy, APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, LastArrayTy: - result = computeSize(b, elementType(b.m.types, t))[0] - else: - raiseAssert "not an array type" - -proc traverseTypes(b: var Bytecode) = - for t in allTypes(b.m.types, b.oldTypeLen): - if b.m.types[t].kind in {ObjectDecl, UnionDecl}: - assert b.m.types[t.firstSon].kind == NameVal - b.typeImpls[b.m.lit.strings[b.m.types[t.firstSon].litId]] = t - - for t in allTypes(b.m.types, b.oldTypeLen): - if b.m.types[t].kind in {ObjectDecl, UnionDecl}: - assert b.m.types[t.firstSon].kind == NameVal - traverseObject b, t, t - b.oldTypeLen = b.m.types.len - -const - InvalidPatchPos* = PatchPos(-1) - -proc isValid(p: PatchPos): bool {.inline.} = p.int != -1 - -proc prepare(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM): PatchPos = - result = PatchPos bc.code.len - bc.code.add toIns(kind, 1'u32) - bc.debug.add info - -proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; raw: uint32) = - bc.code.add toIns(kind, raw) - bc.debug.add info - -proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; lit: LitId) = - add bc, info, kind, uint32(lit) - -proc isAtom(bc: Bytecode; pos: int): bool {.inline.} = bc.code[pos].kind <= LastAtomicValue -proc isAtom(bc: Bytecode; pos: CodePos): bool {.inline.} = bc.code[pos.int].kind <= LastAtomicValue - -proc patch(bc: var Bytecode; pos: PatchPos) = - let pos = pos.int - let k = bc.code[pos].kind - assert k > LastAtomicValue - let distance = int32(bc.code.len - pos) - assert distance > 0 - bc.code[pos] = toIns(k, cast[uint32](distance)) - -template build(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; body: untyped) = - let pos = prepare(bc, info, kind) - body - patch(bc, pos) - -proc len*(bc: Bytecode): int {.inline.} = bc.code.len - -template rawSpan(n: Instr): int = int(operand(n)) - -proc nextChild(bc: Bytecode; pos: var int) {.inline.} = - if bc.code[pos].kind > LastAtomicValue: - assert bc.code[pos].operand > 0'u32 - inc pos, bc.code[pos].rawSpan - else: - inc pos - -proc next(bc: Bytecode; pos: var CodePos) {.inline.} = nextChild bc, int(pos) - -iterator sons(bc: Bytecode; n: CodePos): CodePos = - var pos = n.int - assert bc.code[pos].kind > LastAtomicValue - let last = pos + bc.code[pos].rawSpan - inc pos - while pos < last: - yield CodePos pos - nextChild bc, pos - -iterator sonsFrom1(bc: Bytecode; n: CodePos): CodePos = - var pos = n.int - assert bc.code[pos].kind > LastAtomicValue - let last = pos + bc.code[pos].rawSpan - inc pos - nextChild bc, pos - while pos < last: - yield CodePos pos - nextChild bc, pos - -iterator sonsFrom2(bc: Bytecode; n: CodePos): CodePos = - var pos = n.int - assert bc.code[pos].kind > LastAtomicValue - let last = pos + bc.code[pos].rawSpan - inc pos - nextChild bc, pos - nextChild bc, pos - while pos < last: - yield CodePos pos - nextChild bc, pos - -template firstSon(n: CodePos): CodePos = CodePos(n.int+1) - -template `[]`(t: Bytecode; n: CodePos): Instr = t.code[n.int] - -proc span(bc: Bytecode; pos: int): int {.inline.} = - if bc.code[pos].kind <= LastAtomicValue: 1 else: int(bc.code[pos].operand) - -iterator triples*(bc: Bytecode; n: CodePos): (uint32, int, CodePos) = - var pos = n.int - assert bc.code[pos].kind > LastAtomicValue - let last = pos + bc.code[pos].rawSpan - inc pos - while pos < last: - let offset = bc.code[pos].operand - nextChild bc, pos - let size = bc.code[pos].operand.int - nextChild bc, pos - let val = CodePos pos - yield (offset, size, val) - nextChild bc, pos - -proc toString*(t: Bytecode; pos: CodePos; - r: var string; nesting = 0) = - if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}: - r.add ' ' - - case t[pos].kind - of ImmediateValM: - r.add $t[pos].operand - of IntValM: - r.add "IntVal " - r.add $t.m.lit.numbers[LitId t[pos].operand] - of StrValM: - escapeToNimLit(t.m.lit.strings[LitId t[pos].operand], r) - of LoadLocalM, LoadGlobalM, LoadProcM, AllocLocals, SummonParamM: - r.add $t[pos].kind - r.add ' ' - r.add $t[pos].operand - of PragmaIdM: - r.add $cast[PragmaKey](t[pos].operand) - of TypedM: - r.add "T<" - r.add $t[pos].operand - r.add ">" - of NilValM: - r.add "NilVal" - of GotoM, CheckedGotoM: - 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, r, nesting+1 - r.add "\n" - for i in 0..<nesting*2: r.add ' ' - r.add "}" - -proc debug(b: Bytecode; pos: CodePos) = - var buf = "" - toString(b, pos, buf) - echo buf - -type - Preprocessing = object - u: ref Universe - known: Table[LabelId, CodePos] - toPatch: Table[LabelId, seq[CodePos]] - locals: Table[SymId, (uint32, int)] # address, size - thisModule: uint32 - localsAddr: uint32 - markedWithLabel: IntSet - -proc align(address, alignment: uint32): uint32 = - result = (address + (alignment - 1'u32)) and not (alignment - 1'u32) - -proc genGoto(c: var Preprocessing; bc: var Bytecode; info: PackedLineInfo; lab: LabelId; opc: OpcodeM) = - let dest = c.known.getOrDefault(lab, CodePos(-1)) - if dest.int >= 0: - bc.add info, opc, uint32 dest - else: - let here = CodePos(bc.code.len) - c.toPatch.mgetOrPut(lab, @[]).add here - bc.add info, opc, 1u32 # will be patched once we traversed the label - -type - AddrMode = enum - InDotExpr, WantAddr - -template maybeDeref(doDeref: bool; size: int; body: untyped) = - var pos = PatchPos(-1) - if doDeref: - pos = prepare(bc, info, LoadM) - bc.add info, ImmediateValM, uint32 size - body - if doDeref: - patch(bc, pos) - -proc toReadonlyString(s: string): NimStringVM = - if s.len == 0: - result = NimStringVM(len: 0, p: nil) - else: - result = NimStringVM(len: s.len, p: cast[ptr NimStrPayloadVM](alloc(s.len+1+sizeof(int)))) - copyMem(addr result.p.data[0], addr s[0], s.len+1) - result.p.cap = s.len or (1 shl (8 * 8 - 2)) # see also NIM_STRLIT_FLAG - -const - ForwardedProc = 10_000_000'u32 - -proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; flags: set[AddrMode]) = - let info = t[n].info - - template recurse(opc) = - build bc, info, opc: - for ch in sons(t, n): preprocess(c, bc, t, ch, {WantAddr}) - - case t[n].kind - of Nop, ForeignDecl, ForeignProcDecl: - discard "don't use Nop" - of ImmediateVal: - bc.add info, ImmediateValM, t[n].rawOperand - of IntVal: - bc.add info, IntValM, t[n].rawOperand - of StrVal: - let litId = LitId t[n].rawOperand - if not bc.strings.hasKey(litId): - bc.strings[litId] = toReadonlyString(bc.m.lit.strings[litId]) - bc.add info, StrValM, t[n].rawOperand - of SymDef: - discard "happens for proc decls. Don't copy the node as we don't need it" - of SymUse: - let s = t[n].symId - if c.locals.hasKey(s): - let (address, size) = c.locals[s] - maybeDeref(WantAddr notin flags, size): - bc.add info, LoadLocalM, address - elif bc.procs.hasKey(s): - bc.add info, LoadProcM, uint32 bc.procs[s] - elif bc.globals.hasKey(s): - let (address, size) = bc.globals[s] - maybeDeref(WantAddr notin flags, size): - bc.add info, LoadGlobalM, address - else: - let here = CodePos(bc.code.len) - bc.add info, LoadProcM, ForwardedProc + uint32(s) - bc.procUsagesToPatch.mgetOrPut(s, @[]).add here - #raiseAssert "don't understand SymUse ID " & $int(s) - - of ModuleSymUse: - when false: - let (x, y) = sons2(t, n) - let unit = c.u.unitNames.getOrDefault(bc.m.lit.strings[t[x].litId], -1) - let s = t[y].symId - if c.u.units[unit].procs.hasKey(s): - bc.add info, LoadProcM, uint32 c.u.units[unit].procs[s] - elif bc.globals.hasKey(s): - maybeDeref(WantAddr notin flags): - build bc, info, LoadGlobalM: - bc.add info, ImmediateValM, uint32 unit - bc.add info, LoadLocalM, uint32 s - else: - raiseAssert "don't understand ModuleSymUse ID" - - raiseAssert "don't understand ModuleSymUse ID" - of Typed: - bc.add info, TypedM, t[n].rawOperand - of PragmaId: - bc.add info, PragmaIdM, t[n].rawOperand - of NilVal: - bc.add info, NilValM, t[n].rawOperand - of LoopLabel, Label: - let lab = t[n].label - let here = CodePos(bc.code.len) - c.known[lab] = here - var p: seq[CodePos] = @[] - if c.toPatch.take(lab, p): - for x in p: (bc.code[x]) = toIns(bc.code[x].kind, uint32 here) - c.markedWithLabel.incl here.int # for toString() - of Goto, GotoLoop: - c.genGoto(bc, info, t[n].label, GotoM) - of CheckedGoto: - c.genGoto(bc, info, t[n].label, CheckedGotoM) - of ArrayConstr: - let typ = t[n.firstSon].typeId - let s = computeElemSize(bc, typ) - build bc, info, ArrayConstrM: - bc.add info, ImmediateValM, uint32 s - for ch in sonsFrom1(t, n): - preprocess(c, bc, t, ch, {WantAddr}) - of ObjConstr: - #debug bc, t, n - var i = 0 - let typ = t[n.firstSon].typeId - build bc, info, ObjConstrM: - for ch in sons(t, n): - if i > 0: - if (i mod 2) == 1: - let (offset, typ) = bc.offsets[typ][t[ch].immediateVal] - let size = computeSize(bc, typ)[0] - bc.add info, ImmediateValM, uint32(offset) - bc.add info, ImmediateValM, uint32(size) - else: - preprocess(c, bc, t, ch, {WantAddr}) - inc i - of Ret: - recurse RetM - of Yld: - recurse YldM - of Select: - recurse SelectM - of SelectPair: - recurse SelectPairM - of SelectList: - recurse SelectListM - of SelectValue: - recurse SelectValueM - of SelectRange: - recurse SelectRangeM - of SummonGlobal, SummonThreadLocal, SummonConst: - let (typ, sym) = sons2(t, n) - - let s = t[sym].symId - let tid = t[typ].typeId - let (size, alignment) = computeSize(bc, tid) - - let global = align(bc.globalsAddr, uint32 alignment) - bc.globals[s] = (global, size) - bc.globalsAddr += uint32 size - assert bc.globalsAddr < GlobalsSize - - of Summon: - let (typ, sym) = sons2(t, n) - - let s = t[sym].symId - let tid = t[typ].typeId - let (size, alignment) = computeSize(bc, tid) - - let local = align(c.localsAddr, uint32 alignment) - c.locals[s] = (local, size) - c.localsAddr += uint32 size - # allocation is combined into the frame allocation so there is no - # instruction to emit - of SummonParam, SummonResult: - let (typ, sym) = sons2(t, n) - - let s = t[sym].symId - let tid = t[typ].typeId - let (size, alignment) = computeSize(bc, tid) - - let local = align(c.localsAddr, uint32 alignment) - c.locals[s] = (local, size) - c.localsAddr += uint32 size - bc.add info, SummonParamM, local - bc.add info, ImmediateValM, uint32 size - of Kill: - discard "we don't care about Kill instructions" - of AddrOf: - let (_, arg) = sons2(t, n) - preprocess(c, bc, t, arg, {WantAddr}) - # the address of x is what the VM works with all the time so there is - # nothing to compute. - of ArrayAt: - let (arrayType, a, i) = sons3(t, n) - let tid = t[arrayType].typeId - let size = uint32 computeElemSize(bc, tid) - build bc, info, ArrayAtM: - bc.add info, ImmediateValM, size - preprocess(c, bc, t, a, {WantAddr}) - preprocess(c, bc, t, i, {WantAddr}) - of DerefArrayAt: - let (arrayType, a, i) = sons3(t, n) - let tid = t[arrayType].typeId - let size = uint32 computeElemSize(bc, tid) - build bc, info, DerefArrayAtM: - bc.add info, ImmediateValM, size - preprocess(c, bc, t, a, {WantAddr}) - preprocess(c, bc, t, i, {WantAddr}) - of FieldAt: - let (typ, a, b) = sons3(t, n) - let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0] - build bc, info, FieldAtM: - preprocess(c, bc, t, a, flags+{WantAddr}) - bc.add info, ImmediateValM, uint32(offset) - of DerefFieldAt: - let (typ, a, b) = sons3(t, n) - let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0] - build bc, info, DerefFieldAtM: - preprocess(c, bc, t, a, flags+{WantAddr}) - bc.add info, ImmediateValM, uint32(offset) - of Load: - let (elemType, a) = sons2(t, n) - let tid = t[elemType].typeId - build bc, info, LoadM: - bc.add info, ImmediateValM, uint32 computeSize(bc, tid)[0] - preprocess(c, bc, t, a, {}) - - of Store: - raiseAssert "Assumption was that Store is unused!" - of Asgn: - let (elemType, dest, src) = sons3(t, n) - let tid = t[elemType].typeId - if t[src].kind in {Call, IndirectCall}: - # No support for return values, these are mapped to `var T` parameters! - build bc, info, CallM: - preprocess(c, bc, t, src.skipTyped, {WantAddr}) - preprocess(c, bc, t, dest, {WantAddr}) - for ch in sonsFromN(t, src, 2): preprocess(c, bc, t, ch, {WantAddr}) - elif t[src].kind in {CheckedCall, CheckedIndirectCall}: - let (_, gotoInstr, fn) = sons3(t, src) - build bc, info, CallM: - preprocess(c, bc, t, fn, {WantAddr}) - preprocess(c, bc, t, dest, {WantAddr}) - for ch in sonsFromN(t, src, 3): preprocess(c, bc, t, ch, {WantAddr}) - preprocess c, bc, t, gotoInstr, {} - elif t[dest].kind == Load: - let (typ, a) = sons2(t, dest) - let s = computeSize(bc, tid)[0] - build bc, info, StoreM: - bc.add info, ImmediateValM, uint32 s - preprocess(c, bc, t, a, {WantAddr}) - preprocess(c, bc, t, src, {}) - else: - let s = computeSize(bc, tid)[0] - build bc, info, AsgnM: - bc.add info, ImmediateValM, uint32 s - preprocess(c, bc, t, dest, {WantAddr}) - preprocess(c, bc, t, src, {}) - of SetExc: - recurse SetExcM - of TestExc: - recurse TestExcM - of CheckedRange: - recurse CheckedRangeM - of CheckedIndex: - recurse CheckedIndexM - of Call, IndirectCall: - # avoid the Typed thing at position 0: - build bc, info, CallM: - for ch in sonsFrom1(t, n): preprocess(c, bc, t, ch, {WantAddr}) - of CheckedCall, CheckedIndirectCall: - # avoid the Typed thing at position 0: - let (_, gotoInstr, fn) = sons3(t, n) - build bc, info, CallM: - preprocess(c, bc, t, fn, {WantAddr}) - for ch in sonsFromN(t, n, 3): preprocess(c, bc, t, ch, {WantAddr}) - preprocess c, bc, t, gotoInstr, {WantAddr} - of CheckedAdd: - recurse CheckedAddM - of CheckedSub: - recurse CheckedSubM - of CheckedMul: - recurse CheckedMulM - of CheckedDiv: - recurse CheckedDivM - of CheckedMod: - recurse CheckedModM - of Add: - recurse AddM - of Sub: - recurse SubM - of Mul: - recurse MulM - of Div: - recurse DivM - of Mod: - recurse ModM - of BitShl: - recurse BitShlM - of BitShr: - recurse BitShrM - of BitAnd: - recurse BitAndM - of BitOr: - recurse BitOrM - of BitXor: - recurse BitXorM - of BitNot: - recurse BitNotM - of BoolNot: - recurse BoolNotM - of Eq: - recurse EqM - of Le: - recurse LeM - of Lt: - recurse LtM - of Cast: - recurse CastM - of NumberConv: - recurse NumberConvM - of CheckedObjConv: - recurse CheckedObjConvM - of ObjConv: - recurse ObjConvM - of TestOf: - recurse TestOfM - of Emit: - raiseAssert "cannot interpret: Emit" - of ProcDecl: - var c2 = Preprocessing(u: c.u, thisModule: c.thisModule) - let sym = t[n.firstSon].symId - let here = CodePos(bc.len) - var p: seq[CodePos] = @[] - if bc.procUsagesToPatch.take(sym, p): - for x in p: (bc.code[x]) = toIns(bc.code[x].kind, uint32 here) - bc.procs[sym] = here - build bc, info, ProcDeclM: - let toPatch = bc.code.len - bc.add info, AllocLocals, 0'u32 - for ch in sons(t, n): preprocess(c2, bc, t, ch, {}) - bc.code[toPatch] = toIns(AllocLocals, c2.localsAddr) - when false: - if here.int == 39850: - debug bc, t, n - debug bc, here - - of PragmaPair: - recurse PragmaPairM - -const PayloadSize = 128 - -type - StackFrame = ref object - locals: pointer # usually points into `payload` if size is small enough, otherwise it's `alloc`'ed. - payload: array[PayloadSize, byte] - caller: StackFrame - returnAddr: CodePos - jumpTo: CodePos # exception handling - u: ref Universe - -proc newStackFrame(size: int; caller: StackFrame; returnAddr: CodePos): StackFrame = - result = StackFrame(caller: caller, returnAddr: returnAddr, u: caller.u) - if size <= PayloadSize: - result.locals = addr(result.payload) - else: - result.locals = alloc0(size) - -proc popStackFrame(s: StackFrame): StackFrame = - if s.locals != addr(s.payload): - dealloc s.locals - result = s.caller - -template `+!`(p: pointer; diff: uint): pointer = cast[pointer](cast[uint](p) + diff) - -proc isAtom(tree: seq[Instr]; pos: CodePos): bool {.inline.} = tree[pos.int].kind <= LastAtomicValue - -proc span(bc: seq[Instr]; pos: int): int {.inline.} = - if bc[pos].kind <= LastAtomicValue: 1 else: int(bc[pos].operand) - -proc sons2(tree: seq[Instr]; n: CodePos): (CodePos, CodePos) = - assert(not isAtom(tree, n)) - let a = n.int+1 - let b = a + span(tree, a) - result = (CodePos a, CodePos b) - -proc sons3(tree: seq[Instr]; n: CodePos): (CodePos, CodePos, CodePos) = - assert(not isAtom(tree, n)) - let a = n.int+1 - let b = a + span(tree, a) - let c = b + span(tree, b) - result = (CodePos a, CodePos b, CodePos c) - -proc sons4(tree: seq[Instr]; n: CodePos): (CodePos, CodePos, CodePos, CodePos) = - assert(not isAtom(tree, n)) - let a = n.int+1 - let b = a + span(tree, a) - let c = b + span(tree, b) - let d = c + span(tree, c) - result = (CodePos a, CodePos b, CodePos c, CodePos d) - -proc typeId*(ins: Instr): TypeId {.inline.} = - assert ins.kind == TypedM - result = TypeId(ins.operand) - -proc immediateVal*(ins: Instr): int {.inline.} = - assert ins.kind == ImmediateValM - result = cast[int](ins.operand) - -proc litId*(ins: Instr): LitId {.inline.} = - assert ins.kind in {StrValM, IntValM} - result = LitId(ins.operand) - -proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int) - -proc evalAddr(c: Bytecode; pc: CodePos; s: StackFrame): pointer = - case c.code[pc].kind - of LoadLocalM: - result = s.locals +! c.code[pc].operand - of FieldAtM: - let (x, offset) = sons2(c.code, pc) - result = evalAddr(c, x, s) - result = result +! c.code[offset].operand - of DerefFieldAtM: - let (x, offset) = sons2(c.code, pc) - let p = evalAddr(c, x, s) - result = cast[ptr pointer](p)[] +! c.code[offset].operand - of ArrayAtM: - let (e, a, i) = sons3(c.code, pc) - let elemSize = c.code[e].operand - result = evalAddr(c, a, s) - var idx: int = 0 - eval(c, i, s, addr idx, sizeof(int)) - result = result +! (uint32(idx) * elemSize) - of DerefArrayAtM: - let (e, a, i) = sons3(c.code, pc) - let elemSize = c.code[e].operand - var p = evalAddr(c, a, s) - var idx: int = 0 - eval(c, i, s, addr idx, sizeof(int)) - result = cast[ptr pointer](p)[] +! (uint32(idx) * elemSize) - of LoadGlobalM: - result = c.globalData +! c.code[pc].operand - else: - raiseAssert("unimplemented addressing mode") - -proc `div`(x, y: float32): float32 {.inline.} = x / y -proc `div`(x, y: float64): float64 {.inline.} = x / y - -from std / math import `mod` - -template binop(opr) {.dirty.} = - template impl(typ) {.dirty.} = - var x = default(typ) - var y = default(typ) - eval c, a, s, addr x, sizeof(typ) - eval c, b, s, addr y, sizeof(typ) - cast[ptr typ](result)[] = opr(x, y) - - let (t, a, b) = sons3(c.code, pc) - let tid = TypeId c.code[t].operand - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - of Float32Id: impl float32 - of Float64Id: impl float64 - else: discard - -template checkedBinop(opr) {.dirty.} = - template impl(typ) {.dirty.} = - var x = default(typ) - var y = default(typ) - eval c, a, s, addr x, sizeof(typ) - eval c, b, s, addr y, sizeof(typ) - try: - cast[ptr typ](result)[] = opr(x, y) - except OverflowDefect, DivByZeroDefect: - s.jumpTo = CodePos c.code[j].operand - - let (t, j, a, b) = sons4(c.code, pc) - let tid = TypeId c.code[t].operand - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - of Float32Id: impl float32 - of Float64Id: impl float64 - else: discard - -template bitop(opr) {.dirty.} = - template impl(typ) {.dirty.} = - var x = default(typ) - var y = default(typ) - eval c, a, s, addr x, sizeof(typ) - eval c, b, s, addr y, sizeof(typ) - cast[ptr typ](result)[] = opr(x, y) - - let (t, a, b) = sons3(c.code, pc) - let tid = c.code[t].typeId - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - else: discard - -template cmpop(opr) {.dirty.} = - template impl(typ) {.dirty.} = - var x = default(typ) - var y = default(typ) - eval c, a, s, addr x, sizeof(typ) - eval c, b, s, addr y, sizeof(typ) - cast[ptr bool](result)[] = opr(x, y) - - let (t, a, b) = sons3(c.code, pc) - let tid = c.code[t].typeId - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - of Float32Id: impl float32 - of Float64Id: impl float64 - else: discard - -proc evalSelect(c: Bytecode; pc: CodePos; s: StackFrame): CodePos = - template impl(typ) {.dirty.} = - var selector = default(typ) - eval c, sel, s, addr selector, sizeof(typ) - for pair in sonsFrom2(c, pc): - assert c.code[pair].kind == SelectPairM - let (values, action) = sons2(c.code, pair) - if c.code[values].kind == SelectValueM: - var a = default(typ) - eval c, values.firstSon, s, addr a, sizeof(typ) - if selector == a: - return CodePos c.code[action].operand - else: - assert c.code[values].kind == SelectListM, $c.code[values].kind - for v in sons(c, values): - case c.code[v].kind - of SelectValueM: - var a = default(typ) - eval c, v.firstSon, s, addr a, sizeof(typ) - if selector == a: - return CodePos c.code[action].operand - of SelectRangeM: - let (va, vb) = sons2(c.code, v) - var a = default(typ) - eval c, va, s, addr a, sizeof(typ) - var b = default(typ) - eval c, vb, s, addr a, sizeof(typ) - if a <= selector and selector <= b: - return CodePos c.code[action].operand - else: raiseAssert "unreachable" - result = CodePos(-1) - - let (t, sel) = sons2(c.code, pc) - let tid = c.code[t].typeId - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - else: raiseAssert "unreachable" - -proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int) = - case c.code[pc].kind - of LoadLocalM: - let src = s.locals +! c.code[pc].operand - copyMem result, src, size - of FieldAtM, DerefFieldAtM, ArrayAtM, DerefArrayAtM, LoadGlobalM: - let src = evalAddr(c, pc, s) - copyMem result, src, size - of LoadProcM: - let procAddr = c.code[pc].operand - cast[ptr pointer](result)[] = cast[pointer](procAddr) - of LoadM: - let (_, arg) = sons2(c.code, pc) - let src = evalAddr(c, arg, s) - copyMem result, src, size - of CheckedAddM: checkedBinop `+` - of CheckedSubM: checkedBinop `-` - of CheckedMulM: checkedBinop `*` - of CheckedDivM: checkedBinop `div` - of CheckedModM: checkedBinop `mod` - of AddM: binop `+` - of SubM: binop `-` - of MulM: binop `*` - of DivM: binop `div` - of ModM: binop `mod` - of BitShlM: bitop `shl` - of BitShrM: bitop `shr` - of BitAndM: bitop `and` - of BitOrM: bitop `or` - of BitXorM: bitop `xor` - of EqM: cmpop `==` - of LeM: cmpop `<=` - of LtM: cmpop `<` - - of StrValM: - # binary compatible and no deep copy required: - copyMem(cast[ptr string](result), addr(c.strings[c[pc].litId]), sizeof(string)) - of ObjConstrM: - for offset, size, val in triples(c, pc): - eval c, val, s, result+!offset, size - of ArrayConstrM: - let elemSize = c.code[pc.firstSon].operand - var r = result - for ch in sonsFrom1(c, pc): - eval c, ch, s, r, elemSize.int - r = r+!elemSize # can even do strength reduction here! - of NumberConvM: - let (t, x) = sons2(c.code, pc) - let word = if c[x].kind == NilValM: 0'i64 else: c.m.lit.numbers[c[x].litId] - - template impl(typ: typedesc) {.dirty.} = - cast[ptr typ](result)[] = cast[typ](word) - - let tid = c.code[t].typeId - case tid - of Bool8Id, Char8Id, UInt8Id: impl uint8 - of Int8Id: impl int8 - of Int16Id: impl int16 - of Int32Id: impl int32 - of Int64Id: impl int64 - of UInt16Id: impl uint16 - of UInt32Id: impl uint32 - of UInt64Id: impl uint64 - of Float32Id: impl float32 - of Float64Id: impl float64 - else: - case c.m.types[tid].kind - of ProcTy, UPtrTy, APtrTy, AArrayPtrTy, UArrayPtrTy: - # the VM always uses 64 bit pointers: - impl uint64 - else: - raiseAssert "cannot happen: " & $c.m.types[tid].kind - else: - #debug c, c.debug[pc.int] - raiseAssert "cannot happen: " & $c.code[pc].kind - -proc evalProc(c: Bytecode; pc: CodePos; s: StackFrame): CodePos = - assert c.code[pc].kind == LoadProcM - let procSym = c[pc].operand - when false: - if procSym >= ForwardedProc: - for k, v in c.procUsagesToPatch: - if uint32(k) == procSym - ForwardedProc: - echo k.int, " ", v.len, " <-- this one" - else: - echo k.int, " ", v.len - - assert procSym < ForwardedProc - result = CodePos(procSym) - -proc echoImpl(c: Bytecode; pc: CodePos; frame: StackFrame) = - var s = default(NimStringVM) - for a in sonsFrom1(c, pc): - assert c[a].kind == ArrayConstrM - let elemSize = c.code[a.firstSon].operand.int - for ch in sonsFrom1(c, a): - eval c, ch, frame, addr s, elemSize - if s.len > 0: - discard stdout.writeBuffer(addr(s.p.data[0]), s.len) - stdout.write "\n" - stdout.flushFile() - -type - EvalBuiltinState = enum - DidNothing, DidEval, DidError - -proc evalBuiltin(c: Bytecode; pc: CodePos; s: StackFrame; prc: CodePos; state: var EvalBuiltinState): CodePos = - var prc = prc - while true: - case c[prc].kind - of PragmaPairM: - let (x, y) = sons2(c.code, prc) - let key = cast[PragmaKey](c[x].operand) - case key - of CoreName: - let lit = c[y].litId - case c.m.lit.strings[lit] - of "echoBinSafe": echoImpl(c, pc, s) - else: - raiseAssert "cannot eval: " & c.m.lit.strings[lit] - state = DidEval - of HeaderImport, DllImport: - let lit = c[y].litId - raiseAssert "cannot eval: " & c.m.lit.strings[lit] - else: discard - of PragmaIdM, AllocLocals: discard - else: break - next c, prc - result = prc - -proc exec(c: Bytecode; pc: CodePos; u: ref Universe) = - var pc = pc - var frame = StackFrame(u: u) - while pc.int < c.code.len: - when false: # c.interactive: - echo "running: ", pc.int - debug c, pc - - case c.code[pc].kind - of GotoM: - pc = CodePos(c.code[pc].operand) - of AsgnM: - let (sz, a, b) = sons3(c.code, pc) - let dest = evalAddr(c, a, frame) - eval(c, b, frame, dest, c.code[sz].operand.int) - next c, pc - of StoreM: - let (sz, a, b) = sons3(c.code, pc) - let destPtr = evalAddr(c, a, frame) - let dest = cast[ptr pointer](destPtr)[] - eval(c, b, frame, dest, c.code[sz].operand.int) - next c, pc - of CallM: - # No support for return values, these are mapped to `var T` parameters! - var prc = evalProc(c, pc.firstSon, frame) - assert c.code[prc.firstSon].kind == AllocLocals - let frameSize = int c.code[prc.firstSon].operand - # skip stupid stuff: - var evalState = DidNothing - prc = evalBuiltin(c, pc, frame, prc.firstSon, evalState) - if evalState != DidNothing: - next c, pc - if pc.int < c.code.len and c.code[pc].kind == CheckedGotoM: - if evalState == DidEval: - next c, pc - else: - pc = CodePos(c.code[pc].operand) - else: - # setup storage for the proc already: - let callInstr = pc - next c, pc - let s2 = newStackFrame(frameSize, frame, pc) - for a in sonsFrom1(c, callInstr): - assert c[prc].kind == SummonParamM - let paramAddr = c[prc].operand - next c, prc - assert c[prc].kind == ImmediateValM - let paramSize = c[prc].operand.int - next c, prc - eval(c, a, s2, s2.locals +! paramAddr, paramSize) - frame = s2 - pc = prc - of RetM: - pc = frame.returnAddr - if c.code[pc].kind == CheckedGotoM: - pc = frame.jumpTo - frame = popStackFrame(frame) - of SelectM: - let pc2 = evalSelect(c, pc, frame) - if pc2.int >= 0: - pc = pc2 - else: - next c, pc - of ProcDeclM: - next c, pc - else: - #debug c, c.debug[pc.int] - raiseAssert "unreachable: " & $c.code[pc].kind - -proc execCode*(bc: var Bytecode; t: Tree; n: NodePos) = - traverseTypes bc - var c = Preprocessing(u: nil, thisModule: 1'u32) - let start = CodePos(bc.code.len) - var pc = n - while pc.int < t.len: - #if bc.interactive: - # echo "RUnning: " - # debug bc, t, pc - preprocess c, bc, t, pc, {} - next t, pc - exec bc, start, nil diff --git a/compiler/nir/stringcases.nim b/compiler/nir/stringcases.nim deleted file mode 100644 index afdf8fda4..000000000 --- a/compiler/nir/stringcases.nim +++ /dev/null @@ -1,200 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## included from ast2ir.nim - -#[ - -case s -of "abc", "abbd": - echo 1 -of "hah": - echo 2 -of "gah": - echo 3 - -# we produce code like this: - -if s[0] <= 'a': - if s == "abc: goto L1 - elif s == "abbd": goto L1 -else: - if s[2] <= 'h': - if s == "hah": goto L2 - elif s == "gah": goto L3 -goto afterCase - -L1: - echo 1 - goto afterCase -L2: - echo 2 - goto afterCase -L3: - echo 3 - goto afterCase - -afterCase: ... - -]# - -# We split the set of strings into 2 sets of roughly the same size. -# The condition used for splitting is a (position, char) tuple. -# Every string of length > position for which s[position] <= char is in one -# set else it is in the other set. - -from std/sequtils import addUnique - -type - Key = (LitId, LabelId) - -proc splitValue(strings: BiTable[string]; a: openArray[Key]; position: int): (char, float) = - var cand: seq[char] = @[] - for t in items a: - let s = strings[t[0]] - if s.len > position: cand.addUnique s[position] - - result = ('\0', -1.0) - for disc in items cand: - var hits = 0 - for t in items a: - let s = strings[t[0]] - if s.len > position and s[position] <= disc: - inc hits - # the split is the better, the more `hits` is close to `a.len / 2`: - let grade = 100000.0 - abs(hits.float - a.len.float / 2.0) - if grade > result[1]: - result = (disc, grade) - -proc tryAllPositions(strings: BiTable[string]; a: openArray[Key]): (char, int) = - var m = 0 - for t in items a: - m = max(m, strings[t[0]].len) - - result = ('\0', -1) - var best = -1.0 - for i in 0 ..< m: - let current = splitValue(strings, a, i) - if current[1] > best: - best = current[1] - result = (current[0], i) - -type - SearchKind = enum - LinearSearch, SplitSearch - SearchResult* = object - case kind: SearchKind - of LinearSearch: - a: seq[Key] - of SplitSearch: - span: int - best: (char, int) - -proc emitLinearSearch(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) = - var d = SearchResult(kind: LinearSearch, a: @[]) - for x in a: d.a.add x - dest.add d - -proc split(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) = - if a.len <= 4: - emitLinearSearch strings, a, dest - else: - let best = tryAllPositions(strings, a) - var groupA: seq[Key] = @[] - var groupB: seq[Key] = @[] - for t in items a: - let s = strings[t[0]] - if s.len > best[1] and s[best[1]] <= best[0]: - groupA.add t - else: - groupB.add t - if groupA.len == 0 or groupB.len == 0: - emitLinearSearch strings, a, dest - else: - let toPatch = dest.len - dest.add SearchResult(kind: SplitSearch, span: 1, best: best) - split strings, groupA, dest - split strings, groupB, dest - let dist = dest.len - toPatch - assert dist > 0 - dest[toPatch].span = dist - -proc toProblemDescription(c: var ProcCon; n: PNode): (seq[Key], LabelId) = - result = (@[], newLabels(c.labelGen, n.len)) - assert n.kind == nkCaseStmt - for i in 1..<n.len: - let it = n[i] - let thisBranch = LabelId(result[1].int + i - 1) - if it.kind == nkOfBranch: - for j in 0..<it.len-1: - assert it[j].kind in {nkStrLit..nkTripleStrLit} - result[0].add (c.lit.strings.getOrIncl(it[j].strVal), thisBranch) - -proc decodeSolution(c: var ProcCon; dest: var Tree; s: seq[SearchResult]; i: int; - selector: Value; info: PackedLineInfo) = - case s[i].kind - of SplitSearch: - let thenA = i+1 - let elseA = thenA + (if s[thenA].kind == LinearSearch: 1 else: s[thenA].span) - let best = s[i].best - - let tmp = getTemp(c, Bool8Id, info) - buildTyped dest, info, Asgn, Bool8Id: - dest.copyTree tmp - buildTyped dest, info, Call, Bool8Id: - c.addUseCodegenProc dest, "nimStrAtLe", info - dest.copyTree selector - dest.addIntVal c.lit.numbers, info, c.m.nativeIntId, best[1] - dest.addIntVal c.lit.numbers, info, Char8Id, best[0].int - - template then() = - c.decodeSolution dest, s, thenA, selector, info - template otherwise() = - c.decodeSolution dest, s, elseA, selector, info - buildIfThenElse tmp, then, otherwise - freeTemp c, tmp - - of LinearSearch: - let tmp = getTemp(c, Bool8Id, info) - for x in s[i].a: - buildTyped dest, info, Asgn, Bool8Id: - dest.copyTree tmp - buildTyped dest, info, Call, Bool8Id: - c.addUseCodegenProc dest, "eqStrings", info - dest.copyTree selector - dest.addStrLit info, x[0] - buildIf tmp: - c.code.gotoLabel info, Goto, x[1] - freeTemp c, tmp - -proc genStringCase(c: var ProcCon; n: PNode; d: var Value) = - let (problem, firstBranch) = toProblemDescription(c, n) - var solution: seq[SearchResult] = @[] - split c.lit.strings, problem, solution - - # XXX Todo move complex case selector into a temporary. - let selector = c.genx(n[0]) - - let info = toLineInfo(c, n.info) - decodeSolution c, c.code, solution, 0, selector, info - - let lend = newLabel(c.labelGen) - c.code.addLabel info, Goto, lend - for i in 1..<n.len: - let it = n[i] - let thisBranch = LabelId(firstBranch.int + i - 1) - c.code.addLabel info, Label, thisBranch - if it.kind == nkOfBranch: - gen(c, it.lastSon, d) - c.code.addLabel info, Goto, lend - else: - gen(c, it.lastSon, d) - - c.code.addLabel info, Label, lend - freeTemp c, selector diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim deleted file mode 100644 index 9c9513284..000000000 --- a/compiler/nir/types2ir.nim +++ /dev/null @@ -1,525 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2023 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import std / [assertions, tables, sets] -import ".." / [ast, types, options, sighashes, modulegraphs] -import nirtypes - -type - TypesCon* = object - processed: Table[ItemId, TypeId] - processedByName: Table[string, TypeId] - recursionCheck: HashSet[ItemId] - conf: ConfigRef - stringType: TypeId - -proc initTypesCon*(conf: ConfigRef): TypesCon = - TypesCon(conf: conf, stringType: TypeId(-1)) - -proc mangle(c: var TypesCon; t: PType): string = - result = $sighashes.hashType(t, c.conf) - -template cached(c: var TypesCon; t: PType; body: untyped) = - result = c.processed.getOrDefault(t.itemId) - if result.int == 0: - body - c.processed[t.itemId] = result - -template cachedByName(c: var TypesCon; t: PType; body: untyped) = - let key = mangle(c, t) - result = c.processedByName.getOrDefault(key) - if result.int == 0: - body - c.processedByName[key] = result - -proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId - -proc collectFieldTypes(c: var TypesCon; g: var TypeGraph; n: PNode; dest: var Table[ItemId, TypeId]) = - case n.kind - of nkRecList: - for i in 0..<n.len: - collectFieldTypes(c, g, n[i], dest) - of nkRecCase: - assert(n[0].kind == nkSym) - collectFieldTypes(c, g, n[0], dest) - for i in 1..<n.len: - case n[i].kind - of nkOfBranch, nkElse: - collectFieldTypes c, g, lastSon(n[i]), dest - else: discard - of nkSym: - dest[n.sym.itemId] = typeToIr(c, g, n.sym.typ) - else: - assert false, "unknown node kind: " & $n.kind - -proc objectToIr(c: var TypesCon; g: var TypeGraph; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) = - case n.kind - of nkRecList: - for i in 0..<n.len: - objectToIr(c, g, n[i], fieldTypes, unionId) - of nkRecCase: - assert(n[0].kind == nkSym) - objectToIr(c, g, n[0], fieldTypes, unionId) - let u = openType(g, UnionDecl) - g.addName "u_" & $unionId - inc unionId - for i in 1..<n.len: - case n[i].kind - of nkOfBranch, nkElse: - let subObj = openType(g, ObjectDecl) - g.addName "uo_" & $unionId & "_" & $i - objectToIr c, g, lastSon(n[i]), fieldTypes, unionId - sealType(g, subObj) - else: discard - sealType(g, u) - of nkSym: - g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset - else: - assert false, "unknown node kind: " & $n.kind - -proc objectToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - if t[0] != nil: - # ensure we emitted the base type: - discard typeToIr(c, g, t[0]) - - var unionId = 0 - var fieldTypes = initTable[ItemId, TypeId]() - collectFieldTypes c, g, t.n, fieldTypes - let obj = openType(g, ObjectDecl) - g.addName mangle(c, t) - g.addSize c.conf.getSize(t) - g.addAlign c.conf.getAlign(t) - - if t[0] != nil: - g.addNominalType(ObjectTy, mangle(c, t[0])) - else: - g.addBuiltinType VoidId # object does not inherit - if not lacksMTypeField(t): - let f2 = g.openType FieldDecl - let voidPtr = openType(g, APtrTy) - g.addBuiltinType(VoidId) - sealType(g, voidPtr) - g.addOffset 0 # type field is always at offset 0 - g.addName "m_type" - sealType(g, f2) # FieldDecl - - objectToIr c, g, t.n, fieldTypes, unionId - result = finishType(g, obj) - -proc objectHeaderToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - result = g.nominalType(ObjectTy, mangle(c, t)) - -proc tupleToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - var fieldTypes = newSeq[TypeId](t.len) - for i in 0..<t.len: - fieldTypes[i] = typeToIr(c, g, t[i]) - let obj = openType(g, ObjectDecl) - g.addName mangle(c, t) - g.addSize c.conf.getSize(t) - g.addAlign c.conf.getAlign(t) - - var accum = OffsetAccum(maxAlign: 1) - for i in 0..<t.len: - let child = t[i] - g.addField "f_" & $i, fieldTypes[i], accum.offset - - computeSizeAlign(c.conf, child) - accum.align(child.align) - accum.inc(int32(child.size)) - result = finishType(g, obj) - -proc procToIr(c: var TypesCon; g: var TypeGraph; t: PType; addEnv = false): TypeId = - var fieldTypes = newSeq[TypeId](0) - for i in 0..<t.len: - if t[i] == nil or not isCompileTimeOnly(t[i]): - fieldTypes.add typeToIr(c, g, t[i]) - let obj = openType(g, ProcTy) - - case t.callConv - of ccNimCall, ccFastCall, ccClosure: g.addAnnotation "__fastcall" - of ccStdCall: g.addAnnotation "__stdcall" - of ccCDecl: g.addAnnotation "__cdecl" - of ccSafeCall: g.addAnnotation "__safecall" - of ccSysCall: g.addAnnotation "__syscall" - of ccInline: g.addAnnotation "__inline" - of ccNoInline: g.addAnnotation "__noinline" - of ccThisCall: g.addAnnotation "__thiscall" - of ccNoConvention: g.addAnnotation "" - - for i in 0..<fieldTypes.len: - g.addType fieldTypes[i] - - if addEnv: - let a = openType(g, APtrTy) - g.addBuiltinType(VoidId) - sealType(g, a) - - if tfVarargs in t.flags: - g.addVarargs() - result = finishType(g, obj) - -proc nativeInt(c: TypesCon): TypeId = - case c.conf.target.intSize - of 2: result = Int16Id - of 4: result = Int32Id - else: result = Int64Id - -proc openArrayPayloadType*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - let e = lastSon(t) - let elementType = typeToIr(c, g, e) - let arr = g.openType AArrayPtrTy - g.addType elementType - result = finishType(g, arr) # LastArrayTy - -proc openArrayToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - # object (a: ArrayPtr[T], len: int) - let e = lastSon(t) - let mangledBase = mangle(c, e) - let typeName = "NimOpenArray" & mangledBase - - let elementType = typeToIr(c, g, e) - #assert elementType.int >= 0, typeToString(t) - - let p = openType(g, ObjectDecl) - g.addName typeName - g.addSize c.conf.target.ptrSize*2 - g.addAlign c.conf.target.ptrSize - - let f = g.openType FieldDecl - let arr = g.openType AArrayPtrTy - g.addType elementType - sealType(g, arr) # LastArrayTy - g.addOffset 0 - g.addName "data" - sealType(g, f) # FieldDecl - - g.addField "len", c.nativeInt, c.conf.target.ptrSize - - result = finishType(g, p) # ObjectDecl - -proc strPayloadType(c: var TypesCon; g: var TypeGraph): (string, TypeId) = - result = ("NimStrPayload", TypeId(-1)) - let p = openType(g, ObjectDecl) - g.addName result[0] - g.addSize c.conf.target.ptrSize*2 - g.addAlign c.conf.target.ptrSize - - g.addField "cap", c.nativeInt, 0 - - let f = g.openType FieldDecl - let arr = g.openType LastArrayTy - g.addBuiltinType Char8Id - result[1] = finishType(g, arr) # LastArrayTy - g.addOffset c.conf.target.ptrSize # comes after the len field - g.addName "data" - sealType(g, f) # FieldDecl - - sealType(g, p) - -proc strPayloadPtrType*(c: var TypesCon; g: var TypeGraph): (TypeId, TypeId) = - let (mangled, arrayType) = strPayloadType(c, g) - let ffp = g.openType APtrTy - g.addNominalType ObjectTy, mangled - result = (finishType(g, ffp), arrayType) # APtrTy - -proc stringToIr(c: var TypesCon; g: var TypeGraph): TypeId = - #[ - - NimStrPayload = object - cap: int - data: UncheckedArray[char] - - NimStringV2 = object - len: int - p: ptr NimStrPayload - - ]# - let payload = strPayloadType(c, g) - - let str = openType(g, ObjectDecl) - g.addName "NimStringV2" - g.addSize c.conf.target.ptrSize*2 - g.addAlign c.conf.target.ptrSize - - g.addField "len", c.nativeInt, 0 - - let fp = g.openType FieldDecl - let ffp = g.openType APtrTy - g.addNominalType ObjectTy, "NimStrPayload" - sealType(g, ffp) # APtrTy - g.addOffset c.conf.target.ptrSize # comes after 'len' field - g.addName "p" - sealType(g, fp) # FieldDecl - - result = finishType(g, str) # ObjectDecl - -proc seqPayloadType(c: var TypesCon; g: var TypeGraph; t: PType): (string, TypeId) = - #[ - NimSeqPayload[T] = object - cap: int - data: UncheckedArray[T] - ]# - let e = lastSon(t) - result = (mangle(c, e), TypeId(-1)) - let payloadName = "NimSeqPayload" & result[0] - - let elementType = typeToIr(c, g, e) - - let p = openType(g, ObjectDecl) - g.addName payloadName - g.addSize c.conf.target.intSize - g.addAlign c.conf.target.intSize - - g.addField "cap", c.nativeInt, 0 - - let f = g.openType FieldDecl - let arr = g.openType LastArrayTy - g.addType elementType - # DO NOT USE `finishType` here as it is an inner type. This is subtle and we - # probably need an even better API here. - sealType(g, arr) - result[1] = TypeId(arr) - - g.addOffset c.conf.target.ptrSize - g.addName "data" - sealType(g, f) # FieldDecl - - sealType(g, p) - -proc seqPayloadPtrType*(c: var TypesCon; g: var TypeGraph; t: PType): (TypeId, TypeId) = - let (mangledBase, arrayType) = seqPayloadType(c, g, t) - let ffp = g.openType APtrTy - g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase - result = (finishType(g, ffp), arrayType) # APtrTy - -proc seqToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - #[ - NimSeqV2*[T] = object - len: int - p: ptr NimSeqPayload[T] - ]# - let (mangledBase, _) = seqPayloadType(c, g, t) - - let sq = openType(g, ObjectDecl) - g.addName "NimSeqV2" & mangledBase - g.addSize c.conf.getSize(t) - g.addAlign c.conf.getAlign(t) - - g.addField "len", c.nativeInt, 0 - - let fp = g.openType FieldDecl - let ffp = g.openType APtrTy - g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase - sealType(g, ffp) # APtrTy - g.addOffset c.conf.target.ptrSize - g.addName "p" - sealType(g, fp) # FieldDecl - - result = finishType(g, sq) # ObjectDecl - - -proc closureToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - # struct {fn(args, void* env), env} - # typedef struct {$n" & - # "N_NIMCALL_PTR($2, ClP_0) $3;$n" & - # "void* ClE_0;$n} $1;$n" - let mangledBase = mangle(c, t) - let typeName = "NimClosure" & mangledBase - - let procType = procToIr(c, g, t, addEnv=true) - - let p = openType(g, ObjectDecl) - g.addName typeName - g.addSize c.conf.getSize(t) - g.addAlign c.conf.getAlign(t) - - let f = g.openType FieldDecl - g.addType procType - g.addOffset 0 - g.addName "ClP_0" - sealType(g, f) # FieldDecl - - let f2 = g.openType FieldDecl - let voidPtr = openType(g, APtrTy) - g.addBuiltinType(VoidId) - sealType(g, voidPtr) - - g.addOffset c.conf.target.ptrSize - g.addName "ClE_0" - sealType(g, f2) # FieldDecl - - result = finishType(g, p) # ObjectDecl - -proc bitsetBasetype*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - let s = int(getSize(c.conf, t)) - case s - of 1: result = UInt8Id - of 2: result = UInt16Id - of 4: result = UInt32Id - of 8: result = UInt64Id - else: result = UInt8Id - -proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId = - if t == nil: return VoidId - case t.kind - of tyInt: - case int(getSize(c.conf, t)) - of 2: result = Int16Id - of 4: result = Int32Id - else: result = Int64Id - of tyInt8: result = Int8Id - of tyInt16: result = Int16Id - of tyInt32: result = Int32Id - of tyInt64: result = Int64Id - of tyFloat: - case int(getSize(c.conf, t)) - of 4: result = Float32Id - else: result = Float64Id - of tyFloat32: result = Float32Id - of tyFloat64: result = Float64Id - of tyFloat128: result = getFloat128Type(g) - of tyUInt: - case int(getSize(c.conf, t)) - of 2: result = UInt16Id - of 4: result = UInt32Id - else: result = UInt64Id - of tyUInt8: result = UInt8Id - of tyUInt16: result = UInt16Id - of tyUInt32: result = UInt32Id - of tyUInt64: result = UInt64Id - of tyBool: result = Bool8Id - of tyChar: result = Char8Id - of tyVoid: result = VoidId - of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange: - result = typeToIr(c, g, t.lastSon) - of tyEnum: - if firstOrd(c.conf, t) < 0: - result = Int32Id - else: - case int(getSize(c.conf, t)) - of 1: result = UInt8Id - of 2: result = UInt16Id - of 4: result = Int32Id - of 8: result = Int64Id - else: result = Int32Id - of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic: - if t.len > 0: - result = typeToIr(c, g, t.lastSon) - else: - result = TypeId(-1) - of tyFromExpr: - if t.n != nil and t.n.typ != nil: - result = typeToIr(c, g, t.n.typ) - else: - result = TypeId(-1) - of tyArray: - cached(c, t): - var n = toInt64(lengthOrd(c.conf, t)) - if n <= 0: n = 1 # make an array of at least one element - let elemType = typeToIr(c, g, t[1]) - let a = openType(g, ArrayTy) - g.addType(elemType) - g.addArrayLen n - g.addName mangle(c, t) - result = finishType(g, a) - of tyPtr, tyRef: - cached(c, t): - let e = t.lastSon - if e.kind == tyUncheckedArray: - let elemType = typeToIr(c, g, e.lastSon) - let a = openType(g, AArrayPtrTy) - g.addType(elemType) - result = finishType(g, a) - else: - let elemType = typeToIr(c, g, t.lastSon) - let a = openType(g, APtrTy) - g.addType(elemType) - result = finishType(g, a) - of tyVar, tyLent: - cached(c, t): - let e = t.lastSon - if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: - # skip the modifier, `var openArray` is a (ptr, len) pair too: - result = typeToIr(c, g, e) - else: - let elemType = typeToIr(c, g, e) - let a = openType(g, APtrTy) - g.addType(elemType) - result = finishType(g, a) - of tySet: - let s = int(getSize(c.conf, t)) - case s - of 1: result = UInt8Id - of 2: result = UInt16Id - of 4: result = UInt32Id - of 8: result = UInt64Id - else: - # array[U8, s] - cached(c, t): - let a = openType(g, ArrayTy) - g.addType(UInt8Id) - g.addArrayLen s - g.addName mangle(c, t) - result = finishType(g, a) - of tyPointer, tyNil: - # tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim - let a = openType(g, APtrTy) - g.addBuiltinType(VoidId) - result = finishType(g, a) - of tyObject: - # Objects are special as they can be recursive in Nim. This is easily solvable. - # We check if we are already "processing" t. If so, we produce `ObjectTy` - # instead of `ObjectDecl`. - cached(c, t): - if not c.recursionCheck.containsOrIncl(t.itemId): - result = objectToIr(c, g, t) - else: - result = objectHeaderToIr(c, g, t) - of tyTuple: - cachedByName(c, t): - result = tupleToIr(c, g, t) - of tyProc: - cached(c, t): - if t.callConv == ccClosure: - result = closureToIr(c, g, t) - else: - result = procToIr(c, g, t) - of tyVarargs, tyOpenArray: - cached(c, t): - result = openArrayToIr(c, g, t) - of tyString: - if c.stringType.int < 0: - result = stringToIr(c, g) - c.stringType = result - else: - result = c.stringType - of tySequence: - cachedByName(c, t): - result = seqToIr(c, g, t) - of tyCstring: - cached(c, t): - let a = openType(g, AArrayPtrTy) - g.addBuiltinType Char8Id - result = finishType(g, a) - of tyUncheckedArray: - # We already handled the `ptr UncheckedArray` in a special way. - cached(c, t): - let elemType = typeToIr(c, g, t.lastSon) - let a = openType(g, LastArrayTy) - g.addType(elemType) - result = finishType(g, a) - of tyUntyped, tyTyped: - # this avoids a special case for system.echo which is not a generic but - # uses `varargs[typed]`: - result = VoidId - of tyNone, tyEmpty, tyTypeDesc, - tyGenericInvocation, tyProxy, tyBuiltInTypeClass, - tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, - tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: - result = TypeId(-1) diff --git a/compiler/nodekinds.nim b/compiler/nodekinds.nim new file mode 100644 index 000000000..ccdbbd26d --- /dev/null +++ b/compiler/nodekinds.nim @@ -0,0 +1,211 @@ +# +# +# The Nim Compiler +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## NodeKind enum. + +type + TNodeKind* = enum # order is extremely important, because ranges are used + # to check whether a node belongs to a certain class + nkNone, # unknown node kind: indicates an error + # Expressions: + # Atoms: + nkEmpty, # the node is empty + nkIdent, # node is an identifier + nkSym, # node is a symbol + nkType, # node is used for its typ field + + nkCharLit, # a character literal '' + nkIntLit, # an integer literal + nkInt8Lit, + nkInt16Lit, + nkInt32Lit, + nkInt64Lit, + nkUIntLit, # an unsigned integer literal + nkUInt8Lit, + nkUInt16Lit, + nkUInt32Lit, + nkUInt64Lit, + nkFloatLit, # a floating point literal + nkFloat32Lit, + nkFloat64Lit, + nkFloat128Lit, + nkStrLit, # a string literal "" + nkRStrLit, # a raw string literal r"" + nkTripleStrLit, # a triple string literal """ + nkNilLit, # the nil literal + # end of atoms + nkComesFrom, # "comes from" template/macro information for + # better stack trace generation + nkDotCall, # used to temporarily flag a nkCall node; + # this is used + # for transforming ``s.len`` to ``len(s)`` + + nkCommand, # a call like ``p 2, 4`` without parenthesis + nkCall, # a call like p(x, y) or an operation like +(a, b) + nkCallStrLit, # a call with a string literal + # x"abc" has two sons: nkIdent, nkRStrLit + # x"""abc""" has two sons: nkIdent, nkTripleStrLit + nkInfix, # a call like (a + b) + nkPrefix, # a call like !a + nkPostfix, # something like a! (also used for visibility) + nkHiddenCallConv, # an implicit type conversion via a type converter + + nkExprEqExpr, # a named parameter with equals: ''expr = expr'' + nkExprColonExpr, # a named parameter with colon: ''expr: expr'' + nkIdentDefs, # a definition like `a, b: typeDesc = expr` + # either typeDesc or expr may be nil; used in + # formal parameters, var statements, etc. + nkVarTuple, # a ``var (a, b) = expr`` construct + nkPar, # syntactic (); may be a tuple constructor + nkObjConstr, # object constructor: T(a: 1, b: 2) + nkCurly, # syntactic {} + nkCurlyExpr, # an expression like a{i} + nkBracket, # syntactic [] + nkBracketExpr, # an expression like a[i..j, k] + nkPragmaExpr, # an expression like a{.pragmas.} + nkRange, # an expression like i..j + nkDotExpr, # a.b + nkCheckedFieldExpr, # a.b, but b is a field that needs to be checked + nkDerefExpr, # a^ + nkIfExpr, # if as an expression + nkElifExpr, + nkElseExpr, + nkLambda, # lambda expression + nkDo, # lambda block appering as trailing proc param + nkAccQuoted, # `a` as a node + + nkTableConstr, # a table constructor {expr: expr} + nkBind, # ``bind expr`` node + nkClosedSymChoice, # symbol choice node; a list of nkSyms (closed) + nkOpenSymChoice, # symbol choice node; a list of nkSyms (open) + nkHiddenStdConv, # an implicit standard type conversion + nkHiddenSubConv, # an implicit type conversion from a subtype + # to a supertype + nkConv, # a type conversion + nkCast, # a type cast + nkStaticExpr, # a static expr + nkAddr, # a addr expression + nkHiddenAddr, # implicit address operator + nkHiddenDeref, # implicit ^ operator + nkObjDownConv, # down conversion between object types + nkObjUpConv, # up conversion between object types + nkChckRangeF, # range check for floats + nkChckRange64, # range check for 64 bit ints + nkChckRange, # range check for ints + nkStringToCString, # string to cstring + nkCStringToString, # cstring to string + # end of expressions + + nkAsgn, # a = b + nkFastAsgn, # internal node for a fast ``a = b`` + # (no string copy) + nkGenericParams, # generic parameters + nkFormalParams, # formal parameters + nkOfInherit, # inherited from symbol + + nkImportAs, # a 'as' b in an import statement + nkProcDef, # a proc + nkMethodDef, # a method + nkConverterDef, # a converter + nkMacroDef, # a macro + nkTemplateDef, # a template + nkIteratorDef, # an iterator + + nkOfBranch, # used inside case statements + # for (cond, action)-pairs + nkElifBranch, # used in if statements + nkExceptBranch, # an except section + nkElse, # an else part + nkAsmStmt, # an assembler block + nkPragma, # a pragma statement + nkPragmaBlock, # a pragma with a block + nkIfStmt, # an if statement + nkWhenStmt, # a when expression or statement + nkForStmt, # a for statement + nkParForStmt, # a parallel for statement + nkWhileStmt, # a while statement + nkCaseStmt, # a case statement + nkTypeSection, # a type section (consists of type definitions) + nkVarSection, # a var section + nkLetSection, # a let section + nkConstSection, # a const section + nkConstDef, # a const definition + nkTypeDef, # a type definition + nkYieldStmt, # the yield statement as a tree + nkDefer, # the 'defer' statement + nkTryStmt, # a try statement + nkFinally, # a finally section + nkRaiseStmt, # a raise statement + nkReturnStmt, # a return statement + nkBreakStmt, # a break statement + nkContinueStmt, # a continue statement + nkBlockStmt, # a block statement + nkStaticStmt, # a static statement + nkDiscardStmt, # a discard statement + nkStmtList, # a list of statements + nkImportStmt, # an import statement + nkImportExceptStmt, # an import x except a statement + nkExportStmt, # an export statement + nkExportExceptStmt, # an 'export except' statement + nkFromStmt, # a from * import statement + nkIncludeStmt, # an include statement + nkBindStmt, # a bind statement + nkMixinStmt, # a mixin statement + nkUsingStmt, # an using statement + nkCommentStmt, # a comment statement + nkStmtListExpr, # a statement list followed by an expr; this is used + # to allow powerful multi-line templates + nkBlockExpr, # a statement block ending in an expr; this is used + # to allow powerful multi-line templates that open a + # temporary scope + nkStmtListType, # a statement list ending in a type; for macros + nkBlockType, # a statement block ending in a type; for macros + # types as syntactic trees: + + nkWith, # distinct with `foo` + nkWithout, # distinct without `foo` + + nkTypeOfExpr, # type(1+2) + nkObjectTy, # object body + nkTupleTy, # tuple body + nkTupleClassTy, # tuple type class + nkTypeClassTy, # user-defined type class + nkStaticTy, # ``static[T]`` + nkRecList, # list of object parts + nkRecCase, # case section of object + nkRecWhen, # when section of object + nkRefTy, # ``ref T`` + nkPtrTy, # ``ptr T`` + nkVarTy, # ``var T`` + nkConstTy, # ``const T`` + nkOutTy, # ``out T`` + nkDistinctTy, # distinct type + nkProcTy, # proc type + nkIteratorTy, # iterator type + nkSinkAsgn, # '=sink(x, y)' + nkEnumTy, # enum body + nkEnumFieldDef, # `ident = expr` in an enumeration + nkArgList, # argument list + nkPattern, # a special pattern; used for matching + nkHiddenTryStmt, # a hidden try statement + nkClosure, # (prc, env)-pair (internally used for code gen) + nkGotoState, # used for the state machine (for iterators) + nkState, # give a label to a code section (for iterators) + nkBreakState, # special break statement for easier code generation + nkFuncDef, # a func + nkTupleConstr # a tuple constructor + nkError # erroneous AST node + nkModuleRef # for .rod file support: A (moduleId, itemId) pair + nkReplayAction # for .rod file support: A replay action + nkNilRodNode # for .rod file support: a 'nil' PNode + nkOpenSym # container for captured sym that can be overriden by local symbols + +const + nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, + nkCommand, nkCallStrLit, nkHiddenCallConv} diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim index d39d598ba..34e8ec80f 100644 --- a/compiler/optimizer.nim +++ b/compiler/optimizer.nim @@ -280,7 +280,7 @@ proc optimize*(n: PNode): PNode = Now assume 'use' raises, then we shouldn't do the 'wasMoved(s)' ]# var c: Con = Con() - var b: BasicBlock + var b: BasicBlock = default(BasicBlock) analyse(c, b, n) if c.somethingTodo: result = shallowCopy(n) diff --git a/compiler/options.nim b/compiler/options.nim index 45ed8c23e..b77bdd2a3 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -25,7 +25,7 @@ const useEffectSystem* = true useWriteTracking* = false hasFFI* = defined(nimHasLibFFI) - copyrightYear* = "2023" + copyrightYear* = "2024" nimEnableCovariance* = defined(nimEnableCovariance) @@ -86,6 +86,7 @@ type # please make sure we have under 32 options # also: generate header file optIdeDebug # idetools: debug mode optIdeTerse # idetools: use terse descriptions + optIdeExceptionInlayHints optExcessiveStackTrace # fully qualified module filenames optShowAllMismatches # show all overloading resolution candidates optWholeProject # for 'doc': output any dependency @@ -138,7 +139,6 @@ type backendCpp = "cpp" backendJs = "js" backendObjc = "objc" - backendNir = "nir" # backendNimscript = "nimscript" # this could actually work # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM @@ -146,7 +146,6 @@ type cmdNone # not yet processed command cmdUnknown # command unmapped cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, - cmdCompileToNir, cmdCrun # compile and run in nimache cmdTcc # run the project via TCC backend cmdCheck # semantic checking for whole project @@ -175,12 +174,11 @@ type const cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToJS, cmdCrun, cmdCompileToNir} + cmdCompileToJS, cmdCrun} cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc, cmdCtags, cmdBuildindex} type - NimVer* = tuple[major: int, minor: int, patch: int] TStringSeq* = seq[string] TGCMode* = enum # the selected GC gcUnselected = "unselected" @@ -227,6 +225,9 @@ type strictDefs, strictCaseObjects, inferGenericTypes, + openSym, # remove nfDisabledOpenSym when this is default + # alternative to above: + genericsOpenSym vtables LegacyFeature* = enum @@ -244,13 +245,15 @@ type emitGenerics ## generics are emitted in the module that contains them. ## Useful for libraries that rely on local passC + jsNoLambdaLifting + ## Old transformation for closures in JS backend SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest TSystemCC* = enum ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc, - ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl + ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc ExceptionSystem* = enum excNone, # no exception system selected yet @@ -297,6 +300,7 @@ type SuggestInlayHintKind* = enum sihkType = "Type", sihkParameter = "Parameter" + sihkException = "Exception" SuggestInlayHint* = ref object kind*: SuggestInlayHintKind @@ -365,7 +369,6 @@ type arguments*: string ## the arguments to be passed to the program that ## should be run ideCmd*: IdeCmd - oldNewlines*: bool cCompiler*: TSystemCC # the used compiler modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline @@ -392,8 +395,7 @@ type outDir*: AbsoluteDir jsonBuildFile*: AbsoluteFile prefixDir*, libpath*, nimcacheDir*: AbsoluteDir - nimStdlibVersion*: NimVer - dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef + dllOverrides*, moduleOverrides*, cfileSpecificOptions*: StringTableRef projectName*: string # holds a name like 'nim' projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/ projectFull*: AbsoluteFile # projectPath/projectName @@ -405,7 +407,6 @@ type commandArgs*: seq[string] # any arguments after the main command commandLine*: string extraCmds*: seq[string] # for writeJsonBuildInstructions - keepComments*: bool # whether the parser needs to keep comments implicitImports*: seq[string] # modules that are to be implicitly imported implicitIncludes*: seq[string] # modules that are to be implicitly included docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \ @@ -446,16 +447,6 @@ type clientProcessId*: int -proc parseNimVersion*(a: string): NimVer = - # could be moved somewhere reusable - result = default(NimVer) - if a.len > 0: - let b = a.split(".") - assert b.len == 3, a - template fn(i) = result[i] = b[i].parseInt # could be optimized if needed - fn(0) - fn(1) - fn(2) proc assignIfDefault*[T](result: var T, val: T, def = default(T)) = ## if `result` was already assigned to a value (that wasn't `def`), this is a noop. @@ -589,7 +580,6 @@ proc newConfigRef*(): ConfigRef = command: "", # the main command (e.g. cc, check, scan, etc) commandArgs: @[], # any arguments after the main command commandLine: "", - keepComments: true, # whether the parser needs to keep comments implicitImports: @[], # modules that are to be implicitly imported implicitIncludes: @[], # modules that are to be implicitly included docSeeSrcUrl: "", @@ -630,12 +620,6 @@ proc newPartialConfigRef*(): ConfigRef = proc cppDefine*(c: ConfigRef; define: string) = c.cppDefines.incl define -proc getStdlibVersion*(conf: ConfigRef): NimVer = - if conf.nimStdlibVersion == (0,0,0): - let s = conf.symbols.getOrDefault("nimVersion", "") - conf.nimStdlibVersion = s.parseNimVersion - result = conf.nimStdlibVersion - proc isDefined*(conf: ConfigRef; symbol: string): bool = if conf.symbols.hasKey(symbol): result = true diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 84c2980c4..e8ec22fe1 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -266,7 +266,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in {tyOpenArray, tyTuple, tyObject}: result = isAssignable(owner, n[1]) - elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct): + elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}): # types that are equal modulo distinction preserve l-value: result = isAssignable(owner, n[1]) of nkHiddenDeref: @@ -283,8 +283,15 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: result = isAssignable(owner, n[0]) of nkCallKinds: - # builtin slice keeps lvalue-ness: - if getMagic(n) in {mArrGet, mSlice}: + let m = getMagic(n) + if m == mSlice: + # builtin slice keeps l-value-ness + # except for pointers because slice dereferences + if n[1].typ.kind == tyPtr: + result = arLValue + else: + result = isAssignable(owner, n[1]) + elif m == mArrGet: result = isAssignable(owner, n[1]) elif n.typ != nil: case n.typ.kind diff --git a/compiler/parser.nim b/compiler/parser.nim index 072540dba..747505097 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -49,9 +49,14 @@ when isMainModule or defined(nimTestGrammar): checkGrammarFile() import - llstream, lexer, idents, ast, msgs, options, lineinfos, + llstream, lexer, idents, msgs, options, lineinfos, pathutils +when not defined(nimCustomAst): + import ast +else: + import plugins / customast + import std/strutils when defined(nimpretty): @@ -73,7 +78,8 @@ type bufposPrevious*: int inPragma*: int # Pragma level inSemiStmtList*: int - emptyNode: PNode + when not defined(nimCustomAst): + emptyNode: PNode when defined(nimpretty): em*: Emitter @@ -83,6 +89,10 @@ type PrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmTrySimple +when defined(nimCustomAst): + # For the `customast` version we cannot share nodes, not even empty nodes: + template emptyNode(p: Parser): PNode = newNode(nkEmpty) + # helpers for the other parsers proc isOperator*(tok: Token): bool proc getTok*(p: var Parser) @@ -91,7 +101,7 @@ proc skipComment*(p: var Parser, node: PNode) proc newNodeP*(kind: TNodeKind, p: Parser): PNode proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: Parser): PNode -proc newStrNodeP*(kind: TNodeKind, strVal: string, p: Parser): PNode +proc newStrNodeP*(kind: TNodeKind, strVal: sink string, p: Parser): PNode proc newIdentNodeP*(ident: PIdent, p: Parser): PNode proc expectIdentOrKeyw*(p: Parser) proc expectIdent*(p: Parser) @@ -146,7 +156,8 @@ proc openParser*(p: var Parser, fileIdx: FileIndex, inputStream: PLLStream, openEmitter(p.em, cache, config, fileIdx) getTok(p) # read the first token p.firstTok = true - p.emptyNode = newNode(nkEmpty) + when not defined(nimCustomAst): + p.emptyNode = newNode(nkEmpty) proc openParser*(p: var Parser, filename: AbsoluteFile, inputStream: PLLStream, cache: IdentCache; config: ConfigRef) = @@ -155,8 +166,6 @@ proc openParser*(p: var Parser, filename: AbsoluteFile, inputStream: PLLStream, proc closeParser*(p: var Parser) = ## Close a parser, freeing up its resources. closeLexer(p.lex) - when defined(nimpretty): - closeEmitter(p.em) proc parMessage(p: Parser, msg: TMsgKind, arg = "") = ## Produce and emit the parser message `arg` to output. @@ -263,24 +272,20 @@ proc indAndComment(p: var Parser, n: PNode, maybeMissEquals = false) = skipComment(p, n) proc newNodeP(kind: TNodeKind, p: Parser): PNode = - result = newNodeI(kind, parLineInfo(p)) + result = newNode(kind, parLineInfo(p)) proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode = - result = newNodeP(kind, p) - result.intVal = intVal + result = newAtom(kind, intVal, parLineInfo(p)) proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat, p: Parser): PNode = - result = newNodeP(kind, p) - result.floatVal = floatVal + result = newAtom(kind, floatVal, parLineInfo(p)) -proc newStrNodeP(kind: TNodeKind, strVal: string, p: Parser): PNode = - result = newNodeP(kind, p) - result.strVal = strVal +proc newStrNodeP(kind: TNodeKind, strVal: sink string, p: Parser): PNode = + result = newAtom(kind, strVal, parLineInfo(p)) proc newIdentNodeP(ident: PIdent, p: Parser): PNode = - result = newNodeP(nkIdent, p) - result.ident = ident + result = newAtom(ident, parLineInfo(p)) proc parseExpr(p: var Parser): PNode proc parseStmt(p: var Parser): PNode @@ -383,7 +388,7 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode = while true: case p.tok.tokType of tkAccent: - if result.len == 0: + if not result.hasSon: parMessage(p, errIdentifierExpected, p.tok) break of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi: @@ -393,8 +398,7 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode = tkParLe..tkParDotRi}: accm.add($p.tok) getTok(p) - let node = newNodeI(nkIdent, lineinfo) - node.ident = p.lex.cache.getIdent(accm) + let node = newAtom(p.lex.cache.getIdent(accm), lineinfo) result.add(node) of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCustomLit: result.add(newIdentNodeP(p.lex.cache.getIdent($p.tok), p)) @@ -511,26 +515,26 @@ proc exprColonEqExprList(p: var Parser, kind: TNodeKind, proc dotExpr(p: var Parser, a: PNode): PNode = var info = p.parLineInfo getTok(p) - result = newNodeI(nkDotExpr, info) + result = newNode(nkDotExpr, info) optInd(p, result) result.add(a) result.add(parseSymbol(p, smAfterDot)) if p.tok.tokType == tkBracketLeColon and tsLeading notin p.tok.spacing: - var x = newNodeI(nkBracketExpr, p.parLineInfo) + var x = newNode(nkBracketExpr, p.parLineInfo) # rewrite 'x.y[:z]()' to 'y[z](x)' - x.add result[1] + x.add result.secondSon exprList(p, tkBracketRi, x) eat(p, tkBracketRi) - var y = newNodeI(nkCall, p.parLineInfo) + var y = newNode(nkCall, p.parLineInfo) y.add x - y.add result[0] + y.add result.firstSon if p.tok.tokType == tkParLe and tsLeading notin p.tok.spacing: exprColonEqExprListAux(p, tkParRi, y) result = y proc dotLikeExpr(p: var Parser, a: PNode): PNode = var info = p.parLineInfo - result = newNodeI(nkInfix, info) + result = newNode(nkInfix, info) optInd(p, result) var opNode = newIdentNodeP(p.tok.ident, p) getTok(p) @@ -586,12 +590,18 @@ proc parseCast(p: var Parser): PNode = eat(p, tkParRi) setEndInfo() +template setNodeFlag(n: PNode; f: untyped) = + when defined(nimCustomAst): + discard + else: + incl n.flags, f + proc setBaseFlags(n: PNode, base: NumericalBase) = case base of base10: discard - of base2: incl(n.flags, nfBase2) - of base8: incl(n.flags, nfBase8) - of base16: incl(n.flags, nfBase16) + of base2: setNodeFlag(n, nfBase2) + of base8: setNodeFlag(n, nfBase8) + of base16: setNodeFlag(n, nfBase16) proc parseGStrLit(p: var Parser, a: PNode): PNode = case p.tok.tokType @@ -887,7 +897,7 @@ proc primarySuffix(p: var Parser, r: PNode, result = commandExpr(p, result, mode) break result = namedParams(p, result, nkCall, tkParRi) - if result.len > 1 and result[1].kind == nkExprColonExpr: + if result.has2Sons and result.secondSon.kind == nkExprColonExpr: result.transitionSonsKind(nkObjConstr) of tkDot: # progress guaranteed @@ -1158,7 +1168,7 @@ proc parseParamList(p: var Parser, retColon = true): PNode = if hasRet and p.tok.indent < 0: getTok(p) optInd(p, result) - result[0] = parseTypeDesc(p) + result.replaceFirstSon parseTypeDesc(p) elif not retColon and not hasParLe: # Mark as "not there" in order to mark for deprecation in the semantic pass: result = p.emptyNode @@ -1203,9 +1213,9 @@ proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode = params = params, name = p.emptyNode, pattern = p.emptyNode, genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode) skipComment(p, result) - result[bodyPos] = parseStmt(p) + result.replaceSon bodyPos, parseStmt(p) else: - result = newNodeI(if kind == nkIteratorDef: nkIteratorTy else: nkProcTy, info) + result = newNode(if kind == nkIteratorDef: nkIteratorTy else: nkProcTy, info) if hasSignature or pragmas.kind != nkEmpty: if hasSignature: result.add(params) @@ -1318,7 +1328,7 @@ proc parseExpr(p: var Parser): PNode = result = parseFor(p) of tkWhen: nimprettyDontTouch: - result = parseIfOrWhenExpr(p, nkWhenExpr) + result = parseIfOrWhenExpr(p, nkWhenStmt) of tkCase: # Currently we think nimpretty is good enough with case expressions, # so it is allowed to touch them: @@ -1391,7 +1401,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode = result = primarySuffix(p, result, baseInd, mode) proc binaryNot(p: var Parser; a: PNode): PNode = - if p.tok.tokType == tkNot: + if p.tok.tokType == tkNot and p.tok.indent < 0: let notOpr = newIdentNodeP(p.tok.ident, p) getTok(p) optInd(p, notOpr) @@ -1429,7 +1439,7 @@ proc parseTypeDesc(p: var Parser, fullExpr = false): PNode = result = newNodeP(nkObjectTy, p) getTok(p) of tkConcept: - result = nil + result = p.emptyNode parMessage(p, "the 'concept' keyword is only valid in 'type' sections") of tkVar: result = parseTypeDescKAux(p, nkVarTy, pmTypeDesc) of tkOut: result = parseTypeDescKAux(p, nkOutTy, pmTypeDesc) @@ -1477,7 +1487,7 @@ proc makeCall(n: PNode): PNode = if n.kind in nkCallKinds: result = n else: - result = newNodeI(nkCall, n.info) + result = newNode(nkCall, n.info) result.add n proc postExprBlocks(p: var Parser, x: PNode): PNode = @@ -1508,9 +1518,9 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode = var stmtList = newNodeP(nkStmtList, p) stmtList.add parseStmt(p) # to keep backwards compatibility (see tests/vm/tstringnil) - if stmtList[0].kind == nkStmtList: stmtList = stmtList[0] + if stmtList.firstSon.kind == nkStmtList: stmtList = stmtList.firstSon - stmtList.flags.incl nfBlockArg + setNodeFlag stmtList, nfBlockArg if openingParams.kind != nkEmpty or openingPragmas.kind != nkEmpty: if openingParams.kind == nkEmpty: openingParams = newNodeP(nkFormalParams, p) @@ -1554,7 +1564,7 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode = eat(p, tkColon) nextBlock.add parseStmt(p) - nextBlock.flags.incl nfBlockArg + setNodeFlag nextBlock, nfBlockArg result.add nextBlock if nextBlock.kind in {nkElse, nkFinally}: break @@ -1580,7 +1590,7 @@ proc parseExprStmt(p: var Parser): PNode = # if an expression is starting here, a simplePrimary was parsed and # this is the start of a command if p.tok.indent < 0 and isExprStart(p): - result = newTreeI(nkCommand, a.info, a) + result = newTree(nkCommand, a.info, a) let baseIndent = p.currInd while true: result.add(commandParam(p, isFirstParam, pmNormal)) @@ -1971,13 +1981,13 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = else: result.add(p.emptyNode) indAndComment(p, result, maybeMissEquals) - let body = result[^1] - if body.kind == nkStmtList and body.len > 0 and body[0].comment.len > 0 and body[0].kind != nkCommentStmt: + let body = result.lastSon + if body.kind == nkStmtList and body.hasSon and body.firstSon.comment.len > 0 and body.firstSon.kind != nkCommentStmt: if result.comment.len == 0: # proc fn*(a: int): int = a ## foo # => moves comment `foo` to `fn` - result.comment = body[0].comment - body[0].comment = "" + result.comment = body.firstSon.comment + body.firstSon.comment = "" #else: # assert false, p.lex.config$body.info # avoids hard to track bugs, fail early. # Yeah, that worked so well. There IS a bug in this logic, now what? @@ -2011,7 +2021,7 @@ proc parseSection(p: var Parser, kind: TNodeKind, else: parMessage(p, errIdentifierExpected, p.tok) break - if result.len == 0: parMessage(p, errIdentifierExpected, p.tok) + if not result.hasSon: parMessage(p, errIdentifierExpected, p.tok) elif p.tok.tokType in {tkSymbol, tkAccent, tkParLe} and p.tok.indent < 0: # tkParLe is allowed for ``var (x, y) = ...`` tuple parsing result.add(defparser(p)) @@ -2062,7 +2072,7 @@ proc parseEnum(p: var Parser): PNode = if p.tok.indent >= 0 and p.tok.indent <= p.currInd or p.tok.tokType == tkEof: break - if result.len <= 1: + if not result.has2Sons: parMessage(p, errIdentifierExpected, p.tok) setEndInfo() @@ -2191,7 +2201,8 @@ proc parseObject(p: var Parser): PNode = proc parseTypeClassParam(p: var Parser): PNode = let modifier = case p.tok.tokType - of tkOut, tkVar: nkVarTy + of tkVar: nkVarTy + of tkOut: nkOutTy of tkPtr: nkPtrTy of tkRef: nkRefTy of tkStatic: nkStaticTy @@ -2207,7 +2218,7 @@ proc parseTypeClassParam(p: var Parser): PNode = setEndInfo() proc parseTypeClass(p: var Parser): PNode = - #| conceptParam = ('var' | 'out')? symbol + #| conceptParam = ('var' | 'out' | 'ptr' | 'ref' | 'static' | 'type')? symbol #| conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? #| &IND{>} stmt result = newNodeP(nkTypeClassTy, p) @@ -2321,7 +2332,7 @@ proc parseVariable(p: var Parser): PNode = optInd(p, result) result.add(parseExpr(p)) else: result = parseIdentColonEquals(p, {withPragma, withDot}) - result[^1] = postExprBlocks(p, result[^1]) + result.setLastSon postExprBlocks(p, result.lastSon) indAndComment(p, result) setEndInfo() @@ -2340,8 +2351,8 @@ proc parseConstant(p: var Parser): PNode = eat(p, tkEquals) optInd(p, result) #add(result, parseStmtListExpr(p)) - result.add(parseExpr(p)) - result[^1] = postExprBlocks(p, result[^1]) + let a = parseExpr(p) + result.add postExprBlocks(p, a) indAndComment(p, result) setEndInfo() @@ -2366,7 +2377,7 @@ proc parseStmtPragma(p: var Parser): PNode = result = parsePragma(p) if p.tok.tokType == tkColon and p.tok.indent < 0: let a = result - result = newNodeI(nkPragmaBlock, a.info) + result = newNode(nkPragmaBlock, a.info) getTok(p) skipComment(p, result) result.add a @@ -2516,22 +2527,6 @@ proc parseStmt(p: var Parser): PNode = if err and p.tok.tokType == tkEof: break setEndInfo() -proc parseAll*(p: var Parser): PNode = - ## Parses the rest of the input stream held by the parser into a PNode. - result = newNodeP(nkStmtList, p) - while p.tok.tokType != tkEof: - p.hasProgress = false - var a = complexOrSimpleStmt(p) - if a.kind != nkEmpty and p.hasProgress: - result.add(a) - else: - parMessage(p, errExprExpected, p.tok) - # bugfix: consume a token here to prevent an endless loop: - getTok(p) - if p.tok.indent != 0: - parMessage(p, errInvalidIndentation) - setEndInfo() - proc checkFirstLineIndentation*(p: var Parser) = if p.tok.indent != 0 and tsLeading in p.tok.spacing: parMessage(p, errInvalidIndentation) @@ -2566,6 +2561,16 @@ proc parseTopLevelStmt*(p: var Parser): PNode = break setEndInfo() +proc parseAll*(p: var Parser): PNode = + ## Parses the rest of the input stream held by the parser into a PNode. + result = newNodeP(nkStmtList, p) + while true: + let nextStmt = p.parseTopLevelStmt() + if nextStmt.kind == nkEmpty: + break + result &= nextStmt + setEndInfo() + proc parseString*(s: string; cache: IdentCache; config: ConfigRef; filename: string = ""; line: int = 0; errorHandler: ErrorHandler = nil): PNode = @@ -2576,7 +2581,7 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef; var stream = llStreamOpen(s) stream.lineOffset = line - var p: Parser + var p = Parser() p.lex.errorHandler = errorHandler openParser(p, AbsoluteFile filename, stream, cache, config) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 7b0d7e4fb..32ec7fb53 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -276,10 +276,7 @@ proc addToArgList(result, n: PNode) = proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = ## returns a tree to semcheck if the rule triggered; nil otherwise - var ctx: TPatternContext - ctx.owner = s - ctx.c = c - ctx.formals = s.typ.len-1 + var ctx = TPatternContext(owner: s, c: c, formals: s.typ.paramsLen) var m = matchStmtList(ctx, s.ast[patternPos], n) if isNil(m): return nil # each parameter should have been bound; we simply setup a call and diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index 91f3428be..55e7fe892 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -5,13 +5,14 @@ import sem, cgen, modulegraphs, ast, llstream, parser, msgs, import pipelineutils +import ../dist/checksums/src/checksums/sha1 + when not defined(leanCompiler): import jsgen, docgen2 -import std/[syncio, objectdollar, assertions, tables, strutils] +import std/[syncio, objectdollar, assertions, tables, strutils, strtabs] import renderer import ic/replayer -import nir/nir proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) = graph.pipelinePass = pass @@ -43,10 +44,6 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): result = nil of EvalPass, InterpreterPass: result = interpreterCode(bModule, semNode) - of NirReplPass: - result = runCode(bModule, semNode) - of NirPass: - result = nirBackend(bModule, semNode) of NonePass: raiseAssert "use setPipeLinePass to set a proper PipelinePass" @@ -99,7 +96,7 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator stream: PLLStream): bool = if graph.stopCompile(): return true var - p: Parser + p: Parser = default(Parser) s: PLLStream fileIdx = module.fileIdx @@ -109,8 +106,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator case graph.pipelinePass of CgenPass: setupCgen(graph, module, idgen) - of NirPass: - openNirBackend(graph, module, idgen) of JSgenPass: when not defined(leanCompiler): setupJSgen(graph, module, idgen) @@ -118,8 +113,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator nil of EvalPass, InterpreterPass: setupEvalGen(graph, module, idgen) - of NirReplPass: - setupNirReplGen(graph, module, idgen) of GenDependPass: setupDependPass(graph, module, idgen) of Docgen2Pass: @@ -196,7 +189,7 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator if graph.dispatchers.len > 0: let ctx = preparePContext(graph, module, idgen) for disp in getDispatchers(graph): - let retTyp = disp.typ[0] + let retTyp = disp.typ.returnType if retTyp != nil: # TODO: properly semcheck the code of dispatcher? createTypeBoundOps(graph, ctx, retTyp, disp.ast.info, idgen) @@ -207,10 +200,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator discard finalJSCodeGen(graph, bModule, finalNode) of EvalPass, InterpreterPass: discard interpreterCode(bModule, finalNode) - of NirReplPass: - discard runCode(bModule, finalNode) - of NirPass: - closeNirBackend(bModule, finalNode) of SemPass, GenDependPass: discard of Docgen2Pass, Docgen2TexPass: @@ -244,7 +233,10 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF if result == nil: var cachedModules: seq[FileIndex] = @[] result = moduleFromRodFile(graph, fileIdx, cachedModules) - let filename = AbsoluteFile toFullPath(graph.config, fileIdx) + let path = toFullPath(graph.config, fileIdx) + let filename = AbsoluteFile path + if fileExists(filename): # it could be a stdinfile + graph.cachedFiles[path] = $secureHashFile(path) if result == nil: result = newModule(graph, fileIdx) result.flags.incl flags diff --git a/compiler/plugins/customast.nim b/compiler/plugins/customast.nim new file mode 100644 index 000000000..87461ae39 --- /dev/null +++ b/compiler/plugins/customast.nim @@ -0,0 +1,136 @@ +# This file exists to make it overridable via +# patchFile("plugins", "customast.nim", "customast.nim") + +## This also serves as a blueprint for a possible implementation. + +import "$nim" / compiler / [lineinfos, idents] + +when defined(nimPreviewSlimSystem): + import std/assertions + +import "$nim" / compiler / nodekinds +export nodekinds + +type + PNode* = ref TNode + TNode*{.final, acyclic.} = object + case kind*: TNodeKind + of nkCharLit..nkUInt64Lit: + intVal: BiggestInt + of nkFloatLit..nkFloat128Lit: + floatVal: BiggestFloat + of nkStrLit..nkTripleStrLit: + strVal: string + of nkSym: + discard + of nkIdent: + ident: PIdent + else: + son, next, last: PNode # linked structure instead of a `seq` + info*: TLineInfo + +const + bodyPos* = 6 + paramsPos* = 3 + +proc comment*(n: PNode): string = + result = "" + +proc `comment=`*(n: PNode, a: string) = + discard "XXX implement me" + +proc add*(father, son: PNode) = + assert son != nil + if father.son == nil: + father.son = son + father.last = son + else: + father.last.next = son + father.last = son + +template firstSon*(n: PNode): PNode = n.son +template secondSon*(n: PNode): PNode = n.son.next + +proc replaceFirstSon*(n, newson: PNode) {.inline.} = + let old = n.son + n.son = newson + newson.next = old + +proc replaceSon*(n: PNode; i: int; newson: PNode) = + assert i > 0 + assert newson.next == nil + var i = i + var it = n.son + while i > 0: + it = it.next + dec i + let old = it.next + it.next = newson + newson.next = old + +template newNodeImpl(info2) = + result = PNode(kind: kind, info: info2) + +proc newNode*(kind: TNodeKind): PNode = + ## new node with unknown line info, no type, and no children + newNodeImpl(unknownLineInfo) + +proc newNode*(kind: TNodeKind, info: TLineInfo): PNode = + ## new node with line info, no type, and no children + newNodeImpl(info) + +proc newTree*(kind: TNodeKind; info: TLineInfo; child: PNode): PNode = + result = newNode(kind, info) + result.son = child + +proc newAtom*(ident: PIdent, info: TLineInfo): PNode = + result = newNode(nkIdent) + result.ident = ident + result.info = info + +proc newAtom*(kind: TNodeKind, intVal: BiggestInt, info: TLineInfo): PNode = + result = newNode(kind, info) + result.intVal = intVal + +proc newAtom*(kind: TNodeKind, floatVal: BiggestFloat, info: TLineInfo): PNode = + result = newNode(kind, info) + result.floatVal = floatVal + +proc newAtom*(kind: TNodeKind; strVal: sink string; info: TLineInfo): PNode = + result = newNode(kind, info) + result.strVal = strVal + +proc lastSon*(n: PNode): PNode {.inline.} = n.last +proc setLastSon*(n: PNode, s: PNode) = + assert s.next == nil + n.last = s + if n.son == nil: n.son = s + +proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, + params, + name, pattern, genericParams, + pragmas, exceptions: PNode): PNode = + result = newNode(kind, info) + result.add name + result.add pattern + result.add genericParams + result.add params + result.add pragmas + result.add exceptions + result.add body + +template transitionNodeKindCommon(k: TNodeKind) = + let obj {.inject.} = n[] + n[] = TNode(kind: k, info: obj.info) + # n.comment = obj.comment # shouldn't be needed, the address doesnt' change + +proc transitionSonsKind*(n: PNode, kind: range[nkComesFrom..nkTupleConstr]) = + transitionNodeKindCommon(kind) + n.son = obj.son + +template hasSon*(n: PNode): bool = n.son != nil +template has2Sons*(n: PNode): bool = n.son != nil and n.son.next != nil + +proc isNewStyleConcept*(n: PNode): bool {.inline.} = + assert n.kind == nkTypeClassTy + result = n.firstSon.kind == nkEmpty diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim index c5e9dc853..e2c97bdc5 100644 --- a/compiler/plugins/itersgen.nim +++ b/compiler/plugins/itersgen.nim @@ -25,7 +25,7 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode = return let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst}) - if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject: + if t.kind notin {tyRef, tyPtr} or t.elementType.kind != tyObject: localError(c.config, n[2].info, "type must be a non-generic ref|ptr to object with state field") return diff --git a/compiler/plugins/plugins.nimble b/compiler/plugins/plugins.nimble new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/compiler/plugins/plugins.nimble diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 35b4d63c2..9a298cd90 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -81,14 +81,14 @@ const wRequiresInit, wNoalias, wAlign, wNoInit} - {wExportNims, wNodecl} # why exclude these? varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar, wMagic, wHeader, wCompilerProc, wCore, wDynlib, - wNoInit, wCompileTime, wGlobal, + wNoInit, wCompileTime, wGlobal, wLiftLocals, wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor, wNoalias, wAlign} constPragmas* = declPragmas + {wHeader, wMagic, wGensym, wInject, wIntDefine, wStrDefine, wBoolDefine, wDefine, wCompilerProc, wCore} - paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl} + paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl, wExportc, wExportCpp} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe, @@ -145,41 +145,27 @@ proc pragmaEnsures(c: PContext, n: PNode) = else: openScope(c) let o = getCurrOwner(c) - if o.kind in routineKinds and o.typ != nil and o.typ[0] != nil: + if o.kind in routineKinds and o.typ != nil and o.typ.returnType != nil: var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, o, n.info) - s.typ = o.typ[0] + s.typ = o.typ.returnType incl(s.flags, sfUsed) addDecl(c, s) n[1] = c.semExpr(c, n[1]) closeScope(c) -proc pragmaAsm*(c: PContext, n: PNode): char = - result = '\0' - if n != nil: - for i in 0..<n.len: - let it = n[i] - if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent: - case whichKeyword(it[0].ident) - of wSubsChar: - if it[1].kind == nkCharLit: result = chr(int(it[1].intVal)) - else: invalidPragma(c, it) - else: invalidPragma(c, it) - else: - invalidPragma(c, it) - proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) = # special cases to improve performance: if extname == "$1": - s.loc.r = rope(s.name.s) + s.loc.snippet = rope(s.name.s) elif '$' notin extname: - s.loc.r = rope(extname) + s.loc.snippet = rope(extname) else: try: - s.loc.r = rope(extname % s.name.s) + s.loc.snippet = rope(extname % s.name.s) except ValueError: localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)") when hasFFI: - s.cname = $s.loc.r + s.cname = $s.loc.snippet proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) = @@ -255,7 +241,7 @@ proc processVirtual(c: PContext, n: PNode, s: PSym, flag: TSymFlag) = s.constraint.strVal = s.constraint.strVal % s.name.s s.flags.incl {flag, sfInfixCall, sfExportc, sfMangleCpp} - s.typ.callConv = ccNoConvention + s.typ.callConv = ccMember incl c.config.globalOptions, optMixedMode proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) = @@ -307,6 +293,24 @@ proc pragmaNoForward*(c: PContext, n: PNode; flag=sfNoForward) = "use {.experimental: \"codeReordering\".} instead; " & (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated") +proc pragmaAsm*(c: PContext, n: PNode): char = + ## Checks asm pragmas and get's the asm subschar (default: '`'). + result = '\0' + if n != nil: + for i in 0..<n.len: + let it = n[i] + if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent: + case whichKeyword(it[0].ident) + of wSubsChar: + if it[1].kind == nkCharLit: result = chr(int(it[1].intVal)) + else: invalidPragma(c, it) + of wAsmSyntax: + let s = expectStrLit(c, it) + if s notin ["gcc", "vcc"]: invalidPragma(c, it) + else: invalidPragma(c, it) + else: + invalidPragma(c, it) + proc processCallConv(c: PContext, n: PNode) = if n.kind in nkPragmaCallKinds and n.len == 2 and n[1].kind == nkIdent: let sw = whichKeyword(n[1].ident) @@ -366,7 +370,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = proc processNote(c: PContext, n: PNode) = template handleNote(enumVals, notes) = let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown) - if x != errUnknown: + if x != errUnknown: nk = TNoteKind(x) let x = c.semConstBoolExpr(c, n[1]) n[1] = x @@ -476,6 +480,18 @@ proc processOption(c: PContext, n: PNode, resOptions: var TOptions) = # calling conventions (boring...): localError(c.config, n.info, "option expected") +proc checkPushedPragma(c: PContext, n: PNode) = + let keyDeep = n.kind in nkPragmaCallKinds and n.len > 1 + var key = if keyDeep: n[0] else: n + if key.kind in nkIdentKinds: + let ident = considerQuotedIdent(c, key) + var userPragma = strTableGet(c.userPragmas, ident) + if userPragma == nil: + let k = whichKeyword(ident) + # TODO: might as well make a list which is not accepted by `push`: emit, cast etc. + if k == wEmit: + localError(c.config, n.info, "an 'emit' pragma cannot be pushed") + proc processPush(c: PContext, n: PNode, start: int) = if n[start-1].kind in nkPragmaCallKinds: localError(c.config, n.info, "'push' cannot have arguments") @@ -483,6 +499,7 @@ proc processPush(c: PContext, n: PNode, start: int) = for i in start..<n.len: if not tryProcessOption(c, n[i], c.config.options): # simply store it somewhere: + checkPushedPragma(c, n[i]) if x.otherPragmas.isNil: x.otherPragmas = newNodeI(nkPragma, n.info) x.otherPragmas.add n[i] @@ -783,13 +800,14 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode = var callNode: PNode - if n.kind in {nkIdent, nkSym}: + case n.kind + of nkIdentKinds: # pragma -> pragma() callNode = newTree(nkCall, n) - elif n.kind == nkExprColonExpr: + of nkExprColonExpr: # pragma: arg -> pragma(arg) callNode = newTree(nkCall, n[0], n[1]) - elif n.kind in nkPragmaCallKinds: + of nkPragmaCallKinds - {nkExprColonExpr}: callNode = n else: invalidPragma(c, n) @@ -865,6 +883,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, # number of pragmas increase/decrease with user pragma expansion inc c.instCounter + defer: dec c.instCounter if c.instCounter > 100: globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s) @@ -874,7 +893,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, pragma(c, sym, userPragma.ast, validPragmas, isStatement) n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty - dec c.instCounter else: let k = whichKeyword(ident) if k in validPragmas: @@ -997,7 +1015,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, incl(sym.loc.flags, lfHeader) incl(sym.loc.flags, lfNoDecl) # implies nodecl, because otherwise header would not make sense - if sym.loc.r == "": sym.loc.r = rope(sym.name.s) + if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s) of wNoSideEffect: noVal(c, it) if sym != nil: @@ -1011,7 +1029,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, # Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode! if c.config.exc != excQuirky: incl(sym.flags, sfNoReturn) - if sym.typ[0] != nil: + if sym.typ.returnType != nil: localError(c.config, sym.ast[paramsPos][0].info, ".noreturn with return type not allowed") of wNoDestroy: @@ -1125,13 +1143,20 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wFatal: fatal(c.config, it.info, expectStrLit(c, it)) of wDefine: processDefine(c, it, sym) of wUndef: processUndef(c, it) - of wCompile: processCompile(c, it) + of wCompile: + let m = sym.getModule() + incl(m.flags, sfUsed) + processCompile(c, it) of wLink: processLink(c, it) of wPassl: + let m = sym.getModule() + incl(m.flags, sfUsed) let s = expectStrLit(c, it) extccomp.addLinkOption(c.config, s) recordPragma(c, it, "passl", s) of wPassc: + let m = sym.getModule() + incl(m.flags, sfUsed) let s = expectStrLit(c, it) extccomp.addCompileOption(c.config, s) recordPragma(c, it, "passc", s) @@ -1284,7 +1309,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) if sym == nil: invalidPragma(c, it) else: sym.flags.incl sfUsed - of wLiftLocals: discard + of wLiftLocals: + sym.flags.incl(sfForceLift) of wRequires, wInvariant, wAssume, wAssert: pragmaProposition(c, it) of wEnsures: @@ -1318,6 +1344,16 @@ proc mergePragmas(n, pragmas: PNode) = else: for p in pragmas: n[pragmasPos].add p +proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) = + if n[pragmasPos].kind == nkEmpty: + n[pragmasPos] = newNodeI(nkPragma, n.info) + for p in pragmas: + let prag = whichPragma(p) + if prag in validPragmas: + let copy = copyTree(p) + overwriteLineInfo copy, n.info + n[pragmasPos].add copy + proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, validPragmas: TSpecialWords) = if sym != nil and sym.kind != skModule: @@ -1331,7 +1367,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, internalError(c.config, info, "implicitPragmas") inc i popInfoContext(c.config) - if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o) + if sym.kind in routineKinds and sym.ast != nil: + mergeValidPragmas(sym.ast, o, validPragmas) if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: localError(c.config, info, ".dynlib requires .exportc") @@ -1340,7 +1377,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, sfImportc in sym.flags and lib != nil: incl(sym.loc.flags, lfDynamicLib) addToLib(lib, sym) - if sym.loc.r == "": sym.loc.r = rope(sym.name.s) + if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s) proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = if n == nil: return false diff --git a/compiler/procfind.nim b/compiler/procfind.nim index 468879ba2..c2cc6e71f 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -33,7 +33,7 @@ proc equalGenericParams(procA, procB: PNode): bool = proc searchForProcAux(c: PContext, scope: PScope, fn: PSym): PSym = const flags = {ExactGenericParams, ExactTypeDescValues, ExactConstraints, IgnoreCC} - var it: TIdentIter + var it: TIdentIter = default(TIdentIter) result = initIdentIter(it, scope.symbols, fn.name) while result != nil: if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags): @@ -54,7 +54,7 @@ proc searchForProcAux(c: PContext, scope: PScope, fn: PSym): PSym = proc searchForProc*(c: PContext, scope: PScope, fn: PSym): tuple[proto: PSym, comesFromShadowScope: bool] = var scope = scope - result.proto = searchForProcAux(c, scope, fn) + result = (searchForProcAux(c, scope, fn), false) while result.proto == nil and scope.isShadowScope: scope = scope.parent result.proto = searchForProcAux(c, scope, fn) @@ -76,7 +76,7 @@ when false: proc searchForBorrowProc*(c: PContext, startScope: PScope, fn: PSym): PSym = # Searches for the fn in the symbol table. If the parameter lists are suitable # for borrowing the sym in the symbol table is returned, else nil. - var it: TIdentIter + var it: TIdentIter = default(TIdentIter) for scope in walkScopes(startScope): result = initIdentIter(it, scope.symbols, fn.Name) while result != nil: diff --git a/compiler/pushpoppragmas.nim b/compiler/pushpoppragmas.nim new file mode 100644 index 000000000..773e7013b --- /dev/null +++ b/compiler/pushpoppragmas.nim @@ -0,0 +1,54 @@ +import pragmas, options, ast, trees, lineinfos, idents, wordrecg +import std/assertions + +import renderer + + +proc processNote(config: ConfigRef, n: PNode) = + template handleNote(enumVals, notes) = + let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown) + assert x != errUnknown + assert n[1].kind == nkIntLit + + nk = TNoteKind(x) + if n[1].intVal != 0: incl(notes, nk) + else: excl(notes, nk) + + var nk: TNoteKind + case whichKeyword(n[0][0].ident) + of wHint: handleNote(hintMin .. hintMax, config.notes) + of wWarning: handleNote(warnMin .. warnMax, config.notes) + of wWarningAsError: handleNote(warnMin .. warnMax, config.warningAsErrors) + of wHintAsError: handleNote(hintMin .. hintMax, config.warningAsErrors) + else: discard + +proc pushBackendOption(optionsStack: var seq[(TOptions, TNoteKinds)], options: TOptions, notes: TNoteKinds) = + optionsStack.add (options, notes) + +proc popBackendOption(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) = + let entry = optionsStack[^1] + options = entry[0] + config.notes = entry[1] + optionsStack.setLen(optionsStack.len-1) + +proc processPushBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions, + n: PNode, start: int) = + pushBackendOption(optionsStack, options, config.notes) + for i in start..<n.len: + let it = n[i] + if it.kind in nkPragmaCallKinds and it.len == 2: + if it[0].kind == nkBracketExpr and + it[0].len == 2 and + it[0][1].kind == nkIdent and it[0][0].kind == nkIdent: + processNote(config, it) + elif 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*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) = + popBackendOption(config, optionsStack, options) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 2a586386b..cc07c0c2d 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -25,7 +25,7 @@ type TRenderFlag* = enum renderNone, renderNoBody, renderNoComments, renderDocComments, renderNoPragmas, renderIds, renderNoProcDefs, renderSyms, renderRunnableExamples, - renderIr, renderNonExportedFields, renderExpandUsing + renderIr, renderNonExportedFields, renderExpandUsing, renderNoPostfix TRenderFlags* = set[TRenderFlag] TRenderTok* = object @@ -366,7 +366,7 @@ proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = result = t while result != nil and result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}: - result = lastSon(result) + result = skipModifier(result) result = "" let typ = n.typ.skip @@ -442,6 +442,11 @@ proc atom(g: TSrcGen; n: PNode): string = result = $n.floatVal & "\'f64" else: result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64" + of nkFloat128Lit: + if n.flags * {nfBase2, nfBase8, nfBase16} == {}: + result = $n.floatVal & "\'f128" + else: + result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128" of nkNilLit: result = "nil" of nkType: if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s @@ -514,6 +519,7 @@ proc lsub(g: TSrcGen; n: PNode): int = result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}") of nkClosedSymChoice, nkOpenSymChoice: if n.len > 0: result += lsub(g, n[0]) + of nkOpenSym: result = lsub(g, n[0]) of nkTupleTy: result = lcomma(g, n) + len("tuple[]") of nkTupleClassTy: result = len("tuple") of nkDotExpr: result = lsons(g, n) + 1 @@ -546,7 +552,11 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkInfix: result = lsons(g, n) + 2 of nkPrefix: result = lsons(g, n)+1+(if n.len > 0 and n[1].kind == nkInfix: 2 else: 0) - of nkPostfix: result = lsons(g, n) + of nkPostfix: + if renderNoPostfix notin g.flags: + result = lsons(g, n) + else: + result = lsub(g, n[1]) of nkCallStrLit: result = lsons(g, n) of nkPragmaExpr: result = lsub(g, n[0]) + lcomma(g, n, 1) of nkRange: result = lsons(g, n) + 2 @@ -1009,7 +1019,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind = proc skipHiddenNodes(n: PNode): PNode = result = n while result != nil: - if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1: + if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1: result = result[1] elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and result.len > 0: @@ -1271,6 +1281,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")") else: gsub(g, n, 0) + of nkOpenSym: gsub(g, n, 0) of nkPar, nkClosure: put(g, tkParLe, "(") gcomma(g, n, c) @@ -1330,14 +1341,20 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = put(g, tkColon, ":") gsub(g, n, bodyPos) of nkIdentDefs: - # Skip if this is a property in a type and its not exported - # (While also not allowing rendering of non exported fields) - if ObjectDef in g.inside and (not n[0].isExported() and renderNonExportedFields notin g.flags): - return + var exclFlags: TRenderFlags = {} + if ObjectDef in g.inside: + if not n[0].isExported() and renderNonExportedFields notin g.flags: + # Skip if this is a property in a type and its not exported + # (While also not allowing rendering of non exported fields) + return + # render postfix for object fields: + exclFlags = g.flags * {renderNoPostfix} # We render the identDef without being inside the section incase we render something like # y: proc (x: string) # (We wouldn't want to check if x is exported) g.outside(ObjectDef): + g.flags.excl(exclFlags) gcomma(g, n, 0, -3) + g.flags.incl(exclFlags) if n.len >= 2 and n[^2].kind != nkEmpty: putWithSpace(g, tkColon, ":") gsub(g, n[^2], c) @@ -1416,7 +1433,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = postStatements(g, n, i, fromStmtList) of nkPostfix: gsub(g, n, 1) - gsub(g, n, 0) + if renderNoPostfix notin g.flags: + gsub(g, n, 0) of nkRange: gsub(g, n, 0) put(g, tkDotDot, "..") @@ -1520,17 +1538,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gsub(g, n[0]) gsub(g, n[1]) gcoms(g) + indentNL(g) gsub(g, n[2]) + dedent(g) else: put(g, tkObject, "object") of nkRecList: - indentNL(g) for i in 0..<n.len: optNL(g) gsub(g, n[i], c) gcoms(g) - dedent(g) - putNL(g) of nkOfInherit: putWithSpace(g, tkOf, "of") gsub(g, n, 0) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index f5ec0b2d3..2f7c04af1 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -322,6 +322,14 @@ proc intersects(s1, s2: IntSet): bool = if s2.contains(a): return true +proc hasPushOrPopPragma(n: DepN): bool = + # Checks if the tree node has some pragmas that do not + # play well with reordering, like the push/pop pragma + # no crossing for push/pop barrier + let a = n.pnode + result = a.kind == nkPragma and a[0].kind == nkIdent and + (a[0].ident.s == "push" or a[0].ident.s == "pop") + proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG = # Build a dependency graph result = newSeqOfCap[DepN](deps.len) @@ -363,6 +371,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG = for dep in deps[i][0]: if dep in declares: ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it" + elif hasPushOrPopPragma(nj): + # Every node that comes after a push/pop pragma must + # depend on it; vice versa + if j < i: + ni.kids.add nj + else: + nj.kids.add ni else: for d in declares: if uses.contains(d): @@ -397,24 +412,13 @@ proc getStrongComponents(g: var DepG): seq[seq[DepN]] = ## Tarjan's algorithm. Performs a topological sort ## and detects strongly connected components. result = @[] - var s: seq[DepN] + var s: seq[DepN] = @[] var idx = 0 for v in g.mitems: if v.idx < 0: strongConnect(v, idx, s, result) -proc hasForbiddenPragma(n: PNode): bool = - # Checks if the tree node has some pragmas that do not - # play well with reordering, like the push/pop pragma - result = false - for a in n: - if a.kind == nkPragma and a[0].kind == nkIdent and - a[0].ident.s == "push": - return true - proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode = - if n.hasForbiddenPragma: - return n var includedFiles = initIntSet() let mpath = toFullPath(graph.config, module.fileIdx) let n = expandIncludes(graph, module, n, mpath, diff --git a/compiler/sem.nim b/compiler/sem.nim index 03e599753..2cf93d365 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -21,7 +21,7 @@ import extccomp import vtables -import std/[strtabs, math, tables, intsets, strutils] +import std/[strtabs, math, tables, intsets, strutils, packedsets] when not defined(leanCompiler): import spawn @@ -103,7 +103,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = result = nil for ch in arg: if sameType(ch.typ, formal): - return getConstExpr(c.module, ch, c.idgen, c.graph) + return ch typeMismatch(c.config, info, formal, arg.typ, arg) else: result = indexTypesMatch(c, formal, arg.typ, arg) @@ -144,7 +144,7 @@ proc commonType*(c: PContext; x, y: PType): PType = elif b.kind == tyTyped: result = b elif a.kind == tyTypeDesc: # turn any concrete typedesc into the abstract typedesc type - if a.len == 0: result = a + if not a.hasElementType: result = a else: result = newType(tyTypeDesc, c.idgen, a.owner) rawAddSon(result, newType(tyNone, c.idgen, a.owner)) @@ -153,17 +153,17 @@ proc commonType*(c: PContext; x, y: PType): PType = # check for seq[empty] vs. seq[int] let idx = ord(b.kind == tyArray) if a[idx].kind == tyEmpty: return y - elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len: + elif a.kind == tyTuple and b.kind == tyTuple and sameTupleLengths(a, b): var nt: PType = nil - for i in 0..<a.len: - let aEmpty = isEmptyContainer(a[i]) - let bEmpty = isEmptyContainer(b[i]) + for i, aa, bb in tupleTypePairs(a, b): + let aEmpty = isEmptyContainer(aa) + let bEmpty = isEmptyContainer(bb) if aEmpty != bEmpty: if nt.isNil: nt = copyType(a, c.idgen, a.owner) copyTypeProps(c.graph, c.idgen.module, nt, a) - nt[i] = if aEmpty: b[i] else: a[i] + nt[i] = if aEmpty: bb else: aa if not nt.isNil: result = nt #elif b[idx].kind == tyEmpty: return x elif a.kind == tyRange and b.kind == tyRange: @@ -196,8 +196,8 @@ proc commonType*(c: PContext; x, y: PType): PType = k = a.kind if b.kind != a.kind: return x # bug #7601, array construction of ptr generic - a = a.lastSon.skipTypes({tyGenericInst}) - b = b.lastSon.skipTypes({tyGenericInst}) + a = a.elementType.skipTypes({tyGenericInst}) + b = b.elementType.skipTypes({tyGenericInst}) if a.kind == tyObject and b.kind == tyObject: result = commonSuperclass(a, b) # this will trigger an error later: @@ -222,66 +222,7 @@ proc shouldCheckCaseCovered(caseTyp: PType): bool = else: discard -proc endsInNoReturn(n: PNode): bool = - ## check if expr ends the block like raising or call of noreturn procs do - result = false # assume it does return - - template checkBranch(branch) = - if not endsInNoReturn(branch): - # proved a branch returns - return false - - var it = n - # skip these beforehand, no special handling needed - while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: - it = it.lastSon - - case it.kind - of nkIfStmt: - var hasElse = false - for branch in it: - checkBranch: - if branch.len == 2: - branch[1] - elif branch.len == 1: - hasElse = true - branch[0] - else: - raiseAssert "Malformed `if` statement during endsInNoReturn" - # none of the branches returned - result = hasElse # Only truly a no-return when it's exhaustive - of nkCaseStmt: - let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc}) - # semCase should already have checked for exhaustiveness in this case - # effectively the same as having an else - var hasElse = caseTyp.shouldCheckCaseCovered() - - # actual noreturn checks - for i in 1 ..< it.len: - let branch = it[i] - checkBranch: - case branch.kind - of nkOfBranch: - branch[^1] - of nkElifBranch: - branch[1] - of nkElse: - hasElse = true - branch[0] - else: - raiseAssert "Malformed `case` statement in endsInNoReturn" - # Can only guarantee a noreturn if there is an else or it's exhaustive - result = hasElse - of nkTryStmt: - checkBranch(it[0]) - for i in 1 ..< it.len: - let branch = it[i] - checkBranch(branch[^1]) - # none of the branches returned - result = true - else: - result = it.kind in nkLastBlockStmts or - it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags +proc endsInNoReturn(n: PNode): bool proc commonType*(c: PContext; x: PType, y: PNode): PType = # ignore exception raising branches in case/if expressions @@ -313,6 +254,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = result.owner = getCurrOwner(c) else: result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info) + if find(result.name.s, '`') >= 0: + result.flags.incl sfWasGenSym #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule: # incl(result.flags, sfGlobal) when defined(nimsuggest): @@ -322,7 +265,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym # identifier with visibility proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, - allowed: TSymFlags): PSym + allowed: TSymFlags, fromTopLevel = false): PSym proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind; flags: TTypeAllowedFlags = {}) = @@ -498,15 +441,15 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, c.friendModules.add(s.owner.getModule) result = macroResult resetSemFlag result - if s.typ[0] == nil: + if s.typ.returnType == nil: result = semStmt(c, result, flags) else: - var retType = s.typ[0] + var retType = s.typ.returnType if retType.kind == tyTypeDesc and tfUnresolved in retType.flags and - retType.len == 1: + retType.hasElementType: # bug #11941: template fails(T: type X, v: auto): T # does not mean we expect a tyTypeDesc. - retType = retType[0] + retType = retType.skipModifier case retType.kind of tyUntyped, tyAnything: # Not expecting a type here allows templates like in ``tmodulealias.in``. @@ -530,7 +473,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, # e.g. template foo(T: typedesc): seq[T] # We will instantiate the return type here, because # we now know the supplied arguments - var paramTypes = initIdTable() + var paramTypes = initTypeMapping() for param, value in genericParamsInMacroCall(s, call): var givenType = value.typ # the sym nodes used for the supplied generic arguments for @@ -548,13 +491,12 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, else: result = semExpr(c, result, flags, expectedType) result = fitNode(c, retType, result, result.info) - #globalError(s.info, errInvalidParamKindX, typeToString(s.typ[0])) + #globalError(s.info, errInvalidParamKindX, typeToString(s.typ.returnType)) dec(c.config.evalTemplateCounter) discard c.friendModules.pop() const errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters" - errFloatToString = "cannot convert '$1' to '$2'" proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = @@ -708,7 +650,8 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode = let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes) - if aTypSkip.kind == tyObject: + case aTypSkip.kind + of tyObject: let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault) if child.len > 0: var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp)) @@ -717,7 +660,7 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P result = semExpr(c, asgnExpr) else: result = nil - elif aTypSkip.kind == tyArray: + of tyArray: let child = defaultNodeField(c, a, aTypSkip[1], checkDefault) if child != nil: @@ -730,7 +673,7 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P result.typ = aTyp else: result = nil - elif aTypSkip.kind == tyTuple: + of tyTuple: var hasDefault = false if aTypSkip.n != nil: let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault) @@ -743,6 +686,11 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P result = nil else: result = nil + of tyRange: + if c.graph.config.isDefined("nimPreviewRangeDefault"): + result = firstRange(c.config, aTypSkip) + else: + result = nil else: result = nil @@ -779,6 +727,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo result.semOverloadedCall = semOverloadedCall result.semInferredLambda = semInferredLambda result.semGenerateInstance = generateInstance + result.instantiateOnlyProcType = instantiateOnlyProcType result.semTypeNode = semTypeNode result.instTypeBoundOp = sigmatch.instTypeBoundOp result.hasUnresolvedArgs = hasUnresolvedArgs diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 26a40b4dc..13f2273a9 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -58,10 +58,9 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode, ]# for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}): let paramTyp = paramSym.typ - if paramTyp.n.sym.kind in filter: + if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter: result.add((paramTyp.n.sym, o.lastOverloadScope)) - symx = nextOverloadIter(o, c, headSymbol) if result.len > 0: best = initCandidate(c, result[0].s, initialBinding, @@ -81,7 +80,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, # `matches` may find new symbols, so keep track of count var symCount = c.currentScope.symbols.counter - var o: TOverloadIter + var o: TOverloadIter = default(TOverloadIter) # https://github.com/nim-lang/Nim/issues/21272 # prevent mutation during iteration by storing them in a seq # luckily `initCandidateSymbols` does just that @@ -247,48 +246,151 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add(getProcHeader(c.config, err.sym, prefer)) candidates.addDeclaredLocMaybe(c.config, err.sym) candidates.add("\n") - let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil + const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam} + let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches + var argList = n + if isGenericMismatch and n[0].kind == nkBracketExpr: + argList = n[0] + let nArg = + if err.firstMismatch.arg < argList.len: + argList[err.firstMismatch.arg] + else: + nil let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: "" - if n.len > 1 and verboseTypeMismatch in c.config.legacyFeatures: - candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg) - # candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging - case err.firstMismatch.kind - of kUnknownNamedParam: - if nArg == nil: - candidates.add("\n unknown named parameter") - else: - candidates.add("\n unknown named parameter: " & $nArg[0]) - of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0]) - of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param") - of kExtraArg: candidates.add("\n extra argument given") - of kMissingParam: candidates.add("\n missing parameter: " & nameParam) - of kTypeMismatch, kVarNeeded: - doAssert nArg != nil - let wanted = err.firstMismatch.formal.typ - doAssert err.firstMismatch.formal != nil - candidates.add("\n required type for " & nameParam & ": ") - candidates.addTypeDeclVerboseMaybe(c.config, wanted) - candidates.add "\n but expression '" - if err.firstMismatch.kind == kVarNeeded: + if n.len > 1: + if verboseTypeMismatch notin c.config.legacyFeatures: + case err.firstMismatch.kind + of kUnknownNamedParam: + if nArg == nil: + candidates.add(" unknown named parameter") + else: + candidates.add(" unknown named parameter: " & $nArg[0]) + candidates.add "\n" + of kAlreadyGiven: + candidates.add(" named param already provided: " & $nArg[0]) + candidates.add "\n" + of kPositionalAlreadyGiven: + candidates.add(" positional param was already given as named param") + candidates.add "\n" + of kExtraArg: + candidates.add(" extra argument given") + candidates.add "\n" + of kMissingParam: + candidates.add(" missing parameter: " & nameParam) + candidates.add "\n" + of kExtraGenericParam: + candidates.add(" extra generic param given") + candidates.add "\n" + of kMissingGenericParam: + candidates.add(" missing generic parameter: " & nameParam) + candidates.add "\n" + of kVarNeeded: + doAssert nArg != nil + doAssert err.firstMismatch.formal != nil + candidates.add " expression '" candidates.add renderNotLValue(nArg) candidates.add "' is immutable, not 'var'" - else: - candidates.add renderTree(nArg) - candidates.add "' is of type: " + candidates.add "\n" + of kTypeMismatch: + doAssert nArg != nil + if nArg.kind in nkSymChoices: + candidates.add ambiguousIdentifierMsg(nArg, indent = 2) + let wanted = err.firstMismatch.formal.typ + doAssert err.firstMismatch.formal != nil + doAssert wanted != nil let got = nArg.typ - candidates.addTypeDeclVerboseMaybe(c.config, got) + if got != nil and got.kind == tyProc and wanted.kind == tyProc: + # These are proc mismatches so, + # add the extra explict detail of the mismatch + candidates.add " expression '" + candidates.add renderTree(nArg) + candidates.add "' is of type: " + candidates.addTypeDeclVerboseMaybe(c.config, got) + candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) + effectProblem(wanted, got, candidates, c) + candidates.add "\n" + of kGenericParamTypeMismatch: + let pos = err.firstMismatch.arg + doAssert n[0].kind == nkBracketExpr and pos < n[0].len + let arg = n[0][pos] + doAssert arg != nil + var wanted = err.firstMismatch.formal.typ + if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints: + wanted = wanted.genericConstraint + let got = arg.typ.skipTypes({tyTypeDesc}) + doAssert err.firstMismatch.formal != nil doAssert wanted != nil + doAssert got != nil + candidates.add " generic parameter mismatch, expected " + candidates.addTypeDeclVerboseMaybe(c.config, wanted) + candidates.add " but got '" + candidates.add renderTree(arg) + candidates.add "' of type: " + candidates.addTypeDeclVerboseMaybe(c.config, got) + if nArg.kind in nkSymChoices: + candidates.add "\n" + candidates.add ambiguousIdentifierMsg(nArg, indent = 2) + if got != nil and got.kind == tyProc and wanted.kind == tyProc: + # These are proc mismatches so, + # add the extra explict detail of the mismatch + candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) if got != nil: - if got.kind == tyProc and wanted.kind == tyProc: - # These are proc mismatches so, - # add the extra explict detail of the mismatch - candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) effectProblem(wanted, got, candidates, c) - - of kUnknown: discard "do not break 'nim check'" - candidates.add "\n" - if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and - n.kind == nkCommand: + candidates.add "\n" + of kUnknown: discard "do not break 'nim check'" + else: + candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg) + if err.firstMismatch.kind in genericParamMismatches: + candidates.add(" in generic parameters") + # candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging + case err.firstMismatch.kind + of kUnknownNamedParam: + if nArg == nil: + candidates.add("\n unknown named parameter") + else: + candidates.add("\n unknown named parameter: " & $nArg[0]) + of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0]) + of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param") + of kExtraArg: candidates.add("\n extra argument given") + of kMissingParam: candidates.add("\n missing parameter: " & nameParam) + of kExtraGenericParam: + candidates.add("\n extra generic param given") + of kMissingGenericParam: + candidates.add("\n missing generic parameter: " & nameParam) + of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded: + doAssert nArg != nil + var wanted = err.firstMismatch.formal.typ + if isGenericMismatch and wanted.kind == tyGenericParam and + wanted.genericParamHasConstraints: + wanted = wanted.genericConstraint + doAssert err.firstMismatch.formal != nil + candidates.add("\n required type for " & nameParam & ": ") + candidates.addTypeDeclVerboseMaybe(c.config, wanted) + candidates.add "\n but expression '" + if err.firstMismatch.kind == kVarNeeded: + candidates.add renderNotLValue(nArg) + candidates.add "' is immutable, not 'var'" + else: + candidates.add renderTree(nArg) + candidates.add "' is of type: " + var got = nArg.typ + if isGenericMismatch: got = got.skipTypes({tyTypeDesc}) + candidates.addTypeDeclVerboseMaybe(c.config, got) + if nArg.kind in nkSymChoices: + candidates.add "\n" + candidates.add ambiguousIdentifierMsg(nArg, indent = 2) + doAssert wanted != nil + if got != nil: + if got.kind == tyProc and wanted.kind == tyProc: + # These are proc mismatches so, + # add the extra explict detail of the mismatch + candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) + effectProblem(wanted, got, candidates, c) + + of kUnknown: discard "do not break 'nim check'" + candidates.add "\n" + if err.firstMismatch.arg == 1 and nArg != nil and + nArg.kind == nkTupleConstr and n.kind == nkCommand: maybeWrongSpace = true for diag in err.diagnostics: candidates.add(diag & "\n") @@ -365,23 +467,6 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = result.add("\n" & errExpectedPosition & "\n" & candidates) localError(c.config, n.info, result) -proc bracketNotFoundError(c: PContext; n: PNode) = - var errors: CandidateErrors = @[] - var o: TOverloadIter - let headSymbol = n[0] - var symx = initOverloadIter(o, c, headSymbol) - while symx != nil: - if symx.kind in routineKinds: - errors.add(CandidateError(sym: symx, - firstMismatch: MismatchInfo(), - diagnostics: @[], - enabled: false)) - symx = nextOverloadIter(o, c, headSymbol) - if errors.len == 0: - localError(c.config, n.info, "could not resolve: " & $n) - else: - notFoundError(c, n, errors) - proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = result = "" if c.compilesContextId > 0: @@ -390,7 +475,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = # also avoid slowdowns in evaluating `compiles(expr)`. discard else: - var o: TOverloadIter + var o: TOverloadIter = default(TOverloadIter) var sym = initOverloadIter(o, c, f) while sym != nil: result &= "\n found $1" % [getSymRepr(c.config, sym)] @@ -435,7 +520,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, filter, result, alt, errors, efExplain in flags, errorsEnabled, flags) - var dummyErrors: CandidateErrors + var dummyErrors: CandidateErrors = @[] template pickSpecialOp(headSymbol) = pickBestCandidate(c, headSymbol, n, orig, initialBinding, filter, result, alt, dummyErrors, efExplain in flags, @@ -474,7 +559,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode, if overloadsState == csEmpty and result.state == csEmpty: if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim result.state = csNoMatch - if efNoDiagnostics in flags: + if c.inGenericContext > 0 and nfExprCall in n.flags: + # untyped expression calls end up here, see #24099 return # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident) localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f)) @@ -510,6 +596,39 @@ proc resolveOverloads(c: PContext, n, orig: PNode, getProcHeader(c.config, alt.calleeSym), args]) +proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) = + var errors: CandidateErrors = @[] + let headSymbol = n[0] + block: + # we build a closed symchoice of all `[]` overloads for their errors, + # except add a custom error for the magics which always match + var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c)) + var o: TOverloadIter = default(TOverloadIter) + var symx = initOverloadIter(o, c, headSymbol) + while symx != nil: + if symx.kind in routineKinds: + if symx.magic in {mArrGet, mArrPut}: + errors.add(CandidateError(sym: symx, + firstMismatch: MismatchInfo(), + diagnostics: @[], + enabled: false)) + else: + choice.add newSymNode(symx, headSymbol.info) + symx = nextOverloadIter(o, c, headSymbol) + n[0] = choice + # copied from semOverloadedCallAnalyzeEffects, might be overkill: + const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate} + let filter = + if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: + baseFilter + {skIterator} + else: baseFilter + # this will add the errors: + var r = resolveOverloads(c, n, n, filter, flags, errors, true) + if errors.len == 0: + localError(c.config, n.info, "could not resolve: " & $n) + else: + notFoundError(c, n, errors) + proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) = let a = if a.kind == nkHiddenDeref: a[0] else: a if a.kind == nkHiddenCallConv and a[0].kind == nkSym: @@ -518,7 +637,7 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) = let finalCallee = generateInstance(c, s, x.bindings, a.info) a[0].sym = finalCallee a[0].typ = finalCallee.typ - #a.typ = finalCallee.typ[0] + #a.typ = finalCallee.typ.returnType proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) = assert n.kind in nkCallKinds @@ -551,7 +670,7 @@ proc inferWithMetatype(c: PContext, formal: PType, result = copyTree(arg) result.typ = formal -proc updateDefaultParams(call: PNode) = +proc updateDefaultParams(c: PContext, call: PNode) = # In generic procs, the default parameter may be unique for each # instantiation (see tlateboundgenericparams). # After a call is resolved, we need to re-assign any default value @@ -561,8 +680,18 @@ proc updateDefaultParams(call: PNode) = let calleeParams = call[0].sym.typ.n for i in 1..<call.len: if nfDefaultParam in call[i].flags: - let def = calleeParams[i].sym.ast + let formal = calleeParams[i].sym + let def = formal.ast if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam + # mirrored with sigmatch: + if def.kind == nkEmpty: + # The default param value is set to empty in `instantiateProcType` + # when the type of the default expression doesn't match the type + # of the instantiated proc param: + pushInfoContext(c.config, call.info, call[0].sym.detailedInfo) + typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast) + popInfoContext(c.config) + def.typ = errorType(c) call[i] = def proc getCallLineInfo(n: PNode): TLineInfo = @@ -581,7 +710,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = ## Helper proc to inherit bound generic parameters from expectedType into x. ## Does nothing if 'inferGenericTypes' isn't in c.features. if inferGenericTypes notin c.features: return - if expectedType == nil or x.callee[0] == nil: return # required for inference + if expectedType == nil or x.callee.returnType == nil: return # required for inference var flatUnbound: seq[PType] = @[] @@ -593,14 +722,14 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = ## skips types and puts the skipped version on stack # It might make sense to skip here one by one. It's not part of the main # type reduction because the right side normally won't be skipped - const toSkip = { tyVar, tyLent, tyStatic, tyCompositeTypeClass, tySink } + const toSkip = {tyVar, tyLent, tyStatic, tyCompositeTypeClass, tySink} let x = a.skipTypes(toSkip) y = if a.kind notin toSkip: b else: b.skipTypes(toSkip) typeStack.add((x, y)) - stackPut(x.callee[0], expectedType) + stackPut(x.callee.returnType, expectedType) while typeStack.len() > 0: let (t, u) = typeStack.pop() @@ -608,17 +737,18 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = continue case t.kind of ConcreteTypes, tyGenericInvocation, tyUncheckedArray: + # XXX This logic makes no sense for `tyUncheckedArray` # nested, add all the types to stack let startIdx = if u.kind in ConcreteTypes: 0 else: 1 - endIdx = min(u.len() - startIdx, t.len()) + endIdx = min(u.kidsLen() - startIdx, t.kidsLen()) for i in startIdx ..< endIdx: # early exit with current impl if t[i] == nil or u[i] == nil: return stackPut(t[i], u[i]) of tyGenericParam: - let prebound = x.bindings.idTableGet(t).PType + let prebound = x.bindings.idTableGet(t) if prebound != nil: continue # Skip param, already bound @@ -641,12 +771,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate, markUsed(c, info, finalCallee) onUse(info, finalCallee) assert finalCallee.ast != nil - if x.hasFauxMatch: + if x.matchedErrorType: result = x.call result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) - if containsGenericType(result.typ) or x.fauxMatch == tyUnknown: - result.typ = newTypeS(x.fauxMatch, c) - if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor + if containsGenericType(result.typ): + result.typ = newTypeS(tyError, c) + incl result.typ.flags, tfCheckedForDestructor return let gp = finalCallee.ast[genericParamsPos] if gp.isGenericParams: @@ -680,8 +810,9 @@ proc semResolvedCall(c: PContext, x: var TCandidate, result = x.call instGenericConvertersSons(c, result, x) result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) - result.typ = finalCallee.typ[0] - updateDefaultParams(result) + if finalCallee.magic notin {mArrGet, mArrPut}: + result.typ = finalCallee.typ.returnType + updateDefaultParams(c, result) proc canDeref(n: PNode): bool {.inline.} = result = n.len >= 2 and (let t = n[1].typ; @@ -706,7 +837,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, candidates) result = semResolvedCall(c, r, n, flags, expectedType) else: - if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil: + if c.inGenericContext > 0 and c.matchedConcept == nil: result = semGenericStmt(c, n) result.typ = makeTypeFromExpr(c, result.copyTree) elif efExplain notin flags: @@ -724,21 +855,16 @@ proc explicitGenericInstError(c: PContext; n: PNode): PNode = result = n proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = + if s.kind in {skTemplate, skMacro}: + internalError c.config, n.info, "cannot get explicitly instantiated symbol of " & + (if s.kind == skTemplate: "template" else: "macro") # binding has to stay 'nil' for this to work! var m = newCandidate(c, s, nil) - - for i in 1..<n.len: - let formal = s.ast[genericParamsPos][i-1].typ - var arg = n[i].typ - # try transforming the argument into a static one before feeding it into - # typeRel - if formal.kind == tyStatic and arg.kind != tyStatic: - let evaluated = c.semTryConstExpr(c, n[i]) - if evaluated != nil: - arg = newTypeS(tyStatic, c, sons = @[evaluated.typ]) - arg.n = evaluated - let tm = typeRel(m, formal, arg) - if tm in {isNone, isConvertible}: return nil + matchGenericParams(m, n, s) + if m.state != csMatch: + # state is csMatch only if *all* generic params were matched, + # including implicit parameters + return nil var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved let info = getCallLineInfo(n) @@ -746,10 +872,16 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = onUse(info, s) result = newSymNode(newInst, info) -proc setGenericParams(c: PContext, n: PNode) = +proc setGenericParams(c: PContext, n, expectedParams: PNode) = ## sems generic params in subscript expression for i in 1..<n.len: - let e = semExprWithType(c, n[i]) + let + constraint = + if expectedParams != nil and i <= expectedParams.len: + expectedParams[i - 1].typ + else: + nil + e = semExprWithType(c, n[i], expectedType = constraint) if e.typ == nil: n[i].typ = errorType(c) else: @@ -757,7 +889,7 @@ proc setGenericParams(c: PContext, n: PNode) = proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = assert n.kind == nkBracketExpr - setGenericParams(c, n) + setGenericParams(c, n, s.ast[genericParamsPos]) var s = s var a = n[0] if a.kind == nkSym: @@ -821,7 +953,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PS ]# t = skipTypes(param.typ, desiredTypes) isDistinct = t.kind == tyDistinct or param.typ.kind == tyDistinct - if t.kind == tyGenericInvocation and t[0].lastSon.kind == tyDistinct: + if t.kind == tyGenericInvocation and t.genericHead.last.kind == tyDistinct: result.state = bsGeneric return if isDistinct: hasDistinct = true @@ -840,7 +972,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PS if resolved != nil: result.s = resolved[0].sym result.state = bsMatch - if not compareTypes(result.s.typ[0], fn.typ[0], dcEqIgnoreDistinct, {IgnoreFlags}): + if not compareTypes(result.s.typ.returnType, fn.typ.returnType, dcEqIgnoreDistinct, {IgnoreFlags}): result.state = bsReturnNotMatch elif result.s.magic in {mArrPut, mArrGet}: # cannot borrow these magics for now diff --git a/compiler/semdata.nim b/compiler/semdata.nim index b1ffbec49..ca35ddc53 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -41,7 +41,7 @@ type breakInLoop*: bool # whether we are in a loop without block next*: PProcCon # used for stacking procedure contexts mappingExists*: bool - mapping*: TIdTable + mapping*: Table[ItemId, PSym] caseContext*: seq[tuple[n: PNode, idx: int]] localBindStmts*: seq[PNode] @@ -73,9 +73,9 @@ type efNoUndeclared, efIsDotCall, efCannotBeDotCall, # Use this if undeclared identifiers should not raise an error during # overload resolution. - efNoDiagnostics, efTypeAllowed # typeAllowed will be called after efWantNoDefaults + efIgnoreDefaults # var statements without initialization efAllowSymChoice # symchoice node should not be resolved TExprFlags* = set[TExprFlag] @@ -122,8 +122,6 @@ type converters*: seq[PSym] patterns*: seq[PSym] # sequence of pattern matchers optionStack*: seq[POptionEntry] - symMapping*: TIdTable # every gensym'ed symbol needs to be mapped - # to some new symbol in a generic instantiation libs*: seq[PLib] # all libs used by this module semConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.} # for the pragmas semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.} @@ -138,9 +136,12 @@ type semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} - semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode - semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, + semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode + semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType], info: TLineInfo): PSym + instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping, + prc: PSym, info: TLineInfo): PType + # used by sigmatch for explicit generic instantiations includedFiles*: IntSet # used to detect recursive include files pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously userPragmas*: TStrTable @@ -170,6 +171,7 @@ type inUncheckedAssignSection*: int importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id]) skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies. + inTypeofContext*: int TBorrowState* = enum bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch @@ -251,14 +253,14 @@ proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next proc put*(p: PProcCon; key, val: PSym) = if not p.mappingExists: - p.mapping = initIdTable() + p.mapping = initTable[ItemId, PSym]() p.mappingExists = true #echo "put into table ", key.info - p.mapping.idTablePut(key, val) + p.mapping[key.itemId] = val proc get*(p: PProcCon; key: PSym): PSym = if not p.mappingExists: return nil - result = PSym(p.mapping.idTableGet(key)) + result = p.mapping.getOrDefault(key.itemId) proc getGenSym*(c: PContext; s: PSym): PSym = if sfGenSym notin s.flags: return s @@ -396,12 +398,11 @@ proc addToLib*(lib: PLib, sym: PSym) = # LocalError(sym.info, errInvalidPragma) sym.annex = lib -proc newTypeS*(kind: TTypeKind, c: PContext, sons: seq[PType] = @[]): PType = - result = newType(kind, c.idgen, getCurrOwner(c), sons = sons) +proc newTypeS*(kind: TTypeKind; c: PContext; son: sink PType = nil): PType = + result = newType(kind, c.idgen, getCurrOwner(c), son = son) proc makePtrType*(owner: PSym, baseType: PType; idgen: IdGenerator): PType = - result = newType(tyPtr, idgen, owner) - addSonSkipIntLit(result, baseType, idgen) + result = newType(tyPtr, idgen, owner, skipIntLit(baseType, idgen)) proc makePtrType*(c: PContext, baseType: PType): PType = makePtrType(getCurrOwner(c), baseType, c.idgen) @@ -414,15 +415,13 @@ proc makeTypeWithModifier*(c: PContext, if modifier in {tyVar, tyLent, tyTypeDesc} and baseType.kind == modifier: result = baseType else: - result = newTypeS(modifier, c) - addSonSkipIntLit(result, baseType, c.idgen) + result = newTypeS(modifier, c, skipIntLit(baseType, c.idgen)) proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType = if baseType.kind == kind: result = baseType else: - result = newTypeS(kind, c) - addSonSkipIntLit(result, baseType, c.idgen) + result = newTypeS(kind, c, skipIntLit(baseType, c.idgen)) proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let typedesc = newTypeS(tyTypeDesc, c) @@ -438,40 +437,40 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType = assert n != nil result.n = n -proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]; - idgen: IdGenerator): PType = - result = newType(kind, idgen, owner, sons = sons) +when false: + proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]; + idgen: IdGenerator): PType = + result = newType(kind, idgen, owner, sons = sons) -proc newTypeWithSons*(c: PContext, kind: TTypeKind, - sons: seq[PType]): PType = - result = newType(kind, c.idgen, getCurrOwner(c), sons = sons) - -proc newTypeWithSons*(c: PContext, kind: TTypeKind, - parent: PType): PType = - result = newType(kind, c.idgen, getCurrOwner(c), parent = parent) + proc newTypeWithSons*(c: PContext, kind: TTypeKind, + sons: seq[PType]): PType = + result = newType(kind, c.idgen, getCurrOwner(c), sons = sons) proc makeStaticExpr*(c: PContext, n: PNode): PNode = result = newNodeI(nkStaticExpr, n.info) result.sons = @[n] result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ - else: newTypeWithSons(c, tyStatic, @[n.typ]) + else: newTypeS(tyStatic, c, n.typ) proc makeAndType*(c: PContext, t1, t2: PType): PType = - result = newTypeS(tyAnd, c, sons = @[t1, t2]) + result = newTypeS(tyAnd, c) + result.rawAddSon t1 + result.rawAddSon t2 propagateToOwner(result, t1) propagateToOwner(result, t2) result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) result.flags.incl tfHasMeta proc makeOrType*(c: PContext, t1, t2: PType): PType = - if t1.kind != tyOr and t2.kind != tyOr: - result = newTypeS(tyOr, c, sons = @[t1, t2]) + result = newTypeS(tyOr, c) + result.rawAddSon t1 + result.rawAddSon t2 else: result = newTypeS(tyOr, c) template addOr(t1) = if t1.kind == tyOr: - for x in t1: result.rawAddSon x + for x in t1.kids: result.rawAddSon x else: result.rawAddSon t1 addOr(t1) @@ -482,7 +481,7 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType = result.flags.incl tfHasMeta proc makeNotType*(c: PContext, t1: PType): PType = - result = newTypeS(tyNot, c, sons = @[t1]) + result = newTypeS(tyNot, c, son = t1) propagateToOwner(result, t1) result.flags.incl(t1.flags * {tfHasStatic}) result.flags.incl tfHasMeta @@ -493,7 +492,7 @@ proc nMinusOne(c: PContext; n: PNode): PNode = # Remember to fix the procs below this one when you make changes! proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = let intType = getSysType(c.graph, n.info, tyInt) - result = newTypeS(tyRange, c, sons = @[intType]) + result = newTypeS(tyRange, c, son = intType) if n.typ != nil and n.typ.n == nil: result.flags.incl tfUnresolved result.n = newTreeI(nkRange, n.info, newIntTypeNode(0, intType), @@ -553,9 +552,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = if typ.kind == tyTypeDesc and not isSelf(typ): result = typ else: - result = newTypeS(tyTypeDesc, c) + result = newTypeS(tyTypeDesc, c, skipIntLit(typ, c.idgen)) incl result.flags, tfCheckedForDestructor - result.addSonSkipIntLit(typ, c.idgen) proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym = if t.sym != nil: return t.sym diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 82ff000a7..2885142a7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -54,17 +54,6 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # same as 'semExprWithType' but doesn't check for proc vars result = semExpr(c, n, flags + {efOperand, efAllowSymChoice}) if result.typ != nil: - # XXX tyGenericInst here? - if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}): - #and tfUnresolved in result.typ.flags: - let owner = result.typ.owner - let err = - # consistent error message with evaltempl/semMacroExpr - if owner != nil and owner.kind in {skTemplate, skMacro}: - errMissingGenericParamsForTemplate % n.renderTree - else: - errProcHasNoConcreteType % n.renderTree - localError(c.config, n.info, err) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) elif {efWantStmt, efAllowStmt} * flags != {}: result.typ = newTypeS(tyVoid, c) @@ -131,11 +120,13 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices -proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = - result = n +proc resolveSymChoice(c: PContext, n: var PNode, flags: TExprFlags = {}, expectedType: PType = nil) = + ## Attempts to resolve a symchoice `n`, `n` remains a symchoice if + ## it cannot be resolved (this is the case even when `n.len == 1`). if expectedType != nil: - result = fitNode(c, expectedType, result, n.info) - if isSymChoice(result) and efAllowSymChoice notin flags: + # resolve from type inference, see paramTypesMatch + n = fitNode(c, expectedType, n, n.info) + if isSymChoice(n) and efAllowSymChoice notin flags: # some contexts might want sym choices preserved for later disambiguation # in general though they are ambiguous let first = n[0].sym @@ -145,17 +136,88 @@ proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: P foundSym == first: # choose the first resolved enum field, i.e. the latest in scope # to mirror behavior before overloadable enums - result = n[0] - else: - var err = "ambiguous identifier '" & first.name.s & - "' -- use one of the following:\n" - for child in n: - let candidate = child.sym - err.add " " & candidate.owner.name.s & "." & candidate.name.s - err.add ": " & typeToString(candidate.typ) & "\n" - localError(c.config, n.info, err) - n.typ = errorType(c) - result = n + n = n[0] + +proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType, + warnDisabled = false): PNode = + ## sem the child of an `nkOpenSym` node, that is, captured symbols that can be + ## replaced by newly injected symbols in generics. `s` must be the captured + ## symbol if the original node is an `nkSym` node; and `nil` if it is an + ## `nkOpenSymChoice`, in which case only non-overloadable injected symbols + ## will be considered. + let isSym = n.kind == nkSym + let ident = n.getPIdent + assert ident != nil + let id = newIdentNode(ident, n.info) + c.isAmbiguous = false + let s2 = qualifiedLookUp(c, id, {}) + # for `nkSym`, the first found symbol being different and unambiguous is + # enough to replace the original + # for `nkOpenSymChoice`, the first found symbol must be non-overloadable, + # since otherwise we have to use regular `nkOpenSymChoice` functionality + # but of the overloadable sym kinds, semExpr does not handle skModule, skMacro, skTemplate + # as overloaded in the case where `nkIdent` finds them first + if s2 != nil and not c.isAmbiguous and + ((isSym and s2 != n.sym) or + (not isSym and s2.kind notin OverloadableSyms-{skModule, skMacro, skTemplate})): + # only consider symbols defined under current proc: + var o = s2.owner + while o != nil: + if o == c.p.owner: + if not warnDisabled: + result = semExpr(c, id, flags, expectedType) + return + else: + var msg = + "a new symbol '" & ident.s & "' has been injected during " & + # msgContext should show what is being instantiated: + "template or generic instantiation, however " + if isSym: + msg.add( + getSymRepr(c.config, n.sym) & " captured at " & + "the proc declaration will be used instead; " & + "either enable --experimental:openSym to use the injected symbol, " & + "or `bind` this captured symbol explicitly") + else: + msg.add( + "overloads of " & ident.s & " will be used instead; " & + "either enable --experimental:openSym to use the injected symbol, " & + "or `bind` this symbol explicitly") + message(c.config, n.info, warnIgnoredSymbolInjection, msg) + break + o = o.owner + # nothing found + n.flags.excl nfDisabledOpenSym + if not warnDisabled and isSym: + result = semExpr(c, n, flags, expectedType) + else: + result = nil + if not isSym: + # set symchoice node type back to None + n.typ = newTypeS(tyNone, c) + +proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = + if n.kind == nkOpenSymChoice: + result = semOpenSym(c, n, flags, expectedType, + warnDisabled = nfDisabledOpenSym in n.flags and + genericsOpenSym notin c.features) + if result != nil: + return + result = n + resolveSymChoice(c, result, flags, expectedType) + if isSymChoice(result) and result.len == 1: + # resolveSymChoice can leave 1 sym + result = result[0] + if isSymChoice(result) and efAllowSymChoice notin flags: + var err = "ambiguous identifier: '" & result[0].sym.name.s & + "' -- use one of the following:\n" + for child in n: + let candidate = child.sym + err.add " " & candidate.owner.name.s & "." & candidate.name.s + err.add ": " & typeToString(candidate.typ) & "\n" + localError(c.config, n.info, err) + n.typ = errorType(c) + result = n if result.kind == nkSym: result = semSym(c, result, result.sym, flags) @@ -196,19 +258,19 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = var d = skipTypes(targetTyp, abstractVar) var s = srcTyp if s.kind in tyUserTypeClasses and s.isResolvedUserTypeClass: - s = s.lastSon + s = s.last s = skipTypes(s, abstractVar-{tyTypeDesc, tyOwned}) if s.kind == tyOwned and d.kind != tyOwned: - s = s.lastSon + s = s.skipModifier var pointers = 0 while (d != nil) and (d.kind in {tyPtr, tyRef, tyOwned}): if s.kind == tyOwned and d.kind != tyOwned: - s = s.lastSon + s = s.skipModifier elif d.kind != s.kind: break else: - d = d.lastSon - s = s.lastSon + d = d.elementType + s = s.elementType inc pointers let targetBaseTyp = skipTypes(targetTyp, abstractVarRange) @@ -220,14 +282,16 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers) elif (targetBaseTyp.kind in IntegralTypes) and (srcBaseTyp.kind in IntegralTypes): - if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum: + if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum and + not sameType(targetTyp, srcBaseTyp): message(c.config, src.info, warnSuspiciousEnumConv, "suspicious code: enum to enum conversion") # `elif` would be incorrect here if targetTyp.kind == tyBool: discard "convOk" elif targetTyp.isOrdinalType: if src.kind in nkCharLit..nkUInt64Lit and - src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp): + src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp) and + targetTyp.kind notin {tyUInt..tyUInt64}: result = convNotInRange elif src.kind in nkFloatLit..nkFloat64Lit and (classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or @@ -345,7 +409,7 @@ proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType if targetType.kind in {tySink, tyLent} or isOwnedSym(c, n[0]): let baseType = semTypeNode(c, n[1], nil).skipTypes({tyTypeDesc}) - let t = newTypeS(targetType.kind, c, @[baseType]) + let t = newTypeS(targetType.kind, c, baseType) if targetType.kind == tyOwned: t.flags.incl tfHasOwned result = newNodeI(nkType, n.info) @@ -442,7 +506,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = of tySequence, tyString, tyCstring, tyOpenArray, tyVarargs: n.typ = getSysType(c.graph, n.info, tyInt) of tyArray: - n.typ = typ[0] # indextype + n.typ = typ.indexType if n.typ.kind == tyRange and emptyRange(n.typ.n[0], n.typ.n[1]): #Invalid range n.typ = getSysType(c.graph, n.info, tyInt) of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt..tyUInt64, tyFloat..tyFloat64: @@ -467,7 +531,7 @@ proc fixupStaticType(c: PContext, n: PNode) = # apply this measure only in code that is enlightened to work # with static types. if n.typ.kind != tyStatic: - n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ], c.idgen) + n.typ = newTypeS(tyStatic, c, n.typ) n.typ.n = n # XXX: cycles like the one here look dangerous. # Consider using `n.copyTree` @@ -520,8 +584,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = result.typ = n.typ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = - if n.len != 3: + if n.len != 3 or n[2].kind == nkEmpty: localError(c.config, n.info, "'is' operator takes 2 arguments") + return errorNode(c, n) let boolType = getSysType(c.graph, n.info, tyBool) result = n @@ -555,15 +620,14 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = n[1] = makeTypeSymNode(c, lhsType, n[1].info) lhsType = n[1].typ else: - if lhsType.base.kind == tyNone or - (c.inGenericContext > 0 and lhsType.base.containsGenericType): + if c.inGenericContext > 0 and lhsType.base.containsUnresolvedType: # BUGFIX: don't evaluate this too early: ``T is void`` return result = isOpImpl(c, n, flags) proc semOpAux(c: PContext, n: PNode) = - const flags = {efDetermineType} + const flags = {efDetermineType, efAllowSymChoice} for i in 1..<n.len: var a = n[i] if a.kind == nkExprEqExpr and a.len == 2: @@ -588,7 +652,14 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode = proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = case n.kind - of nkCurly, nkBracket: + of nkCurly: + for i in 0..<n.len: + if n[i].kind == nkRange: + changeType(c, n[i][0], elemType(newType), check) + changeType(c, n[i][1], elemType(newType), check) + else: + changeType(c, n[i], elemType(newType), check) + of nkBracket: for i in 0..<n.len: changeType(c, n[i], elemType(newType), check) of nkPar, nkTupleConstr: @@ -621,14 +692,20 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = a.add m changeType(m, tup[i], check) of nkCharLit..nkUInt64Lit: - if check and n.kind != nkUInt64Lit and not sameType(n.typ, newType): + if check and n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType): let value = n.intVal if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): localError(c.config, n.info, "cannot convert " & $value & - " to " & typeToString(newType)) + " to " & typeNameAndDesc(newType)) of nkFloatLit..nkFloat64Lit: if check and not floatRangeCheck(n.floatVal, newType): - localError(c.config, n.info, errFloatToString % [$n.floatVal, typeToString(newType)]) + localError(c.config, n.info, errFloatToString % [$n.floatVal, typeNameAndDesc(newType)]) + of nkSym: + if check and n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType): + let value = n.sym.position + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + localError(c.config, n.info, "cannot convert '" & n.sym.name.s & + "' to '" & typeNameAndDesc(newType) & "'") else: discard n.typ = newType @@ -640,34 +717,46 @@ proc arrayConstrType(c: PContext, n: PNode): PType = else: var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) addSonSkipIntLit(typ, t, c.idgen) - typ[0] = makeRangeType(c, 0, n.len - 1, n.info) + typ.setIndexType makeRangeType(c, 0, n.len - 1, n.info) result = typ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = result = newNodeI(nkBracket, n.info) - result.typ = newTypeS(tyArray, c) + # nkBracket nodes can also be produced by the VM as seq constant nodes + # in which case, we cannot produce a new array type for the node, + # as this might lose type info even when the node has array type + let constructType = n.typ.isNil var expectedElementType, expectedIndexType: PType = nil - if expectedType != nil: - let expected = expectedType.skipTypes(abstractRange-{tyDistinct}) - case expected.kind + var expectedBase: PType = nil + if constructType: + result.typ = newTypeS(tyArray, c) + rawAddSon(result.typ, nil) # index type + if expectedType != nil: + expectedBase = expectedType.skipTypes(abstractRange-{tyDistinct}) + else: + result.typ = n.typ + expectedBase = n.typ.skipTypes(abstractRange) # include tyDistinct this time + if expectedBase != nil: + case expectedBase.kind of tyArray: - expectedIndexType = expected[0] - expectedElementType = expected[1] - of tyOpenArray: - expectedElementType = expected[0] + expectedIndexType = expectedBase[0] + expectedElementType = expectedBase[1] + of tyOpenArray, tySequence: + # typed bracket expressions can also have seq type + expectedElementType = expectedBase[0] else: discard - rawAddSon(result.typ, nil) # index type var firstIndex, lastIndex: Int128 = Zero indexType = getSysType(c.graph, n.info, tyInt) lastValidIndex = lastOrd(c.config, indexType) if n.len == 0: - rawAddSon(result.typ, - if expectedElementType != nil and - typeAllowed(expectedElementType, skLet, c) == nil: - expectedElementType - else: - newTypeS(tyEmpty, c)) # needs an empty basetype! + if constructType: + rawAddSon(result.typ, + if expectedElementType != nil and + typeAllowed(expectedElementType, skLet, c) == nil: + expectedElementType + else: + newTypeS(tyEmpty, c)) # needs an empty basetype! lastIndex = toInt128(-1) else: var x = n[0] @@ -684,9 +773,13 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp x = x[1] let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType) - var typ = yy.typ - if expectedElementType == nil: - expectedElementType = typ + var typ: PType + if constructType: + typ = yy.typ + if expectedElementType == nil: + expectedElementType = typ + else: + typ = expectedElementType result.add yy #var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal}) for i in 1..<n.len: @@ -706,15 +799,20 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType) result.add xx - typ = commonType(c, typ, xx.typ) + if constructType: + typ = commonType(c, typ, xx.typ) #n[i] = semExprWithType(c, x, {}) #result.add fitNode(c, typ, n[i]) inc(lastIndex) - addSonSkipIntLit(result.typ, typ, c.idgen) + if constructType: + addSonSkipIntLit(result.typ, typ, c.idgen) for i in 0..<result.len: result[i] = fitNode(c, typ, result[i], result[i].info) - result.typ[0] = makeRangeType(c, toInt64(firstIndex), toInt64(lastIndex), n.info, - indexType) + if constructType: + result.typ.setIndexType( + makeRangeType(c, + toInt64(firstIndex), toInt64(lastIndex), + n.info, indexType)) proc fixAbstractType(c: PContext, n: PNode) = for i in 1..<n.len: @@ -739,7 +837,7 @@ proc isUnresolvedSym(s: PSym): bool = result = s.kind == skGenericParam if not result and s.typ != nil: result = tfInferrableStatic in s.typ.flags or - (s.kind == skParam and s.typ.isMetaType) or + (s.kind == skParam and (s.typ.isMetaType or sfTemplateParam in s.flags)) or (s.kind == skType and s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) @@ -813,10 +911,13 @@ proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode = proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) = checkMinSonsLen(n, 1, c.config) + if n[0].typ == nil: + # n[0] might be erroring node in nimsuggest + return const FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, - mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy, mMove, + mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, mWasMoved} template checkIfConverterCalled(c: PContext, n: PNode) = @@ -901,7 +1002,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = if n[i].typ.isNil or n[i].typ.kind != tyStatic or tfUnresolved notin n[i].typ.flags: break maybeLabelAsStatic - n.typ = newTypeWithSons(c, tyStatic, @[n.typ]) + n.typ = newTypeS(tyStatic, c, n.typ) n.typ.flags.incl tfUnresolved # optimization pass: not necessary for correctness of the semantic pass @@ -915,7 +1016,8 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = if callee.magic notin ctfeWhitelist: return - if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine: + if callee.kind notin {skProc, skFunc, skConverter, skConst} or + callee.isGenericRoutineStrict: return if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return @@ -972,7 +1074,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, if result != nil: if result[0].kind != nkSym: - if not (efDetermineType in flags and c.inGenericContext > 0): + if not (c.inGenericContext > 0): # see generic context check in semOverloadedCall internalError(c.config, "semOverloadedCallAnalyseEffects") return let callee = result[0].sym @@ -996,13 +1098,33 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode; result = initCandidate(c, t) matches(c, n, nOrig, result) -proc bracketedMacro(n: PNode): PSym = - if n.len >= 1 and n[0].kind == nkSym: - result = n[0].sym - if result.kind notin {skMacro, skTemplate}: - result = nil +proc finishOperand(c: PContext, a: PNode): PNode = + if a.typ.isNil: + result = c.semOperand(c, a, {efDetermineType}) else: - result = nil + result = a + # XXX tyGenericInst here? + if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}): + #and tfUnresolved in result.typ.flags: + let owner = result.typ.owner + let err = + # consistent error message with evaltempl/semMacroExpr + if owner != nil and owner.kind in {skTemplate, skMacro}: + errMissingGenericParamsForTemplate % a.renderTree + else: + errProcHasNoConcreteType % a.renderTree + localError(c.config, a.info, err) + considerGenSyms(c, result) + +proc semFinishOperands(c: PContext; n: PNode; isBracketExpr = false) = + # this needs to be called to ensure that after overloading resolution every + # argument has been sem'checked + + # skip the first argument for operands of `[]` since it may be an unresolved + # generic proc, which is handled in semMagic + let start = 1 + ord(isBracketExpr) + for i in start..<n.len: + n[i] = finishOperand(c, n[i]) proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError: @@ -1024,7 +1146,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType) of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType) else: - semFinishOperands(c, result) + semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut}) activate(c, result) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) @@ -1032,10 +1154,11 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy result = magicsAfterOverloadResolution(c, result, flags, expectedType) when false: if result.typ != nil and - not (result.typ.kind == tySequence and result.typ[0].kind == tyEmpty): + not (result.typ.kind == tySequence and result.elementType.kind == tyEmpty): liftTypeBoundOps(c, result.typ, n.info) #result = patchResolvedTypeBoundOp(c, result) - if c.matchedConcept == nil: + if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone): + # don't fold calls in concepts and typeof result = evalAtCompileTime(c, result) proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = @@ -1052,6 +1175,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType result.flags.incl nfExplicitCall for i in 1..<n.len: result.add n[i] return semExpr(c, result, flags, expectedType) + elif n0.typ.kind == tyFromExpr and c.inGenericContext > 0: + # don't make assumptions, entire expression needs to be tyFromExpr + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return else: n[0] = n0 else: @@ -1059,11 +1187,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType let t = n[0].typ if t != nil and t.kind in {tyVar, tyLent}: n[0] = newDeref(n[0]) - elif n[0].kind == nkBracketExpr: - let s = bracketedMacro(n[0]) - if s != nil: - setGenericParams(c, n[0]) - return semDirectOp(c, n, flags, expectedType) elif isSymChoice(n[0]) and nfDotField notin n.flags: # overloaded generic procs e.g. newSeq[int] can end up here return semDirectOp(c, n, flags, expectedType) @@ -1263,7 +1386,7 @@ proc readTypeParameter(c: PContext, typ: PType, discard if typ.kind != tyUserTypeClass: - let ty = if typ.kind == tyCompositeTypeClass: typ[1].skipGenericAlias + let ty = if typ.kind == tyCompositeTypeClass: typ.firstGenericParam.skipGenericAlias else: typ.skipGenericAlias let tbody = ty[0] for s in 0..<tbody.len-1: @@ -1292,7 +1415,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = onUse(n.info, s) let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc}) case typ.kind - of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, + of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, tyTuple, tySet, tyUInt..tyUInt64: if s.magic == mNone: result = inlineConst(c, n, s) else: result = newSymNode(s, n.info) @@ -1392,20 +1515,34 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode = case t.kind - of tyTypeParamsHolders: + of tyGenericInst: result = readTypeParameter(c, t, i, n.info) if result == c.graph.emptyNode: - result = n - n.typ = makeTypeFromExpr(c, n.copyTree) + if c.inGenericContext > 0: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + else: + result = nil of tyUserTypeClasses: if t.isResolvedUserTypeClass: result = readTypeParameter(c, t, i, n.info) + elif c.inGenericContext > 0: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) else: - n.typ = makeTypeFromExpr(c, copyTree(n)) - result = n - of tyGenericParam, tyAnything: - n.typ = makeTypeFromExpr(c, copyTree(n)) - result = n + result = nil + of tyGenericBody, tyCompositeTypeClass: + if c.inGenericContext > 0: + result = readTypeParameter(c, t, i, n.info) + if result != nil: + # generic parameter exists, stop here but delay until instantiation + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) + else: + result = nil + elif c.inGenericContext > 0 and t.containsUnresolvedType: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, copyTree(result)) else: result = nil @@ -1433,7 +1570,7 @@ proc tryReadingTypeField(c: PContext, n: PNode, i: PIdent, ty: PType): PNode = n.typ = makeTypeDesc(c, field.typ) result = n of tyGenericInst: - result = tryReadingTypeField(c, n, i, ty.lastSon) + result = tryReadingTypeField(c, n, i, ty.skipModifier) if result == nil: result = tryReadingGenericParam(c, n, i, ty) else: @@ -1493,7 +1630,7 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = return nil if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: - ty = ty.lastSon + ty = ty.last ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink, tyStatic}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst, tyAlias}) var check: PNode = nil @@ -1569,18 +1706,20 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = result.add(newIdentNode(ident, n.info)) for s in n: result.add s -proc semDeref(c: PContext, n: PNode): PNode = +proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode = checkSonsLen(n, 1, c.config) n[0] = semExprWithType(c, n[0]) let a = getConstExpr(c.module, n[0], c.idgen, c.graph) if a != nil: - if a.kind == nkNilLit: + if a.kind == nkNilLit and efInTypeof notin flags: localError(c.config, n.info, "nil dereference is not allowed") n[0] = a result = n var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned}) case t.kind - of tyRef, tyPtr: n.typ = t.lastSon + of tyRef, tyPtr: n.typ = t.elementType + of tyMetaTypes, tyFromExpr: + n.typ = makeTypeFromExpr(c, n.copyTree) else: result = nil #GlobalError(n[0].info, errCircumNeedsPointer) @@ -1603,16 +1742,17 @@ proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode = result = explicitGenericInstantiation(c, n, s) if result == n: n[0] = copyTree(result[0]) - else: - n[0] = result proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if not a built-in subscript operator; also called for the ## checking of assignments result = nil if n.len == 1: - let x = semDeref(c, n) + let x = semDeref(c, n, flags) if x == nil: return nil + if x.typ.kind == tyFromExpr: + # depends on generic type + return x result = newNodeIT(nkDerefExpr, x.info, x.typ) result.add(x[0]) return @@ -1677,7 +1817,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil else: let s = if n[0].kind == nkSym: n[0].sym - elif n[0].kind in nkSymChoices: n[0][0].sym + elif n[0].kind in nkSymChoices + {nkOpenSym}: n[0][0].sym else: nil if s != nil: case s.kind @@ -1758,6 +1898,8 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = else: localError(c.config, n.info, errExprHasNoAddress) result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ)) + if n.typ.kind in {tyVar, tyLent}: + n.typ = n.typ.elementType result.add(n) proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = @@ -1884,7 +2026,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]=")) result.add(n[1]) if mode == noOverloadedSubscript: - bracketNotFoundError(c, result) + bracketNotFoundError(c, result, {}) return errorNode(c, n) else: result = semExprNoType(c, result) @@ -1929,16 +2071,16 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ): var rhsTyp = rhs.typ if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass: - rhsTyp = rhsTyp.lastSon + rhsTyp = rhsTyp.last if lhs.sym.typ.kind == tyAnything: - rhsTyp = rhsTyp.skipIntLit(c.idgen) + rhsTyp = rhsTyp.skipTypes({tySink}).skipIntLit(c.idgen) if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}: internalAssert c.config, c.p.resultSym != nil # Make sure the type is valid for the result variable typeAllowedCheck(c, n.info, rhsTyp, skResult) lhs.typ = rhsTyp c.p.resultSym.typ = rhsTyp - c.p.owner.typ[0] = rhsTyp + c.p.owner.typ.setReturnType rhsTyp else: typeMismatch(c.config, n.info, lhs.typ, rhsTyp, rhs) borrowCheck(c, n, lhs, rhs) @@ -2004,12 +2146,12 @@ proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode = if isEmptyType(result.typ): # we inferred a 'void' return type: c.p.resultSym.typ = errorType(c) - c.p.owner.typ[0] = nil + c.p.owner.typ.setReturnType nil else: localError(c.config, c.p.resultSym.info, errCannotInferReturnType % c.p.owner.name.s) - if isIterator(c.p.owner.typ) and c.p.owner.typ[0] != nil and - c.p.owner.typ[0].kind == tyAnything: + if isIterator(c.p.owner.typ) and c.p.owner.typ.returnType != nil and + c.p.owner.typ.returnType.kind == tyAnything: localError(c.config, c.p.owner.info, errCannotInferReturnType % c.p.owner.name.s) closeScope(c) @@ -2049,12 +2191,10 @@ proc semYield(c: PContext, n: PNode): PNode = if c.p.owner == nil or c.p.owner.kind != skIterator: localError(c.config, n.info, errYieldNotAllowedHere) elif n[0].kind != nkEmpty: - n[0] = semExprWithType(c, n[0]) # check for type compatibility: var iterType = c.p.owner.typ let restype = iterType[0] + n[0] = semExprWithType(c, n[0], {}, restype) # check for type compatibility: if restype != nil: - if restype.kind != tyUntyped: - n[0] = fitNode(c, restype, n[0], n.info) if n[0].typ == nil: internalError(c.config, n.info, "semYield") if resultTypeIsInferrable(restype): @@ -2062,11 +2202,13 @@ proc semYield(c: PContext, n: PNode): PNode = iterType[0] = inferred if c.p.resultSym != nil: c.p.resultSym.typ = inferred + else: + n[0] = fitNode(c, restype, n[0], n.info) semYieldVarResult(c, n, restype) else: localError(c.config, n.info, errCannotReturnExpr) - elif c.p.owner.typ[0] != nil: + elif c.p.owner.typ.returnType != nil: localError(c.config, n.info, errGenerated, "yield statement must yield a value") proc considerQuotedIdentOrDot(c: PContext, n: PNode, origin: PNode = nil): PIdent = @@ -2113,6 +2255,8 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = result = n.sym of nkOpenSymChoice, nkClosedSymChoice: result = n[0].sym + of nkOpenSym: + result = lookUpForDeclared(c, n[0], onlyCurrentScope) else: localError(c.config, n.info, "identifier expected, but got: " & renderTree(n)) result = nil @@ -2173,7 +2317,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = let headSymbol = macroCall[0] var cands = 0 var cand: PSym = nil - var o: TOverloadIter + var o: TOverloadIter = default(TOverloadIter) var symx = initOverloadIter(o, c, headSymbol) while symx != nil: if symx.kind in {skTemplate, skMacro} and symx.typ.len == macroCall.len: @@ -2393,14 +2537,14 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType; let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar") if sym == nil: localError(c.config, info, "system needs: nimCreateFlowVar") - var bindings: TIdTable = initIdTable() + var bindings = initTypeMapping() bindings.idTablePut(sym.ast[genericParamsPos][0].typ, t) result = c.semGenerateInstance(c, sym, bindings, info) # since it's an instantiation, we unmark it as a compilerproc. Otherwise # codegen would fail: if sfCompilerProc in result.flags: result.flags.excl {sfCompilerProc, sfExportc, sfImportc} - result.loc.r = "" + result.loc.snippet = "" proc setMs(n: PNode, s: PSym): PNode = result = n @@ -2423,9 +2567,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P of mAddr: markUsed(c, n.info, s) checkSonsLen(n, 2, c.config) - result[0] = newSymNode(s, n[0].info) - result[1] = semAddrArg(c, n[1]) - result.typ = makePtrType(c, result[1].typ) + result = semAddr(c, n[1]) of mTypeOf: markUsed(c, n.info, s) result = semTypeOf(c, n) @@ -2552,9 +2694,10 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = # If semCheck is set to false, ``when`` will return the verbatim AST of # the correct branch. Otherwise the AST will be passed through semStmt. result = nil + let flags = if semCheck: {efWantStmt} else: {} template setResult(e: untyped) = - if semCheck: result = semExpr(c, e) # do not open a new scope! + if semCheck: result = semExpr(c, e, flags) # do not open a new scope! else: result = e # Check if the node is "when nimvm" @@ -2566,13 +2709,16 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = var typ = commonTypeBegin if n.len in 1..2 and n[0].kind == nkElifBranch and ( n.len == 1 or n[1].kind == nkElse): - let exprNode = n[0][0] + var exprNode = n[0][0] + if exprNode.kind == nkOpenSym: + exprNode = exprNode[0] if exprNode.kind == nkIdent: whenNimvm = lookUp(c, exprNode).magic == mNimvm elif exprNode.kind == nkSym: whenNimvm = exprNode.sym.magic == mNimvm if whenNimvm: n.flags.incl nfLL + var cannotResolve = false for i in 0..<n.len: var it = n[i] case it.kind @@ -2580,9 +2726,22 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = checkSonsLen(it, 2, c.config) if whenNimvm: if semCheck: - it[1] = semExpr(c, it[1]) + it[1] = semExpr(c, it[1], flags) typ = commonType(c, typ, it[1].typ) result = n # when nimvm is not elimited until codegen + elif c.inGenericContext > 0: + let e = semExprWithType(c, it[0]) + if e.typ.kind == tyFromExpr: + it[0] = makeStaticExpr(c, e) + cannotResolve = true + else: + it[0] = forceBool(c, e) + let val = getConstExpr(c.module, it[0], c.idgen, c.graph) + if val == nil or val.kind != nkIntLit: + cannotResolve = true + elif not cannotResolve and val.intVal != 0 and result == nil: + setResult(it[1]) + return # we're not in nimvm and we already have a result else: let e = forceBool(c, semConstExpr(c, it[0])) if e.kind != nkIntLit: @@ -2594,13 +2753,21 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = return # we're not in nimvm and we already have a result of nkElse, nkElseExpr: checkSonsLen(it, 1, c.config) - if result == nil or whenNimvm: + if cannotResolve: + discard + elif result == nil or whenNimvm: if semCheck: - it[0] = semExpr(c, it[0]) + it[0] = semExpr(c, it[0], flags) typ = commonType(c, typ, it[0].typ) + if typ != nil and typ.kind != tyUntyped: + it[0] = fitNode(c, typ, it[0], it[0].info) if result == nil: result = it[0] else: illFormedAst(n, c.config) + if cannotResolve: + result = semGenericStmt(c, n) + result.typ = makeTypeFromExpr(c, result.copyTree) + return if result == nil: result = newNodeI(nkEmpty, n.info) if whenNimvm: @@ -2742,13 +2909,19 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType # can check if field name matches expected type here let expectedElemType = if expected != nil: expected[i] else: nil n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType) + if expectedElemType != nil and + (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)): + # hasEmpty/nil check is to not break existing code like + # `const foo = [(1, {}), (2, {false})]`, + # `const foo = if true: (0, nil) else: (1, new(int))` + n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info) if n[i][1].typ.kind == tyTypeDesc: localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.") n[i][1].typ = errorType(c) var f = newSymS(skField, n[i][0], c) - f.typ = skipIntLit(n[i][1].typ, c.idgen) + f.typ = skipIntLit(n[i][1].typ.skipTypes({tySink}), c.idgen) f.position = i rawAddSon(typ, f.typ) typ.n.add newSymNode(f) @@ -2768,7 +2941,13 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT for i in 0..<n.len: let expectedElemType = if expected != nil: expected[i] else: nil n[i] = semExprWithType(c, n[i], {}, expectedElemType) - addSonSkipIntLit(typ, n[i].typ, c.idgen) + if expectedElemType != nil and + (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)): + # hasEmpty/nil check is to not break existing code like + # `const foo = [(1, {}), (2, {false})]`, + # `const foo = if true: (0, nil) else: (1, new(int))` + n[i] = fitNode(c, expectedElemType, n[i], n[i].info) + addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen) result.typ = typ include semobjconstr @@ -2823,7 +3002,7 @@ proc semExport(c: PContext, n: PNode): PNode = result = newNodeI(nkExportStmt, n.info) for i in 0..<n.len: let a = n[i] - var o: TOverloadIter + var o: TOverloadIter = default(TOverloadIter) var s = initOverloadIter(o, c, a) if s == nil: localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a)) @@ -2873,19 +3052,42 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp else: result = tupexp -proc shouldBeBracketExpr(n: PNode): bool = - result = false +proc isExplicitGenericCall(c: PContext, n: PNode): bool = + ## checks if a call node `n` is a routine call with explicit generic params + ## + ## the callee node needs to be either an nkBracketExpr or a call to a + ## symchoice of `[]` in which case it will be transformed into nkBracketExpr + ## + ## the LHS of the bracket expr has to either be a symchoice or resolve to + ## a routine symbol + template checkCallee(n: PNode) = + # check subscript LHS, `n` must be mutable + if isSymChoice(n): + result = true + else: + let s = qualifiedLookUp(c, n, {}) + if s != nil and s.kind in routineKinds: + result = true + n = semSymGenericInstantiation(c, n, s) assert n.kind in nkCallKinds + result = false let a = n[0] - if a.kind in nkCallKinds: + case a.kind + of nkBracketExpr: + checkCallee(a[0]) + of nkCallKinds: let b = a[0] if b.kind in nkSymChoices: - for i in 0..<b.len: - if b[i].kind == nkSym and b[i].sym.magic == mArrGet: - let be = newNodeI(nkBracketExpr, n.info) + let name = b.getPIdent + if name != nil and name.s == "[]": + checkCallee(a[1]) + if result: + # transform callee into normal bracket expr, only on success + let be = newNodeI(nkBracketExpr, a.info) for i in 1..<a.len: be.add(a[i]) n[0] = be - return true + else: + result = false proc asBracketExpr(c: PContext; n: PNode): PNode = proc isGeneric(c: PContext; n: PNode): bool = @@ -2906,6 +3108,18 @@ proc asBracketExpr(c: PContext; n: PNode): PNode = return result return nil +proc isOpenArraySym(x: PNode): bool = + var x = x + while true: + case x.kind + of {nkAddr, nkHiddenAddr}: + x = x[0] + of {nkHiddenStdConv, nkHiddenDeref}: + x = x[1] + else: + break + result = x.kind == nkSym + proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) = # This takes care of complicated signatures such as: # proc foo(a: int, b = a) @@ -2923,10 +3137,17 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) # duty is activated by returning a non-nil value. The caller is responsible # for replacing the input to the function with the returned non-nil value. # (which is the hoisted symbol) - if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym: + if defExpr.kind == nkSym and defExpr.sym.kind == skParam and + (defExpr.sym.owner == call[0].sym or + # symbol was resolved before proc was instantiated: + (sfFromGeneric in call[0].sym.flags and + defExpr.sym.owner == call[0].sym.instantiatedFrom)): let paramPos = defExpr.sym.position + 1 - if call[paramPos].skipAddr.kind != nkSym: + if call[paramPos].skipAddr.kind != nkSym and not ( + skipTypes(call[paramPos].typ, abstractVar).kind in {tyOpenArray, tyVarargs} and + isOpenArraySym(call[paramPos]) + ): let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen, c.p.owner, letSection.info, c.p.owner.options) hoistedVarSym.typ = call[paramPos].typ @@ -2952,8 +3173,8 @@ proc getNilType(c: PContext): PType = result.align = c.config.target.ptrSize.int16 c.nilTypeCache = result -proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = - var o: TOverloadIter +proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode = + var o: TOverloadIter = default(TOverloadIter) var i = 0 var a = initOverloadIter(o, c, n) while a != nil: @@ -2965,7 +3186,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = if i <= 1: if sfGenSym notin s.flags: result = newSymNode(s, info) - markUsed(c, info, s) + markUsed(c, info, s, efInCall notin flags) onUse(info, s) else: result = n @@ -2986,6 +3207,55 @@ proc semPragmaStmt(c: PContext; n: PNode) = else: pragma(c, c.p.owner, n, stmtPragmas, true) +proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode, + flags: TExprFlags, expectedType: PType): PSym = + # result is nil on error or if a node that can't produce a sym is resolved + let ident = considerQuotedIdent(c, n) + var filter = {low(TSymKind)..high(TSymKind)} + if efNoEvaluateGeneric in flags or expectedType != nil: + # `a[...]` where `a` is a module or package is not possible + filter.excl {skModule, skPackage} + let includePureEnum = expectedType != nil and + expectedType.skipTypes(abstractRange-{tyDistinct}).kind == tyEnum + let candidates = lookUpCandidates(c, ident, filter, + includePureEnum = includePureEnum) + if candidates.len == 0: + result = errorUndeclaredIdentifierHint(c, ident, n.info) + elif candidates.len == 1 or {efNoEvaluateGeneric, efInCall} * flags != {}: + # unambiguous, or we don't care about ambiguity + result = candidates[0] + else: + # ambiguous symbols have 1 last chance as a symchoice + var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c)) + for cand in candidates: + case cand.kind + of skModule, skPackage: + discard + of skType: + choice.add newSymNodeTypeDesc(cand, c.idgen, n.info) + else: + choice.add newSymNode(cand, n.info) + if choice.len == 0: + # we know candidates.len > 1, we just couldn't put any in a symchoice + errorUseQualifier(c, n.info, candidates) + return nil + resolveSymChoice(c, choice, flags, expectedType) + # choice.len == 1 can be true here but as long as it's a symchoice + # it's still not resolved + if isSymChoice(choice): + result = nil + if efAllowSymChoice in flags: + resultNode = choice + else: + errorUseQualifier(c, n.info, candidates) + else: + if choice.kind == nkSym: + result = choice.sym + else: + # resolution could have generated nkHiddenStdConv etc + resultNode = semExpr(c, choice, flags, expectedType) + result = nil + proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind @@ -3023,25 +3293,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType if nfSem in n.flags: return case n.kind of nkIdent, nkAccQuoted: - var s: PSym = nil - if expectedType != nil and ( - let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); - expected.kind == tyEnum): - let nameId = considerQuotedIdent(c, n).id - for f in expected.n: - if f.kind == nkSym and f.sym.name.id == nameId: - s = f.sym - break + let s = resolveIdentToSym(c, n, result, flags, expectedType) if s == nil: - let checks = if efNoEvaluateGeneric in flags: - {checkUndeclared, checkPureEnumFields} - elif efInCall in flags: - {checkUndeclared, checkModule, checkPureEnumFields} - else: - {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} - s = qualifiedLookUp(c, n, checks) - if s == nil: - return + # resolveIdentToSym either errored or gave a result node + return if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) case s.kind of skProc, skFunc, skMethod, skConverter, skIterator: @@ -3055,23 +3310,35 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) of skEnumField: - result = enumFieldSymChoice(c, n, s) + result = enumFieldSymChoice(c, n, s, flags) else: result = semSym(c, n, s, flags) if isSymChoice(result): result = semSymChoice(c, result, flags, expectedType) of nkClosedSymChoice, nkOpenSymChoice: - result = semSymChoice(c, result, flags, expectedType) + result = semSymChoice(c, n, flags, expectedType) of nkSym: + let s = n.sym + if nfDisabledOpenSym in n.flags: + let override = genericsOpenSym in c.features + let res = semOpenSym(c, n, flags, expectedType, + warnDisabled = not override) + if res != nil: + assert override + return res # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! - result = semSym(c, n, n.sym, flags) + result = semSym(c, n, s, flags) + of nkOpenSym: + assert n.len == 1 + let inner = n[0] + result = semOpenSym(c, inner, flags, expectedType) of nkEmpty, nkNone, nkCommentStmt, nkType: discard of nkNilLit: if result.typ == nil: result.typ = getNilType(c) - if expectedType != nil: + if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}: var m = newCandidate(c, result.typ) if typeRel(m, expectedType, result.typ) >= isSubtype: result.typ = expectedType @@ -3083,7 +3350,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType expected.kind in {tyInt..tyInt64, tyUInt..tyUInt64, tyFloat..tyFloat128}): - result.typ = expected if expected.kind in {tyFloat..tyFloat128}: n.transitionIntToFloatKind(nkFloatLit) changeType(c, result, expectedType, check=true) @@ -3132,7 +3398,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType let modifier = n.modifierTypeKindOfNode if modifier != tyNone: var baseType = semExpr(c, n[0]).typ.skipTypes({tyTypeDesc}) - result.typ = c.makeTypeDesc(c.newTypeWithSons(modifier, @[baseType])) + result.typ = c.makeTypeDesc(newTypeS(modifier, c, baseType)) return var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) result.typ = makeTypeDesc(c, typ) @@ -3154,12 +3420,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType of skType: # XXX think about this more (``set`` procs) let ambig = c.isAmbiguous - if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2: + if not (n[0].kind in nkSymChoices + {nkIdent, nkDotExpr} and ambig) and n.len == 2: result = semConv(c, n, flags, expectedType) - elif ambig and n.len == 1: - errorUseQualifier(c, n.info, s) elif n.len == 1: - result = semObjConstr(c, n, flags, expectedType) + if ambig: + errorUseQualifier(c, n.info, s) + else: + result = semObjConstr(c, n, flags, expectedType) elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType) else: result = semMagic(c, n, s, flags, expectedType) of skProc, skFunc, skMethod, skConverter, skIterator: @@ -3168,11 +3435,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType else: #liMessage(n.info, warnUser, renderTree(n)); result = semIndirectOp(c, n, flags, expectedType) - elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and - isSymChoice(n[0][0]): - # indirectOp can deal with explicit instantiations; the fixes - # the 'newSeq[T](x)' bug - setGenericParams(c, n[0]) + elif isExplicitGenericCall(c, n): # this modifies `n` if true result = semDirectOp(c, n, flags, expectedType) elif nfDotField in n.flags: result = semDirectOp(c, n, flags, expectedType) @@ -3237,12 +3500,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType result = semArrayConstr(c, n, flags, expectedType) of nkObjConstr: result = semObjConstr(c, n, flags, expectedType) of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags) - of nkDerefExpr: result = semDeref(c, n) + of nkDerefExpr: result = semDeref(c, n, flags) of nkAddr: result = n checkSonsLen(n, 1, c.config) - result[0] = semAddrArg(c, n[0]) - result.typ = makePtrType(c, result[0].typ) + result = semAddr(c, n[0]) of nkHiddenAddr, nkHiddenDeref: checkSonsLen(n, 1, c.config) n[0] = semExpr(c, n[0], flags, expectedType) diff --git a/compiler/semfields.nim b/compiler/semfields.nim index 5f3172f81..874055cdc 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -64,10 +64,12 @@ type proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = case typ.kind of nkSym: - var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid - fc.c = c.c - fc.field = typ.sym - fc.replaceByFieldName = c.m == mFieldPairs + # either 'tup[i]' or 'field' is valid + var fc = TFieldInstCtx( + c: c.c, + field: typ.sym, + replaceByFieldName: c.m == mFieldPairs + ) openScope(c.c) inc c.c.inUnrolledContext let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop) @@ -139,25 +141,24 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = var loopBody = n[^1] for i in 0..<tupleTypeA.len: openScope(c) - var fc: TFieldInstCtx - fc.tupleType = tupleTypeA - fc.tupleIndex = i - fc.c = c - fc.replaceByFieldName = m == mFieldPairs + var fc = TFieldInstCtx( + tupleType: tupleTypeA, + tupleIndex: i, + c: c, + replaceByFieldName: m == mFieldPairs + ) var body = instFieldLoopBody(fc, loopBody, n) inc c.inUnrolledContext stmts.add(semStmt(c, body, {})) dec c.inUnrolledContext closeScope(c) else: - var fc: TFieldsCtx - fc.m = m - fc.c = c + var fc = TFieldsCtx(m: m, c: c) var t = tupleTypeA while t.kind == tyObject: semForObjectFields(fc, t.n, n, stmts) - if t[0] == nil: break - t = skipTypes(t[0], skipPtrs) + if t.baseClass == nil: break + t = skipTypes(t.baseClass, skipPtrs) c.p.breakInLoop = oldBreakInLoop dec(c.p.nestedLoopCounter) # for TR macros this 'while true: ...; break' loop is pretty bad, so diff --git a/compiler/semfold.nim b/compiler/semfold.nim index faa609584..80144ccc0 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -122,10 +122,10 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string = result = $x proc isFloatRange(t: PType): bool {.inline.} = - result = t.kind == tyRange and t[0].kind in {tyFloat..tyFloat128} + result = t.kind == tyRange and t.elementType.kind in {tyFloat..tyFloat128} proc isIntRange(t: PType): bool {.inline.} = - result = t.kind == tyRange and t[0].kind in { + result = t.kind == tyRange and t.elementType.kind in { tyInt..tyInt64, tyUInt8..tyUInt32} proc pickIntRange(a, b: PType): PType = @@ -307,11 +307,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P of mRepr: # BUGFIX: we cannot eval mRepr here for reasons that I forgot. discard - of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n, g) of mBoolToStr: if getOrdValue(a) == 0: result = newStrNodeT("false", n, g) else: result = newStrNodeT("true", n, g) - of mFloatToStr: result = newStrNodeT($getFloat(a), n, g) of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n, g) of mStrToStr: result = newStrNodeT(getStrOrChar(a), n, g) @@ -422,14 +420,17 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g) of tyChar, tyUInt..tyUInt64, tyInt..tyInt64: var val = a.getOrdValue - if check: rangeCheck(n, val, g) - result = newIntNodeT(val, n, idgen, g) if dstTyp.kind in {tyUInt..tyUInt64}: + result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g) result.transitionIntKind(nkUIntLit) + else: + if check: rangeCheck(n, val, g) + result = newIntNodeT(val, n, idgen, g) else: result = a result.typ = n.typ - if check and result.kind in {nkCharLit..nkUInt64Lit}: + if check and result.kind in {nkCharLit..nkUInt64Lit} and + dstTyp.kind notin {tyUInt..tyUInt64}: rangeCheck(n, getInt(result), g) of tyFloat..tyFloat64: case srcTyp.kind @@ -703,10 +704,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode except DivByZeroDefect: localError(g.config, n.info, "division by zero") of nkAddr: - var a = getConstExpr(m, n[0], idgen, g) - if a != nil: - result = n - n[0] = a + result = nil # don't fold paths containing nkAddr of nkBracket, nkCurly: result = copyNode(n) for son in n.items: @@ -752,6 +750,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode if leValueConv(n[1], a) and leValueConv(a, n[2]): result = a # a <= x and x <= b result.typ = n.typ + elif n.typ.kind in {tyUInt..tyUInt64}: + discard "don't check uints" else: localError(g.config, n.info, "conversion from $1 to $2 is invalid" % @@ -773,7 +773,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of nkCast: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return - if n.typ != nil and n.typ.kind in NilableTypes: + if n.typ != nil and n.typ.kind in NilableTypes and + not (n.typ.kind == tyProc and a.typ.kind == tyProc): # we allow compile-time 'cast' for pointer types: result = a result.typ = n.typ diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index eebf11c0a..2639aba6c 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -56,8 +56,12 @@ template isMixedIn(sym): bool = s.magic == mNone and s.kind in OverloadableSyms) +template canOpenSym(s): bool = + {withinMixin, withinConcept} * flags == {withinMixin} and s.id notin ctx.toBind + proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, ctx: var GenericCtx; flags: TSemGenericFlags, + isAmbiguous: bool, fromDotExpr=false): PNode = result = nil semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody) @@ -69,6 +73,15 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result.transitionSonsKind(nkClosedSymChoice) else: result = symChoice(c, n, s, scOpen) + if canOpenSym(s): + if openSym in c.features: + if result.kind == nkSym: + result = newOpenSym(result) + else: + result.typ = nil + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil case s.kind of skUnknown: # Introduced in this pass! Leave it as an identifier. @@ -92,10 +105,28 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, if s.typ != nil and s.typ.kind == tyStatic: if s.typ.n != nil: result = s.typ.n + elif c.inGenericContext > 0 and withinConcept notin flags: + # don't leave generic param as identifier node in generic type, + # sigmatch will try to instantiate generic type AST without all params + # fine to give a symbol node a generic type here since + # we are in a generic context and `prepareNode` will be called + result = newSymNodeTypeDesc(s, c.idgen, n.info) + if canOpenSym(result.sym): + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil else: result = n else: result = newSymNodeTypeDesc(s, c.idgen, n.info) + if canOpenSym(result.sym): + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil onUse(n.info, s) of skParam: result = n @@ -103,12 +134,41 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skType: if (s.typ != nil) and (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}): + if isAmbiguous: + # ambiguous types should be symchoices since lookup behaves + # differently for them in regular expressions + maybeDotChoice(c, n, s, fromDotExpr) + return result = newSymNodeTypeDesc(s, c.idgen, n.info) + if canOpenSym(result.sym): + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil + elif c.inGenericContext > 0 and withinConcept notin flags: + # don't leave generic param as identifier node in generic type, + # sigmatch will try to instantiate generic type AST without all params + # fine to give a symbol node a generic type here since + # we are in a generic context and `prepareNode` will be called + result = newSymNodeTypeDesc(s, c.idgen, n.info) + if canOpenSym(result.sym): + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil else: result = n onUse(n.info, s) else: result = newSymNode(s, n.info) + if canOpenSym(result.sym): + if openSym in c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil onUse(n.info, s) proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, @@ -130,7 +190,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, elif s.isMixedIn: result = symChoice(c, n, s, scForceOpen) else: - result = semGenericStmtSymbol(c, n, s, ctx, flags) + result = semGenericStmtSymbol(c, n, s, ctx, flags, amb) # else: leave as nkIdent proc newDot(n, b: PNode): PNode = @@ -146,9 +206,11 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule} + c.isAmbiguous = false var s = qualifiedLookUp(c, n, luf) if s != nil: - result = semGenericStmtSymbol(c, n, s, ctx, flags) + isMacro = s.kind in {skTemplate, skMacro} + result = semGenericStmtSymbol(c, n, s, ctx, flags, c.isAmbiguous) else: n[0] = semGenericStmt(c, n[0], flags, ctx) result = n @@ -162,24 +224,21 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, isMacro = s.kind in {skTemplate, skMacro} if withinBind in flags or s.id in ctx.toBind: if s.kind == skType: # don't put types in sym choice - result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)) + var ambig = false + if candidates.len > 1: + let s2 = searchInScopes(c, ident, ambig) + result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags, + isAmbiguous = ambig, fromDotExpr = true)) else: result = newDot(result, symChoice(c, n, s, scClosed)) elif s.isMixedIn: result = newDot(result, symChoice(c, n, s, scForceOpen)) else: + var ambig = false if s.kind == skType and candidates.len > 1: - var ambig = false - let s2 = searchInScopes(c, ident, ambig) - if ambig: - # this is a type conversion like a.T where T is ambiguous with - # other types or routines - # in regular code, this never considers a type conversion and - # skips to routine overloading - # so symchoices are used which behave similarly with type symbols - result = newDot(result, symChoice(c, n, s, scForceOpen)) - return - let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true) + discard searchInScopes(c, ident, ambig) + let syms = semGenericStmtSymbol(c, n, s, ctx, flags, + isAmbiguous = ambig, fromDotExpr = true) result = newDot(result, syms) proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = @@ -221,7 +280,7 @@ proc semGenericStmt(c: PContext, n: PNode, #var s = qualifiedLookUp(c, n, luf) #if s != nil: result = semGenericStmtSymbol(c, n, s) # XXX for example: ``result.add`` -- ``add`` needs to be looked up here... - var dummy: bool + var dummy: bool = false result = fuzzyLookup(c, n, flags, ctx, dummy) of nkSym: let a = n.sym @@ -246,7 +305,9 @@ proc semGenericStmt(c: PContext, n: PNode, # check if it is an expression macro: checkMinSonsLen(n, 1, c.config) let fn = n[0] + c.isAmbiguous = false var s = qualifiedLookUp(c, fn, {}) + let ambig = c.isAmbiguous if s == nil and {withinMixin, withinConcept}*flags == {} and fn.kind in {nkIdent, nkAccQuoted} and @@ -299,7 +360,12 @@ proc semGenericStmt(c: PContext, n: PNode, of skType: # bad hack for generics: if (s.typ != nil) and (s.typ.kind != tyGenericParam): - result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info) + if ambig: + # ambiguous types should be symchoices since lookup behaves + # differently for them in regular expressions + result[0] = sc + else: + result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info) onUse(fn.info, s) first = 1 else: @@ -547,7 +613,8 @@ proc semGenericStmt(c: PContext, n: PNode, else: body = getBody(c.graph, s) else: body = n[bodyPos] - n[bodyPos] = semGenericStmtScope(c, body, flags, ctx) + let bodyFlags = if n.kind == nkTemplateDef: flags + {withinMixin} else: flags + n[bodyPos] = semGenericStmtScope(c, body, bodyFlags, ctx) closeScope(c) of nkPragma, nkPragmaExpr: discard of nkExprColonExpr, nkExprEqExpr: @@ -575,16 +642,18 @@ proc semGenericStmt(c: PContext, n: PNode, if withinTypeDesc in flags: dec c.inTypeContext proc semGenericStmt(c: PContext, n: PNode): PNode = - var ctx: GenericCtx - ctx.toMixin = initIntSet() - ctx.toBind = initIntSet() + var ctx = GenericCtx( + toMixin: initIntSet(), + toBind: initIntSet() + ) result = semGenericStmt(c, n, {}, ctx) semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody) proc semConceptBody(c: PContext, n: PNode): PNode = - var ctx: GenericCtx - ctx.toMixin = initIntSet() - ctx.toBind = initIntSet() + var ctx = GenericCtx( + toMixin: initIntSet(), + toBind: initIntSet() + ) result = semGenericStmt(c, n, {withinConcept}, ctx) semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 085769bdd..1bc6d31a2 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -34,7 +34,7 @@ proc pushProcCon*(c: PContext; owner: PSym) = const errCannotInstantiateX = "cannot instantiate: '$1'" -iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = +iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PSym = internalAssert c.config, n.kind == nkGenericParams for a in n.items: internalAssert c.config, a.kind == nkSym @@ -43,7 +43,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym let symKind = if q.typ.kind == tyStatic: skConst else: skType var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info) s.flags.incl {sfUsed, sfFromGeneric} - var t = PType(idTableGet(pt, q.typ)) + var t = idTableGet(pt, q.typ) if t == nil: if tfRetType in q.typ.flags: # keep the generic type and allow the return type to be bound @@ -53,7 +53,16 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym if q.typ.kind != tyCompositeTypeClass: localError(c.config, a.info, errCannotInstantiateX % s.name.s) t = errorType(c) - elif t.kind in {tyGenericParam, tyConcept}: + elif t.kind in {tyGenericParam, tyConcept, tyFromExpr}: + localError(c.config, a.info, errCannotInstantiateX % q.name.s) + t = errorType(c) + elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or + (q.typ.kind == tyGenericParam and + q.typ.genericParamHasConstraints and + q.typ.genericConstraint.kind == tyStatic)) and + c.inGenericContext == 0 and c.matchedConcept == nil: + # generic/concept type bodies will try to instantiate static values but + # won't actually use them localError(c.config, a.info, errCannotInstantiateX % q.name.s) t = errorType(c) elif t.kind == tyGenericInvocation: @@ -86,7 +95,7 @@ when false: proc `$`(x: PSym): string = result = x.name.s & " " & " id " & $x.id -proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var TIdTable) = +proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var SymMapping) = # we need to create a fresh set of gensym'ed symbols: #if n.kind == nkSym and sfGenSym in n.sym.flags: # if n.sym.owner != orig: @@ -94,7 +103,7 @@ proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var TIdTable if n.kind == nkSym and sfGenSym in n.sym.flags: # and # (n.sym.owner == orig or n.sym.owner.kind in {skPackage}): let s = n.sym - var x = PSym(idTableGet(symMap, s)) + var x = idTableGet(symMap, s) if x != nil: n.sym = x elif s.owner == nil or s.owner.kind == skPackage: @@ -118,7 +127,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = inc c.inGenericInst # add it here, so that recursive generic procs are possible: var b = n[bodyPos] - var symMap: TIdTable = initIdTable() + var symMap = initSymMapping() if params != nil: for i in 1..<params.len: let param = params[i].sym @@ -133,7 +142,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = if result.kind == skMacro: sysTypeFromName(c.graph, n.info, "NimNode") elif not isInlineIterator(result.typ): - result.typ[0] + result.typ.returnType else: nil b = semProcBody(c, b, resultType) @@ -168,12 +177,12 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, allowMetaTypes = false): PType = internalAssert c.config, header.kind == tyGenericInvocation - var cl: TReplTypeVars = TReplTypeVars(symMap: initIdTable(), - localCache: initIdTable(), typeMap: LayeredIdTable(), + var cl: TReplTypeVars = TReplTypeVars(symMap: initSymMapping(), + localCache: initTypeMapping(), typeMap: LayeredIdTable(), info: info, c: c, allowMetaTypes: allowMetaTypes ) - cl.typeMap.topLayer = initIdTable() + cl.typeMap.topLayer = initTypeMapping() # We must add all generic params in scope, because the generic body # may include tyFromExpr nodes depending on these generic params. @@ -181,8 +190,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, # perhaps the code can be extracted in a shared function. openScope(c) let genericTyp = header.base - for i in 0..<genericTyp.len - 1: - let genParam = genericTyp[i] + for i, genParam in genericBodyParams(genericTyp): var param: PSym template paramSym(kind): untyped = @@ -212,7 +220,7 @@ proc referencesAnotherParam(n: PNode, p: PSym): bool = if referencesAnotherParam(n[i], p): return true return false -proc instantiateProcType(c: PContext, pt: TIdTable, +proc instantiateProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo) = # XXX: Instantiates a generic proc signature, while at the same # time adding the instantiated proc params into the current scope. @@ -234,20 +242,26 @@ proc instantiateProcType(c: PContext, pt: TIdTable, var result = instCopyType(cl, prc.typ) let originalParams = result.n result.n = originalParams.shallowCopy - for i in 1..<result.len: + for i, resulti in paramTypes(result): # twrong_field_caching requires these 'resetIdTable' calls: - if i > 1: + if i > FirstParamAt: resetIdTable(cl.symMap) resetIdTable(cl.localCache) # take a note of the original type. If't a free type or static parameter # we'll need to keep it unbound for the `fitNode` operation below... - var typeToFit = result[i] + var typeToFit = resulti - let needsStaticSkipping = result[i].kind == tyFromExpr - result[i] = replaceTypeVarsT(cl, result[i]) + let needsStaticSkipping = resulti.kind == tyFromExpr + let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags + if resulti.kind == tyFromExpr: + resulti.flags.incl tfNonConstExpr + result[i] = replaceTypeVarsT(cl, resulti) if needsStaticSkipping: result[i] = result[i].skipTypes({tyStatic}) + if needsTypeDescSkipping: + result[i] = result[i].skipTypes({tyTypeDesc}) + typeToFit = result[i] # ...otherwise, we use the instantiated type in `fitNode` if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and @@ -265,9 +279,10 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # call head symbol, because this leads to infinite recursion. if oldParam.ast != nil: var def = oldParam.ast.copyTree - if def.kind in nkCallKinds: - for i in 1..<def.len: - def[i] = replaceTypeVarsN(cl, def[i], 1) + if def.typ.kind == tyFromExpr: + def.typ.flags.incl tfNonConstExpr + if not isIntLit(def.typ): + def = prepareNode(cl, def) # allow symchoice since node will be fit later # although expectedType should cover it @@ -284,6 +299,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # the user calls an explicit instantiation of the proc (this is # the only way the default value might be inserted). param.ast = errorNode(c, def) + # we know the node is empty, we need the actual type for error message + param.ast.typ = def.typ else: param.ast = fitNodePostMatch(c, typeToFit, converted) param.typ = result[i] @@ -295,7 +312,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, resetIdTable(cl.symMap) resetIdTable(cl.localCache) cl.isReturnType = true - result[0] = replaceTypeVarsT(cl, result[0]) + result.setReturnType replaceTypeVarsT(cl, result.returnType) cl.isReturnType = false result.n[0] = originalParams[0].copyTree if result[0] != nil: @@ -307,6 +324,22 @@ proc instantiateProcType(c: PContext, pt: TIdTable, prc.typ = result popInfoContext(c.config) +proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType = + # instantiates only the type of a given proc symbol + # used by sigmatch for explicit generics + # wouldn't be needed if sigmatch could handle complex cases, + # examples are in texplicitgenerics + # might be buggy, see rest of generateInstance if problems occur + let fakeSym = copySym(prc, c.idgen) + incl(fakeSym.flags, sfFromGeneric) + fakeSym.instantiatedFrom = prc + openScope(c) + for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt): + addDecl(c, s) + instantiateProcType(c, pt, fakeSym, info) + closeScope(c) + result = fakeSym.typ + proc fillMixinScope(c: PContext) = var p = c.p while p != nil: @@ -315,8 +348,9 @@ proc fillMixinScope(c: PContext) = addSym(c.currentScope, n.sym) p = p.next -proc getLocalPassC(c: PContext, s: PSym): string = - if s.ast == nil or s.ast.len == 0: return "" +proc getLocalPassC(c: PContext, s: PSym): string = + when defined(nimsuggest): return "" + if s.ast == nil or s.ast.len == 0: return "" result = "" template extractPassc(p: PNode) = if p.kind == nkPragma and p[0][0].ident == c.cache.getIdent"localpassc": @@ -325,8 +359,8 @@ proc getLocalPassC(c: PContext, s: PSym): string = for n in s.ast: for p in n: extractPassc(p) - -proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, + +proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, info: TLineInfo): PSym = ## Generates a new instance of a generic procedure. ## The `pt` parameter is a type-unsafe mapping table used to link generic @@ -336,7 +370,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # generates an instantiated proc if c.instCounter > 50: globalError(c.config, info, "generic instantiation too nested") - inc(c.instCounter) + inc c.instCounter + defer: dec c.instCounter # careful! we copy the whole AST including the possibly nil body! var n = copyTree(fn.ast) # NOTE: for access of private fields within generics from a different module @@ -354,7 +389,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, let passc = getLocalPassC(c, producer) if passc != "": #pass the local compiler options to the consumer module too extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex)) - result.owner = c.module + result.owner = c.module else: result.owner = fn result.ast = n @@ -377,16 +412,20 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # generic[void](), generic[int]() # see ttypeor.nim test. var i = 0 - newSeq(entry.concreteTypes, fn.typ.len+gp.len-1) + newSeq(entry.concreteTypes, fn.typ.paramsLen+gp.len) + # let param instantiation know we are in a concept for unresolved statics: + c.matchedConcept = oldMatchedConcept for s in instantiateGenericParamList(c, gp, pt): addDecl(c, s) entry.concreteTypes[i] = s.typ inc i + c.matchedConcept = nil pushProcCon(c, result) instantiateProcType(c, pt, result, info) - for j in 1..<result.typ.len: - entry.concreteTypes[i] = result.typ[j] + for _, param in paramTypes(result.typ): + entry.concreteTypes[i] = param inc i + #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len if tfTriggersCompileTime in result.typ.flags: incl(result.flags, sfCompileTime) n[genericParamsPos] = c.graph.emptyNode @@ -415,7 +454,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, if result.magic notin {mSlice, mTypeOf}: # 'toOpenArray' is special and it is allowed to return 'openArray': paramsTypeCheck(c, result.typ) + #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len else: + #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len result = oldPrc popProcCon(c) popInfoContext(c.config) @@ -424,7 +465,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, popOwner(c) c.currentScope = oldScope discard c.friendModules.pop() - dec(c.instCounter) c.matchedConcept = oldMatchedConcept if result.kind == skMethod: finishMethod(c, result) diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index 12d7d32e0..727f36470 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -35,12 +35,12 @@ proc ithField(n: PNode, field: var int): PSym = else: discard proc ithField(t: PType, field: var int): PSym = - var base = t[0] + var base = t.baseClass while base != nil: let b = skipTypes(base, skipPtrs) result = ithField(b.n, field) if result != nil: return result - base = b[0] + base = b.baseClass result = ithField(t.n, field) proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = @@ -51,6 +51,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = of nkObjConstr: let x = t.skipTypes(abstractPtrs) n.typ = t + n[0].typ = t for i in 1..<n.len: var j = i-1 let field = x.ithField(j) @@ -63,7 +64,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = if x.kind == tyTuple: n.typ = t for i in 0..<n.len: - if i >= x.len: globalError conf, n.info, "invalid field at index " & $i + if i >= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i else: annotateType(n[i], x[i], conf) elif x.kind == tyProc and x.callConv == ccClosure: n.typ = t @@ -78,11 +79,11 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = of nkStrKinds: for i in left..right: bracketExpr.add newIntNode(nkCharLit, BiggestInt n[0].strVal[i]) - annotateType(bracketExpr[^1], t[0], conf) + annotateType(bracketExpr[^1], x.elementType, conf) of nkBracket: for i in left..right: bracketExpr.add n[0][i] - annotateType(bracketExpr[^1], t[0], conf) + annotateType(bracketExpr[^1], x.elementType, conf) else: globalError(conf, n.info, "Incorrectly generated tuple constr") n[] = bracketExpr[] diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 548b922fe..a12e933e7 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -22,7 +22,7 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode = var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0] while true: asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false) - let base = t[0] + let base = t.baseClass if base == nil: break t = skipTypes(base, skipPtrs) @@ -30,13 +30,15 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode = if asgnExpr.sons.len > 1: result = newTree(nkAsgn, result[1], asgnExpr) -proc semAddrArg(c: PContext; n: PNode): PNode = +proc semAddr(c: PContext; n: PNode): PNode = + result = newNodeI(nkAddr, n.info) let x = semExprWithType(c, n) if x.kind == nkSym: x.sym.flags.incl(sfAddrTaken) if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}: localError(c.config, n.info, errExprHasNoAddress) - result = x + result.add x + result.typ = makePtrType(c, x.typ) proc semTypeOf(c: PContext; n: PNode): PNode = var m = BiggestInt 1 # typeOfIter @@ -47,8 +49,12 @@ proc semTypeOf(c: PContext; n: PNode): PNode = else: m = mode.intVal result = newNodeI(nkTypeOfExpr, n.info) + inc c.inTypeofContext + defer: dec c.inTypeofContext # compiles can raise an exception let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {}) result.add typExpr + if typExpr.typ.kind == tyFromExpr: + typExpr.typ.flags.incl tfNonConstExpr result.typ = makeTypeDesc(c, typExpr.typ) type @@ -64,14 +70,23 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = if result.isNil: let x = copyTree(n) x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info) - bracketNotFoundError(c, x) + if c.inGenericContext > 0: + for i in 0..<n.len: + let a = n[i] + if a.typ != nil and a.typ.kind in {tyGenericParam, tyFromExpr}: + # expression is compiled early in a generic body + result = semGenericStmt(c, x) + result.typ = makeTypeFromExpr(c, copyTree(result)) + result.typ.flags.incl tfNonConstExpr + return + bracketNotFoundError(c, x, flags) #localError(c.config, n.info, "could not resolve: " & $n) result = errorNode(c, n) proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode = # rewrite `[]=`(a, i, x) back to ``a[i] = x``. let b = newNodeI(nkBracketExpr, n.info) - b.add(n[1].skipAddr) + b.add(n[1].skipHiddenAddr) for i in 2..<n.len-1: b.add(n[i]) result = newNodeI(nkAsgn, n.info, 2) result[0] = b @@ -132,7 +147,7 @@ proc uninstantiate(t: PType): PType = result = case t.kind of tyMagicGenerics: t of tyUserDefinedGenerics: t.base - of tyCompositeTypeClass: uninstantiate t[1] + of tyCompositeTypeClass: uninstantiate t.firstGenericParam else: t proc getTypeDescNode(c: PContext; typ: PType, sym: PSym, info: TLineInfo): PNode = @@ -140,6 +155,14 @@ proc getTypeDescNode(c: PContext; typ: PType, sym: PSym, info: TLineInfo): PNode rawAddSon(resType, typ) result = toNode(resType, info) +proc buildBinaryPredicate(kind: TTypeKind; c: PContext; context: PSym; a, b: sink PType): PType = + result = newType(kind, c.idgen, context) + result.rawAddSon a + result.rawAddSon b + +proc buildNotPredicate(c: PContext; context: PSym; a: sink PType): PType = + result = newType(tyNot, c.idgen, context, a) + proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym): PNode = const skippedTypes = {tyTypeDesc, tyAlias, tySink} let trait = traitCall[0] @@ -149,20 +172,17 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) template operand2: PType = traitCall[2].typ.skipTypes({tyTypeDesc}) - template typeWithSonsResult(kind, sons): PNode = - newTypeWithSons(context, kind, sons, c.idgen).toNode(traitCall.info) - if operand.kind == tyGenericParam or (traitCall.len > 2 and operand2.kind == tyGenericParam): return traitCall ## too early to evaluate let s = trait.sym.name.s case s of "or", "|": - return typeWithSonsResult(tyOr, @[operand, operand2]) + return buildBinaryPredicate(tyOr, c, context, operand, operand2).toNode(traitCall.info) of "and": - return typeWithSonsResult(tyAnd, @[operand, operand2]) + return buildBinaryPredicate(tyAnd, c, context, operand, operand2).toNode(traitCall.info) of "not": - return typeWithSonsResult(tyNot, @[operand]) + return buildNotPredicate(c, context, operand).toNode(traitCall.info) of "typeToString": var prefer = preferTypeName if traitCall.len >= 2: @@ -217,8 +237,9 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) of "rangeBase": # return the base type of a range type var arg = operand.skipTypes({tyGenericInst}) - assert arg.kind == tyRange - result = getTypeDescNode(c, arg.base, operand.owner, traitCall.info) + if arg.kind == tyRange: + arg = arg.base + result = getTypeDescNode(c, arg, operand.owner, traitCall.info) of "isCyclic": var operand = operand.skipTypes({tyGenericInst}) let isCyclic = canFormAcycle(c.graph, operand) @@ -230,7 +251,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) proc semTypeTraits(c: PContext, n: PNode): PNode = checkMinSonsLen(n, 2, c.config) let t = n[1].typ - internalAssert c.config, t != nil and t.kind == tyTypeDesc + internalAssert c.config, t != nil and t.skipTypes({tyAlias}).kind == tyTypeDesc if t.len > 0: # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) @@ -393,7 +414,7 @@ proc semUnown(c: PContext; n: PNode): PNode = for e in elems: result.rawAddSon(e) else: result = t - of tyOwned: result = t[0] + of tyOwned: result = t.elementType of tySequence, tyOpenArray, tyArray, tyVarargs, tyVar, tyLent, tyGenericInst, tyAlias: let b = unownedType(c, t[^1]) @@ -433,7 +454,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym result.info = info result.flags.incl sfFromGeneric result.owner = orig - let origParamType = orig.typ[1] + let origParamType = orig.typ.firstParamType let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen) let oldParam = orig.typ.n[1].sym let newParam = newSym(skParam, oldParam.name, c.idgen, result, result.info) @@ -497,7 +518,7 @@ proc semNewFinalize(c: PContext; n: PNode): PNode = localError(c.config, n.info, "finalizer must be a direct reference to a proc") # check if we converted this finalizer into a destructor already: - let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef})) + let t = whereToBindTypeHook(c, fin.typ.firstParamType.skipTypes(abstractInst+{tyRef})) if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and getAttachedOp(c.graph, t, attachedDestructor).owner == fin: discard "already turned this one into a finalizer" @@ -506,13 +527,13 @@ proc semNewFinalize(c: PContext; n: PNode): PNode = fin.owner = fin.instantiatedFrom let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info) let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen)) - selfSymNode.typ = fin.typ[1] + selfSymNode.typ = fin.typ.firstParamType wrapperSym.flags.incl sfUsed let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode), params = nkFormalParams.newTree(c.graph.emptyNode, newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType, - fin.ast[paramsPos][1][1].info, fin.typ[1]), c.graph.emptyNode) + fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode) ), name = newSymNode(wrapperSym), pattern = fin.ast[patternPos], genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {}) @@ -532,7 +553,7 @@ proc semNewFinalize(c: PContext; n: PNode): PNode = result = addDefaultFieldForNew(c, n) proc semPrivateAccess(c: PContext, n: PNode): PNode = - let t = n[1].typ[0].toObjectFromRefPtrGeneric + let t = n[1].typ.elementType.toObjectFromRefPtrGeneric if t.kind == tyObject: assert t.sym != nil c.currentScope.allowPrivateAccess.add t.sym @@ -556,9 +577,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, case n[0].sym.magic of mAddr: checkSonsLen(n, 2, c.config) - result = n - result[1] = semAddrArg(c, n[1]) - result.typ = makePtrType(c, result[1].typ) + result = semAddr(c, n[1]) of mTypeOf: result = semTypeOf(c, n) of mSizeOf: @@ -618,8 +637,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, let op = getAttachedOp(c.graph, t, attachedDestructor) if op != nil: result[0] = newSymNode(op) - - if op.typ != nil and op.typ.len == 2 and op.typ[1].kind != tyVar: + if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar: if n[1].kind == nkSym and n[1].sym.kind == skParam and n[1].typ.kind == tyVar: result[1] = genDeref(n[1]) @@ -631,6 +649,16 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, let op = getAttachedOp(c.graph, t, attachedTrace) if op != nil: result[0] = newSymNode(op) + of mDup: + result = n + let t = n[1].typ.skipTypes(abstractVar) + let op = getAttachedOp(c.graph, t, attachedDup) + if op != nil: + result[0] = newSymNode(op) + if op.typ.len == 3: + let boolLit = newIntLit(c.graph, n.info, 1) + boolLit.typ = getSysType(c.graph, n.info, tyBool) + result.add boolLit of mWasMoved: result = n let t = n[1].typ.skipTypes(abstractVar) @@ -669,7 +697,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result = semPrivateAccess(c, n) of mArrToSeq: result = n - if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and expectedType.kind == tySequence and result.typ[0].kind == tyEmpty: + if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and + expectedType.kind == tySequence and result.typ.elementType.kind == tyEmpty: result.typ = expectedType # type inference for empty sequence # bug #21377 of mEnsureMove: result = n diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 37c939bcd..048053115 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -387,10 +387,13 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, if e != nil: result.status = initFull elif field.ast != nil: - result.status = initUnknown - result.defaults.add newTree(nkExprColonExpr, n, field.ast) + if efIgnoreDefaults notin flags: + result.status = initUnknown + result.defaults.add newTree(nkExprColonExpr, n, field.ast) + else: + result.status = initNone else: - if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass + if {efWantNoDefaults, efIgnoreDefaults} * flags == {}: # cannot compute defaults at the typeRightPass let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault) if defaultExpr != nil: result.status = initUnknown @@ -405,7 +408,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, proc semConstructTypeAux(c: PContext, constrCtx: var ObjConstrContext, flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] = - result.status = initUnknown + result = (initUnknown, @[]) var t = constrCtx.typ while true: let (status, defaults) = semConstructFields(c, t.n, constrCtx, flags) @@ -413,10 +416,10 @@ proc semConstructTypeAux(c: PContext, result.defaults.add defaults if status in {initPartial, initNone, initUnknown}: discard collectMissingFields(c, t.n, constrCtx, result.defaults) - let base = t[0] + let base = t.baseClass if base == nil or base.id == t.id or - base.kind in {tyRef, tyPtr} and base[0].id == t.id: - break + base.kind in {tyRef, tyPtr} and base.elementType.id == t.id: + break t = skipTypes(base, skipPtrs) if t.kind != tyObject: # XXX: This is not supposed to happen, but apparently @@ -439,11 +442,11 @@ proc computeRequiresInit(c: PContext, t: PType): bool = proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = var objType = t while objType.kind notin {tyObject, tyDistinct}: - objType = objType.lastSon + objType = objType.last assert objType != nil if objType.kind == tyObject: var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) - let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults}) + let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults}) if constrCtx.missingFields.len > 0: localError(c.config, info, "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) @@ -470,7 +473,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned}) if t.kind == tyRef: - t = skipTypes(t[0], {tyGenericInst, tyAlias, tySink, tyOwned}) + t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned}) if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) # we have to watch out, there are also 'owned proc' types that can be used @@ -478,7 +481,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType result.typ.flags.incl tfHasOwned if t.kind != tyObject: return localErrorNode(c, result, if t.kind != tyGenericBody: - "object constructor needs an object type".dup(addDeclaredLoc(c.config, t)) + "object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t)) else: "cannot instantiate: '" & typeToString(t, preferDesc) & "'; the object's generic parameters cannot be inferred and must be explicitly given" diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index d5fc72760..23a8e6362 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -77,12 +77,12 @@ type graph: ModuleGraph proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx = - result.locals = @[] - result.slices = @[] - result.args = @[] + result = AnalysisCtx(locals: @[], + slices: @[], + args: @[], + graph: g) result.guards.s = @[] result.guards.g = g - result.graph = g proc lookupSlot(c: AnalysisCtx; s: PSym): int = for i in 0..<c.locals.len: @@ -406,7 +406,7 @@ proc transformSlices(g: ModuleGraph; idgen: IdGenerator; n: PNode): PNode = if op.name.s == "[]" and op.fromSystem: result = copyNode(n) var typ = newType(tyOpenArray, idgen, result.typ.owner) - typ.add result.typ[0] + typ.add result.typ.elementType result.typ = typ let opSlice = newSymNode(createMagic(g, idgen, "slice", mSlice)) opSlice.typ = getSysType(g, n.info, tyInt) @@ -441,7 +441,7 @@ proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: if result.isNil: result = newNodeI(nkStmtList, n.info) result.add n - let t = b[1][0].typ[0] + let t = b[1][0].typ.returnType if spawnResult(t, true) == srByVar: result.add wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, it[0]) it[^1] = newNodeI(nkEmpty, it.info) @@ -450,7 +450,7 @@ proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: if result.isNil: result = n of nkAsgn, nkFastAsgn, nkSinkAsgn: let b = n[1] - if getMagic(b) == mSpawn and (let t = b[1][0].typ[0]; + if getMagic(b) == mSpawn and (let t = b[1][0].typ.returnType; spawnResult(t, true) == srByVar): let m = transformSlices(g, idgen, b) return wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, n[0]) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index e010bf179..0a160897f 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -11,9 +11,9 @@ import ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg, options, guards, lineinfos, semfold, semdata, modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling, - semstrictfuncs + semstrictfuncs, suggestsymdb, pushpoppragmas -import std/[tables, intsets, strutils] +import std/[tables, intsets, strutils, sequtils] when defined(nimPreviewSlimSystem): import std/assertions @@ -24,9 +24,6 @@ when defined(useDfa): import liftdestructors include sinkparameter_inference - -import std/options as opt - #[ Second semantic checking pass over the AST. Necessary because the old way had some inherent problems. Performs: @@ -69,8 +66,12 @@ discard """ """ type + CaughtExceptionsStack = object + nodes: seq[seq[PType]] TEffects = object exc: PNode # stack of exceptions + when defined(nimsuggest): + caughtExceptions: CaughtExceptionsStack tags: PNode # list of tags forbids: PNode # list of tags bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt, currentBlock: int @@ -84,6 +85,7 @@ type isInnerProc: bool inEnforcedNoSideEffects: bool currOptions: TOptions + optionsStack: seq[(TOptions, TNoteKinds)] config: ConfigRef graph: ModuleGraph c: PContext @@ -94,29 +96,26 @@ const errXCannotBeAssignedTo = "'$1' cannot be assigned to" errLetNeedsInit = "'let' symbol requires an initialization" -proc getObjDepth(t: PType): Option[tuple[depth: int, root: ItemId]] = +proc getObjDepth(t: PType): (int, ItemId) = var x = t - var res: tuple[depth: int, root: ItemId] - res.depth = -1 + result = (-1, default(ItemId)) var stack = newSeq[ItemId]() while x != nil: x = skipTypes(x, skipPtrs) if x.kind != tyObject: - return none(tuple[depth: int, root: ItemId]) + return (-3, default(ItemId)) stack.add x.itemId - x = x[0] - inc(res.depth) - res.root = stack[^2] - result = some(res) + x = x.baseClass + inc(result[0]) + result[1] = stack[^2] proc collectObjectTree(graph: ModuleGraph, n: PNode) = for section in n: - if section.kind == nkTypeDef and section[^1].kind in {nkObjectTy, nkRefTy, nkPtrTy}: + if section.kind == nkTypeDef and section[^1].kind in {nkObjectTy, nkRefTy, nkPtrTy} and section[^1].typ != nil: let typ = section[^1].typ.skipTypes(skipPtrs) - if typ.len > 0 and typ[0] != nil: - let depthItem = getObjDepth(typ) - if isSome(depthItem): - let (depthLevel, root) = depthItem.unsafeGet + if typ.kind == tyObject and typ.baseClass != nil: + let (depthLevel, root) = getObjDepth(typ) + if depthLevel != -3: if depthLevel == 1: graph.objectTree[root] = @[] else: @@ -417,7 +416,7 @@ proc throws(tracked, n, orig: PNode) = else: tracked.add n -proc getEbase(g: ModuleGraph; info: TLineInfo): PType = +proc getEbase*(g: ModuleGraph; info: TLineInfo): PType = result = g.sysTypeFromName(info, "Exception") proc excType(g: ModuleGraph; n: PNode): PType = @@ -498,6 +497,18 @@ proc catchesAll(tracked: PEffects) = if tracked.exc.len > 0: setLen(tracked.exc.sons, tracked.bottom) +proc push(s: var CaughtExceptionsStack) = + s.nodes.add(@[]) + +proc pop(s: var CaughtExceptionsStack) = + s.nodes.del(high(s.nodes)) + +proc addCatch(s: var CaughtExceptionsStack, e: PType) = + s.nodes[high(s.nodes)].add(e) + +proc addCatchAll(s: var CaughtExceptionsStack) = + s.nodes[high(s.nodes)].add(nil) + proc track(tracked: PEffects, n: PNode) proc trackTryStmt(tracked: PEffects, n: PNode) = let oldBottom = tracked.bottom @@ -506,12 +517,33 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = let oldState = tracked.init.len var inter: TIntersection = @[] + when defined(nimsuggest): + tracked.caughtExceptions.push + for i in 1..<n.len: + let b = n[i] + if b.kind == nkExceptBranch: + if b.len == 1: + tracked.caughtExceptions.addCatchAll + else: + for j in 0..<b.len - 1: + if b[j].isInfixAs(): + assert(b[j][1].kind == nkType) + tracked.caughtExceptions.addCatch(b[j][1].typ) + else: + assert(b[j].kind == nkType) + tracked.caughtExceptions.addCatch(b[j].typ) + else: + assert b.kind == nkFinally + inc tracked.inTryStmt track(tracked, n[0]) dec tracked.inTryStmt for i in oldState..<tracked.init.len: addToIntersection(inter, tracked.init[i], bsNone) + when defined(nimsuggest): + tracked.caughtExceptions.pop + var branches = 1 var hasFinally = false inc tracked.inExceptOrFinallyStmt @@ -584,9 +616,16 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) = for i in 0..<n.len: var it = n[i] let pragma = whichPragma(it) - if pragma == wEffects: + case pragma + of wEffects: # list the computed effects up to here: listEffects(tracked) + of wPush: + processPushBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions, n, i+1) + of wPop: + processPopBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions) + else: + discard template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {} @@ -625,7 +664,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = if paramType != nil and tfNotNil in paramType.flags and n.typ != nil: let ntyp = n.typ.skipTypesOrNil({tyVar, tyLent, tySink}) if ntyp != nil and tfNotNil notin ntyp.flags: - if isAddrNode(n): + if n.kind in {nkAddr, nkHiddenAddr}: # addr(x[]) can't be proven, but addr(x) can: if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return elif (n.kind == nkSym and n.sym.kind in routineKinds) or @@ -666,7 +705,7 @@ proc isTrival(caller: PNode): bool {.inline.} = proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) = let a = skipConvCastAndClosure(n) let op = a.typ - let param = if formals != nil and argIndex < formals.len and formals.n != nil: formals.n[argIndex].sym else: nil + let param = if formals != nil and formals.n != nil and argIndex < formals.n.len: formals.n[argIndex].sym else: nil # assume indirect calls are taken here: if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and not isTrival(caller) and @@ -701,7 +740,7 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar markGcUnsafe(tracked, a) elif tfNoSideEffect notin op.flags: markSideEffect(tracked, a, n.info) - let paramType = if formals != nil and argIndex < formals.len: formals[argIndex] else: nil + let paramType = if formals != nil and argIndex < formals.signatureLen: formals[argIndex] else: nil if paramType != nil and paramType.kind in {tyVar}: invalidateFacts(tracked.guards, n) if n.kind == nkSym and isLocalSym(tracked, n.sym): @@ -757,7 +796,7 @@ proc addIdToIntersection(tracked: PEffects, inter: var TIntersection, resCounter template hasResultSym(s: PSym): bool = s != nil and s.kind in {skProc, skFunc, skConverter, skMethod} and - not isEmptyType(s.typ[0]) + not isEmptyType(s.typ.returnType) proc trackCase(tracked: PEffects, n: PNode) = track(tracked, n[0]) @@ -923,6 +962,19 @@ proc checkForSink(tracked: PEffects; n: PNode) = if tracked.inIfStmt == 0 and optSinkInference in tracked.config.options: checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n) +proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) = + when defined(nimsuggest): + proc internalMarkCaughtExceptions(tracked: PEffects; q: var SuggestFileSymbolDatabase; info: TLineInfo) = + var si = q.findSymInfoIndex(info) + if si != -1: + q.caughtExceptionsSet[si] = true + for w1 in tracked.caughtExceptions.nodes: + for w2 in w1: + q.caughtExceptions[si].add(w2) + + if optIdeExceptionInlayHints in tracked.config.globalOptions: + internalMarkCaughtExceptions(tracked, g.suggestSymbols.mgetOrPut(info.fileIndex, newSuggestFileSymbolDatabase(info.fileIndex, true)), info) + proc trackCall(tracked: PEffects; n: PNode) = template gcsafeAndSideeffectCheck() = if notGcSafe(op) and not importedFromC(a): @@ -943,6 +995,13 @@ proc trackCall(tracked: PEffects; n: PNode) = if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray: createTypeBoundOps(tracked, n.typ, n.info) + when defined(nimsuggest): + var actualLoc = a.info + if n.kind == nkHiddenCallConv: + actualLoc = n.info + if a.kind == nkSym: + markCaughtExceptions(tracked, tracked.graph, actualLoc, a.sym, tracked.graph.usageSym) + let notConstExpr = getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil if notConstExpr: if a.kind == nkCast and a[1].typ.kind == tyProc: @@ -985,7 +1044,7 @@ proc trackCall(tracked: PEffects; n: PNode) = # may not look like an assignment, but it is: let arg = n[1] initVarViaNew(tracked, arg) - if arg.typ.len != 0 and {tfRequiresInit} * arg.typ.lastSon.flags != {}: + if arg.typ.hasElementType and {tfRequiresInit} * arg.typ.elementType.flags != {}: if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and n[2].intVal == 0: # var s: seq[notnil]; newSeq(s, 0) is a special case! @@ -994,8 +1053,8 @@ proc trackCall(tracked: PEffects; n: PNode) = message(tracked.config, arg.info, warnProveInit, $arg) # check required for 'nim check': - if n[1].typ.len > 0: - createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) + if n[1].typ.hasElementType: + createTypeBoundOps(tracked, n[1].typ.elementType, n.info) createTypeBoundOps(tracked, n[1].typ, n.info) # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? @@ -1018,11 +1077,11 @@ proc trackCall(tracked: PEffects; n: PNode) = n[0].sym = op if op != nil and op.kind == tyProc: - for i in 1..<min(n.safeLen, op.len): + for i in 1..<min(n.safeLen, op.signatureLen): let paramType = op[i] case paramType.kind of tySink: - createTypeBoundOps(tracked, paramType[0], n.info) + createTypeBoundOps(tracked, paramType.elementType, n.info) checkForSink(tracked, n[i]) of tyVar: if isOutParam(paramType): @@ -1151,9 +1210,10 @@ proc track(tracked: PEffects, n: PNode) = if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags: tracked.owner.flags.incl sfInjectDestructors # bug #15038: ensure consistency - if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ + if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ = n.sym.typ of nkHiddenAddr, nkAddr: - if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym): + if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym) and + n.typ.kind notin {tyVar, tyLent}: useVarNoInitCheck(tracked, n[0], n[0].sym) else: track(tracked, n[0]) @@ -1174,7 +1234,10 @@ proc track(tracked: PEffects, n: PNode) = trackCall(tracked, n) of nkDotExpr: guardDotAccess(tracked, n) + let oldLeftPartOfAsgn = tracked.leftPartOfAsgn + tracked.leftPartOfAsgn = 0 for i in 0..<n.len: track(tracked, n[i]) + tracked.leftPartOfAsgn = oldLeftPartOfAsgn of nkCheckedFieldExpr: track(tracked, n[0]) if tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features: @@ -1322,7 +1385,7 @@ proc track(tracked: PEffects, n: PNode) = if tracked.owner.kind != skMacro: # XXX n.typ can be nil in runnableExamples, we need to do something about it. if n.typ != nil and n.typ.skipTypes(abstractInst).kind == tyRef: - createTypeBoundOps(tracked, n.typ.lastSon, n.info) + createTypeBoundOps(tracked, n.typ.elementType, n.info) createTypeBoundOps(tracked, n.typ, n.info) of nkTupleConstr: for i in 0..<n.len: @@ -1427,7 +1490,7 @@ proc track(tracked: PEffects, n: PNode) = proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool = if spec.typ.kind == tyOr: result = false - for t in spec.typ: + for t in spec.typ.kids: if safeInheritanceDiff(g.excType(real), t) <= 0: return true else: @@ -1539,7 +1602,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; c: PContext): TEffects result = TEffects(exc: effects[exceptionEffects], tags: effects[tagEffects], forbids: effects[forbiddenEffects], owner: s, ownerModule: s.getModule, init: @[], locked: @[], graph: g, config: g.config, c: c, - currentBlock: 1 + currentBlock: 1, optionsStack: @[(g.config.options, g.config.notes)] ) result.guards.s = @[] result.guards.g = g @@ -1571,7 +1634,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = var t: TEffects = initEffects(g, inferredEffects, s, c) rawInitEffects g, effects - if not isEmptyType(s.typ[0]) and + if not isEmptyType(s.typ.returnType) and s.kind in {skProc, skFunc, skConverter, skMethod}: var res = s.ast[resultPos].sym # get result symbol t.scopes[res.id] = t.currentBlock @@ -1590,13 +1653,13 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = if isOutParam(typ) and param.id notin t.init: message(g.config, param.info, warnProveInit, param.name.s) - if not isEmptyType(s.typ[0]) and - (s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar or + if not isEmptyType(s.typ.returnType) and + (s.typ.returnType.requiresInit or s.typ.returnType.skipTypes(abstractInst).kind == tyVar or strictDefs in c.features) and s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone: var res = s.ast[resultPos].sym # get result symbol if res.id notin t.init and breaksBlock(body) != bsNoReturn: - if tfRequiresInit in s.typ[0].flags: + if tfRequiresInit in s.typ.returnType.flags: localError(g.config, body.info, "'$1' requires explicit initialization" % "result") else: message(g.config, body.info, warnProveInit, "result") @@ -1670,7 +1733,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = dataflowAnalysis(s, body) when false: trackWrites(s, body) - if strictNotNil in c.features and s.kind == skProc: + if strictNotNil in c.features and s.kind in {skProc, skFunc, skMethod, skConverter}: checkNil(s, body, g.config, c.idgen) proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 70818bb67..f5f8fea0c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -42,10 +42,10 @@ proc implicitlyDiscardable(n: PNode): bool proc hasEmpty(typ: PType): bool = if typ.kind in {tySequence, tyArray, tySet}: - result = typ.lastSon.kind == tyEmpty + result = typ.elementType.kind == tyEmpty elif typ.kind == tyTuple: result = false - for s in typ: + for s in typ.kids: result = result or hasEmpty(s) else: result = false @@ -132,17 +132,141 @@ proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode closeScope(c) const - skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch, - nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch, + skipForDiscardable = {nkStmtList, nkStmtListExpr, + nkOfBranch, nkElse, nkFinally, nkExceptBranch, nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr, - nkHiddenStdConv, nkHiddenDeref} + nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref} proc implicitlyDiscardable(n: PNode): bool = - var n = n - while n.kind in skipForDiscardable: n = n.lastSon - result = n.kind in nkLastBlockStmts or - (isCallExpr(n) and n[0].kind == nkSym and - sfDiscardable in n[0].sym.flags) + # same traversal as endsInNoReturn + template checkBranch(branch) = + if not implicitlyDiscardable(branch): + return false + + var it = n + # skip these beforehand, no special handling needed + while it.kind in skipForDiscardable and it.len > 0: + it = it.lastSon + + case it.kind + of nkIfExpr, nkIfStmt: + for branch in it: + checkBranch: + if branch.len == 2: + branch[1] + elif branch.len == 1: + branch[0] + else: + raiseAssert "Malformed `if` statement during implicitlyDiscardable" + # all branches are discardable + result = true + of nkCaseStmt: + for i in 1 ..< it.len: + let branch = it[i] + checkBranch: + case branch.kind + of nkOfBranch: + branch[^1] + of nkElifBranch: + branch[1] + of nkElse: + branch[0] + else: + raiseAssert "Malformed `case` statement in implicitlyDiscardable" + # all branches are discardable + result = true + of nkTryStmt: + checkBranch(it[0]) + for i in 1 ..< it.len: + let branch = it[i] + if branch.kind != nkFinally: + checkBranch(branch[^1]) + # all branches are discardable + result = true + of nkCallKinds: + result = it[0].kind == nkSym and {sfDiscardable, sfNoReturn} * it[0].sym.flags != {} + of nkLastBlockStmts: + result = true + else: + result = false + +proc endsInNoReturn(n: PNode, returningNode: var PNode; discardableCheck = false): bool = + ## check if expr ends the block like raising or call of noreturn procs do + result = false # assume it does return + + template checkBranch(branch) = + if not endsInNoReturn(branch, returningNode, discardableCheck): + # proved a branch returns + return false + + var it = n + # skip these beforehand, no special handling needed + let skips = if discardableCheck: skipForDiscardable else: skipForDiscardable-{nkBlockExpr, nkBlockStmt} + while it.kind in skips and it.len > 0: + it = it.lastSon + + case it.kind + of nkIfExpr, nkIfStmt: + var hasElse = false + for branch in it: + checkBranch: + if branch.len == 2: + branch[1] + elif branch.len == 1: + hasElse = true + branch[0] + else: + raiseAssert "Malformed `if` statement during endsInNoReturn" + # none of the branches returned + result = hasElse # Only truly a no-return when it's exhaustive + of nkCaseStmt: + let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc}) + # semCase should already have checked for exhaustiveness in this case + # effectively the same as having an else + var hasElse = caseTyp.shouldCheckCaseCovered() + + # actual noreturn checks + for i in 1 ..< it.len: + let branch = it[i] + checkBranch: + case branch.kind + of nkOfBranch: + branch[^1] + of nkElifBranch: + branch[1] + of nkElse: + hasElse = true + branch[0] + else: + raiseAssert "Malformed `case` statement in endsInNoReturn" + # Can only guarantee a noreturn if there is an else or it's exhaustive + result = hasElse + of nkTryStmt: + checkBranch(it[0]) + var lastIndex = it.len - 1 + if it[lastIndex].kind == nkFinally: + # if finally is noreturn, then the entire statement is noreturn + if endsInNoReturn(it[lastIndex][^1], returningNode, discardableCheck): + return true + dec lastIndex + for i in 1 .. lastIndex: + let branch = it[i] + checkBranch(branch[^1]) + # none of the branches returned + result = true + of nkLastBlockStmts: + result = true + of nkCallKinds: + result = it[0].kind == nkSym and sfNoReturn in it[0].sym.flags + if not result: + returningNode = it + else: + result = false + returningNode = it + +proc endsInNoReturn(n: PNode): bool = + var dummy: PNode = nil + result = endsInNoReturn(n, dummy) proc fixNilType(c: PContext; n: PNode) = if isAtom(n): @@ -160,15 +284,17 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) = if implicitlyDiscardable(result): var n = newNodeI(nkDiscardStmt, result.info, 1) n[0] = result + # notes that it doesn't transform nodes into discard statements elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: if result.typ.kind == tyNone: localError(c.config, result.info, "expression has no type: " & renderTree(result, {renderNoComments})) else: + # Ignore noreturn procs since they don't have a type var n = result - while n.kind in skipForDiscardable: - if n.kind == nkTryStmt: n = n[0] - else: n = n.lastSon + if result.endsInNoReturn(n, discardableCheck = true): + return + var s = "expression '" & $n & "' is of type '" & result.typ.typeToString & "' and has to be used (or discarded)" if result.info.line != n.info.line or @@ -334,6 +460,8 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result.typ = typ if not floatRangeCheck(result.floatVal, typ): localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)]) + elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring: + discard "keep nkHiddenStdConv for cstring conversions" else: changeType(c, r1, typ, check=true) result = r1 @@ -354,7 +482,7 @@ proc identWithin(n: PNode, s: PIdent): bool = proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym = if isTopLevel(c): - result = semIdentWithPragma(c, kind, n, {sfExported}) + result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true) incl(result.flags, sfGlobal) #if kind in {skVar, skLet}: # echo "global variable here ", n.info, " ", result.name.s @@ -451,16 +579,16 @@ proc hasUnresolvedParams(n: PNode; flags: TExprFlags): bool = proc makeDeref(n: PNode): PNode = var t = n.typ if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass: - t = t.lastSon + t = t.last t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned}) result = n if t.kind in {tyVar, tyLent}: - result = newNodeIT(nkHiddenDeref, n.info, t[0]) + result = newNodeIT(nkHiddenDeref, n.info, t.elementType) result.add n - t = skipTypes(t[0], {tyGenericInst, tyAlias, tySink, tyOwned}) + t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned}) while t.kind in {tyPtr, tyRef}: var a = result - let baseTyp = t.lastSon + let baseTyp = t.elementType result = newNodeIT(nkHiddenDeref, n.info, baseTyp) result.add a t = skipTypes(baseTyp, {tyGenericInst, tyAlias, tySink, tyOwned}) @@ -494,15 +622,15 @@ proc setVarType(c: PContext; v: PSym, typ: PType) = proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool = # make sure it's not a normal pragma, and calls an identifier # considerQuotedIdent below will fail on non-identifiers - result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds + result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds+{nkDotExpr} if result: # make sure it's not a user pragma - let ident = considerQuotedIdent(c, key) - result = strTableGet(c.userPragmas, ident) == nil + if key.kind != nkDotExpr: + let ident = considerQuotedIdent(c, key) + result = strTableGet(c.userPragmas, ident) == nil if result: # make sure it's not a custom pragma - var amb = false - let sym = searchInScopes(c, ident, amb) + let sym = qualifiedLookUp(c, key, {}) result = sym == nil or sfCustomPragma notin sym.flags proc copyExcept(n: PNode, i: int): PNode = @@ -662,6 +790,24 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) + + # transform var x, y = 12 into var x = 12; var y = 12 + # bug #18104; transformation should be finished before templates expansion + # TODO: move warnings for tuple here + var transformed = copyNode(n) + for i in 0..<n.len: + var a = n[i] + if a.kind == nkIdentDefs and a.len > 3 and a[^1].kind != nkEmpty: + for j in 0..<a.len-2: + var b = newNodeI(nkIdentDefs, a.info) + b.add a[j] + b.add a[^2] + b.add copyTree(a[^1]) + transformed.add b + else: + transformed.add a + let n = transformed + for i in 0..<n.len: var a = n[i] if c.config.cmd == cmdIdeTools: suggestStmt(c, a) @@ -683,6 +829,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var typFlags: TTypeAllowedFlags = {} var def: PNode = c.graph.emptyNode + if typ != nil and typ.kind == tyRange and + c.graph.config.isDefined("nimPreviewRangeDefault") and + a[^1].kind == nkEmpty: + a[^1] = firstRange(c.config, typ) + if a[^1].kind != nkEmpty: def = semExprWithType(c, a[^1], {efTypeAllowed}, typ) @@ -703,7 +854,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = else: typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen) if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass: - typ = typ.lastSon + typ = typ.last if hasEmpty(typ): localError(c.config, def.info, errCannotInferTypeOfTheLiteral % typ.kind.toHumanStr) elif typ.kind == tyProc and def.kind == nkSym and isGenericRoutine(def.sym.ast): @@ -834,6 +985,7 @@ proc semConst(c: PContext, n: PNode): PNode = var typFlags: TTypeAllowedFlags = {} # don't evaluate here since the type compatibility check below may add a converter + openScope(c) var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ) if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}: @@ -860,6 +1012,7 @@ proc semConst(c: PContext, n: PNode): PNode = if c.matchedConcept != nil: typFlags.incl taConcept typeAllowedCheck(c, a.info, typ, skConst, typFlags) + closeScope(c) if a.kind == nkVarTuple: # generate new section from tuple unpacking and embed it into this one @@ -925,7 +1078,10 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = if iterAfterVarLent.kind != tyTuple or n.len == 3: if n.len == 3: if n[0].kind == nkVarTuple: - if n[0].len-1 != iterAfterVarLent.len: + if iterAfterVarLent.kind != tyTuple: + return localErrorNode(c, n, n[0].info, errTupleUnpackingTupleExpected % + [typeToString(n[1].typ, preferDesc)]) + elif n[0].len-1 != iterAfterVarLent.len: return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables) for i in 0..<n[0].len-1: @@ -1038,12 +1194,12 @@ proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string; if maType == nil: return let headSymbol = selector[0] - var o: TOverloadIter + var o: TOverloadIter = default(TOverloadIter) var match: PSym = nil var symx = initOverloadIter(o, c, headSymbol) while symx != nil: if symx.kind in {skTemplate, skMacro}: - if symx.typ.len == 2 and symx.typ[1] == maType.typ: + if symx.typ.len == 2 and symx.typ.firstParamType == maType.typ: if match == nil: match = symx else: @@ -1074,8 +1230,8 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode = toResolve.add newIdentNode(getIdent(c.cache, "case"), n.info) toResolve.add n[0] - var errors: CandidateErrors - var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoDiagnostics}, + var errors: CandidateErrors = @[] + var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoUndeclared}, errors, false) if r.state == csMatch: var match = r.calleeSym @@ -1089,8 +1245,6 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode = of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, flags) of skTemplate: result = semTemplateExpr(c, toExpand, match, flags) else: result = errorNode(c, n[0]) - elif r.state == csNoMatch: - result = errorNode(c, n[0]) else: result = errorNode(c, n[0]) if result.kind == nkEmpty: @@ -1238,7 +1392,7 @@ proc semRaise(c: PContext, n: PNode): PNode = typ = typ.skipTypes({tyAlias, tyGenericInst, tyOwned}) if typ.kind != tyRef: localError(c.config, n.info, errExprCannotBeRaised) - if typ.len > 0 and not isException(typ.lastSon): + if typ.len > 0 and not isException(typ.elementType): localError(c.config, n.info, "raised object of type $1 does not inherit from Exception" % typeToString(typ)) proc addGenericParamListToScope(c: PContext, n: PNode) = @@ -1254,6 +1408,9 @@ proc typeSectionTypeName(c: PContext; n: PNode): PNode = result = n[0] else: result = n + if result.kind == nkPostfix: + if result.len != 2: illFormedAst(n, c.config) + result = result[1] if result.kind != nkSym: illFormedAst(n, c.config) proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = @@ -1321,9 +1478,15 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = elif s.owner == nil: s.owner = getCurrOwner(c) if name.kind == nkPragmaExpr: - typeDef[0][0] = newSymNode(s) + if name[0].kind == nkPostfix: + typeDef[0][0][1] = newSymNode(s) + else: + typeDef[0][0] = newSymNode(s) else: - typeDef[0] = newSymNode(s) + if name.kind == nkPostfix: + typeDef[0][1] = newSymNode(s) + else: + typeDef[0] = newSymNode(s) proc typeSectionLeftSidePass(c: PContext, n: PNode) = # process the symbols on the left side for the whole type section, before @@ -1343,7 +1506,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = inc i proc checkCovariantParamsUsages(c: PContext; genericType: PType) = - var body = genericType[^1] + var body = genericType.typeBodyImpl proc traverseSubTypes(c: PContext; t: PType): bool = template error(msg) = localError(c.config, genericType.sym.info, msg) @@ -1360,17 +1523,17 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) = for field in t.n: subresult traverseSubTypes(c, field.typ) of tyArray: - return traverseSubTypes(c, t[1]) + return traverseSubTypes(c, t.elementType) of tyProc: - for subType in t: + for subType in t.signature: if subType != nil: subresult traverseSubTypes(c, subType) if result: error("non-invariant type param used in a proc type: " & $t) of tySequence: - return traverseSubTypes(c, t[0]) + return traverseSubTypes(c, t.elementType) of tyGenericInvocation: - let targetBody = t[0] + let targetBody = t.genericHead for i in 1..<t.len: let param = t[i] if param.kind == tyGenericParam: @@ -1395,13 +1558,13 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) = of tyUserTypeClass, tyUserTypeClassInst: error("non-invariant type parameters are not supported in concepts") of tyTuple: - for fieldType in t: + for fieldType in t.kids: subresult traverseSubTypes(c, fieldType) of tyPtr, tyRef, tyVar, tyLent: - if t.base.kind == tyGenericParam: return true - return traverseSubTypes(c, t.base) + if t.elementType.kind == tyGenericParam: return true + return traverseSubTypes(c, t.elementType) of tyDistinct, tyAlias, tySink, tyOwned: - return traverseSubTypes(c, t.lastSon) + return traverseSubTypes(c, t.skipModifier) of tyGenericInst: internalAssert c.config, false else: @@ -1439,7 +1602,11 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # we fill it out later. For magic generics like 'seq', it won't be filled # so we use tyNone instead of nil to not crash for strange conversions # like: mydata.seq - rawAddSon(s.typ, newTypeS(tyNone, c)) + if s.typ.kind in {tyOpenArray, tyVarargs} and s.typ.len == 1: + # XXX investigate why `tySequence` cannot be added here for now. + discard + else: + rawAddSon(s.typ, newTypeS(tyNone, c)) s.ast = a inc c.inGenericContext var body = semTypeNode(c, a[2], s.typ) @@ -1466,7 +1633,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # possibilities such as instantiating C++ generic types with # garbage collected Nim types. if sfImportc in s.flags: - var body = s.typ.lastSon + var body = s.typ.last if body.kind == tyObject: # erases all declared fields body.n.sons = @[] @@ -1509,11 +1676,11 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = aa[0].kind == nkObjectTy: # give anonymous object a dummy symbol: var st = s.typ - if st.kind == tyGenericBody: st = st.lastSon + if st.kind == tyGenericBody: st = st.typeBodyImpl internalAssert c.config, st.kind in {tyPtr, tyRef} - internalAssert c.config, st.lastSon.sym == nil + internalAssert c.config, st.last.sym == nil incl st.flags, tfRefsAnonObj - let objTy = st.lastSon + let objTy = st.last # add flags for `ref object` etc to underlying `object` incl(objTy.flags, oldFlags) # {.inheritable, final.} is already disallowed, but @@ -1526,12 +1693,19 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = let symNode = newSymNode(obj) obj.ast = a.shallowCopy case a[0].kind - of nkSym: obj.ast[0] = symNode - of nkPragmaExpr: - obj.ast[0] = a[0].shallowCopy + of nkSym: obj.ast[0] = symNode + of nkPragmaExpr: + obj.ast[0] = a[0].shallowCopy + if a[0][0].kind == nkPostfix: + obj.ast[0][0] = a[0][0].shallowCopy + obj.ast[0][0][1] = symNode + else: obj.ast[0][0] = symNode - obj.ast[0][1] = a[0][1] - else: assert(false) + obj.ast[0][1] = a[0][1] + of nkPostfix: + obj.ast[0] = a[0].shallowCopy + obj.ast[0][1] = symNode + else: assert(false) obj.ast[1] = a[1] obj.ast[2] = a[2][0] if sfPure in s.flags: @@ -1541,21 +1715,27 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = for sk in c.skipTypes: discard semTypeNode(c, sk, nil) c.skipTypes = @[] -proc checkForMetaFields(c: PContext; n: PNode) = - proc checkMeta(c: PContext; n: PNode; t: PType) = - if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags: - if t.kind == tyBuiltInTypeClass and t.len == 1 and t[0].kind == tyProc: + +proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) = + proc checkMeta(c: PContext; n: PNode; t: PType; hasError: var bool; parent: PType) = + if t != nil and (t.isMetaType or t.kind == tyNone) and tfGenericTypeParam notin t.flags: + if t.kind == tyBuiltInTypeClass and t.len == 1 and t.elementType.kind == tyProc: localError(c.config, n.info, ("'$1' is not a concrete type; " & "for a callback without parameters use 'proc()'") % t.typeToString) + elif t.kind == tyNone and parent != nil: + # TODO: openarray has the `tfGenericTypeParam` flag & generics + # TODO: handle special cases (sink etc.) and views + localError(c.config, n.info, errTIsNotAConcreteType % parent.typeToString) else: localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString) + hasError = true if n.isNil: return case n.kind of nkRecList, nkRecCase: - for s in n: checkForMetaFields(c, s) + for s in n: checkForMetaFields(c, s, hasError) of nkOfBranch, nkElse: - checkForMetaFields(c, n.lastSon) + checkForMetaFields(c, n.lastSon, hasError) of nkSym: let t = n.sym.typ case t.kind @@ -1563,9 +1743,9 @@ proc checkForMetaFields(c: PContext; n: PNode) = tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink, tyOwned: let start = ord(t.kind in {tyGenericInvocation, tyGenericInst}) for i in start..<t.len: - checkMeta(c, n, t[i]) + checkMeta(c, n, t[i], hasError, t) else: - checkMeta(c, n, t) + checkMeta(c, n, t, hasError, nil) else: internalAssert c.config, false @@ -1600,13 +1780,16 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assert s.typ != nil assignType(s.typ, t) s.typ.itemId = t.itemId # same id - checkConstructedType(c.config, s.info, s.typ) - if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: - checkForMetaFields(c, s.typ.n) - - # fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name: - if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c): - incl(s.flags, sfGenSym) + var hasError = false + let baseType = s.typ.safeSkipTypes(abstractPtrs) + if baseType.kind in {tyObject, tyTuple} and not baseType.n.isNil and + (x.kind in {nkObjectTy, nkTupleTy} or + (x.kind in {nkRefTy, nkPtrTy} and x.len == 1 and + x[0].kind in {nkObjectTy, nkTupleTy}) + ): + checkForMetaFields(c, baseType.n, hasError) + if not hasError: + checkConstructedType(c.config, s.info, s.typ) #instAllTypeBoundOp(c, n.info) @@ -1682,25 +1865,25 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) = # search for the correct alias: var (b, state) = searchForBorrowProc(c, c.currentScope.parent, s) case state - of bsMatch: - # store the alias: - n[bodyPos] = newSymNode(b) - # Carry over the original symbol magic, this is necessary in order to ensure - # the semantic pass is correct - s.magic = b.magic - if b.typ != nil and b.typ.len > 0: - s.typ.n[0] = b.typ.n[0] - s.typ.flags = b.typ.flags - of bsNoDistinct: - localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless") - of bsReturnNotMatch: - localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ[0])) - of bsGeneric: - localError(c.config, n.info, "borrow with generic parameter is not supported") - of bsNotSupported: - localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s) - else: - localError(c.config, n.info, errNoSymbolToBorrowFromFound) + of bsMatch: + # store the alias: + n[bodyPos] = newSymNode(b) + # Carry over the original symbol magic, this is necessary in order to ensure + # the semantic pass is correct + s.magic = b.magic + if b.typ != nil and b.typ.len > 0: + s.typ.n[0] = b.typ.n[0] + s.typ.flags = b.typ.flags + of bsNoDistinct: + localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless") + of bsReturnNotMatch: + localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ.returnType)) + of bsGeneric: + localError(c.config, n.info, "borrow with generic parameter is not supported") + of bsNotSupported: + localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s) + else: + localError(c.config, n.info, errNoSymbolToBorrowFromFound) proc swapResult(n: PNode, sRes: PSym, dNode: PNode) = ## Swap nodes that are (skResult) symbols to d(estination)Node. @@ -1786,7 +1969,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; return result -proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = +proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode = ## used for resolving 'auto' in lambdas based on their callsite var n = n let original = n[namePos].sym @@ -1801,7 +1984,6 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = n[genericParamsPos] = c.graph.emptyNode # for LL we need to avoid wrong aliasing let params = copyTree n.typ.n - n[paramsPos] = params s.typ = n.typ for i in 1..<params.len: if params[i].typ.kind in {tyTypeDesc, tyGenericParam, @@ -1813,8 +1995,8 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = pushOwner(c, s) addParams(c, params, skProc) pushProcCon(c, s) - addResult(c, n, n.typ[0], skProc) - s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ[0])) + addResult(c, n, n.typ.returnType, skProc) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ.returnType)) trackProc(c, s, s.ast[bodyPos]) popProcCon(c) popOwner(c) @@ -1844,8 +2026,8 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) = if s.kind == skMacro: let resultType = sysTypeFromName(c.graph, n.info, "NimNode") addResult(c, n, resultType, s.kind) - elif s.typ[0] != nil and not isInlineIterator(s.typ): - addResult(c, n, s.typ[0], s.kind) + elif s.typ.returnType != nil and not isInlineIterator(s.typ): + addResult(c, n, s.typ.returnType, s.kind) proc canonType(c: PContext, t: PType): PType = if t.kind == tySequence: @@ -1864,7 +2046,7 @@ proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) = proc whereToBindTypeHook(c: PContext; t: PType): PType = result = t while true: - if result.kind in {tyGenericBody, tyGenericInst}: result = result.lastSon + if result.kind in {tyGenericBody, tyGenericInst}: result = result.skipModifier elif result.kind == tyGenericInvocation: result = result[0] else: break if result.kind in {tyObject, tyDistinct, tySequence, tyString}: @@ -1873,20 +2055,20 @@ proc whereToBindTypeHook(c: PContext; t: PType): PType = proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = let t = s.typ var noError = false - let cond = t.len == 2 and t[0] != nil + let cond = t.len == 2 and t.returnType != nil if cond: - var obj = t[1] + var obj = t.firstParamType while true: incl(obj.flags, tfHasAsgn) - if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon - elif obj.kind == tyGenericInvocation: obj = obj[0] + if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier + elif obj.kind == tyGenericInvocation: obj = obj.genericHead else: break - var res = t[0] + var res = t.returnType while true: - if res.kind in {tyGenericBody, tyGenericInst}: res = res.lastSon - elif res.kind == tyGenericInvocation: res = res[0] + if res.kind in {tyGenericBody, tyGenericInst}: res = res.skipModifier + elif res.kind == tyGenericInvocation: res = res.genericHead else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, res): @@ -1915,21 +2097,27 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressV var noError = false let cond = case op of attachedWasMoved: - t.len == 2 and t[0] == nil and t[1].kind == tyVar + t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar of attachedTrace: - t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer + t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar and t[2].kind == tyPointer + of attachedDestructor: + if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: + t.len == 2 and t.returnType == nil + else: + t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar else: - t.len >= 2 and t[0] == nil + t.len >= 2 and t.returnType == nil if cond: - var obj = t[1].skipTypes({tyVar}) + var obj = t.firstParamType.skipTypes({tyVar}) while true: incl(obj.flags, tfHasAsgn) - if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon - elif obj.kind == tyGenericInvocation: obj = obj[0] + if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier + elif obj.kind == tyGenericInvocation: obj = obj.genericHead else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: - if (not suppressVarDestructorWarning) and op == attachedDestructor and t[1].kind == tyVar: + if (not suppressVarDestructorWarning) and op == attachedDestructor and t.firstParamType.kind == tyVar and + c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter") obj = canonType(c, obj) let ao = getAttachedOp(c.graph, obj, op) @@ -1949,8 +2137,12 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressV localError(c.config, n.info, errGenerated, "signature for '=trace' must be proc[T: object](x: var T; env: pointer)") of attachedDestructor: - localError(c.config, n.info, errGenerated, - "signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)") + if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: + localError(c.config, n.info, errGenerated, + "signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)") + else: + localError(c.config, n.info, errGenerated, + "signature for '=destroy' must be proc[T: object](x: var T)") else: localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") @@ -1969,14 +2161,14 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = newIdentNode(c.cache.getIdent("raises"), s.info), newNodeI(nkBracket, s.info)) of "deepcopy", "=deepcopy": if s.typ.len == 2 and - s.typ[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and - sameType(s.typ[1], s.typ[0]): + s.typ.firstParamType.skipTypes(abstractInst).kind in {tyRef, tyPtr} and + sameType(s.typ.firstParamType, s.typ.returnType): # Note: we store the deepCopy in the base of the pointer to mitigate # the problem that pointers are structural types: - var t = s.typ[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst) + var t = s.typ.firstParamType.skipTypes(abstractInst).elementType.skipTypes(abstractInst) while true: - if t.kind == tyGenericBody: t = t.lastSon - elif t.kind == tyGenericInvocation: t = t[0] + if t.kind == tyGenericBody: t = t.typeBodyImpl + elif t.kind == tyGenericInvocation: t = t.genericHead else: break if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}: if getAttachedOp(c.graph, t, attachedDeepCopy).isNil: @@ -2004,18 +2196,18 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if name == "=": message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead") let t = s.typ - if t.len == 3 and t[0] == nil and t[1].kind == tyVar: - var obj = t[1][0] + if t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar: + var obj = t.firstParamType.elementType while true: incl(obj.flags, tfHasAsgn) - if obj.kind == tyGenericBody: obj = obj.lastSon - elif obj.kind == tyGenericInvocation: obj = obj[0] + if obj.kind == tyGenericBody: obj = obj.skipModifier + elif obj.kind == tyGenericInvocation: obj = obj.genericHead else: break var objB = t[2] while true: - if objB.kind == tyGenericBody: objB = objB.lastSon + if objB.kind == tyGenericBody: objB = objB.skipModifier elif objB.kind in {tyGenericInvocation, tyGenericInst}: - objB = objB[0] + objB = objB.genericHead else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): # attach these ops to the canonical tySequence @@ -2082,20 +2274,20 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) = if c.config.backend == backendCpp: if s.typ.len < 2 and not isCtor: localError(c.config, n.info, pragmaName & " must have at least one parameter") - for son in s.typ: + for son in s.typ.signature: if son!=nil and son.isMetaType: localError(c.config, n.info, pragmaName & " unsupported for generic routine") var typ: PType if isCtor: - typ = s.typ[0] + typ = s.typ.returnType if typ == nil or typ.kind != tyObject: localError(c.config, n.info, "constructor must return an object") if sfImportc in typ.sym.flags: localError(c.config, n.info, "constructor in an imported type needs importcpp pragma") else: - typ = s.typ[1] + typ = s.typ.firstParamType if typ.kind == tyPtr and not isCtor: - typ = typ[0] + typ = typ.elementType if typ.kind != tyObject: localError(c.config, n.info, pragmaName & " must be either ptr to object or object type.") if typ.owner.id == s.owner.id and c.module.id == s.owner.id: @@ -2106,7 +2298,7 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) = else: localError(c.config, n.info, pragmaName & " procs are only supported in C++") else: - var typ = s.typ[0] + var typ = s.typ.returnType if typ != nil and typ.kind == tyObject and typ.itemId notin c.graph.initializersPerType: var initializerCall = newTree(nkCall, newSymNode(s)) var isInitializer = n[paramsPos].len > 1 @@ -2132,7 +2324,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = for col in 1..<tt.len: let t = tt[col] if t != nil and t.kind == tyGenericInvocation: - var x = skipTypes(t[0], {tyVar, tyLent, tyPtr, tyRef, tyGenericInst, + var x = skipTypes(t.genericHead, {tyVar, tyLent, tyPtr, tyRef, tyGenericInst, tyGenericInvocation, tyGenericBody, tyAlias, tySink, tyOwned}) if x.kind == tyObject and t.len-1 == n[genericParamsPos].len: @@ -2345,7 +2537,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if sfBorrow in s.flags and c.config.cmd notin cmdDocLike: result[bodyPos] = c.graph.emptyNode - if sfCppMember * s.flags != {}: + if sfCppMember * s.flags != {} and sfWasForwarded notin s.flags: semCppMember(c, s, n) if n[bodyPos].kind != nkEmpty and sfError notin s.flags: @@ -2360,8 +2552,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # absolutely no generics (empty) or a single generic return type are # allowed, everything else, including a nullary generic is an error. pushProcCon(c, s) - addResult(c, n, s.typ[0], skProc) - s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], s.typ[0])) + addResult(c, n, s.typ.returnType, skProc) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], s.typ.returnType)) trackProc(c, s, s.ast[bodyPos]) popProcCon(c) elif efOperand notin flags: @@ -2377,7 +2569,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if s.kind == skMacro: sysTypeFromName(c.graph, n.info, "NimNode") elif not isInlineIterator(s.typ): - s.typ[0] + s.typ.returnType else: nil # semantic checking also needed with importc in case used in VM @@ -2386,7 +2578,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # context as it may even be evaluated in 'system.compiles': trackProc(c, s, s.ast[bodyPos]) else: - if (s.typ[0] != nil and s.kind != skIterator): + if (s.typ.returnType != nil and s.kind != skIterator): addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), c.idgen, s, n.info)) openScope(c) @@ -2401,7 +2593,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if hasProto: localError(c.config, n.info, errImplOfXexpected % proto.name.s) if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone: # this is a forward declaration and we're building the prototype - if s.kind in {skProc, skFunc} and s.typ[0] != nil and s.typ[0].kind == tyAnything: + if s.kind in {skProc, skFunc} and s.typ.returnType != nil and s.typ.returnType.kind == tyAnything: localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations") incl(s.flags, sfForward) @@ -2446,7 +2638,7 @@ proc semIterator(c: PContext, n: PNode): PNode = if result.kind != n.kind: return var s = result[namePos].sym var t = s.typ - if t[0] == nil and s.typ.callConv != ccClosure: + if t.returnType == nil and s.typ.callConv != ccClosure: localError(c.config, n.info, "iterator needs a return type") # iterators are either 'inline' or 'closure'; for backwards compatibility, # we require first class iterators to be marked with 'closure' explicitly @@ -2483,9 +2675,9 @@ proc semMethod(c: PContext, n: PNode): PNode = # test case): let disp = getDispatcher(s) # auto return type? - if disp != nil and disp.typ[0] != nil and disp.typ[0].kind == tyUntyped: - let ret = s.typ[0] - disp.typ[0] = ret + if disp != nil and disp.typ.returnType != nil and disp.typ.returnType.kind == tyUntyped: + let ret = s.typ.returnType + disp.typ.setReturnType ret if disp.ast[resultPos].kind == nkSym: if isEmptyType(ret): disp.ast[resultPos] = c.graph.emptyNode else: disp.ast[resultPos].sym.typ = ret @@ -2501,7 +2693,7 @@ proc semConverterDef(c: PContext, n: PNode): PNode = if result.kind != nkConverterDef: return var s = result[namePos].sym var t = s.typ - if t[0] == nil: localError(c.config, n.info, errXNeedsReturnType % "converter") + if t.returnType == nil: localError(c.config, n.info, errXNeedsReturnType % "converter") if t.len != 2: localError(c.config, n.info, "a converter takes exactly one argument") addConverterDef(c, LazySym(sym: s)) @@ -2543,20 +2735,23 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) = proc evalInclude(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) result.add n + template checkAs(it: PNode) = + if it.kind == nkInfix and it.len == 3: + let op = it[0].getPIdent + if op != nil and op.id == ord(wAs): + localError(c.config, it.info, "Cannot use '" & it[0].renderTree & "' in 'include'.") for i in 0..<n.len: - var imp: PNode let it = n[i] - if it.kind == nkInfix and it.len == 3 and it[0].ident.s != "/": - localError(c.config, it.info, "Cannot use '" & it[0].ident.s & "' in 'include'.") - if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket: - let sep = it[0] - let dir = it[1] - imp = newNodeI(nkInfix, it.info) - imp.add sep - imp.add dir - imp.add sep # dummy entry, replaced in the loop - for x in it[2]: - imp[2] = x + checkAs(it) + if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket: + let lastPos = it.len - 1 + var imp = copyNode(it) + newSons(imp, it.len) + for i in 0 ..< lastPos: imp[i] = it[i] + imp[lastPos] = imp[0] # dummy entry, replaced in the loop + for x in it[lastPos]: + checkAs(x) + imp[lastPos] = x incMod(c, n, imp, result) else: incMod(c, n, it, result) @@ -2667,7 +2862,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = let verdict = semConstExpr(c, n[i]) if verdict == nil or verdict.kind != nkIntLit or verdict.intVal == 0: localError(c.config, result.info, "concept predicate failed") - of tyUnknown: continue + of tyFromExpr: continue else: discard if n[i].typ == c.enforceVoidContext: #or usesResult(n[i]): voidContext = true diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 3256b8d85..817cb6249 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -52,7 +52,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; isField = false): PNode = var a: PSym - o: TOverloadIter + o: TOverloadIter = default(TOverloadIter) var i = 0 a = initOverloadIter(o, c, n) while a != nil: @@ -218,63 +218,98 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = if k == skParam and c.inTemplateHeader > 0: local.flags.incl sfTemplateParam -proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = +proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode = incl(s.flags, sfUsed) # bug #12885; ideally sem'checking is performed again afterwards marking # the symbol as used properly, but the nfSem mechanism currently prevents # that from happening, so we mark the module as used here already: - markOwnerModuleAsUsed(c, s) + markOwnerModuleAsUsed(c.c, s) # we do not call onUse here, as the identifier is not really # resolved here. We will fixup the used identifiers later. case s.kind of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of OverloadableSyms-{skTemplate,skMacro}: - result = symChoice(c, n, s, scOpen, isField) - of skTemplate, skMacro: - result = symChoice(c, n, s, scOpen, isField) - if result.kind == nkSym: - # template/macro symbols might need to be semchecked again - # prepareOperand etc don't do this without setting the type to nil - result.typ = nil + of OverloadableSyms: + result = symChoice(c.c, n, s, scOpen, isField) + if not isField and result.kind in {nkSym, nkOpenSymChoice}: + if openSym in c.c.features: + if result.kind == nkSym: + result = newOpenSym(result) + else: + result.typ = nil + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil of skGenericParam: if isField and sfGenSym in s.flags: result = n - else: result = newSymNodeTypeDesc(s, c.idgen, n.info) + else: + result = newSymNodeTypeDesc(s, c.c.idgen, n.info) + if not isField and s.owner != c.owner: + if openSym in c.c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil of skParam: result = n of skType: if isField and sfGenSym in s.flags: result = n - else: result = newSymNodeTypeDesc(s, c.idgen, n.info) + else: + if isAmbiguous: + # ambiguous types should be symchoices since lookup behaves + # differently for them in regular expressions + result = symChoice(c.c, n, s, scOpen, isField) + else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info) + if not isField and not (s.owner == c.owner and + s.typ != nil and s.typ.kind == tyGenericParam) and + result.kind in {nkSym, nkOpenSymChoice}: + if openSym in c.c.features: + if result.kind == nkSym: + result = newOpenSym(result) + else: + result.typ = nil + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil else: if isField and sfGenSym in s.flags: result = n - else: result = newSymNode(s, n.info) + else: + result = newSymNode(s, n.info) + if not isField: + if openSym in c.c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil # Issue #12832 when defined(nimsuggest): - suggestSym(c.graph, n.info, s, c.graph.usageSym, false) + suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false) # field access (dot expr) will be handled by builtinFieldAccess if not isField: - styleCheckUse(c, n.info, s) + styleCheckUse(c.c, n.info, s) -proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = +proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode = result = n if n.kind == nkIdent: let s = qualifiedLookUp(c.c, n, {}) if s != nil: - if s.owner == c.owner and (s.kind == skParam or sfGenSym in s.flags): + if s.owner == c.owner and (s.kind == skParam or + (sfGenSym in s.flags and not explicitInject)): incl(s.flags, sfUsed) result = newSymNode(s, n.info) onUse(n.info, s) else: for i in 0..<n.safeLen: - result[i] = semRoutineInTemplName(c, n[i]) + result[i] = semRoutineInTemplName(c, n[i], explicitInject) proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = result = n checkSonsLen(n, bodyPos + 1, c.c.config) if n.kind notin nkLambdaKinds: # routines default to 'inject': - if symBinding(n[pragmasPos]) == spGenSym: + let binding = symBinding(n[pragmasPos]) + if binding == spGenSym: let (ident, hasParam) = getIdentReplaceParams(c, n[namePos]) if not hasParam: var s = newGenSym(k, ident, c) @@ -286,7 +321,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = else: n[namePos] = ident else: - n[namePos] = semRoutineInTemplName(c, n[namePos]) + n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject) # open scope for parameters openScope(c) for i in patternPos..paramsPos-1: @@ -343,6 +378,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = case n.kind of nkIdent: if n.ident.id in c.toInject: return n + c.c.isAmbiguous = false let s = qualifiedLookUp(c.c, n, {}) if s != nil: if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags: @@ -360,9 +396,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = newSymNode(s, n.info) onUse(n.info, s) else: - if s.kind in {skType, skVar, skLet, skConst}: + if s.kind in {skVar, skLet, skConst}: discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule}) - result = semTemplSymbol(c.c, n, s, c.noGenSym > 0) + result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous) of nkBind: result = semTemplBody(c, n[0]) of nkBindStmt: @@ -507,14 +543,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = if x.kind == nkExprColonExpr: x[1] = semTemplBody(c, x[1]) of nkBracketExpr: - result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info) - for i in 0..<n.len: result.add(n[i]) + if n.typ == nil: + # if a[b] is nested inside a typed expression, don't convert it + # back to `[]`(a, b), prepareOperand will not typecheck it again + # and so `[]` will not be resolved + # checking if a[b] is typed should be enough to cover this case + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info) + for i in 0..<n.len: result.add(n[i]) result = semTemplBodySons(c, result) of nkCurlyExpr: - result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info) - for i in 0..<n.len: result.add(n[i]) + if n.typ == nil: + # see nkBracketExpr case for explanation + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info) + for i in 0..<n.len: result.add(n[i]) result = semTemplBodySons(c, result) of nkAsgn, nkFastAsgn, nkSinkAsgn: checkSonsLen(n, 2, c.c.config) @@ -524,17 +567,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = let k = a.kind case k of nkBracketExpr: - result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info) - for i in 0..<a.len: result.add(a[i]) - result.add(b) + if a.typ == nil: + # see nkBracketExpr case above for explanation + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info) + for i in 0..<a.len: result.add(a[i]) + result.add(b) let a0 = semTemplBody(c, a[0]) result = semTemplBodySons(c, result) of nkCurlyExpr: - result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info) - for i in 0..<a.len: result.add(a[i]) - result.add(b) + if a.typ == nil: + # see nkBracketExpr case above for explanation + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info) + for i in 0..<a.len: result.add(a[i]) + result.add(b) result = semTemplBodySons(c, result) else: result = semTemplBodySons(c, n) @@ -545,6 +592,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkDotExpr, nkAccQuoted: # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # so we use the generic code for nkDotExpr too + c.c.isAmbiguous = false let s = qualifiedLookUp(c.c, n, {}) if s != nil: # mirror the nkIdent case @@ -559,9 +607,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = elif contains(c.toMixin, s.name.id): return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) else: - if s.kind in {skType, skVar, skLet, skConst}: + if s.kind in {skVar, skLet, skConst}: discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule}) - return semTemplSymbol(c.c, n, s, c.noGenSym > 0) + return semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous) if n.kind == nkDotExpr: result = n result[0] = semTemplBody(c, n[0]) @@ -645,6 +693,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = pushOwner(c, s) openScope(c) n[namePos] = newSymNode(s) + s.ast = n # for implicitPragmas to use pragmaCallable(c, s, n, templatePragmas) implicitPragmas(c, s, n.info, templatePragmas) @@ -657,7 +706,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = # a template's parameters are not gensym'ed even if that was originally the # case as we determine whether it's a template parameter in the template # body by the absence of the sfGenSym flag: - let retType = s.typ[0] + let retType = s.typ.returnType if retType != nil and retType.kind != tyUntyped: allUntyped = false for i in 1..<s.typ.n.len: @@ -673,7 +722,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = # XXX why do we need tyTyped as a return type again? s.typ.n = newNodeI(nkFormalParams, n.info) rawAddSon(s.typ, newTypeS(tyTyped, c)) - s.typ.n.add newNodeIT(nkType, n.info, s.typ[0]) + s.typ.n.add newNodeIT(nkType, n.info, s.typ.returnType) if n[genericParamsPos].safeLen == 0: # restore original generic type params as no explicit or implicit were found n[genericParamsPos] = n[miscPos][1] @@ -688,12 +737,24 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = if n[patternPos].kind != nkEmpty: n[patternPos] = semPattern(c, n[patternPos], s) - var ctx: TemplCtx - ctx.toBind = initIntSet() - ctx.toMixin = initIntSet() - ctx.toInject = initIntSet() - ctx.c = c - ctx.owner = s + var ctx = TemplCtx( + toBind: initIntSet(), + toMixin: initIntSet(), + toInject: initIntSet(), + c: c, + owner: s + ) + # handle default params: + for i in 1..<s.typ.n.len: + let param = s.typ.n[i].sym + if param.ast != nil: + # param default values need to be treated like template body: + if sfDirty in s.flags: + param.ast = semTemplBodyDirty(ctx, param.ast) + else: + param.ast = semTemplBody(ctx, param.ast) + if param.ast.referencesAnotherParam(s): + param.ast.flags.incl nfDefaultRefsParam if sfDirty in s.flags: n[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos]) else: @@ -703,11 +764,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = closeScope(c) popOwner(c) - # set the symbol AST after pragmas, at least. This stops pragma that have - # been pushed (implicit) to be explicitly added to the template definition - # and misapplied to the body. see #18113 - s.ast = n - if sfCustomPragma in s.flags: if n[bodyPos].kind != nkEmpty: localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) @@ -844,12 +900,13 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = proc semPattern(c: PContext, n: PNode; s: PSym): PNode = openScope(c) - var ctx: TemplCtx - ctx.toBind = initIntSet() - ctx.toMixin = initIntSet() - ctx.toInject = initIntSet() - ctx.c = c - ctx.owner = getCurrOwner(c) + var ctx = TemplCtx( + toBind: initIntSet(), + toMixin: initIntSet(), + toInject: initIntSet(), + c: c, + owner: getCurrOwner(c) + ) result = flattenStmts(semPatternBody(ctx, n)) if result.kind in {nkStmtList, nkStmtListExpr}: if result.len == 1: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 7968122ed..113946fef 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -15,7 +15,7 @@ const errStringLiteralExpected = "string literal expected" errIntLiteralExpected = "integer literal expected" errWrongNumberOfVariables = "wrong number of variables" - errInvalidOrderInEnumX = "invalid order in enum '$1'" + errDuplicateAliasInEnumX = "duplicate value in enum '$1'" errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)" errOrdinalTypeExpected = "ordinal type expected; given: $1" errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements" @@ -38,12 +38,12 @@ const errNoGenericParamsAllowedForX = "no generic parameters allowed for $1" errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types" -proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, sons: seq[PType]): PType = +proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, son: sink PType): PType = if prev == nil or prev.kind == tyGenericBody: - result = newTypeS(kind, c, sons = sons) + result = newTypeS(kind, c, son) else: result = prev - result.setSons(sons) + result.setSon(son) if result.kind == tyForward: result.kind = kind #if kind == tyError: result.flags.incl tfCheckedForDestructor @@ -69,6 +69,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = e: PSym = nil base: PType = nil identToReplace: ptr PNode = nil + counterSet = initPackedSet[BiggestInt]() counter = 0 base = nil result = newOrPrevType(tyEnum, prev, c) @@ -85,6 +86,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = var hasNull = false for i in 1..<n.len: if n[i].kind == nkEmpty: continue + var useAutoCounter = false case n[i].kind of nkEnumFieldDef: if n[i][0].kind == nkPragmaExpr: @@ -112,6 +114,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = of tyString, tyCstring: strVal = v x = counter + useAutoCounter = true else: if isOrdinalType(v.typ, allowEnumWithHoles=true): x = toInt64(getOrdValue(v)) @@ -120,22 +123,30 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc)) if i != 1: if x != counter: incl(result.flags, tfEnumHasHoles) - if x < counter: - localError(c.config, n[i].info, errInvalidOrderInEnumX % e.name.s) - x = counter e.ast = strVal # might be nil counter = x of nkSym: e = n[i].sym + useAutoCounter = true of nkIdent, nkAccQuoted: e = newSymS(skEnumField, n[i], c) identToReplace = addr n[i] + useAutoCounter = true of nkPragmaExpr: e = newSymS(skEnumField, n[i][0], c) pragma(c, e, n[i][1], enumFieldPragmas) identToReplace = addr n[i][0] + useAutoCounter = true else: illFormedAst(n[i], c.config) + + if useAutoCounter: + while counter in counterSet and counter != high(typeof(counter)): + inc counter + counterSet.incl counter + elif counterSet.containsOrIncl(counter): + localError(c.config, n[i].info, errDuplicateAliasInEnumX % e.name.s) + e.typ = result e.position = int(counter) let symNode = newSymNode(e) @@ -173,7 +184,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = if n.len == 2 and n[1].kind != nkEmpty: var base = semTypeNode(c, n[1], nil) addSonSkipIntLit(result, base, c.idgen) - if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base) + if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base) if base.kind notin {tyGenericParam, tyGenericInvocation}: if base.kind == tyForward: c.skipTypes.add n @@ -232,7 +243,7 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool = return true case t.kind of tyAlias, tyGenericInst, tyDistinct: - return isRecursiveType(t.lastSon, cycleDetector) + return isRecursiveType(t.skipModifier, cycleDetector) else: return false @@ -379,11 +390,11 @@ proc semArrayIndex(c: PContext, n: PNode): PType = localError(c.config, n.info, "Array length can't be negative, but was " & $e.intVal) result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) - elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc) : + elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc): if e.typ.kind == tyStatic: if e.sym.ast != nil: return semArrayIndex(c, e.sym.ast) - if e.typ.lastSon.kind != tyGenericParam and not isOrdinalType(e.typ.lastSon): + if e.typ.skipModifier.kind != tyGenericParam and not isOrdinalType(e.typ.skipModifier): let info = if n.safeLen > 1: n[1].info else: n.info localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) result = makeRangeWithStaticExpr(c, e) @@ -412,21 +423,24 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = # 3 = length(array indx base) let indx = semArrayIndex(c, n[1]) var indxB = indx - if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB) + if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = skipModifier(indxB) if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr} and tfUnresolved notin indxB.flags: - if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}: - discard - elif not isOrdinalType(indxB): + if not isOrdinalType(indxB): localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(indxB, preferDesc)) elif enumHasHoles(indxB): localError(c.config, n[1].info, "enum '$1' has holes" % typeToString(indxB.skipTypes({tyRange}))) + elif indxB.kind != tyRange and + lengthOrd(c.config, indxB) > high(uint16).int: + # assume range type is intentional + localError(c.config, n[1].info, + "index type '$1' for array is too large" % typeToString(indxB)) base = semTypeNode(c, n[2], nil) # ensure we only construct a tyArray when there was no error (bug #3048): # bug #6682: Do not propagate initialization requirements etc for the # index type: - result = newOrPrevType(tyArray, prev, c, @[indx]) + result = newOrPrevType(tyArray, prev, c, indx) addSonSkipIntLit(result, base, c.idgen) else: localError(c.config, n.info, errArrayExpectsTwoTypeParams) @@ -461,6 +475,13 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = let t = semTypeNode(c, it, nil) addSonSkipIntLitChecked(c, result, t, it, c.idgen) +proc firstRange(config: ConfigRef, t: PType): PNode = + if t.skipModifier().kind in tyFloat..tyFloat64: + result = newFloatNode(nkFloatLit, firstFloat(t)) + else: + result = newIntNode(nkIntLit, firstOrd(config, t)) + result.typ = t + proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType result = newOrPrevType(tyTuple, prev, c) @@ -477,8 +498,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = elif a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange: - a[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ)) - a[^1].typ = typ + a[^1] = firstRange(c.config, typ) hasDefaultField = true else: localError(c.config, a.info, errTypeExpected) @@ -526,7 +546,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, result = newSymG(kind, n, c) proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, - allowed: TSymFlags): PSym = + allowed: TSymFlags, fromTopLevel = false): PSym = if n.kind == nkPragmaExpr: checkSonsLen(n, 2, c.config) result = semIdentVis(c, kind, n[0], allowed) @@ -541,11 +561,15 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, else: discard else: result = semIdentVis(c, kind, n, allowed) + let invalidPragmasForPush = if fromTopLevel and sfWasGenSym notin result.flags: + {} + else: + {wExportc, wExportCpp, wDynlib} case kind of skField: implicitPragmas(c, result, n.info, fieldPragmas) - of skVar: implicitPragmas(c, result, n.info, varPragmas) - of skLet: implicitPragmas(c, result, n.info, letPragmas) - of skConst: implicitPragmas(c, result, n.info, constPragmas) + of skVar: implicitPragmas(c, result, n.info, varPragmas-invalidPragmasForPush) + of skLet: implicitPragmas(c, result, n.info, letPragmas-invalidPragmasForPush) + of skConst: implicitPragmas(c, result, n.info, constPragmas-invalidPragmasForPush) else: discard proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = @@ -556,15 +580,20 @@ proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = if overlap(t[i][j].skipConv, ex): localError(c.config, ex.info, errDuplicateCaseLabel) -proc semBranchRange(c: PContext, t, a, b: PNode, covered: var Int128): PNode = - checkMinSonsLen(t, 1, c.config) +proc semBranchRange(c: PContext, n, a, b: PNode, covered: var Int128): PNode = + checkMinSonsLen(n, 1, c.config) let ac = semConstExpr(c, a) let bc = semConstExpr(c, b) if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}: localError(c.config, b.info, "range of string is invalid") - let at = fitNode(c, t[0].typ, ac, ac.info).skipConvTakeType - let bt = fitNode(c, t[0].typ, bc, bc.info).skipConvTakeType - + var at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType + var bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType + # the calls to fitNode may introduce calls to converters + # mirrored with semCaseBranch for single elements + if at.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}: + at = semConstExpr(c, at) + if bt.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}: + bt = semConstExpr(c, bt) result = newNodeI(nkRange, a.info) result.add(at) result.add(bt) @@ -576,42 +605,44 @@ proc semCaseBranchRange(c: PContext, t, b: PNode, checkSonsLen(b, 3, c.config) result = semBranchRange(c, t, b[1], b[2], covered) -proc semCaseBranchSetElem(c: PContext, t, b: PNode, +proc semCaseBranchSetElem(c: PContext, n, b: PNode, covered: var Int128): PNode = if isRange(b): checkSonsLen(b, 3, c.config) - result = semBranchRange(c, t, b[1], b[2], covered) + result = semBranchRange(c, n, b[1], b[2], covered) elif b.kind == nkRange: checkSonsLen(b, 2, c.config) - result = semBranchRange(c, t, b[0], b[1], covered) + result = semBranchRange(c, n, b[0], b[1], covered) else: - result = fitNode(c, t[0].typ, b, b.info) + result = fitNode(c, n[0].typ, b, b.info) inc(covered) -proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, +proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int, covered: var Int128) = let lastIndex = branch.len - 2 for i in 0..lastIndex: var b = branch[i] if b.kind == nkRange: branch[i] = b + # same check as in semBranchRange for exhaustiveness + covered = covered + getOrdValue(b[1]) + 1 - getOrdValue(b[0]) elif isRange(b): - branch[i] = semCaseBranchRange(c, t, b, covered) + branch[i] = semCaseBranchRange(c, n, b, covered) else: # constant sets and arrays are allowed: # set expected type to selector type for type inference # even if it can be a different type like a set or array - var r = semConstExpr(c, b, expectedType = t[0].typ) + var r = semConstExpr(c, b, expectedType = n[0].typ) if r.kind in {nkCurly, nkBracket} and r.len == 0 and branch.len == 2: # discarding ``{}`` and ``[]`` branches silently delSon(branch, 0) return elif r.kind notin {nkCurly, nkBracket} or r.len == 0: - checkMinSonsLen(t, 1, c.config) - var tmp = fitNode(c, t[0].typ, r, r.info) + checkMinSonsLen(n, 1, c.config) + var tmp = fitNode(c, n[0].typ, r, r.info) # the call to fitNode may introduce a call to a converter - if tmp.kind == nkHiddenCallConv or - (tmp.kind == nkHiddenStdConv and t[0].typ.kind == tyCstring): + # mirrored with semBranchRange + if tmp.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}: tmp = semConstExpr(c, tmp) branch[i] = skipConv(tmp) inc(covered) @@ -620,18 +651,18 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, r = deduplicate(c.config, r) # first element is special and will overwrite: branch[i]: - branch[i] = semCaseBranchSetElem(c, t, r[0], covered) + branch[i] = semCaseBranchSetElem(c, n, r[0], covered) # other elements have to be added to ``branch`` for j in 1..<r.len: - branch.add(semCaseBranchSetElem(c, t, r[j], covered)) + branch.add(semCaseBranchSetElem(c, n, r[j], covered)) # caution! last son of branch must be the actions to execute: swap(branch[^2], branch[^1]) - checkForOverlap(c, t, i, branchIndex) + checkForOverlap(c, n, i, branchIndex) # Elements added above needs to be checked for overlaps. for i in lastIndex.succ..<branch.len - 1: - checkForOverlap(c, t, i, branchIndex) + checkForOverlap(c, n, i, branchIndex) proc toCover(c: PContext, t: PType): Int128 = let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) @@ -723,7 +754,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, of tyFloat..tyFloat128, tyError: discard of tyRange: - if skipTypes(typ[0], abstractInst).kind in shouldChckCovered: + if skipTypes(typ.elementType, abstractInst).kind in shouldChckCovered: chckCovered = true of tyForward: errorUndeclaredIdentifier(c, n[0].info, typ.sym.name.s) @@ -765,6 +796,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, of nkRecWhen: var a = copyTree(n) var branch: PNode = nil # the branch to take + var cannotResolve = false # no branch should be taken for i in 0..<a.len: var it = a[i] if it == nil: illFormedAst(n, c.config) @@ -782,24 +814,30 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, let e = semExprWithType(c, it[0], {efDetermineType}) if e.typ.kind == tyFromExpr: it[0] = makeStaticExpr(c, e) + cannotResolve = true else: it[0] = forceBool(c, e) + let val = getConstExpr(c.module, it[0], c.idgen, c.graph) + if val == nil or val.kind != nkIntLit: + cannotResolve = true + elif not cannotResolve and val.intVal != 0 and branch == nil: + branch = it[1] of nkElse: checkSonsLen(it, 1, c.config) - if branch == nil: branch = it[0] + if branch == nil and not cannotResolve: branch = it[0] idx = 0 else: illFormedAst(n, c.config) - if c.inGenericContext > 0: + if c.inGenericContext > 0 and cannotResolve: # use a new check intset here for each branch: var newCheck: IntSet = check var newPos = pos var newf = newNodeI(nkRecList, n.info) semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields) it[idx] = if newf.len == 1: newf[0] else: newf - if c.inGenericContext > 0: - father.add a - elif branch != nil: + if branch != nil: semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields) + elif cannotResolve: + father.add a elif father.kind in {nkElse, nkOfBranch}: father.add newNodeI(nkRecList, n.info) of nkRecCase: @@ -828,8 +866,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, else: typ = semTypeNode(c, n[^2], nil) if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange: - n[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ)) - n[^1].typ = typ + n[^1] = firstRange(c.config, typ) hasDefaultField = true propagateToOwner(rectype, typ) var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner @@ -846,8 +883,8 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, f.options = c.config.options if fieldOwner != nil and {sfImportc, sfExportc} * fieldOwner.flags != {} and - not hasCaseFields and f.loc.r == "": - f.loc.r = rope(f.name.s) + not hasCaseFields and f.loc.snippet == "": + f.loc.snippet = rope(f.name.s) f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags inc(pos) if containsOrIncl(check, f.name.id): @@ -897,7 +934,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} = if result.kind == tyGenericInvocation: result = result[0] while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias, tySink, tyOwned}: - result = lastSon(result) + result = skipModifier(result) proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int, obj: PType, n: PNode, isPartial = false, innerObj: PType = nil): bool = @@ -1013,16 +1050,16 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = addSonSkipIntLit(result, region, c.idgen) addSonSkipIntLit(result, t, c.idgen) if tfPartial in result.flags: - if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial) + if result.elementType.kind == tyObject: incl(result.elementType.flags, tfPartial) # if not isNilable: result.flags.incl tfNotNil case wrapperKind of tyOwned: if optOwnedRefs in c.config.globalOptions: - let t = newTypeS(tyOwned, c, @[result]) + let t = newTypeS(tyOwned, c, result) t.flags.incl tfHasOwned result = t of tySink: - let t = newTypeS(tySink, c, @[result]) + let t = newTypeS(tySink, c, result) result = t else: discard if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: @@ -1036,7 +1073,7 @@ proc findEnforcedStaticType(t: PType): PType = if t == nil: return nil if t.kind == tyStatic: return t if t.kind == tyAnd: - for s in t: + for s in t.kids: let t = findEnforcedStaticType(s) if t != nil: return t @@ -1117,7 +1154,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, let base = (if lifted != nil: lifted else: paramType.base) if base.isMetaType and procKind == skMacro: localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName) - result = addImplicitGeneric(c, c.newTypeWithSons(tyStatic, @[base]), + result = addImplicitGeneric(c, newTypeS(tyStatic, c, base), paramTypId, info, genericParams, paramName) if result != nil: result.flags.incl({tfHasStatic, tfUnresolved}) @@ -1129,7 +1166,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramTypId.id == getIdent(c.cache, "type").id): # XXX Why doesn't this check for tyTypeDesc instead? paramTypId = nil - let t = c.newTypeWithSons(tyTypeDesc, @[paramType.base]) + let t = newTypeS(tyTypeDesc, c, paramType.base) incl t.flags, tfCheckedForDestructor result = addImplicitGeneric(c, t, paramTypId, info, genericParams, paramName) else: @@ -1148,20 +1185,20 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType[i] = t result = paramType - of tyAlias, tyOwned, tySink: + of tyAlias, tyOwned: result = recurse(paramType.base) of tySequence, tySet, tyArray, tyOpenArray, - tyVar, tyLent, tyPtr, tyRef, tyProc: + tyVar, tyLent, tyPtr, tyRef, tyProc, tySink: # XXX: this is a bit strange, but proc(s: seq) # produces tySequence(tyGenericParam, tyNone). # This also seems to be true when creating aliases # like: type myseq = distinct seq. # Maybe there is another better place to associate # the seq type class with the seq identifier. - if paramType.kind == tySequence and paramType.lastSon.kind == tyNone: - let typ = c.newTypeWithSons(tyBuiltInTypeClass, - @[newTypeS(paramType.kind, c)]) + if paramType.kind == tySequence and paramType.elementType.kind == tyNone: + let typ = newTypeS(tyBuiltInTypeClass, c, + newTypeS(paramType.kind, c)) result = addImplicitGeneric(c, typ, paramTypId, info, genericParams, paramName) else: result = nil @@ -1185,21 +1222,21 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, else: result.rawAddSon newTypeS(tyAnything, c) - if paramType.lastSon.kind == tyUserTypeClass: + if paramType.typeBodyImpl.kind == tyUserTypeClass: result.kind = tyUserTypeClassInst - result.rawAddSon paramType.lastSon + result.rawAddSon paramType.typeBodyImpl return addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName) let x = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) - result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, x]) - #result = newTypeS(tyCompositeTypeClass, c) - #for i in 0..<x.len: result.rawAddSon(x[i]) + result = newTypeS(tyCompositeTypeClass, c) + result.rawAddSon paramType + result.rawAddSon x result = addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName) of tyGenericInst: result = nil - if paramType.lastSon.kind == tyUserTypeClass: + if paramType.skipModifier.kind == tyUserTypeClass: var cp = copyType(paramType, c.idgen, getCurrOwner(c)) copyTypeProps(c.graph, c.idgen.module, cp, paramType) @@ -1211,9 +1248,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if lifted != nil: paramType[i] = lifted result = paramType - result.lastSon.shouldHaveMeta + result.last.shouldHaveMeta - let liftBody = recurse(paramType.lastSon, true) + let liftBody = recurse(paramType.skipModifier, true) if liftBody != nil: result = liftBody result.flags.incl tfHasMeta @@ -1232,7 +1269,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # before one of its param types return - if body.lastSon.kind == tyUserTypeClass: + if body.last.kind == tyUserTypeClass: let expanded = instGenericContainer(c, info, paramType, allowMetaTypes = true) result = recurse(expanded, true) @@ -1259,7 +1296,7 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = result = semTypeNode(c, n[0], nil) constraint = semNodeKindConstraints(n, c.config, 1) elif n.kind == nkCall and - n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and + n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and considerQuotedIdent(c, n[0]).s == "{}": result = semTypeNode(c, n[1], nil) constraint = semNodeKindConstraints(n, c.config, 2) @@ -1289,6 +1326,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result = newProcType(c, n.info, prev) var check = initIntSet() var counter = 0 + template isCurrentlyGeneric: bool = + # genericParams might update as implicit generic params are added + genericParams != nil and genericParams.len > 0 for i in 1..<n.len: var a = n[i] @@ -1309,7 +1349,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, hasDefault = a[^1].kind != nkEmpty if hasType: + let isGeneric = isCurrentlyGeneric() + inc c.inGenericContext, ord(isGeneric) typ = semParamType(c, a[^2], constraint) + dec c.inGenericContext, ord(isGeneric) # TODO: Disallow typed/untyped in procs in the compiler/stdlib if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped): if not isMagic(getCurrOwner(c)): @@ -1329,15 +1372,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, "either use ';' (semicolon) or explicitly write each default value") message(c.config, a.info, warnImplicitDefaultValue, msg) block determineType: - var defTyp = typ - if genericParams != nil and genericParams.len > 0: - defTyp = nil - def = semGenericStmt(c, def) - if hasUnresolvedArgs(c, def): + var canBeVoid = false + if kind == skTemplate: + if typ != nil and typ.kind == tyUntyped: + # don't do any typechecking or assign a type for + # `untyped` parameter default value + break determineType + elif hasUnresolvedArgs(c, def): + # template default value depends on other parameter + # don't do any typechecking def.typ = makeTypeFromExpr(c, def.copyTree) break determineType - - def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, defTyp) + elif typ != nil and typ.kind == tyTyped: + canBeVoid = true + let isGeneric = isCurrentlyGeneric() + inc c.inGenericContext, ord(isGeneric) + if canBeVoid: + def = semExpr(c, def, {efDetermineType, efAllowSymChoice}, typ) + else: + def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ) + dec c.inGenericContext, ord(isGeneric) if def.referencesAnotherParam(getCurrOwner(c)): def.flags.incl nfDefaultRefsParam @@ -1353,10 +1407,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # which will prevent other types from matching - clearly a very # surprising behavior. We must instead fix the expected type of # the proc to be the unbound typedesc type: - typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) + typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c)) typ.flags.incl tfCheckedForDestructor - else: + elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void # if def.typ != nil and def.typ.kind != tyNone: # example code that triggers it: # proc sort[T](cmp: proc(a, b: T): int = cmp) @@ -1409,11 +1463,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, onDef(a[j].info, arg) a[j] = newSymNode(arg) - var r: PType = - if n[0].kind != nkEmpty: - semTypeNode(c, n[0], nil) - else: - nil + var r: PType = nil + if n[0].kind != nkEmpty: + let isGeneric = isCurrentlyGeneric() + inc c.inGenericContext, ord(isGeneric) + r = semTypeNode(c, n[0], nil) + dec c.inGenericContext, ord(isGeneric) if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped: # XXX: To implement the proposed change in the warning, just @@ -1436,7 +1491,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, "' is only valid for macros and templates") # 'auto' as a return type does not imply a generic: elif r.kind == tyAnything: - discard + r = copyType(r, c.idgen, r.owner) + r.flags.incl tfRetType elif r.kind == tyStatic: # type allowed should forbid this type discard @@ -1465,7 +1521,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result.flags.excl tfHasMeta result.n.typ = r - if genericParams != nil and genericParams.len > 0: + if isCurrentlyGeneric(): for n in genericParams: if {sfUsed, sfAnon} * n.sym.flags == {}: result.flags.incl tfUnresolved @@ -1509,7 +1565,7 @@ proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): b check = initIntSet() pos = 0 let - realBase = t[0] + realBase = t.baseClass base = skipTypesOrNil(realBase, skipPtrs) result = true if base.isNil: @@ -1622,7 +1678,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = recomputeFieldPositions(tx, tx.n, position) proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType = - if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody} and prev != nil: + if prev != nil and (prev.kind == tyGenericBody or + typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}): result = newTypeS(tyAlias, c) result.rawAddSon typeExpr result.sym = prev.sym @@ -1660,6 +1717,10 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType = # unnecessary new type creation let alias = maybeAliasType(c, result, prev) if alias != nil: result = alias + elif n.typ.kind == tyFromExpr and c.inGenericContext > 0: + # sometimes not possible to distinguish type from value in generic body, + # for example `T.Foo`, so both are handled under `tyFromExpr` + result = n.typ else: localError(c.config, n.info, "expected type, but got: " & n.renderTree) result = errorType(c) @@ -1693,8 +1754,8 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = inherited = n[2] var owner = getCurrOwner(c) - var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType], c.idgen) - result = newOrPrevType(tyUserTypeClass, prev, c, sons = @[candidateTypeSlot]) + var candidateTypeSlot = newTypeS(tyAlias, c, c.errorType) + result = newOrPrevType(tyUserTypeClass, prev, c, son = candidateTypeSlot) result.flags.incl tfCheckedForDestructor result.n = n @@ -1745,12 +1806,15 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode = discard "builtin pragma" else: trySuggestPragmas(c, key) - let ident = considerQuotedIdent(c, key) - if strTableGet(c.userPragmas, ident) != nil: + let ident = + if key.kind in nkIdentKinds: + considerQuotedIdent(c, key) + else: + nil + if ident != nil and strTableGet(c.userPragmas, ident) != nil: discard "User-defined pragma" else: - var amb = false - let sym = searchInScopes(c, ident, amb) + let sym = qualifiedLookUp(c, key, {}) # XXX: What to do here if amb is true? if sym != nil and sfCustomPragma in sym.flags: discard "Custom user pragma" @@ -1817,10 +1881,14 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType = proc semTypeOf(c: PContext; n: PNode; prev: PType): PType = openScope(c) + inc c.inTypeofContext + defer: dec c.inTypeofContext # compiles can raise an exception let t = semExprWithType(c, n, {efInTypeof}) closeScope(c) fixupTypeOf(c, prev, t) result = t.typ + if result.kind == tyFromExpr: + result.flags.incl tfNonConstExpr proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType = openScope(c) @@ -1831,10 +1899,14 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType = localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time") else: m = mode.intVal + inc c.inTypeofContext + defer: dec c.inTypeofContext # compiles can raise an exception let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {}) closeScope(c) fixupTypeOf(c, prev, t) result = t.typ + if result.kind == tyFromExpr: + result.flags.incl tfNonConstExpr proc semTypeIdent(c: PContext, n: PNode): PSym = if n.kind == nkSym: @@ -1857,7 +1929,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = # it's not bound when it's used multiple times in the # proc signature for example if c.inGenericInst > 0: - let bound = result.typ[0].sym + let bound = result.typ.elementType.sym if bound != nil: return bound return result if result.typ.sym == nil: @@ -1879,7 +1951,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = return errorSym(c, n) if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}: # this implements the wanted ``var v: V, x: V`` feature ... - var ov: TOverloadIter + var ov: TOverloadIter = default(TOverloadIter) var amb = initOverloadIter(ov, c, n) while amb != nil and amb.kind != skType: amb = nextOverloadIter(ov, c, n) @@ -1922,11 +1994,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkTupleConstr: result = semAnonTuple(c, n, prev) of nkCallKinds: let x = n[0] - let ident = case x.kind - of nkIdent: x.ident - of nkSym: x.sym.name - of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name - else: nil + let ident = x.getPIdent if ident != nil and ident.s == "[]": let b = newNodeI(nkBracketExpr, n.info) for i in 1..<n.len: b.add(n[i]) @@ -2016,20 +2084,21 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = elif op.id == ord(wType): checkSonsLen(n, 2, c.config) result = semTypeOf(c, n[1], prev) - elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf: + elif op.s == "typeof" and ( + (n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or + (n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)): result = semTypeOf2(c, n, prev) elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2: result = semTypeExpr(c, n[1], prev) else: - if c.inGenericContext > 0 and n.kind == nkCall: - let n = semGenericStmt(c, n) - result = makeTypeFromExpr(c, n.copyTree) - else: - result = semTypeExpr(c, n, prev) + result = semTypeExpr(c, n, prev) of nkWhenStmt: var whenResult = semWhen(c, n, false) if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType) - result = semTypeNode(c, whenResult, prev) + if whenResult.kind == nkWhenStmt: + result = whenResult.typ + else: + result = semTypeNode(c, whenResult, prev) of nkBracketExpr: checkMinSonsLen(n, 2, c.config) var head = n[0] @@ -2074,6 +2143,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mRef: result = semAnyRef(c, n, tyRef, prev) of mPtr: result = semAnyRef(c, n, tyPtr, prev) of mTuple: result = semTuple(c, n, prev) + of mBuiltinType: + case s.name.s + of "lent": result = semAnyRef(c, n, tyLent, prev) + of "sink": result = semAnyRef(c, n, tySink, prev) + of "owned": result = semAnyRef(c, n, tyOwned, prev) + else: result = semGeneric(c, n, s, prev) else: result = semGeneric(c, n, s, prev) of nkDotExpr: let typeExpr = semExpr(c, n) @@ -2103,7 +2178,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if s.kind != skError: localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) elif s.kind == skParam and s.typ.kind == tyTypeDesc: - internalAssert c.config, s.typ.base.kind != tyNone and prev == nil + internalAssert c.config, s.typ.base.kind != tyNone result = s.typ.base elif prev == nil: result = s.typ @@ -2127,7 +2202,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if s.kind == skType: s.typ else: - internalAssert c.config, s.typ.base.kind != tyNone and prev == nil + internalAssert c.config, s.typ.base.kind != tyNone s.typ.base let alias = maybeAliasType(c, t, prev) if alias != nil: @@ -2176,6 +2251,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: let symKind = if n.kind == nkIteratorTy: skIterator else: skProc result = semProcTypeWithScope(c, n, prev, symKind) + if result == nil: + localError(c.config, n.info, "type expected, but got: " & renderTree(n)) + result = newOrPrevType(tyError, prev, c) + if n.kind == nkIteratorTy and result.kind == tyProc: result.flags.incl(tfIterator) if result.callConv == ccClosure and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: @@ -2184,6 +2263,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkType: result = n.typ of nkStmtListType: result = semStmtListType(c, n, prev) of nkBlockType: result = semBlockType(c, n, prev) + of nkOpenSym: result = semTypeNode(c, n[0], prev) else: result = semTypeExpr(c, n, prev) when false: @@ -2296,7 +2376,7 @@ proc processMagicType(c: PContext, m: PSym) = else: localError(c.config, m.info, errTypeExpected) proc semGenericConstraints(c: PContext, x: PType): PType = - result = newTypeWithSons(c, tyGenericParam, @[x]) + result = newTypeS(tyGenericParam, c, x) proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = @@ -2322,8 +2402,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = typ = semTypeNode(c, constraint, nil) if typ.kind != tyStatic or typ.len == 0: if typ.kind == tyTypeDesc: - if typ[0].kind == tyNone: - typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) + if typ.elementType.kind == tyNone: + typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c)) incl typ.flags, tfCheckedForDestructor else: typ = semGenericConstraints(c, typ) @@ -2332,7 +2412,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = def = semConstExpr(c, def) if typ == nil: if def.typ.kind != tyTypeDesc: - typ = newTypeWithSons(c, tyStatic, @[def.typ]) + typ = newTypeS(tyStatic, c, def.typ) else: # the following line fixes ``TV2*[T:SomeNumber=TR] = array[0..1, T]`` # from manyloc/named_argument_bug/triengine: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index bb664c71f..759e8e6ab 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -9,6 +9,8 @@ # This module does the instantiation of generic types. +import std / tables + import ast, astalgo, msgs, types, magicsys, semdata, renderer, options, lineinfos, modulegraphs @@ -18,20 +20,16 @@ when defined(nimPreviewSlimSystem): const tfInstClearedFlags = {tfHasMeta, tfUnresolved} proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) = - if t.kind in {tyVar, tyLent} and t[0].kind in {tyVar, tyLent}: + if t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}: localError(conf, info, "type 'var var' is not allowed") proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) = var t = typ.skipTypes({tyDistinct}) if t.kind in tyTypeClasses: discard - elif t.kind in {tyVar, tyLent} and t[0].kind in {tyVar, tyLent}: + elif t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}: localError(conf, info, "type 'var var' is not allowed") elif computeSize(conf, t) == szIllegalRecursion or isTupleRecursive(t): localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'") - when false: - if t.kind == tyObject and t[0] != nil: - if t[0].kind != tyObject or tfFinal in t[0].flags: - localError(info, errInheritanceOnlyWithNonFinalObjects) proc searchInstTypes*(g: ModuleGraph; key: PType): PType = result = nil @@ -41,7 +39,7 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType = for inst in typeInstCacheItems(g, genericTyp.sym): if inst.id == key.id: return inst - if inst.len < key.len: + if inst.kidsLen < key.kidsLen: # XXX: This happens for prematurely cached # types such as Channel[empty]. Why? # See the notes for PActor in handleGenericInvocation @@ -51,7 +49,7 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType = continue block matchType: - for j in 1..<len(key): + for j in FirstGenericParamAt..<key.kidsLen: # XXX sameType is not really correct for nested generics? if not compareTypes(inst[j], key[j], flags = {ExactGenericParams, PickyCAliases}): @@ -61,21 +59,21 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType = proc cacheTypeInst(c: PContext; inst: PType) = let gt = inst[0] - let t = if gt.kind == tyGenericBody: gt.lastSon else: gt + let t = if gt.kind == tyGenericBody: gt.typeBodyImpl else: gt if t.kind in {tyStatic, tyError, tyGenericParam} + tyTypeClasses: return addToGenericCache(c, gt.sym, inst) type LayeredIdTable* {.acyclic.} = ref object - topLayer*: TIdTable + topLayer*: TypeMapping nextLayer*: LayeredIdTable TReplTypeVars* = object c*: PContext typeMap*: LayeredIdTable # map PType to PType - symMap*: TIdTable # map PSym to PSym - localCache*: TIdTable # local cache for remembering already replaced + symMap*: SymMapping # map PSym to PSym + localCache*: TypeMapping # local cache for remembering already replaced # types during instantiation of meta types # (they are not stored in the global cache) info*: TLineInfo @@ -87,26 +85,26 @@ type recursionLimit: int proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType -proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym +proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode -proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable = +proc initLayeredTypeMap*(pt: sink TypeMapping): LayeredIdTable = result = LayeredIdTable() - copyIdTable(result.topLayer, pt) + result.topLayer = pt proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable = - result = LayeredIdTable(nextLayer: cl.typeMap, topLayer: initIdTable()) + result = LayeredIdTable(nextLayer: cl.typeMap, topLayer: initTable[ItemId, PType]()) proc lookup(typeMap: LayeredIdTable, key: PType): PType = result = nil var tm = typeMap while tm != nil: - result = PType(idTableGet(tm.topLayer, key)) + result = getOrDefault(tm.topLayer, key.itemId) if result != nil: return tm = tm.nextLayer template put(typeMap: LayeredIdTable, key, value: PType) = - idTablePut(typeMap.topLayer, key, value) + typeMap.topLayer[key.itemId] = value template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code when false: @@ -120,19 +118,86 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = result = replaceTypeVarsTAux(cl, t) checkMetaInvariants(cl, result) -proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = +proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode = + ## instantiates a given generic expression, not a type node + if n.kind == nkSym and n.sym.kind == skType and + n.sym.typ != nil and n.sym.typ.kind == tyGenericBody: + # generic body types are allowed as user expressions, see #24090 + return n let t = replaceTypeVarsT(cl, n.typ) if t != nil and t.kind == tyStatic and t.n != nil: return if tfUnresolved in t.flags: prepareNode(cl, t.n) else: t.n result = copyNode(n) result.typ = t - if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym) - let isCall = result.kind in nkCallKinds - for i in 0..<n.safeLen: - # XXX HACK: ``f(a, b)``, avoid to instantiate `f` - if isCall and i == 0: result.add(n[i]) - else: result.add(prepareNode(cl, n[i])) + if result.kind == nkSym: + result.sym = + if n.typ != nil and n.typ == n.sym.typ: + replaceTypeVarsS(cl, n.sym, result.typ) + else: + replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ)) + # we need to avoid trying to instantiate nodes that can have uninstantiated + # types, like generic proc symbols or raw generic type symbols + case n.kind + of nkSymChoices: + # don't try to instantiate symchoice symbols, they can be + # generic procs which the compiler will think are uninstantiated + # because their type will contain uninstantiated params + for i in 0..<n.len: + result.add(n[i]) + of nkCallKinds: + # don't try to instantiate call names since they may be generic proc syms + # also bracket expressions can turn into calls with symchoice [] and + # we need to not instantiate the Generic in Generic[int] + # exception exists for the call name being a dot expression since + # dot expressions need their LHS instantiated + assert n.len != 0 + # avoid instantiating generic proc symbols, refine condition if needed: + let ignoreFirst = n[0].kind notin {nkDotExpr, nkBracketExpr} + nkCallKinds + let name = n[0].getPIdent + let ignoreSecond = name != nil and name.s == "[]" and n.len > 1 and + # generic type instantiation: + ((n[1].typ != nil and n[1].typ.kind == tyTypeDesc) or + # generic proc instantiation: + (n[1].kind == nkSym and n[1].sym.isGenericRoutineStrict)) + if ignoreFirst: + result.add(n[0]) + else: + result.add(prepareNode(cl, n[0])) + if n.len > 1: + if ignoreSecond: + result.add(n[1]) + else: + result.add(prepareNode(cl, n[1])) + for i in 2..<n.len: + result.add(prepareNode(cl, n[i])) + of nkBracketExpr: + # don't instantiate Generic body type in expression like Generic[T] + # exception exists for the call name being a dot expression since + # dot expressions need their LHS instantiated + assert n.len != 0 + let ignoreFirst = n[0].kind != nkDotExpr and + # generic type instantiation: + ((n[0].typ != nil and n[0].typ.kind == tyTypeDesc) or + # generic proc instantiation: + (n[0].kind == nkSym and n[0].sym.isGenericRoutineStrict)) + if ignoreFirst: + result.add(n[0]) + else: + result.add(prepareNode(cl, n[0])) + for i in 1..<n.len: + result.add(prepareNode(cl, n[i])) + of nkDotExpr: + # don't try to instantiate RHS of dot expression, it can outright be + # undeclared, but definitely instantiate LHS + assert n.len >= 2 + result.add(prepareNode(cl, n[0])) + result.add(n[1]) + for i in 2..<n.len: + result.add(prepareNode(cl, n[i])) + else: + for i in 0..<n.safeLen: + result.add(prepareNode(cl, n[i])) proc isTypeParam(n: PNode): bool = # XXX: generic params should use skGenericParam instead of skType @@ -215,6 +280,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT if n == nil: return result = copyNode(n) if n.typ != nil: + if n.typ.kind == tyFromExpr: + # type of node should not be evaluated as a static value + n.typ.flags.incl tfNonConstExpr result.typ = replaceTypeVarsT(cl, n.typ) checkMetaInvariants(cl, result.typ) case n.kind @@ -222,8 +290,13 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT discard of nkOpenSymChoice, nkClosedSymChoice: result = n of nkSym: - result.sym = replaceTypeVarsS(cl, n.sym) - if result.sym.typ.kind == tyVoid: + result.sym = + if n.typ != nil and n.typ == n.sym.typ: + replaceTypeVarsS(cl, n.sym, result.typ) + else: + replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ)) + # sym type can be nil if was gensym created by macro, see #24048 + if result.sym.typ != nil and result.sym.typ.kind == tyVoid: # don't add the 'void' field result = newNodeI(nkRecList, n.info) of nkRecWhen: @@ -264,7 +337,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT for i in start..<n.len: result[i] = replaceTypeVarsN(cl, n[i]) -proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = +proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym = if s == nil: return nil # symbol is not our business: if cl.owner != nil and s.owner != cl.owner: @@ -305,11 +378,14 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = incl(result.flags, sfFromGeneric) #idTablePut(cl.symMap, s, result) result.owner = s.owner - result.typ = replaceTypeVarsT(cl, s.typ) + result.typ = t if result.kind != skType: result.ast = replaceTypeVarsN(cl, s.ast) proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType = + if tfRetType in t.flags and t.kind == tyAnything: + # don't bind `auto` return type to a previous binding of `auto` + return nil result = cl.typeMap.lookup(t) if result == nil: if cl.allowMetaTypes or tfRetType in t.flags: return @@ -347,13 +423,13 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # tyGenericInvocation[A, tyGenericInvocation[A, B]] # is difficult to handle: - var body = t[0] + var body = t.genericHead if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body") var header = t # search for some instantiation here: if cl.allowMetaTypes: - result = PType(idTableGet(cl.localCache, t)) + result = getOrDefault(cl.localCache, t.itemId) else: result = searchInstTypes(cl.c.graph, t) @@ -361,7 +437,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = when defined(reportCacheHits): echo "Generic instantiation cached ", typeToString(result), " for ", typeToString(t) return - for i in 1..<t.len: + for i in FirstGenericParamAt..<t.kidsLen: var x = t[i] if x.kind in {tyGenericParam}: x = lookupTypeVar(cl, x) @@ -383,7 +459,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = else: header = instCopyType(cl, t) - result = newType(tyGenericInst, cl.c.idgen, t[0].owner, sons = @[header[0]]) + result = newType(tyGenericInst, cl.c.idgen, t.genericHead.owner, son = header.genericHead) result.flags = header.flags # be careful not to propagate unnecessary flags here (don't use rawAddSon) # ugh need another pass for deeply recursive generic types (e.g. PActor) @@ -392,14 +468,14 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = if not cl.allowMetaTypes: cacheTypeInst(cl.c, result) else: - idTablePut(cl.localCache, t, result) + cl.localCache[t.itemId] = result let oldSkipTypedesc = cl.skipTypedesc cl.skipTypedesc = true cl.typeMap = newTypeMapLayer(cl) - for i in 1..<t.len: + for i in FirstGenericParamAt..<t.kidsLen: var x = replaceTypeVarsT(cl): if header[i].kind == tyGenericInst: t[i] @@ -410,7 +486,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = propagateToOwner(header, x) cl.typeMap.put(body[i-1], x) - for i in 1..<t.len: + for i in FirstGenericParamAt..<t.kidsLen: # if one of the params is not concrete, we cannot do anything # but we already raised an error! rawAddSon(result, header[i], propagateHasAsgn = false) @@ -418,7 +494,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = if body.kind == tyError: return - let bbody = lastSon body + let bbody = last body var newbody = replaceTypeVarsT(cl, bbody) cl.skipTypedesc = oldSkipTypedesc newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) @@ -449,11 +525,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim # need to look into this issue later assert newbody.kind in {tyRef, tyPtr} - if newbody.lastSon.typeInst != nil: + if newbody.last.typeInst != nil: #internalError(cl.c.config, cl.info, "ref already has a 'typeInst' field") discard else: - newbody.lastSon.typeInst = result + newbody.last.typeInst = result # DESTROY: adding object|opt for opt[topttree.Tree] # sigmatch: Formal opt[=destroy.T] real opt[topttree.Tree] # adding myseq for myseq[system.int] @@ -473,14 +549,14 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = proc eraseVoidParams*(t: PType) = # transform '(): void' into '()' because old parts of the compiler really # don't deal with '(): void': - if t[0] != nil and t[0].kind == tyVoid: - t[0] = nil + if t.returnType != nil and t.returnType.kind == tyVoid: + t.setReturnType nil - for i in 1..<t.len: + for i in FirstParamAt..<t.signatureLen: # don't touch any memory unless necessary if t[i].kind == tyVoid: var pos = i - for j in i+1..<t.len: + for j in i+1..<t.signatureLen: if t[j].kind != tyVoid: t[pos] = t[j] t.n[pos] = t.n[j] @@ -490,8 +566,7 @@ proc eraseVoidParams*(t: PType) = break proc skipIntLiteralParams*(t: PType; idgen: IdGenerator) = - for i in 0..<t.len: - let p = t[i] + for i, p in t.ikids: if p == nil: continue let skipped = p.skipIntLit(idgen) if skipped != p: @@ -500,8 +575,8 @@ proc skipIntLiteralParams*(t: PType; idgen: IdGenerator) = # when the typeof operator is used on a static input # param, the results gets infected with static as well: - if t[0] != nil and t[0].kind == tyStatic: - t[0] = t[0].base + if t.returnType != nil and t.returnType.kind == tyStatic: + t.setReturnType t.returnType.skipModifier proc propagateFieldFlags(t: PType, n: PNode) = # This is meant for objects and tuples @@ -540,24 +615,27 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = # type # Vector[N: static[int]] = array[N, float64] # TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb]) - result = PType(idTableGet(cl.localCache, t)) + result = getOrDefault(cl.localCache, t.itemId) if result != nil: return result inc cl.recursionLimit result = t if t == nil: return - if t.kind in {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses: + const lookupMetas = {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses - {tyAnything} + if t.kind in lookupMetas or + (t.kind == tyAnything and tfRetType notin t.flags): let lookup = cl.typeMap.lookup(t) if lookup != nil: return lookup case t.kind of tyGenericInvocation: result = handleGenericInvocation(cl, t) - if result.lastSon.kind == tyUserTypeClass: + if result.last.kind == tyUserTypeClass: result.kind = tyUserTypeClassInst of tyGenericBody: + if cl.allowMetaTypes: return localError( cl.c.config, cl.info, @@ -576,20 +654,25 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = assert t.n.typ != t var n = prepareNode(cl, t.n) if n.kind != nkEmpty: - n = cl.c.semConstExpr(cl.c, n) + if tfNonConstExpr in t.flags: + n = cl.c.semExprWithType(cl.c, n, flags = {efInTypeof}) + else: + n = cl.c.semConstExpr(cl.c, n) if n.typ.kind == tyTypeDesc: # XXX: sometimes, chained typedescs enter here. # It may be worth investigating why this is happening, # because it may cause other bugs elsewhere. result = n.typ.skipTypes({tyTypeDesc}) # result = n.typ.base + elif tfNonConstExpr in t.flags: + result = n.typ else: if n.typ.kind != tyStatic and n.kind != nkType: # XXX: In the future, semConstExpr should # return tyStatic values to let anyone make # use of this knowledge. The patching here # won't be necessary then. - result = newTypeS(tyStatic, cl.c, sons = @[n.typ]) + result = newTypeS(tyStatic, cl.c, son = n.typ) result.n = n else: result = n.typ @@ -605,19 +688,42 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = makeTypeDesc(cl.c, result) elif tfUnresolved in t.flags or cl.skipTypedesc: result = result.base - elif t[0].kind != tyNone: - result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t[0])) + elif t.elementType.kind != tyNone: + result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.elementType)) - of tyUserTypeClass, tyStatic: + of tyUserTypeClass: result = t + + of tyStatic: + if cl.c.matchedConcept != nil: + # allow concepts to not instantiate statics for now + # they can't always infer them + return + if not containsGenericType(t) and (t.n == nil or t.n.kind in nkLiterals): + # no need to instantiate + return + bailout() + result = instCopyType(cl, t) + cl.localCache[t.itemId] = result + for i in FirstGenericParamAt..<result.kidsLen: + var r = result[i] + if r != nil: + r = replaceTypeVarsT(cl, r) + result[i] = r + propagateToOwner(result, r) + result.n = replaceTypeVarsN(cl, result.n) + if not cl.allowMetaTypes and result.n != nil and + result.base.kind != tyNone: + result.n = cl.c.semConstExpr(cl.c, result.n) + result.n.typ = result.base of tyGenericInst, tyUserTypeClassInst: bailout() result = instCopyType(cl, t) - idTablePut(cl.localCache, t, result) - for i in 1..<result.len: + cl.localCache[t.itemId] = result + for i in FirstGenericParamAt..<result.kidsLen: result[i] = replaceTypeVarsT(cl, result[i]) - propagateToOwner(result, result.lastSon) + propagateToOwner(result, result.last) else: if containsGenericType(t): @@ -626,17 +732,17 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = instCopyType(cl, t) result.size = -1 # needs to be recomputed #if not cl.allowMetaTypes: - idTablePut(cl.localCache, t, result) + cl.localCache[t.itemId] = result - for i in 0..<result.len: - if result[i] != nil: - if result[i].kind == tyGenericBody: + for i, resulti in result.ikids: + if resulti != nil: + if resulti.kind == tyGenericBody and not cl.allowMetaTypes: localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info, "cannot instantiate '" & typeToString(result[i], preferDesc) & "' inside of type definition: '" & t.owner.name.s & "'; Maybe generic arguments are missing?") - var r = replaceTypeVarsT(cl, result[i]) + var r = replaceTypeVarsT(cl, resulti) if result.kind == tyObject: # carefully coded to not skip the precious tyGenericInst: let r2 = r.skipTypes({tyAlias, tySink, tyOwned}) @@ -649,7 +755,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result.n = replaceTypeVarsN(cl, result.n, ord(result.kind==tyProc)) case result.kind of tyArray: - let idx = result[0] + let idx = result.indexType internalAssert cl.c.config, idx.kind != tyStatic of tyObject, tyTuple: @@ -662,7 +768,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = skipIntLiteralParams(result, cl.c.idgen) of tyRange: - result[0] = result[0].skipTypes({tyStatic, tyDistinct}) + result.setIndexType result.indexType.skipTypes({tyStatic, tyDistinct}) else: discard else: @@ -671,8 +777,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = t # Slow path, we have some work to do - if t.kind == tyRef and t.len > 0 and t[0].kind == tyObject and t[0].n != nil: - discard replaceObjBranches(cl, t[0].n) + if t.kind == tyRef and t.hasElementType and t.elementType.kind == tyObject and t.elementType.n != nil: + discard replaceObjBranches(cl, t.elementType.n) elif result.n != nil and t.kind == tyObject: # Invalidate the type size as we may alter its structure @@ -681,11 +787,11 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo; owner: PSym): TReplTypeVars = - result = TReplTypeVars(symMap: initIdTable(), - localCache: initIdTable(), typeMap: typeMap, + result = TReplTypeVars(symMap: initSymMapping(), + localCache: initTypeMapping(), typeMap: typeMap, info: info, c: p, owner: owner) -proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; +proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode; owner: PSym, allowMetaTypes = false, fromStaticExpr = false, expectedType: PType = nil): PNode = var typeMap = initLayeredTypeMap(pt) @@ -695,6 +801,14 @@ proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; result = replaceTypeVarsN(cl, n, expectedType = expectedType) popInfoContext(p.config) +proc prepareTypesInBody*(p: PContext, pt: TypeMapping, n: PNode; + owner: PSym = nil): PNode = + var typeMap = initLayeredTypeMap(pt) + var cl = initTypeVars(p, typeMap, n.info, owner) + pushInfoContext(p.config, n.info) + result = prepareNode(cl, n) + popInfoContext(p.config) + when false: # deadcode proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; @@ -707,8 +821,8 @@ when false: popInfoContext(p.config) proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) = - if t != nil and t.len > 0 and t[0] != nil: - let b = skipTypes(t[0], skipPtrs) + if t != nil and t.baseClass != nil: + let b = skipTypes(t.baseClass, skipPtrs) recomputeFieldPositions(b, b.n, currPosition) case obj.kind of nkRecList: @@ -722,7 +836,7 @@ proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) = inc currPosition else: discard "cannot happen" -proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, +proc generateTypeInstance*(p: PContext, pt: TypeMapping, info: TLineInfo, t: PType): PType = # Given `t` like Foo[T] # pt: Table with type mappings: T -> int @@ -738,7 +852,7 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, var position = 0 recomputeFieldPositions(objType, objType.n, position) -proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo, +proc prepareMetatypeForSigmatch*(p: PContext, pt: TypeMapping, info: TLineInfo, t: PType): PType = var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, typeMap, info, nil) @@ -747,6 +861,6 @@ proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo, result = replaceTypeVarsT(cl, t) popInfoContext(p.config) -template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode, +template generateTypeInstance*(p: PContext, pt: TypeMapping, arg: PNode, t: PType): untyped = generateTypeInstance(p, pt, arg.info, t) diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 2f1a72fd6..d8dfe1828 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -55,6 +55,8 @@ proc hashSym(c: var MD5Context, s: PSym) = c &= it.name.s c &= "." it = it.owner + c &= "#" + c &= s.disamb proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) = if sfAnon in s.flags or s.kind == skGenericParam: @@ -69,6 +71,8 @@ proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) = c &= it.name.s c &= "." it = it.owner + c &= "#" + c &= s.disamb proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: ConfigRef) = if n == nil: @@ -104,15 +108,15 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi case t.kind of tyGenericInvocation: - for i in 0..<t.len: - c.hashType t[i], flags, conf + for a in t.kids: + c.hashType a, flags, conf of tyDistinct: if CoDistinct in flags: if t.sym != nil: c.hashSym(t.sym) if t.sym == nil or tfFromGeneric in t.flags: - c.hashType t.lastSon, flags, conf + c.hashType t.elementType, flags, conf elif CoType in flags or t.sym == nil: - c.hashType t.lastSon, flags, conf + c.hashType t.elementType, flags, conf else: c.hashSym(t.sym) of tyGenericInst: @@ -121,16 +125,17 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi # We cannot trust the `lastSon` to hold a properly populated and unique # value for each instantiation, so we hash the generic parameters here: let normalizedType = t.skipGenericAlias - for i in 0..<normalizedType.len - 1: - c.hashType t[i], flags, conf + c.hashType normalizedType.genericHead, flags, conf + for _, a in normalizedType.genericInstParams: + c.hashType a, flags, conf else: - c.hashType t.lastSon, flags, conf + c.hashType t.skipModifier, flags, conf of tyAlias, tySink, tyUserTypeClasses, tyInferred: - c.hashType t.lastSon, flags, conf + c.hashType t.skipModifier, flags, conf of tyOwned: if CoConsiderOwned in flags: c &= char(t.kind) - c.hashType t.lastSon, flags, conf + c.hashType t.skipModifier, flags, conf of tyBool, tyChar, tyInt..tyUInt64: # no canonicalization for integral types, so that e.g. ``pid_t`` is # produced instead of ``NI``: @@ -143,8 +148,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi let inst = t.typeInst t.typeInst = nil assert inst.kind == tyGenericInst - for i in 0..<inst.len - 1: - c.hashType inst[i], flags, conf + c.hashType inst.genericHead, flags, conf + for _, a in inst.genericInstParams: + c.hashType a, flags, conf t.typeInst = inst return c &= char(t.kind) @@ -152,9 +158,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi # is actually safe without an infinite recursion check: if t.sym != nil: if {sfCompilerProc} * t.sym.flags != {}: - doAssert t.sym.loc.r != "" + doAssert t.sym.loc.snippet != "" # The user has set a specific name for this type - c &= t.sym.loc.r + c &= t.sym.loc.snippet elif CoOwnerSig in flags: c.hashTypeSym(t.sym, conf) else: @@ -184,37 +190,40 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi c &= ".empty" else: c &= t.id - if t.len > 0 and t[0] != nil: - hashType c, t[0], flags, conf - of tyRef, tyPtr, tyGenericBody, tyVar: + if t.hasElementType and t.baseClass != nil: + hashType c, t.baseClass, flags, conf + of tyRef, tyPtr, tyVar: c &= char(t.kind) - if t.len > 0: - c.hashType t.lastSon, flags, conf + if t.hasElementType: + c.hashType t.elementType, flags, conf if tfVarIsPtr in t.flags: c &= ".varisptr" + of tyGenericBody: + c &= char(t.kind) + if t.hasElementType: + c.hashType t.typeBodyImpl, flags, conf of tyFromExpr: c &= char(t.kind) c.hashTree(t.n, {}, conf) of tyTuple: c &= char(t.kind) if t.n != nil and CoType notin flags: - assert(t.n.len == t.len) for i in 0..<t.n.len: assert(t.n[i].kind == nkSym) c &= t.n[i].sym.name.s c &= ':' - c.hashType(t[i], flags+{CoIgnoreRange}, conf) + c.hashType(t.n[i].sym.typ, flags+{CoIgnoreRange}, conf) c &= ',' else: - for i in 0..<t.len: c.hashType t[i], flags+{CoIgnoreRange}, conf + for a in t.kids: c.hashType a, flags+{CoIgnoreRange}, conf of tyRange: if CoIgnoreRange notin flags: c &= char(t.kind) c.hashTree(t.n, {}, conf) - c.hashType(t[0], flags, conf) + c.hashType(t.elementType, flags, conf) of tyStatic: c &= char(t.kind) c.hashTree(t.n, {}, conf) - c.hashType(t[0], flags, conf) + c.hashType(t.skipModifier, flags, conf) of tyProc: c &= char(t.kind) c &= (if tfIterator in t.flags: "iterator " else: "proc ") @@ -226,9 +235,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi c &= ':' c.hashType(param.typ, flags, conf) c &= ',' - c.hashType(t[0], flags, conf) + c.hashType(t.returnType, flags, conf) else: - for i in 0..<t.len: c.hashType(t[i], flags, conf) + for a in t.signature: c.hashType(a, flags, conf) c &= char(t.callConv) # purity of functions doesn't have to affect the mangling (which is in fact # problematic for HCR - someone could have cached a pointer to another @@ -240,10 +249,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi if tfVarargs in t.flags: c &= ".varargs" of tyArray: c &= char(t.kind) - for i in 0..<t.len: c.hashType(t[i], flags-{CoIgnoreRange}, conf) + c.hashType(t.indexType, flags-{CoIgnoreRange}, conf) + c.hashType(t.elementType, flags-{CoIgnoreRange}, conf) else: c &= char(t.kind) - for i in 0..<t.len: c.hashType(t[i], flags, conf) + for a in t.kids: c.hashType(a, flags, conf) if tfNotNil in t.flags and CoType notin flags: c &= "not nil" when defined(debugSigHashes): @@ -262,7 +272,7 @@ when defined(debugSigHashes): proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): SigHash = result = default(SigHash) - var c: MD5Context + var c: MD5Context = default(MD5Context) md5Init c hashType c, t, flags+{CoOwnerSig}, conf md5Final c, result.MD5Digest @@ -272,7 +282,7 @@ proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): proc hashProc(s: PSym; conf: ConfigRef): SigHash = result = default(SigHash) - var c: MD5Context + var c: MD5Context = default(MD5Context) md5Init c hashType c, s.typ, {CoProc}, conf @@ -293,7 +303,7 @@ proc hashProc(s: PSym; conf: ConfigRef): SigHash = proc hashNonProc*(s: PSym): SigHash = result = default(SigHash) - var c: MD5Context + var c: MD5Context = default(MD5Context) md5Init c hashSym(c, s) var it = s @@ -310,7 +320,7 @@ proc hashNonProc*(s: PSym): SigHash = proc hashOwner*(s: PSym): SigHash = result = default(SigHash) - var c: MD5Context + var c: MD5Context = default(MD5Context) md5Init c var m = s while m.kind != skModule: m = m.owner @@ -383,7 +393,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = graph.symBodyHashes.withValue(sym.id, value): return value[] - var c: MD5Context + var c: MD5Context = default(MD5Context) md5Init(c) c.hashType(sym.typ, {CoProc}, graph.config) c &= char(sym.kind) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index aa7a72dd9..6ea2c7bb5 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -15,7 +15,7 @@ import magicsys, idents, lexer, options, parampatterns, trees, linter, lineinfos, lowerings, modulegraphs, concepts -import std/[intsets, strutils] +import std/[intsets, strutils, tables] when defined(nimPreviewSlimSystem): import std/assertions @@ -23,7 +23,8 @@ when defined(nimPreviewSlimSystem): type MismatchKind* = enum kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded, - kMissingParam, kExtraArg, kPositionalAlreadyGiven + kMissingParam, kExtraArg, kPositionalAlreadyGiven, + kGenericParamTypeMismatch, kMissingGenericParam, kExtraGenericParam MismatchInfo* = object kind*: MismatchKind # reason for mismatch @@ -55,12 +56,12 @@ type calleeScope*: int # scope depth: # is this a top-level symbol or a nested proc? call*: PNode # modified call - bindings*: TIdTable # maps types to types + bindings*: TypeMapping # maps types to types magic*: TMagic # magic of operation baseTypeMatch: bool # needed for conversions from T to openarray[T] # for example - fauxMatch*: TTypeKind # the match was successful only due to the use - # of error or wildcard (unknown) types. + matchedErrorType*: bool # match is considered successful after matching + # error type to avoid cascading errors # this is used to prevent instantiations. genericConverter*: bool # true if a generic converter needs to # be instantiated @@ -80,7 +81,8 @@ type # or when the explain pragma is used. may be # triggered with an idetools command in the # future. - inheritancePenalty: int # to prefer closest father object type + # to prefer closest father object type + inheritancePenalty: int firstMismatch*: MismatchInfo # mismatch info for better error messages diagnosticsEnabled*: bool @@ -95,32 +97,31 @@ type const isNilConversion = isConvertible # maybe 'isIntConv' fits better? + maxInheritancePenalty = high(int) div 2 -proc markUsed*(c: PContext; info: TLineInfo, s: PSym) +proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true) proc markOwnerModuleAsUsed*(c: PContext; s: PSym) -template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone - proc initCandidateAux(ctx: PContext, callee: PType): TCandidate {.inline.} = result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0, convMatches: 0, intConvMatches: 0, genericMatches: 0, state: csEmpty, firstMismatch: MismatchInfo(), callee: callee, call: nil, baseTypeMatch: false, - genericConverter: false, inheritancePenalty: 0 + genericConverter: false, inheritancePenalty: -1 ) proc initCandidate*(ctx: PContext, callee: PType): TCandidate = result = initCandidateAux(ctx, callee) result.calleeSym = nil - result.bindings = initIdTable() + result.bindings = initTypeMapping() proc put(c: var TCandidate, key, val: PType) {.inline.} = ## Given: proc foo[T](x: T); foo(4) ## key: 'T' ## val: 'int' (typeof(4)) when false: - let old = PType(idTableGet(c.bindings, key)) + let old = idTableGet(c.bindings, key) if old != nil: echo "Putting ", typeToString(key), " ", typeToString(val), " and old is ", typeToString(old) if typeToString(old) == "float32": @@ -129,39 +130,138 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} = echo "binding ", key, " -> ", val idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen)) +proc typeRel*(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation + +proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) = + var arg = n.typ + if m.c.inGenericContext > 0: + # don't match yet-unresolved generic instantiations + while arg != nil and arg.kind == tyGenericParam: + arg = idTableGet(m.bindings, arg) + if arg == nil or arg.containsUnresolvedType: + m.state = csNoMatch + return + # fix up the type to get ready to match formal: + var formalBase = formal + while formalBase.kind == tyGenericParam and + formalBase.genericParamHasConstraints: + formalBase = formalBase.genericConstraint + if formalBase.kind == tyStatic and arg.kind != tyStatic: + # maybe call `paramTypesMatch` here, for now be conservative + if n.kind in nkSymChoices: n.flags.excl nfSem + let evaluated = m.c.semTryConstExpr(m.c, n, formalBase.skipTypes({tyStatic})) + if evaluated != nil: + arg = newTypeS(tyStatic, m.c, son = evaluated.typ) + arg.n = evaluated + elif formalBase.kind == tyTypeDesc: + if arg.kind != tyTypeDesc: + arg = makeTypeDesc(m.c, arg) + else: + arg = arg.skipTypes({tyTypeDesc}) + let tm = typeRel(m, formal, arg) + if tm in {isNone, isConvertible}: + m.state = csNoMatch + m.firstMismatch.kind = kGenericParamTypeMismatch + return + +proc matchGenericParams*(m: var TCandidate, binding: PNode, callee: PSym) = + ## matches explicit generic instantiation `binding` against generic params of + ## proc symbol `callee` + ## state is set to `csMatch` if all generic params match, `csEmpty` if + ## implicit generic parameters are missing (matches but cannot instantiate), + ## `csNoMatch` if a constraint fails or param count doesn't match + let c = m.c + let typeParams = callee.ast[genericParamsPos] + let paramCount = typeParams.len + let bindingCount = binding.len-1 + if bindingCount > paramCount: + m.state = csNoMatch + m.firstMismatch.kind = kExtraGenericParam + m.firstMismatch.arg = paramCount + 1 + return + for i in 1..bindingCount: + matchGenericParam(m, typeParams[i-1].typ, binding[i]) + if m.state == csNoMatch: + m.firstMismatch.arg = i + m.firstMismatch.formal = typeParams[i-1].sym + return + # not enough generic params given, check if remaining have defaults: + for i in bindingCount ..< paramCount: + let param = typeParams[i] + assert param.kind == nkSym + let paramSym = param.sym + if paramSym.ast != nil: + matchGenericParam(m, param.typ, paramSym.ast) + if m.state == csNoMatch: + m.firstMismatch.arg = i + 1 + m.firstMismatch.formal = paramSym + return + elif tfImplicitTypeParam in paramSym.typ.flags: + # not a mismatch, but can't create sym + m.state = csEmpty + return + else: + m.state = csNoMatch + m.firstMismatch.kind = kMissingGenericParam + m.firstMismatch.arg = i + 1 + m.firstMismatch.formal = paramSym + return + m.state = csMatch + +proc copyingEraseVoidParams(m: TCandidate, t: var PType) = + ## if `t` is a proc type with void parameters, copies it and erases them + assert t.kind == tyProc + let original = t + var copied = false + for i in 1 ..< original.len: + var f = original[i] + var isVoidParam = f.kind == tyVoid + if not isVoidParam: + let prev = idTableGet(m.bindings, f) + if prev != nil: f = prev + isVoidParam = f.kind == tyVoid + if isVoidParam: + if not copied: + # keep first i children + t = copyType(original, m.c.idgen, t.owner) + t.setSonsLen(i) + t.n = copyNode(original.n) + t.n.sons = original.n.sons + t.n.sons.setLen(i) + copied = true + elif copied: + t.add(f) + t.n.add(original.n[i]) + proc initCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1, diagnosticsEnabled = false): TCandidate = result = initCandidateAux(ctx, callee.typ) result.calleeSym = callee if callee.kind in skProcKinds and calleeScope == -1: - if callee.originatingModule == ctx.module: - result.calleeScope = 2 - var owner = callee - while true: - owner = owner.skipGenericOwner - if owner.kind == skModule: break - inc result.calleeScope - else: - result.calleeScope = 1 + result.calleeScope = cmpScopes(ctx, callee) else: result.calleeScope = calleeScope result.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil result.diagnosticsEnabled = diagnosticsEnabled result.magic = result.calleeSym.magic - result.bindings = initIdTable() + result.bindings = initTypeMapping() if binding != nil and callee.kind in routineKinds: - var typeParams = callee.ast[genericParamsPos] - for i in 1..min(typeParams.len, binding.len-1): - var formalTypeParam = typeParams[i-1].typ - var bound = binding[i].typ - if bound != nil: - if formalTypeParam.kind == tyTypeDesc: - if bound.kind != tyTypeDesc: - bound = makeTypeDesc(ctx, bound) - else: - bound = bound.skipTypes({tyTypeDesc}) - put(result, formalTypeParam, bound) + matchGenericParams(result, binding, callee) + let genericMatch = result.state + if genericMatch != csNoMatch: + result.state = csEmpty + if genericMatch == csMatch: # csEmpty if not fully instantiated + # instantiate the type, emulates old compiler behavior + # wouldn't be needed if sigmatch could handle complex cases, + # examples are in texplicitgenerics + # might be buggy, see rest of generateInstance if problems occur + let typ = ctx.instantiateOnlyProcType(ctx, result.bindings, callee, binding.info) + result.callee = typ + else: + # createThread[void] requires this if the above branch is removed: + copyingEraseVoidParams(result, result.callee) proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = @@ -170,37 +270,34 @@ proc newCandidate*(ctx: PContext, callee: PSym, proc newCandidate*(ctx: PContext, callee: PType): TCandidate = result = initCandidate(ctx, callee) -proc copyCandidate(a: var TCandidate, b: TCandidate) = - a.c = b.c - a.exactMatches = b.exactMatches - a.subtypeMatches = b.subtypeMatches - a.convMatches = b.convMatches - a.intConvMatches = b.intConvMatches - a.genericMatches = b.genericMatches - a.state = b.state - a.callee = b.callee - a.calleeSym = b.calleeSym - a.call = copyTree(b.call) - a.baseTypeMatch = b.baseTypeMatch - copyIdTable(a.bindings, b.bindings) - -proc typeRel*(c: var TCandidate, f, aOrig: PType, - flags: TTypeRelFlags = {}): TTypeRelation +proc copyCandidate(dest: var TCandidate, src: TCandidate) = + dest.c = src.c + dest.exactMatches = src.exactMatches + dest.subtypeMatches = src.subtypeMatches + dest.convMatches = src.convMatches + dest.intConvMatches = src.intConvMatches + dest.genericMatches = src.genericMatches + dest.state = src.state + dest.callee = src.callee + dest.calleeSym = src.calleeSym + dest.call = copyTree(src.call) + dest.baseTypeMatch = src.baseTypeMatch + dest.bindings = src.bindings proc checkGeneric(a, b: TCandidate): int = let c = a.c let aa = a.callee let bb = b.callee var winner = 0 - for i in 1..<min(aa.len, bb.len): - var ma = newCandidate(c, bb[i]) - let tra = typeRel(ma, bb[i], aa[i], {trDontBind}) - var mb = newCandidate(c, aa[i]) - let trb = typeRel(mb, aa[i], bb[i], {trDontBind}) - if tra == isGeneric and trb == isNone: + for aai, bbi in underspecifiedPairs(aa, bb, 1): + var ma = newCandidate(c, bbi) + let tra = typeRel(ma, bbi, aai, {trDontBind}) + var mb = newCandidate(c, aai) + let trb = typeRel(mb, aai, bbi, {trDontBind}) + if tra == isGeneric and trb in {isNone, isInferred, isInferredConvertible}: if winner == -1: return 0 winner = 1 - if trb == isGeneric and tra == isNone: + if trb == isGeneric and tra in {isNone, isInferred, isInferredConvertible}: if winner == 1: return 0 winner = -1 result = winner @@ -211,54 +308,63 @@ proc sumGeneric(t: PType): int = # specific than Foo[T]. result = 0 var t = t - var isvar = 0 while true: case t.kind - of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray, - tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody, - tyLent, tyOwned: - t = t.lastSon + of tyAlias, tySink, tyNot: t = t.skipModifier + of tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray, + tyOpenArray, tyVarargs, tySet, tyRange, tySequence, + tyLent, tyOwned, tyVar: + t = t.elementType + inc result + of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyVoid, + tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128, + tyUInt..tyUInt64, tyCompositeTypeClass, tyBuiltInTypeClass: + inc result + break + of tyGenericBody: + t = t.typeBodyImpl + of tyGenericInst, tyStatic: + t = t.skipModifier inc result of tyOr: var maxBranch = 0 - for branch in t: + for branch in t.kids: let branchSum = sumGeneric(branch) if branchSum > maxBranch: maxBranch = branchSum inc result, maxBranch break - of tyVar: - t = t[0] - inc result - inc isvar of tyTypeDesc: - t = t.lastSon + t = t.elementType if t.kind == tyEmpty: break inc result - of tyGenericInvocation, tyTuple, tyProc, tyAnd: - result += ord(t.kind in {tyGenericInvocation, tyAnd}) - for i in 0..<t.len: - if t[i] != nil: - result += sumGeneric(t[i]) + of tyGenericParam: + if t.len > 0: + t = t.skipModifier + else: + inc result + break + of tyUntyped, tyTyped: break + of tyGenericInvocation, tyTuple, tyAnd: + result += ord(t.kind == tyAnd) + for a in t.kids: + if a != nil: + result += sumGeneric(a) + break + of tyProc: + if t.returnType != nil: + result += sumGeneric(t.returnType) + for _, a in t.paramTypes: + result += sumGeneric(a) break - of tyStatic: - return sumGeneric(t[0]) + 1 - of tyGenericParam, tyUntyped, tyTyped: break - of tyAlias, tySink: t = t.lastSon - of tyBool, tyChar, tyEnum, tyObject, tyPointer, - tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128, - tyUInt..tyUInt64, tyCompositeTypeClass: - return isvar + 1 - of tyBuiltInTypeClass: - return isvar else: - return 0 + break proc complexDisambiguation(a, b: PType): int = # 'a' matches better if *every* argument matches better or equal than 'b'. var winner = 0 - for i in 1..<min(a.len, b.len): - let x = a[i].sumGeneric - let y = b[i].sumGeneric + for ai, bi in underspecifiedPairs(a, b, 1): + let x = ai.sumGeneric + let y = bi.sumGeneric if x != y: if winner == 0: if x > y: winner = 1 @@ -273,8 +379,8 @@ proc complexDisambiguation(a, b: PType): int = result = winner when false: var x, y: int - for i in 1..<a.len: x += a[i].sumGeneric - for i in 1..<b.len: y += b[i].sumGeneric + for i in 1..<a.len: x += ai.sumGeneric + for i in 1..<b.len: y += bi.sumGeneric result = x - y proc writeMatches*(c: TCandidate) = @@ -286,7 +392,16 @@ proc writeMatches*(c: TCandidate) = echo " conv matches: ", c.convMatches echo " inheritance: ", c.inheritancePenalty -proc cmpCandidates*(a, b: TCandidate): int = +proc cmpInheritancePenalty(a, b: int): int = + var eb = b + var ea = a + if b < 0: + eb = maxInheritancePenalty # defacto max penalty + if a < 0: + ea = maxInheritancePenalty + eb - ea + +proc cmpCandidates*(a, b: TCandidate, isFormal=true): int = result = a.exactMatches - b.exactMatches if result != 0: return result = a.genericMatches - b.genericMatches @@ -297,16 +412,16 @@ proc cmpCandidates*(a, b: TCandidate): int = if result != 0: return result = a.convMatches - b.convMatches if result != 0: return - # the other way round because of other semantics: - result = b.inheritancePenalty - a.inheritancePenalty + result = cmpInheritancePenalty(a.inheritancePenalty, b.inheritancePenalty) if result != 0: return - # check for generic subclass relation - result = checkGeneric(a, b) + if isFormal: + # check for generic subclass relation + result = checkGeneric(a, b) + if result != 0: return + # prefer more specialized generic over more general generic: + result = complexDisambiguation(a.callee, b.callee) if result != 0: return - # prefer more specialized generic over more general generic: - result = complexDisambiguation(a.callee, b.callee) # only as a last resort, consider scoping: - if result != 0: return result = a.calleeScope - b.calleeScope proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = @@ -326,16 +441,27 @@ template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = p result.add renderTree(n[i][0]) result.add ": " if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}: - # XXX we really need to 'tryExpr' here! - arg = c.semOperand(c, n[i][1]) - n[i].typ = arg.typ - n[i][1] = arg + arg = c.semTryExpr(c, n[i][1]) + if arg == nil: + arg = n[i][1] + arg.typ = newTypeS(tyUntyped, c) + else: + if arg.typ == nil: + arg.typ = newTypeS(tyVoid, c) + n[i].typ = arg.typ + n[i][1] = arg else: if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse, nkOfBranch, nkElifBranch, nkExceptBranch}: - arg = c.semOperand(c, n[i]) - n[i] = arg + arg = c.semTryExpr(c, n[i]) + if arg == nil: + arg = n[i] + arg.typ = newTypeS(tyUntyped, c) + else: + if arg.typ == nil: + arg.typ = newTypeS(tyVoid, c) + n[i] = arg if arg.typ != nil and arg.typ.kind == tyError: return result.add argTypeToString(arg, prefer) @@ -355,12 +481,13 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType = if c.isNoCall: result = t else: result = nil of tySequence, tySet: - if t[0].kind == tyEmpty: result = nil + if t.elementType.kind == tyEmpty: result = nil else: result = t of tyGenericParam, tyAnything, tyConcept: result = t + if c.isNoCall: return while true: - result = PType(idTableGet(c.bindings, t)) + result = idTableGet(c.bindings, t) if result == nil: break # it's ok, no match # example code that triggers it: @@ -371,8 +498,8 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType = of tyOwned: # bug #11257: the comparison system.`==`[T: proc](x, y: T) works # better without the 'owned' type: - if f != nil and f.len > 0 and f[0].skipTypes({tyBuiltInTypeClass, tyOr}).kind == tyProc: - result = t.lastSon + if f != nil and f.hasElementType and f.elementType.skipTypes({tyBuiltInTypeClass, tyOr}).kind == tyProc: + result = t.skipModifier else: result = t else: @@ -386,9 +513,16 @@ proc handleRange(c: PContext, f, a: PType, min, max: TTypeKind): TTypeRelation = let k = ab.kind let nf = c.config.normalizeKind(f.kind) let na = c.config.normalizeKind(k) - if k == f.kind: result = isSubrange - elif k == tyInt and f.kind in {tyRange, tyInt..tyInt64, - tyUInt..tyUInt64} and + if k == f.kind: + # `a` is a range type matching its base type + # see very bottom for range types matching different types + if isIntLit(ab): + # range type can only give isFromIntLit for base type + result = isFromIntLit + else: + result = isSubrange + elif a.kind == tyInt and f.kind in {tyRange, tyInt..tyInt64, + tyUInt..tyUInt64} and isIntLit(ab) and getInt(ab.n) >= firstOrd(nil, f) and getInt(ab.n) <= lastOrd(nil, f): # passing 'nil' to firstOrd/lastOrd here as type checking rules should @@ -455,43 +589,50 @@ proc handleFloatRange(f, a: PType): TTypeRelation = else: result = isIntConv else: result = isNone -proc getObjectTypeOrNil(f: PType): PType = +proc reduceToBase(f: PType): PType = #[ - Returns a type that is f's effective typeclass. This is usually just one level deeper - in the hierarchy of generality for a type. `object`, `ref object`, `enum` and user defined - tyObjects are common return values. + Returns the lowest order (most general) type that that is compatible with the input. + E.g. + A[T] = ptr object ... A -> ptr object + A[N: static[int]] = array[N, int] ... A -> array ]# - if f == nil: return nil case f.kind: - of tyGenericInvocation, tyCompositeTypeClass, tyAlias: - if f.len <= 0 or f[0] == nil: - result = nil + of tyGenericParam: + if f.len <= 0 or f.skipModifier == nil: + result = f + else: + result = reduceToBase(f.skipModifier) + of tyGenericInvocation: + result = reduceToBase(f.baseClass) + of tyCompositeTypeClass, tyAlias: + if not f.hasElementType or f.elementType == nil: + result = f else: - result = getObjectTypeOrNil(f[0]) - of tyGenericBody, tyGenericInst: - result = getObjectTypeOrNil(f.lastSon) + result = reduceToBase(f.elementType) + of tyGenericInst: + result = reduceToBase(f.skipModifier) + of tyGenericBody: + result = reduceToBase(f.typeBodyImpl) of tyUserTypeClass: if f.isResolvedUserTypeClass: result = f.base # ?? idk if this is right else: - result = f.lastSon + result = f.skipModifier of tyStatic, tyOwned, tyVar, tyLent, tySink: - result = getObjectTypeOrNil(f.base) + result = reduceToBase(f.base) of tyInferred: # This is not true "After a candidate type is selected" - result = getObjectTypeOrNil(f.base) - of tyTyped, tyUntyped, tyFromExpr: - result = nil + result = reduceToBase(f.base) of tyRange: - result = f.lastSon + result = f.elementType else: result = f proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) = if fGenericOrigin != nil and last.kind == tyGenericInst and - last.len-1 == fGenericOrigin.len: - for i in 1..<fGenericOrigin.len: - let x = PType(idTableGet(c.bindings, fGenericOrigin[i])) + last.kidsLen-1 == fGenericOrigin.kidsLen: + for i in FirstGenericParamAt..<fGenericOrigin.kidsLen: + let x = idTableGet(c.bindings, fGenericOrigin[i]) if x == nil: put(c, fGenericOrigin[i], last[i]) @@ -503,7 +644,7 @@ proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int = while t != nil and not sameObjectTypes(f, t): if t.kind != tyObject: # avoid entering generic params etc return -1 - t = t[0] + t = t.baseClass if t == nil: break last = t t = skipTypes(t, skipPtrs) @@ -524,17 +665,19 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType = while r != nil: case r.kind of tyGenericInvocation: - r = r[0] + r = r.genericHead of tyRef: inc ptrs skipped = skippedRef - r = r.lastSon + r = r.elementType of tyPtr: inc ptrs skipped = skippedPtr - r = r.lastSon - of tyGenericBody, tyGenericInst, tyAlias, tySink, tyOwned: - r = r.lastSon + r = r.elementType + of tyGenericInst, tyAlias, tySink, tyOwned: + r = r.elementType + of tyGenericBody: + r = r.typeBodyImpl else: break if r.kind == tyObject and ptrs <= 1: result = r @@ -552,7 +695,7 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin # XXX sameObjectType can return false here. Need to investigate # why that is but sameObjectType does way too much work here anyway. while t != nil and r.sym != t.sym and askip == fskip: - t = t[0] + t = t.baseClass if t == nil: break last = t t = t.skipToObject(askip) @@ -568,17 +711,22 @@ proc minRel(a, b: TTypeRelation): TTypeRelation = if a <= b: result = a else: result = b -proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = +proc recordRel(c: var TCandidate, f, a: PType, flags: TTypeRelFlags): TTypeRelation = result = isNone if sameType(f, a): result = isEqual - elif a.len == f.len: + elif sameTupleLengths(a, f): result = isEqual let firstField = if f.kind == tyTuple: 0 else: 1 - for i in firstField..<f.len: - var m = typeRel(c, f[i], a[i]) + for _, ff, aa in tupleTypePairs(f, a): + var m = typeRel(c, ff, aa, flags) if m < isSubtype: return isNone + if m == isSubtype and aa.kind != tyNil and c.inheritancePenalty > -1: + # we can't process individual element type conversions from a + # type conversion for the whole tuple + # subtype relations need type conversions when inheritance is used + return isNone result = minRel(result, m) if f.n != nil and a.n != nil: for i in 0..<f.n.len: @@ -588,7 +736,7 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = else: var x = f.n[i].sym var y = a.n[i].sym - if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype: + if f.kind == tyObject and typeRel(c, x.typ, y.typ, flags) < isSubtype: return isNone if x.name.id != y.name.id: return isNone @@ -600,7 +748,7 @@ proc inconsistentVarTypes(f, a: PType): bool {.inline.} = (f.kind in {tyVar, tyLent, tySink} or a.kind in {tyVar, tyLent, tySink})) or isOutParam(f) != isOutParam(a) -proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = +proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation = ## For example we have: ## ```nim ## proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ... @@ -622,7 +770,7 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = a = a if a.isMetaType: - let aResolved = PType(idTableGet(c.bindings, a)) + let aResolved = idTableGet(c.bindings, a) if aResolved != nil: a = aResolved if a.isMetaType: @@ -660,7 +808,9 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = case a.kind of tyProc: - if f.len != a.len: return + var f = f + copyingEraseVoidParams(c, f) + if f.signatureLen != a.signatureLen: return result = isEqual # start with maximum; also correct for no # params at all @@ -718,7 +868,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = c = m.c typeClass = ff.skipTypes({tyUserTypeClassInst}) body = typeClass.n[3] - matchedConceptContext: TMatchedConcept + matchedConceptContext = TMatchedConcept() prevMatchedConcept = c.matchedConcept prevCandidateType = typeClass[0][0] @@ -746,7 +896,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = typeParamName = ff.base[i-1].sym.name typ = ff[i] param: PSym = nil - alreadyBound = PType(idTableGet(m.bindings, typ)) + alreadyBound = idTableGet(m.bindings, typ) if alreadyBound != nil: typ = alreadyBound @@ -769,14 +919,14 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = param.typ.flags.incl tfInferrableStatic else: param.ast = typ.n - of tyUnknown: + of tyFromExpr: param = paramSym skVar param.typ = typ.exactReplica #copyType(typ, c.idgen, typ.owner) else: param = paramSym skType param.typ = if typ.isMetaType: - c.newTypeWithSons(tyInferred, @[typ]) + newTypeS(tyInferred, c, typ) else: makeTypeDesc(c, typ) @@ -848,6 +998,7 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType = proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, allowUnresolved = false, + allowCalls = false, expectedType: PType = nil): PNode = # Consider this example: # type Value[N: static[int]] = object @@ -857,7 +1008,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, # This proc is used to evaluate such static expressions. let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil, allowMetaTypes = allowUnresolved) - if instantiated.kind in nkCallKinds: + if not allowCalls and instantiated.kind in nkCallKinds: return nil result = c.c.semExpr(c.c, instantiated) @@ -929,8 +1080,9 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = else: discard - elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil: - var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ) + elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and + (lhs.typ.n == nil or idTableGet(c.bindings, lhs.typ) == nil): + var inferred = newTypeS(tyStatic, c.c, lhs.typ.elementType) inferred.n = newIntNode(nkIntLit, rhs) put(c, lhs.typ, inferred) if c.c.matchedConcept != nil: @@ -973,9 +1125,21 @@ proc inferStaticsInRange(c: var TCandidate, doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete)) template subtypeCheck() = - if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in { - tyRef, tyPtr, tyVar, tyLent, tyOwned}: + case result + of isIntConv: result = isNone + of isSubrange: + discard # XXX should be isNone with preview define, warnings + of isConvertible: + if f.last.skipTypes(abstractInst).kind != tyOpenArray: + # exclude var openarray which compiler supports + result = isNone + of isSubtype: + if f.last.skipTypes(abstractInst).kind in { + tyRef, tyPtr, tyVar, tyLent, tyOwned}: + # compiler can't handle subtype conversions with pointer indirection + result = isNone + else: discard proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = # this proc is always called for a pair of matching types @@ -1073,7 +1237,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, case f.kind of tyGenericParam: - var prev = PType(idTableGet(c.bindings, f)) + var prev = idTableGet(c.bindings, f) if prev != nil: candidate = prev of tyFromExpr: let computedType = tryResolvingStaticExpr(c, f.n).typ @@ -1105,16 +1269,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # situation when nkDotExpr are rotated to nkDotCalls if aOrig.kind in {tyAlias, tySink}: - return typeRel(c, f, lastSon(aOrig), flags) + return typeRel(c, f, skipModifier(aOrig), flags) if a.kind == tyGenericInst and skipTypes(f, {tyStatic, tyVar, tyLent, tySink}).kind notin { tyGenericBody, tyGenericInvocation, tyGenericInst, tyGenericParam} + tyTypeClasses: - return typeRel(c, f, lastSon(a), flags) + return typeRel(c, f, skipModifier(a), flags) if a.isResolvedUserTypeClass: - return typeRel(c, f, a.lastSon, flags) + return typeRel(c, f, a.skipModifier, flags) template bindingRet(res) = if doBind: @@ -1123,10 +1287,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return res template considerPreviousT(body: untyped) = - var prev = PType(idTableGet(c.bindings, f)) + var prev = idTableGet(c.bindings, f) if prev == nil: body else: return typeRel(c, prev, a, flags) + if c.c.inGenericContext > 0 and not c.isNoCall and + (tfUnresolved in a.flags or a.kind in tyTypeClasses): + # cheap check for unresolved arg, not nested + return isNone + case a.kind of tyOr: # XXX: deal with the current dual meaning of tyGenericParam @@ -1135,23 +1304,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # both int and string must match against number # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): result = isGeneric - for branch in a: + for branch in a.kids: let x = typeRel(c, f, branch, flags + {trDontBind}) if x == isNone: return isNone if x < result: result = x return result - of tyAnd: # XXX: deal with the current dual meaning of tyGenericParam c.typedescMatched = true # seq[Sortable and Iterable] vs seq[Sortable] # only one match is enough - for branch in a: + for branch in a.kids: let x = typeRel(c, f, branch, flags + {trDontBind}) if x != isNone: return if x >= isGeneric: isGeneric else: x return isNone - of tyIterable: if f.kind != tyIterable: return isNone of tyNot: @@ -1161,18 +1328,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # seq[float] matches the first, but not the second # we must turn the problem around: # is number a subset of int? - return typeRel(c, a.lastSon, f.lastSon, flags) + return typeRel(c, a.elementType, f.elementType, flags) else: # negative type classes are essentially infinite, # so only the `any` type class is their superset return if f.kind == tyAnything: isGeneric else: isNone - of tyAnything: if f.kind == tyAnything: return isGeneric else: return isNone - of tyUserTypeClass, tyUserTypeClassInst: if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4: # consider this: 'var g: Node' *within* a concept where 'Node' @@ -1181,14 +1346,19 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let x = typeRel(c, a, f, flags + {trDontBind}) if x >= isGeneric: return isGeneric - of tyFromExpr: if c.c.inGenericContext > 0: - # generic type bodies can sometimes compile call expressions - # prevent expressions with unresolved types from - # being passed as parameters - return isNone + if not c.isNoCall: + # generic type bodies can sometimes compile call expressions + # prevent expressions with unresolved types from + # being passed as parameters + return isNone + else: + # Foo[templateCall(T)] shouldn't fail early if Foo has a constraint + # and we can't evaluate `templateCall(T)` yet + return isGeneric else: discard + case f.kind of tyEnum: if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual @@ -1246,14 +1416,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) subtypeCheck() of tyArray: - case a.kind - of tyArray: - var fRange = f[0] - var aRange = a[0] + a = reduceToBase(a) + if a.kind == tyArray: + var fRange = f.indexType + var aRange = a.indexType if fRange.kind in {tyGenericParam, tyAnything}: - var prev = PType(idTableGet(c.bindings, fRange)) + var prev = idTableGet(c.bindings, fRange) if prev == nil: - put(c, fRange, a[0]) + if typeRel(c, fRange, aRange) == isNone: + return isNone + put(c, fRange, a.indexType) fRange = a else: fRange = prev @@ -1261,11 +1433,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # This typeDesc rule is wrong, see bug #7331 let aa = a[1] #.skipTypes({tyTypeDesc}) - if f[0].kind != tyGenericParam and aa.kind == tyEmpty: + if f.indexType.kind != tyGenericParam and aa.kind == tyEmpty: result = isGeneric else: result = typeRel(c, ff, aa, flags) - if result < isGeneric: if nimEnableCovariance and trNoCovariance notin flags and @@ -1276,6 +1447,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return isNone if fRange.rangeHasUnresolvedStatic: + if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange: + return return inferStaticsInRange(c, fRange, a) elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, aRange, f) @@ -1284,18 +1457,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange): result = isNone - else: discard - of tyUncheckedArray: - if a.kind == tyUncheckedArray: - result = typeRel(c, base(f), base(a), flags) - if result < isGeneric: result = isNone - else: discard of tyOpenArray, tyVarargs: # varargs[untyped] is special too but handled earlier. So we only need to # handle varargs[typed]: if f.kind == tyVarargs: if tfVarargs in a.flags: - return typeRel(c, f.base, a.lastSon, flags) + return typeRel(c, f.base, a.elementType, flags) if f[0].kind == tyTyped: return template matchArrayOrSeq(aBase: PType) = @@ -1315,13 +1482,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = typeRel(c, base(f), base(a), flags) if result < isGeneric: result = isNone of tyArray: - if (f[0].kind != tyGenericParam) and (a[1].kind == tyEmpty): + if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty): return isSubtype - matchArrayOrSeq(a[1]) + matchArrayOrSeq(a.elementType) of tySequence: - if (f[0].kind != tyGenericParam) and (a[0].kind == tyEmpty): + if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty): return isConvertible - matchArrayOrSeq(a[0]) + matchArrayOrSeq(a.elementType) of tyString: if f.kind == tyOpenArray: if f[0].kind == tyChar: @@ -1330,14 +1497,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, typeRel(c, base(f), base(a), flags) >= isGeneric: result = isConvertible else: discard - of tySequence: - case a.kind - of tySequence: - if (f[0].kind != tyGenericParam) and (a[0].kind == tyEmpty): + of tySequence, tyUncheckedArray: + if a.kind == f.kind: + if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty): result = isSubtype else: let ff = f[0] - let aa = a[0] + let aa = a.elementType result = typeRel(c, ff, aa, flags) if result < isGeneric: if nimEnableCovariance and @@ -1347,11 +1513,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isSubtype else: result = isNone - of tyNil: result = isNone - else: discard + elif a.kind == tyNil: + result = isNone of tyOrdinal: if isOrdinalType(a): - var x = if a.kind == tyOrdinal: a[0] else: a + var x = if a.kind == tyOrdinal: a.elementType else: a if f[0].kind == tyNone: result = isGeneric else: @@ -1366,21 +1532,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, skipOwned(a) if a.kind == f.kind: result = isEqual of tyTuple: - if a.kind == tyTuple: result = recordRel(c, f, a) + if a.kind == tyTuple: result = recordRel(c, f, a, flags) of tyObject: let effectiveArgType = if useTypeLoweringRuleInTypeClass: a else: - getObjectTypeOrNil(a) - if effectiveArgType == nil: return isNone + reduceToBase(a) if effectiveArgType.kind == tyObject: if sameObjectTypes(f, effectiveArgType): + c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0 result = isEqual # elif tfHasMeta in f.flags: result = recordRel(c, f, a) elif trIsOutParam notin flags: - var depth = isObjectSubtype(c, effectiveArgType, f, nil) - if depth > 0: - inc(c.inheritancePenalty, depth) + c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil) + if c.inheritancePenalty > 0: result = isSubtype of tyDistinct: a = a.skipTypes({tyOwned, tyGenericInst, tyRange}) @@ -1396,20 +1561,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: result = typeRel(c, f[0], a[0], flags) if result < isGeneric: - if result <= isConvertible: - result = isNone - elif tfIsConstructor notin a.flags: - # set constructors are a bit special... + if tfIsConstructor notin a.flags: + # set['a'..'z'] and set[char] have different representations result = isNone - + else: + # but we can convert individual elements of the constructor + result = isConvertible of tyPtr, tyRef: - skipOwned(a) + a = reduceToBase(a) if a.kind == f.kind: # ptr[R, T] can be passed to ptr[T], but not the other way round: if a.len < f.len: return isNone for i in 0..<f.len-1: if typeRel(c, f[i], a[i], flags) == isNone: return isNone - result = typeRel(c, f.lastSon, a.lastSon, flags + {trNoCovariance}) + result = typeRel(c, f.elementType, a.elementType, flags + {trNoCovariance}) subtypeCheck() if result <= isIntConv: result = isNone elif tfNotNil in f.flags and tfNotNil notin a.flags: @@ -1424,7 +1589,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyOwned: case a.kind of tyOwned: - result = typeRel(c, lastSon(f), lastSon(a), flags) + result = typeRel(c, skipModifier(f), skipModifier(a), flags) of tyNil: result = f.allowsNil else: discard of tyPointer: @@ -1476,24 +1641,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, pointsTo[1].kind == tyChar: result = isConvertible else: discard - of tyEmpty, tyVoid: if a.kind == f.kind: result = isEqual - of tyAlias, tySink: - result = typeRel(c, lastSon(f), a, flags) - + result = typeRel(c, skipModifier(f), a, flags) of tyIterable: if a.kind == tyIterable: if f.len == 1: - result = typeRel(c, lastSon(f), lastSon(a), flags) + result = typeRel(c, skipModifier(f), skipModifier(a), flags) else: # f.len = 3, for some reason result = isGeneric else: result = isNone of tyGenericInst: - var prev = PType(idTableGet(c.bindings, f)) + var prev = idTableGet(c.bindings, f) let origF = f var f = if prev == nil: f else: prev @@ -1534,13 +1696,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return isNone if prev == nil: put(c, f, a) else: - let fKind = rootf.lastSon.kind + let fKind = rootf.last.kind if fKind in {tyAnd, tyOr}: - result = typeRel(c, lastSon(f), a, flags) + result = typeRel(c, last(f), a, flags) if result != isNone: put(c, f, a) return - var aAsObject = roota.lastSon + var aAsObject = roota.last if fKind in {tyRef, tyPtr}: if aAsObject.kind == tyObject: @@ -1553,34 +1715,32 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if aAsObject.kind == tyObject and trIsOutParam notin flags: let baseType = aAsObject.base if baseType != nil: - c.inheritancePenalty += 1 + inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0) let ret = typeRel(c, f, baseType, flags) return if ret in {isEqual,isGeneric}: isSubtype else: ret result = isNone else: - assert lastSon(origF) != nil - result = typeRel(c, lastSon(origF), a, flags) + assert last(origF) != nil + result = typeRel(c, last(origF), a, flags) if result != isNone and a.kind != tyNil: put(c, f, a) - of tyGenericBody: considerPreviousT: if a == f or a.kind == tyGenericInst and a.skipGenericAlias[0] == f: bindingRet isGeneric - let ff = lastSon(f) + let ff = last(f) if ff != nil: result = typeRel(c, ff, a, flags) - of tyGenericInvocation: var x = a.skipGenericAlias if x.kind == tyGenericParam and x.len > 0: - x = x.lastSon + x = x.last let concpt = f[0].skipTypes({tyGenericBody}) var preventHack = concpt.kind == tyConcept if x.kind == tyOwned and f[0].kind != tyOwned: preventHack = true - x = x.lastSon + x = x.last # XXX: This is very hacky. It should be moved back into liftTypeParam if x.kind in {tyGenericInst, tyArray} and c.calleeSym != nil and @@ -1613,7 +1773,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, var askip = skippedNone var fskip = skippedNone let aobj = x.skipToObject(askip) - let fobj = genericBody.lastSon.skipToObject(fskip) + let fobj = genericBody.last.skipToObject(fskip) result = typeRel(c, genericBody, x, flags) if result != isNone: # see tests/generics/tgeneric3.nim for an example that triggers this @@ -1627,14 +1787,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # # we steal the generic parameters from the tyGenericBody: for i in 1..<f.len: - let x = PType(idTableGet(c.bindings, genericBody[i-1])) + let x = idTableGet(c.bindings, genericBody[i-1]) if x == nil: discard "maybe fine (for e.g. a==tyNil)" elif x.kind in {tyGenericInvocation, tyGenericParam}: internalError(c.c.graph.config, "wrong instantiated type!") else: let key = f[i] - let old = PType(idTableGet(c.bindings, key)) + let old = idTableGet(c.bindings, key) if old == nil: put(c, key, x) elif typeRel(c, old, x, flags + {trDontBind}) == isNone: @@ -1652,60 +1812,56 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, depth = -1 if depth >= 0: - c.inheritancePenalty += depth + inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0) # bug #4863: We still need to bind generic alias crap, so # we cannot return immediately: result = if depth == 0: isGeneric else: isSubtype of tyAnd: considerPreviousT: result = isEqual - for branch in f: + for branch in f.kids: let x = typeRel(c, branch, aOrig, flags) if x < isSubtype: return isNone # 'and' implies minimum matching result: if x < result: result = x if result > isGeneric: result = isGeneric bindingRet result - of tyOr: considerPreviousT: result = isNone let oldInheritancePenalty = c.inheritancePenalty - var maxInheritance = 0 - for branch in f: - c.inheritancePenalty = 0 + var minInheritance = maxInheritancePenalty + for branch in f.kids: + c.inheritancePenalty = -1 let x = typeRel(c, branch, aOrig, flags) - maxInheritance = max(maxInheritance, c.inheritancePenalty) - # 'or' implies maximum matching result: - if x > result: result = x + if x >= result: + if c.inheritancePenalty > -1: + minInheritance = min(minInheritance, c.inheritancePenalty) + result = x if result >= isIntConv: + if minInheritance < maxInheritancePenalty: + c.inheritancePenalty = oldInheritancePenalty + minInheritance if result > isGeneric: result = isGeneric bindingRet result else: result = isNone - c.inheritancePenalty = oldInheritancePenalty + maxInheritance - of tyNot: considerPreviousT: - for branch in f: - if typeRel(c, branch, aOrig, flags) != isNone: - return isNone + if typeRel(c, f.elementType, aOrig, flags) != isNone: + return isNone bindingRet isGeneric - of tyAnything: considerPreviousT: var concrete = concreteType(c, a) if concrete != nil and doBind: put(c, f, concrete) return isGeneric - of tyBuiltInTypeClass: considerPreviousT: - let target = f[0] + let target = f.genericHead let targetKind = target.kind - var effectiveArgType = a.getObjectTypeOrNil() - if effectiveArgType == nil: return isNone + var effectiveArgType = reduceToBase(a) effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass}) if targetKind == effectiveArgType.kind: if effectiveArgType.isEmptyContainer: @@ -1716,14 +1872,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if tfExplicitCallConv in target.flags and target.callConv != effectiveArgType.callConv: return isNone - put(c, f, a) + if doBind: put(c, f, a) return isGeneric else: return isNone - of tyUserTypeClassInst, tyUserTypeClass: if f.isResolvedUserTypeClass: - result = typeRel(c, f.lastSon, a, flags) + result = typeRel(c, f.last, a, flags) else: considerPreviousT: if aOrig == f: return isEqual @@ -1732,7 +1887,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, bindConcreteTypeToUserTypeClass(matched, a) if doBind: put(c, f, matched) result = isGeneric - elif a.len > 0 and a.lastSon == f: + elif a.len > 0 and a.last == f: # Needed for checking `Y` == `Addable` in the following #[ type @@ -1743,15 +1898,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isGeneric else: result = isNone - of tyConcept: result = if concepts.conceptMatch(c.c, f, a, c.bindings, nil): isGeneric else: isNone - of tyCompositeTypeClass: considerPreviousT: let roota = a.skipGenericAlias - let rootf = f.lastSon.skipGenericAlias + let rootf = f.last.skipGenericAlias if a.kind == tyGenericInst and roota.base == rootf.base: for i in 1..<rootf.len-1: let ff = rootf[i] @@ -1760,14 +1913,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if result == isNone: return if ff.kind == tyRange and result != isEqual: return isNone else: - result = typeRel(c, rootf.lastSon, a, flags) + result = typeRel(c, rootf.last, a, flags) if result != isNone: put(c, f, a) result = isGeneric - of tyGenericParam: let doBindGP = doBind or trBindGenericParam in flags - var x = PType(idTableGet(c.bindings, f)) + var x = idTableGet(c.bindings, f) if x == nil: if c.callee.kind == tyGenericBody and not c.typedescMatched: # XXX: The fact that generic types currently use tyGenericParam for @@ -1785,7 +1937,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, c.typedescMatched = true var aa = a while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0: - aa = lastSon(aa) + aa = last(aa) if aa.kind in {tyGenericParam} + tyTypeClasses: # If the constraint is a genericParam or typeClass this isGeneric return isGeneric @@ -1802,18 +1954,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: # check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)' if f.len > 0 and f[0].kind != tyNone: - let oldInheritancePenalty = c.inheritancePenalty - result = typeRel(c, f[0], a, flags + {trDontBind,trBindGenericParam}) + result = typeRel(c, f[0], a, flags + {trDontBind, trBindGenericParam}) if doBindGP and result notin {isNone, isGeneric}: let concrete = concreteType(c, a, f) if concrete == nil: return isNone put(c, f, concrete) - # bug #6526 if result in {isEqual, isSubtype}: - # 'T: Class' is a *better* match than just 'T' - # but 'T: Subclass' is even better: - c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty - - 100 * ord(result == isEqual) result = isGeneric elif a.kind == tyTypeDesc: # somewhat special typing rule, the following is illegal: @@ -1829,10 +1975,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, a.sym.transitionGenericParamToType() a.flags.excl tfWildcard elif doBind: - # The mechanics of `doBind` being a flag that also denotes sig cmp via - # negation is potentially problematic. `IsNone` is appropriate for - # preventing illegal bindings, but it is not necessarily appropriate - # before the bindings have been finalized. + # careful: `trDontDont` (set by `checkGeneric`) is not always respected in this call graph. + # typRel having two different modes (binding and non-binding) can make things harder to + # reason about and maintain. Refactoring typeRel to not be responsible for setting, or + # at least validating, bindings can have multiple benefits. This is debatable. I'm not 100% sure. + # A design that allows a proper complexity analysis of types like `tyOr` would be ideal. concrete = concreteType(c, a, f) if concrete == nil: return isNone @@ -1845,21 +1992,34 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, elif x.kind == tyGenericParam: result = isGeneric else: + # This is the bound type - can't benifit from these tallies + let + inheritancePenaltyOld = c.inheritancePenalty result = typeRel(c, x, a, flags) # check if it fits + c.inheritancePenalty = inheritancePenaltyOld if result > isGeneric: result = isGeneric of tyStatic: - let prev = PType(idTableGet(c.bindings, f)) + let prev = idTableGet(c.bindings, f) if prev == nil: if aOrig.kind == tyStatic: - if f.base.kind notin {tyNone, tyGenericParam}: + if c.c.inGenericContext > 0 and aOrig.n == nil and not c.isNoCall: + # don't match unresolved static value to static param to avoid + # faulty instantiations in calls in generic bodies + # but not for generic invocations as they only check constraints + result = isNone + elif f.base.kind notin {tyNone, tyGenericParam}: result = typeRel(c, f.base, a, flags) if result != isNone and f.n != nil: - if not exprStructuralEquivalent(f.n, aOrig.n): + var r = tryResolvingStaticExpr(c, f.n) + if r == nil: r = f.n + if not exprStructuralEquivalent(r, aOrig.n) and + not (aOrig.n != nil and aOrig.n.kind == nkIntLit and + inferStaticParam(c, r, aOrig.n.intVal)): result = isNone elif f.base.kind == tyGenericParam: # Handling things like `type A[T; Y: static T] = object` if f.base.len > 0: # There is a constraint, handle it - result = typeRel(c, f.base.lastSon, a, flags) + result = typeRel(c, f.base.last, a, flags) else: # No constraint if tfGenericTypeParam in f.flags: @@ -1872,17 +2032,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if result != isNone: put(c, f, aOrig) elif aOrig.n != nil and aOrig.n.typ != nil: result = if f.base.kind != tyNone: - typeRel(c, f.lastSon, aOrig.n.typ, flags) + typeRel(c, f.last, aOrig.n.typ, flags) else: isGeneric if result != isNone: - var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ]) + var boundType = newTypeS(tyStatic, c.c, aOrig.n.typ) boundType.n = aOrig.n put(c, f, boundType) else: result = isNone elif prev.kind == tyStatic: if aOrig.kind == tyStatic: - result = typeRel(c, prev.lastSon, a, flags) + result = typeRel(c, prev.last, a, flags) if result != isNone and prev.n != nil: if not exprStructuralEquivalent(prev.n, aOrig.n): result = isNone @@ -1891,7 +2051,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # XXX endless recursion? #result = typeRel(c, prev, aOrig, flags) result = isNone - of tyInferred: let prev = f.previouslyInferred if prev != nil: @@ -1901,15 +2060,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if result != isNone: c.inferredTypes.add f f.add a - of tyTypeDesc: - var prev = PType(idTableGet(c.bindings, f)) + var prev = idTableGet(c.bindings, f) if prev == nil: # proc foo(T: typedesc, x: T) # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier - if c.c.inGenericContext > 0 and - a.skipTypes({tyTypeDesc}).kind == tyGenericParam: + if c.c.inGenericContext > 0 and a.containsUnresolvedType: # generic type bodies can sometimes compile call expressions # prevent unresolved generic parameters from being passed to procs as # typedesc parameters @@ -1934,35 +2091,40 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = typeRel(c, prev.base, a.base, flags) else: result = isNone - of tyTyped: if aOrig != nil: put(c, f, aOrig) result = isGeneric - - of tyProxy: + of tyError: result = isEqual - of tyFromExpr: # fix the expression, so it contains the already instantiated types if f.n == nil or f.n.kind == nkEmpty: return isGeneric - let reevaluated = tryResolvingStaticExpr(c, f.n) - if reevaluated == nil: + if c.c.inGenericContext > 0: + # need to delay until instantiation + # also prevent infinite recursion below + return isNone + inc c.c.inGenericContext # to generate tyFromExpr again if unresolved + # use prepareNode for consistency with other tyFromExpr in semtypinst: + let instantiated = prepareTypesInBody(c.c, c.bindings, f.n) + let reevaluated = c.c.semExpr(c.c, instantiated).typ + dec c.c.inGenericContext + case reevaluated.kind + of tyFromExpr: + # not resolved result = isNone - return - case reevaluated.typ.kind of tyTypeDesc: - result = typeRel(c, a, reevaluated.typ.base, flags) + result = typeRel(c, reevaluated.base, a, flags) of tyStatic: - result = typeRel(c, a, reevaluated.typ.base, flags) - if result != isNone and reevaluated.typ.n != nil: - if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n): + result = typeRel(c, reevaluated.base, a, flags) + if result != isNone and reevaluated.n != nil: + if not exprStructuralEquivalent(aOrig.n, reevaluated.n): result = isNone else: # bug #14136: other types are just like 'tyStatic' here: - result = typeRel(c, a, reevaluated.typ, flags) - if result != isNone and reevaluated.typ.n != nil: - if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n): + result = typeRel(c, reevaluated, a, flags) + if result != isNone and reevaluated.n != nil: + if not exprStructuralEquivalent(aOrig.n, reevaluated.n): result = isNone of tyNone: if a.kind == tyNone: result = isEqual @@ -1990,7 +2152,7 @@ proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, f: PType): PType = - result = PType(idTableGet(m.bindings, f)) + result = idTableGet(m.bindings, f) if result == nil: result = generateTypeInstance(c, m.bindings, arg, f) if result == nil: @@ -2001,7 +2163,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, c: PContext): PNode = result = newNodeI(kind, arg.info) if containsGenericType(f): - if not m.hasFauxMatch: + if not m.matchedErrorType: result.typ = getInstantiatedType(c, arg, m, f).skipTypes({tySink}) else: result.typ = errorType(c) @@ -2016,12 +2178,87 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv") result.add c.graph.emptyNode if arg.typ != nil and arg.typ.kind == tyLent: - let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ[0]) + let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ.elementType) a.add arg result.add a else: result.add arg +proc convertLiteral(kind: TNodeKind, c: PContext, m: TCandidate; n: PNode, newType: PType): PNode = + # based off changeType but generates implicit conversions instead + template addConsiderNil(s, node) = + let val = node + if val.isNil: return nil + s.add(val) + case n.kind + of nkCurly: + result = copyNode(n) + for i in 0..<n.len: + if n[i].kind == nkRange: + var x = copyNode(n[i]) + x.addConsiderNil convertLiteral(kind, c, m, n[i][0], elemType(newType)) + x.addConsiderNil convertLiteral(kind, c, m, n[i][1], elemType(newType)) + result.add x + else: + result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType)) + result.typ = newType + return + of nkBracket: + result = copyNode(n) + for i in 0..<n.len: + result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType)) + result.typ = newType + return + of nkPar, nkTupleConstr: + let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}) + if tup.kind == tyTuple: + result = copyNode(n) + if n.len > 0 and n[0].kind == nkExprColonExpr: + # named tuple? + for i in 0..<n.len: + var name = n[i][0] + if name.kind != nkSym: + #globalError(c.config, name.info, "invalid tuple constructor") + return nil + if tup.n != nil: + var f = getSymFromList(tup.n, name.sym.name) + if f == nil: + #globalError(c.config, name.info, "unknown identifier: " & name.sym.name.s) + return nil + result.addConsiderNil convertLiteral(kind, c, m, n[i][1], f.typ) + else: + result.addConsiderNil convertLiteral(kind, c, m, n[i][1], tup[i]) + else: + for i in 0..<n.len: + result.addConsiderNil convertLiteral(kind, c, m, n[i], tup[i]) + result.typ = newType + return + of nkCharLit..nkUInt64Lit: + if n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType) and isOrdinalType(newType): + let value = n.intVal + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + return nil + result = copyNode(n) + result.typ = newType + return + of nkFloatLit..nkFloat64Lit: + if newType.skipTypes(abstractVarRange-{tyTypeDesc}).kind == tyFloat: + if not floatRangeCheck(n.floatVal, newType): + return nil + result = copyNode(n) + result.typ = newType + return + of nkSym: + if n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType) and isOrdinalType(newType): + let value = n.sym.position + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + return nil + result = copyNode(n) + result.typ = newType + return + else: discard + return implicitConv(kind, newType, n, m, c) + proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} = let aa = isAssignable(nil, n) case aa @@ -2039,8 +2276,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, arg: PNode): PNode = result = nil for i in 0..<c.converters.len: - var src = c.converters[i].typ[1] - var dest = c.converters[i].typ[0] + var src = c.converters[i].typ.firstParamType + var dest = c.converters[i].typ.returnType # for generic type converters we need to check 'src <- a' before # 'f <- dest' in order to not break the unification: # see tests/tgenericconverter: @@ -2073,7 +2310,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c) elif src.kind in {tyVar}: # Analyse the converter return type. - param = newNodeIT(nkHiddenAddr, arg.info, s.typ[1]) + param = newNodeIT(nkHiddenAddr, arg.info, s.typ.firstParamType) param.add copyTree(arg) else: param = copyTree(arg) @@ -2129,11 +2366,12 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) = of isNone: discard template matchesVoidProc(t: PType): bool = - (t.kind == tyProc and t.len == 1 and t[0] == nil) or - (t.kind == tyBuiltInTypeClass and t[0].kind == tyProc) + (t.kind == tyProc and t.len == 1 and t.returnType == nil) or + (t.kind == tyBuiltInTypeClass and t.elementType.kind == tyProc) proc paramTypesMatchAux(m: var TCandidate, f, a: PType, argSemantized, argOrig: PNode): PNode = + result = nil var fMaybeStatic = f.skipTypes({tyDistinct}) arg = argSemantized @@ -2158,12 +2396,15 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, a.n == nil and tfGenericTypeParam notin a.flags: return newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg)) + elif a.kind == tyFromExpr and c.inGenericContext > 0: + # don't try to evaluate + discard elif arg.kind != nkEmpty: var evaluated = c.semTryConstExpr(c, arg) if evaluated != nil: # Don't build the type in-place because `evaluated` and `arg` may point # to the same object and we'd end up creating recursive types (#9255) - let typ = newTypeS(tyStatic, c, sons = @[evaluated.typ]) + let typ = newTypeS(tyStatic, c, son = evaluated.typ) typ.n = evaluated arg = copyTree(arg) # fix #12864 arg.typ = typ @@ -2196,35 +2437,53 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, else: return argSemantized # argOrig - # If r == isBothMetaConvertible then we rerun typeRel. - # bothMetaCounter is for safety to avoid any infinite loop, - # I don't have any example when it is needed. - # lastBindingsLenth is used to check whether m.bindings remains the same, - # because in that case there is no point in continuing. - var bothMetaCounter = 0 - var lastBindingsLength = -1 - while r == isBothMetaConvertible and - lastBindingsLength != m.bindings.counter and - bothMetaCounter < 100: - lastBindingsLength = m.bindings.counter - inc(bothMetaCounter) - if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds: - result = c.semInferredLambda(c, m.bindings, arg) - elif arg.kind != nkSym: - return nil - else: - let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) - result = newSymNode(inferred, arg.info) - inc(m.convMatches) - arg = result - r = typeRel(m, f, arg.typ) + block instantiateGenericRoutine: + # In the case where the matched value is a generic proc, we need to + # fully instantiate it and then rerun typeRel to make sure it matches. + # instantiationCounter is for safety to avoid any infinite loop, + # I don't have any example when it is needed. + # lastBindingCount is used to check whether m.bindings remains the same, + # because in that case there is no point in continuing. + var instantiationCounter = 0 + var lastBindingCount = -1 + while r in {isBothMetaConvertible, isInferred, isInferredConvertible} and + lastBindingCount != m.bindings.len and + instantiationCounter < 100: + lastBindingCount = m.bindings.len + inc(instantiationCounter) + if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds: + result = c.semInferredLambda(c, m.bindings, arg) + elif arg.kind != nkSym: + return nil + elif arg.sym.kind in {skMacro, skTemplate}: + return nil + else: + if arg.sym.ast == nil: + return nil + let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) + result = newSymNode(inferred, arg.info) + arg = result + r = typeRel(m, f, arg.typ) case r of isConvertible: if f.skipTypes({tyRange}).kind in {tyInt, tyUInt}: inc(m.convMatches) inc(m.convMatches) - result = implicitConv(nkHiddenStdConv, f, arg, m, c) + if skipTypes(f, abstractVar-{tyTypeDesc}).kind == tySet: + if tfIsConstructor in a.flags and arg.kind == nkCurly: + # we marked the set as convertible only because the arg is a literal + # in which case we individually convert each element + let t = + if containsGenericType(f): + getInstantiatedType(c, arg, m, f).skipTypes({tySink}) + else: + f.skipTypes({tySink}) + result = convertLiteral(nkHiddenStdConv, c, m, arg, t) + else: + result = nil + else: + result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isIntConv: # I'm too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: @@ -2244,29 +2503,20 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, result = arg else: result = implicitConv(nkHiddenStdConv, f, arg, m, c) - of isInferred, isInferredConvertible: - if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds: - result = c.semInferredLambda(c, m.bindings, arg) - elif arg.kind != nkSym: - return nil - elif arg.sym.kind in {skMacro, skTemplate}: - return nil - else: - if arg.sym.ast == nil: - return nil - let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) - result = newSymNode(inferred, arg.info) - if r == isInferredConvertible: - inc(m.convMatches) - result = implicitConv(nkHiddenStdConv, f, result, m, c) - else: - inc(m.genericMatches) + of isInferred: + # result should be set in above while loop: + assert result != nil + inc(m.genericMatches) + of isInferredConvertible: + # result should be set in above while loop: + assert result != nil + inc(m.convMatches) + result = implicitConv(nkHiddenStdConv, f, result, m, c) of isGeneric: inc(m.genericMatches) if arg.typ == nil: result = arg - elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or - m.inheritancePenalty > oldInheritancePenalty: + elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or cmpInheritancePenalty(oldInheritancePenalty, m.inheritancePenalty) > 0: result = implicitConv(nkHiddenSubConv, f, arg, m, c) elif arg.typ.isEmptyContainer: result = arg.copyTree @@ -2274,8 +2524,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, else: result = arg of isBothMetaConvertible: - # This is the result for the 101th time. - result = nil + # result should be set in above while loop: + assert result != nil + inc(m.convMatches) + result = arg of isFromIntLit: # too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: @@ -2290,13 +2542,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, result = implicitConv(nkHiddenSubConv, f, arg, m, c) of isNone: # do not do this in ``typeRel`` as it then can't infer T in ``ref T``: - if a.kind in {tyProxy, tyUnknown}: - if a.kind == tyUnknown and c.inGenericContext > 0: - # don't bother with fauxMatch mechanism in generic type, - # reject match, typechecking will be delayed to instantiation - return nil + if a.kind == tyFromExpr: return nil + elif a.kind == tyError: inc(m.genericMatches) - m.fauxMatch = a.kind + m.matchedErrorType = true return arg elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList: # lift do blocks without params to lambdas @@ -2318,6 +2567,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, if f.n != nil: # Forward to the varargs converter result = localConvMatch(c, m, f, a, arg) + elif f[0].kind == tyTyped: + inc m.genericMatches + result = arg else: r = typeRel(m, base(f), a) case r @@ -2345,61 +2597,103 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, result = userConvMatch(c, m, base(f), a, arg) if result != nil: m.baseTypeMatch = true +proc staticAwareTypeRel(m: var TCandidate, f: PType, arg: var PNode): TTypeRelation = + if f.kind == tyStatic and f.base.kind == tyProc: + # The ast of the type does not point to the symbol. + # Without this we will never resolve a `static proc` with overloads + let copiedNode = copyNode(arg) + copiedNode.typ = exactReplica(copiedNode.typ) + copiedNode.typ.n = arg + arg = copiedNode + typeRel(m, f, arg.typ) + + proc paramTypesMatch*(m: var TCandidate, f, a: PType, arg, argOrig: PNode): PNode = if arg == nil or arg.kind notin nkSymChoices: result = paramTypesMatchAux(m, f, a, arg, argOrig) else: - # CAUTION: The order depends on the used hashing scheme. Thus it is - # incorrect to simply use the first fitting match. However, to implement - # this correctly is inefficient. We have to copy `m` here to be able to - # roll back the side effects of the unification algorithm. - let c = m.c - var - x = newCandidate(c, m.callee) - y = newCandidate(c, m.callee) - z = newCandidate(c, m.callee) - x.calleeSym = m.calleeSym - y.calleeSym = m.calleeSym - z.calleeSym = m.calleeSym + # symbol kinds that don't participate in symchoice type disambiguation: + let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage} + var best = -1 - for i in 0..<arg.len: - if arg[i].sym.kind in {skProc, skFunc, skMethod, skConverter, - skIterator, skMacro, skTemplate, skEnumField}: - copyCandidate(z, m) - z.callee = arg[i].typ - if tfUnresolved in z.callee.flags: continue - z.calleeSym = arg[i].sym - # XXX this is still all wrong: (T, T) should be 2 generic matches - # and (int, int) 2 exact matches, etc. Essentially you cannot call - # typeRel here and expect things to work! - let r = typeRel(z, f, arg[i].typ) - incMatches(z, r, 2) - if r != isNone: - z.state = csMatch - case x.state - of csEmpty, csNoMatch: - x = z + result = arg + + var actingF = f + if f.kind == tyVarargs: + if m.calleeSym.kind in {skTemplate, skMacro}: + actingF = f[0] + if actingF.kind in {tyTyped, tyUntyped}: + var + bestScope = -1 + counts = 0 + for i in 0..<arg.len: + if arg[i].sym.kind in matchSet: + let thisScope = cmpScopes(m.c, arg[i].sym) + if thisScope > bestScope: best = i - of csMatch: - let cmp = cmpCandidates(x, z) - if cmp < 0: - best = i - x = z - elif cmp == 0: - y = z # z is as good as x - - if x.state == csEmpty: - result = nil - elif y.state == csMatch and cmpCandidates(x, y) == 0: - if x.state != csMatch: - internalError(m.c.graph.config, arg.info, "x.state is not csMatch") - # ambiguous: more than one symbol fits! - # See tsymchoice_for_expr as an example. 'f.kind == tyUntyped' should match - # anyway: - if f.kind in {tyUntyped, tyTyped}: result = arg - else: result = nil + bestScope = thisScope + counts = 0 + elif thisScope == bestScope: + inc counts + if best == -1: + result = nil + elif counts > 0: + m.genericMatches = 1 + best = -1 else: + # CAUTION: The order depends on the used hashing scheme. Thus it is + # incorrect to simply use the first fitting match. However, to implement + # this correctly is inefficient. We have to copy `m` here to be able to + # roll back the side effects of the unification algorithm. + let c = m.c + var + x = newCandidate(c, m.callee) # potential "best" + y = newCandidate(c, m.callee) # potential competitor with x + z = newCandidate(c, m.callee) # buffer for copies of m + x.calleeSym = m.calleeSym + y.calleeSym = m.calleeSym + z.calleeSym = m.calleeSym + + for i in 0..<arg.len: + if arg[i].sym.kind in matchSet: + copyCandidate(z, m) + z.callee = arg[i].typ + if arg[i].sym.kind == skType and z.callee.kind != tyTypeDesc: + # creating the symchoice with the type sym having typedesc type + # breaks a lot of stuff, so we make the typedesc type here + # mirrored from `newSymNodeTypeDesc` + z.callee = newType(tyTypeDesc, c.idgen, arg[i].sym.owner) + z.callee.addSonSkipIntLit(arg[i].sym.typ, c.idgen) + if tfUnresolved in z.callee.flags: continue + z.calleeSym = arg[i].sym + z.calleeScope = cmpScopes(m.c, arg[i].sym) + # XXX this is still all wrong: (T, T) should be 2 generic matches + # and (int, int) 2 exact matches, etc. Essentially you cannot call + # typeRel here and expect things to work! + let r = staticAwareTypeRel(z, f, arg[i]) + incMatches(z, r, 2) + if r != isNone: + z.state = csMatch + case x.state + of csEmpty, csNoMatch: + x = z + best = i + of csMatch: + let cmp = cmpCandidates(x, z, isFormal=false) + if cmp < 0: + best = i + x = z + elif cmp == 0: + y = z # z is as good as x + + if x.state == csEmpty: + result = nil + elif y.state == csMatch and cmpCandidates(x, y, isFormal=false) == 0: + if x.state != csMatch: + internalError(m.c.graph.config, arg.info, "x.state is not csMatch") + result = nil + if best > -1 and result != nil: # only one valid interpretation found: markUsed(m.c, arg.info, arg[best].sym) onUse(arg.info, arg[best].sym) @@ -2421,7 +2715,7 @@ proc setSon(father: PNode, at: int, son: PNode) = # we are allowed to modify the calling node in the 'prepare*' procs: proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = if formal.kind == tyUntyped and formal.len != 1: - # {tyTypeDesc, tyUntyped, tyTyped, tyProxy}: + # {tyTypeDesc, tyUntyped, tyTyped, tyError}: # a.typ == nil is valid result = a elif a.typ.isNil: @@ -2459,7 +2753,7 @@ proc arrayConstr(c: PContext, n: PNode): PType = result = newTypeS(tyArray, c) rawAddSon(result, makeRangeType(c, 0, 0, n.info)) addSonSkipIntLit(result, skipTypes(n.typ, - {tyGenericInst, tyVar, tyLent, tyOrdinal}), c.idgen) + {tyVar, tyLent, tyOrdinal}), c.idgen) proc arrayConstr(c: PContext, info: TLineInfo): PType = result = newTypeS(tyArray, c) @@ -2468,11 +2762,14 @@ proc arrayConstr(c: PContext, info: TLineInfo): PType = proc incrIndexType(t: PType) = assert t.kind == tyArray - inc t[0].n[1].intVal + inc t.indexType.n[1].intVal template isVarargsUntyped(x): untyped = x.kind == tyVarargs and x[0].kind == tyUntyped +template isVarargsTyped(x): untyped = + x.kind == tyVarargs and x[0].kind == tyTyped + proc findFirstArgBlock(m: var TCandidate, n: PNode): int = # see https://github.com/nim-lang/RFCs/issues/405 result = int.high @@ -2647,7 +2944,15 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int n[a], nOrig[a]) if arg == nil: noMatch() - if m.baseTypeMatch: + if formal.typ.isVarargsTyped and m.calleeSym.kind in {skTemplate, skMacro}: + if container.isNil: + container = newNodeIT(nkBracket, n[a].info, arrayConstr(c, n.info)) + setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) + else: + incrIndexType(container.typ) + container.add n[a] + f = max(f, formalLen - n.len + a + 1) + elif m.baseTypeMatch: assert formal.typ.kind == tyVarargs #assert(container == nil) if container.isNil: @@ -2687,12 +2992,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int m.firstMismatch.arg = a m.firstMismatch.formal = formal -proc semFinishOperands*(c: PContext, n: PNode) = - # this needs to be called to ensure that after overloading resolution every - # argument has been sem'checked: - for i in 1..<n.len: - n[i] = prepareOperand(c, n[i]) - proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) = # for 'suggest' support: var marker = initIntSet() @@ -2708,6 +3007,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = inc m.genericMatches inc m.exactMatches return + # initCandidate may have given csNoMatch if generic params didn't match: + if m.state == csNoMatch: return var marker = initIntSet() matchesAux(c, n, nOrig, m, marker) if m.state == csNoMatch: return @@ -2730,14 +3031,16 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = m.firstMismatch.formal = formal break else: + # mirrored with updateDefaultParams: if formal.ast.kind == nkEmpty: # The default param value is set to empty in `instantiateProcType` # when the type of the default expression doesn't match the type # of the instantiated proc param: - localError(c.config, m.call.info, - ("The default parameter '$1' has incompatible type " & - "with the explicitly requested proc instantiation") % - formal.name.s) + pushInfoContext(c.config, m.call.info, + if m.calleeSym != nil: m.calleeSym.detailedInfo else: "") + typeMismatch(c.config, formal.ast.info, formal.typ, formal.ast.typ, formal.ast) + popInfoContext(c.config) + formal.ast.typ = errorType(c) if nfDefaultRefsParam in formal.ast.flags: m.call.flags.incl nfDefaultRefsParam var defaultValue = copyTree(formal.ast) @@ -2746,7 +3049,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = # proc foo(x: T = 0.0) # foo() if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}: - let existing = PType(idTableGet(m.bindings, formal.typ)) + let existing = idTableGet(m.bindings, formal.typ) if existing == nil or existing.kind == tyTypeDesc: # see bug #11600: put(m, formal.typ, defaultValue.typ) @@ -2779,9 +3082,9 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; var f = dc.typ[col] if op == attachedDeepCopy: - if f.kind in {tyRef, tyPtr}: f = f.lastSon + if f.kind in {tyRef, tyPtr}: f = f.elementType else: - if f.kind in {tyVar}: f = f.lastSon + if f.kind in {tyVar}: f = f.elementType if typeRel(m, f, t) == isNone: result = nil localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 9e5a9ab90..1dd481ec0 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -248,7 +248,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = conf.target.ptrSize typ.align = int16(conf.target.ptrSize) of tyCstring, tySequence, tyPtr, tyRef, tyVar, tyLent: - let base = typ.lastSon + let base = typ.last if base == typ: # this is not the correct location to detect ``type A = ptr A`` typ.size = szIllegalRecursion @@ -262,9 +262,9 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = conf.target.ptrSize of tyArray: - computeSizeAlign(conf, typ[1]) - let elemSize = typ[1].size - let len = lengthOrd(conf, typ[0]) + computeSizeAlign(conf, typ.elementType) + let elemSize = typ.elementType.size + let len = lengthOrd(conf, typ.indexType) if elemSize < 0: typ.size = elemSize typ.align = int16(elemSize) @@ -273,10 +273,10 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.align = szUnknownSize else: typ.size = toInt64Checked(len * int32(elemSize), szTooBigSize) - typ.align = typ[1].align + typ.align = typ.elementType.align of tyUncheckedArray: - let base = typ.lastSon + let base = typ.last computeSizeAlign(conf, base) typ.size = 0 typ.align = base.align @@ -300,11 +300,11 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = 8 typ.align = int16(conf.floatInt64Align) of tySet: - if typ[0].kind == tyGenericParam: + if typ.elementType.kind == tyGenericParam: typ.size = szUncomputedSize typ.align = szUncomputedSize else: - let length = toInt64(lengthOrd(conf, typ[0])) + let length = toInt64(lengthOrd(conf, typ.elementType)) if length <= 8: typ.size = 1 typ.align = 1 @@ -324,16 +324,15 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = align(length, 8) div 8 + 1 typ.align = 1 of tyRange: - computeSizeAlign(conf, typ[0]) - typ.size = typ[0].size - typ.align = typ[0].align - typ.paddingAtEnd = typ[0].paddingAtEnd + computeSizeAlign(conf, typ.elementType) + typ.size = typ.elementType.size + typ.align = typ.elementType.align + typ.paddingAtEnd = typ.elementType.paddingAtEnd of tyTuple: try: var accum = OffsetAccum(maxAlign: 1) - for i in 0..<typ.len: - let child = typ[i] + for i, child in typ.ikids: computeSizeAlign(conf, child) accum.align(child.align) if typ.n != nil: # is named tuple (has field symbols)? @@ -351,11 +350,11 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = of tyObject: try: var accum = - if typ[0] != nil: + if typ.baseClass != nil: # compute header size - var st = typ[0] + var st = typ.baseClass while st.kind in skipPtrs: - st = st[^1] + st = st.skipModifier computeSizeAlign(conf, st) if conf.backend == backendCpp: OffsetAccum( @@ -386,6 +385,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = accum.maxAlign = 1 computeObjectOffsetsFoldFunction(conf, typ.n, true, accum) else: + if typ.baseClass == nil and lacksMTypeField(typ) and typ.n.len == 1 and + typ.n[0].kind == nkSym and + typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray: + # a dummy field is generated for an object with a single field + # with an UncheckedArray type + assert accum.offset == 0 + accum.offset = 1 computeObjectOffsetsFoldFunction(conf, typ.n, false, accum) let paddingAtEnd = int16(accum.finish()) if typ.sym != nil and @@ -403,24 +409,24 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.align = szIllegalRecursion typ.paddingAtEnd = szIllegalRecursion of tyInferred: - if typ.len > 1: - computeSizeAlign(conf, typ.lastSon) - typ.size = typ.lastSon.size - typ.align = typ.lastSon.align - typ.paddingAtEnd = typ.lastSon.paddingAtEnd + if typ.hasElementType: + computeSizeAlign(conf, typ.last) + typ.size = typ.last.size + typ.align = typ.last.align + typ.paddingAtEnd = typ.last.paddingAtEnd of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned: - computeSizeAlign(conf, typ.lastSon) - typ.size = typ.lastSon.size - typ.align = typ.lastSon.align - typ.paddingAtEnd = typ.lastSon.paddingAtEnd + computeSizeAlign(conf, typ.skipModifier) + typ.size = typ.skipModifier.size + typ.align = typ.skipModifier.align + typ.paddingAtEnd = typ.last.paddingAtEnd of tyTypeClasses: if typ.isResolvedUserTypeClass: - computeSizeAlign(conf, typ.lastSon) - typ.size = typ.lastSon.size - typ.align = typ.lastSon.align - typ.paddingAtEnd = typ.lastSon.paddingAtEnd + computeSizeAlign(conf, typ.last) + typ.size = typ.last.size + typ.align = typ.last.align + typ.paddingAtEnd = typ.last.paddingAtEnd else: typ.size = szUnknownSize typ.align = szUnknownSize @@ -439,10 +445,10 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = of tyStatic: if typ.n != nil: - computeSizeAlign(conf, typ.lastSon) - typ.size = typ.lastSon.size - typ.align = typ.lastSon.align - typ.paddingAtEnd = typ.lastSon.paddingAtEnd + computeSizeAlign(conf, typ.last) + typ.size = typ.last.size + typ.align = typ.last.align + typ.paddingAtEnd = typ.last.paddingAtEnd else: typ.size = szUnknownSize typ.align = szUnknownSize diff --git a/compiler/sourcemap.nim b/compiler/sourcemap.nim index b0b6fea2e..1395168cd 100644 --- a/compiler/sourcemap.nim +++ b/compiler/sourcemap.nim @@ -11,7 +11,7 @@ type Mapping = object ## Mapping refers to a line in the JS output. ## It is made up of segments which refer to the tokens in the line - case inSource: bool # Whether the line in JS has Nim equivilant + case inSource: bool # Whether the line in JS has Nim equivalent of true: file: int # Index into files list line: int # 0 indexed line of code in the Nim source @@ -160,10 +160,7 @@ func parse*(source: string): SourceInfo = func toSourceMap*(info: SourceInfo, file: string): SourceMap {.raises: [].} = ## Convert from high level SourceInfo into the required SourceMap object # Add basic info - result.version = 3 - result.file = file - result.sources = info.files - result.names = info.names + result = SourceMap(version: 3, file: file, sources: info.files, names: info.names) # Convert nodes into mappings. # Mappings are split into blocks where each block referes to a line in the outputted JS. # Blocks can be separated into statements which refere to tokens on the line. diff --git a/compiler/spawn.nim b/compiler/spawn.nim index bfcdd78ea..58d5a4928 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -16,7 +16,7 @@ from trees import getMagic, getRoot proc callProc(a: PNode): PNode = result = newNodeI(nkCall, a.info) result.add a - result.typ = a.typ[0] + result.typ = a.typ.returnType # we have 4 cases to consider: # - a void proc --> nothing to do @@ -50,7 +50,7 @@ proc typeNeedsNoDeepCopy(t: PType): bool = # note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var' # for the stricter check and likewise we can skip 'seq' for a less # strict check: - if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon + if t.kind in {tyVar, tyLent, tySequence}: t = t.elementType result = not containsGarbageCollectedRef(t) proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; owner: PSym; typ: PType; @@ -109,6 +109,16 @@ stmtList: """ +proc castToVoidPointer(g: ModuleGraph, n: PNode, fvField: PNode): PNode = + if g.config.backend == backendCpp: + result = fvField + else: + let ptrType = getSysType(g, n.info, tyPointer) + result = newNodeI(nkCast, fvField.info) + result.add newNodeI(nkEmpty, fvField.info) + result.add fvField + result.typ = ptrType + proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; varSection, varInit, call, barrier, fv: PNode; idgen: IdGenerator; @@ -141,10 +151,10 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; if spawnKind == srByVar: body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call) elif fv != nil: - let fk = flowVarKind(g.config, fv.typ[1]) + let fk = flowVarKind(g.config, fv.typ.firstGenericParam) if fk == fvInvalid: localError(g.config, f.info, "cannot create a flowVar of type: " & - typeToString(fv.typ[1])) + typeToString(fv.typ.firstGenericParam)) body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, if fk == fvGC: "data" else: "blob", fv.info, g.cache), call) if fk == fvGC: @@ -156,8 +166,9 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; if barrier == nil: # by now 'fv' is shared and thus might have beeen overwritten! we need # to use the thread-local view instead: + let castExpr = castToVoidPointer(g, f, threadLocalProm.newSymNode) body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info, - threadLocalProm.newSymNode) + castExpr) else: body.add call if barrier != nil: @@ -193,7 +204,7 @@ proc createCastExpr(argsParam: PSym; objType: PType; idgen: IdGenerator): PNode result.typ.rawAddSon(objType) template checkMagicProcs(g: ModuleGraph, n: PNode, formal: PNode) = - if (formal.typ.kind == tyVarargs and formal.typ[0].kind in {tyTyped, tyUntyped}) or + if (formal.typ.kind == tyVarargs and formal.typ.elementType.kind in {tyTyped, tyUntyped}) or formal.typ.kind in {tyTyped, tyUntyped}: localError(g.config, n.info, "'spawn'ed function cannot have a 'typed' or 'untyped' parameter") @@ -413,7 +424,8 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp # create flowVar: result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1])) if barrier == nil: - result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, fvField) + let castExpr = castToVoidPointer(g, n, fvField) + result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, castExpr) elif spawnKind == srByVar: var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 802da1c3e..a5213086b 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -32,13 +32,13 @@ # included from sigmatch.nim -import prefixmatches +import prefixmatches, suggestsymdb from wordrecg import wDeprecated, wError, wAddr, wYield import std/[algorithm, sets, parseutils, tables] when defined(nimsuggest): - import std/tables, pathutils # importer + import pathutils # importer const sep = '\t' @@ -85,7 +85,16 @@ proc cmpSuggestions(a, b: Suggest): int = # independent of hashing order: result = cmp(a.name[], b.name[]) -proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int = +proc scanForTrailingAsterisk(line: string, start: int): int = + result = 0 + while start+result < line.len and line[start+result] in {' ', '\t'}: + inc result + if start+result < line.len and line[start+result] == '*': + inc result + else: + result = 0 + +proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo; skipTrailingAsterisk: bool = false): int = let line = sourceLine(conf, info) column = toColumn(info) @@ -105,10 +114,16 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int result = skipUntil(line, '`', column) if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0: result = 0 + elif column >= 0 and line[column] == '`' and isOpeningBacktick(column): + result = skipUntil(line, '`', column + 1) + 2 + if cmpIgnoreStyle(line[column + 1..column + result - 2], ident) != 0: + result = 0 elif ident[0] in linter.Letters and ident[^1] != '=': result = identLen(line, column) if cmpIgnoreStyle(line[column..column + result - 1], ident[0..min(result-1,len(ident)-1)]) != 0: result = 0 + if skipTrailingAsterisk and result > 0: + result += scanForTrailingAsterisk(line, column + result) else: var sourceIdent: string = "" result = parseWhile(line, sourceIdent, @@ -161,7 +176,7 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info if section == ideInlayHints: result.forth = typeToString(s.typ, preferInlayHint) else: - result.forth = typeToString(s.typ) + result.forth = typeToString(s.typ, preferInferredEffects) else: result.forth = "" when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler): @@ -184,7 +199,7 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info result.tokenLen = if section notin {ideHighlight, ideInlayHints}: s.name.s.len else: - getTokenLenFromSource(g.config, s.name.s, infox) + getTokenLenFromSource(g.config, s.name.s, infox, section == ideInlayHints) result.version = g.config.suggestVersion result.endLine = endLine result.endCol = endCol @@ -254,7 +269,7 @@ proc `$`*(suggest: Suggest): string = result.add(sep) result.add($suggest.endCol) -proc suggestToSuggestInlayHint*(sug: Suggest): SuggestInlayHint = +proc suggestToSuggestInlayTypeHint*(sug: Suggest): SuggestInlayHint = SuggestInlayHint( kind: sihkType, line: sug.line, @@ -266,6 +281,30 @@ proc suggestToSuggestInlayHint*(sug: Suggest): SuggestInlayHint = tooltip: "" ) +proc suggestToSuggestInlayExceptionHintLeft*(sug: Suggest, propagatedExceptions: seq[PType]): SuggestInlayHint = + SuggestInlayHint( + kind: sihkException, + line: sug.line, + column: sug.column, + label: "try ", + paddingLeft: false, + paddingRight: false, + allowInsert: false, + tooltip: "propagated exceptions: " & $propagatedExceptions + ) + +proc suggestToSuggestInlayExceptionHintRight*(sug: Suggest, propagatedExceptions: seq[PType]): SuggestInlayHint = + SuggestInlayHint( + kind: sihkException, + line: sug.line, + column: sug.column + sug.tokenLen, + label: "!", + paddingLeft: false, + paddingRight: false, + allowInsert: false, + tooltip: "propagated exceptions: " & $propagatedExceptions + ) + proc suggestResult*(conf: ConfigRef; s: Suggest) = if not isNil(conf.suggestionResultHook): conf.suggestionResultHook(s) @@ -326,8 +365,8 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = proc getQuality(s: PSym): range[0..100] = result = 100 - if s.typ != nil and s.typ.len > 1: - var exp = s.typ[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) + if s.typ != nil and s.typ.paramsLen > 0: + var exp = s.typ.firstParamType.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) if exp.kind == tyVarargs: exp = elemType(exp) if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: result = 50 @@ -396,17 +435,17 @@ proc suggestVar(c: PContext, n: PNode, outputs: var Suggestions) = wholeSymTab(nameFits(c, it, n), ideCon) proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = - if s.typ != nil and s.typ.len > 1 and s.typ[1] != nil: + if s.typ != nil and s.typ.paramsLen > 0 and s.typ.firstParamType != nil: # special rule: if system and some weird generic match via 'tyUntyped' # or 'tyGenericParam' we won't list it either to reduce the noise (nobody # wants 'system.`-|` as suggestion let m = s.getModule() if m != nil and sfSystemModule in m.flags: if s.kind == skType: return - var exp = s.typ[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) + var exp = s.typ.firstParamType.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) if exp.kind == tyVarargs: exp = elemType(exp) if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return - result = sigmatch.argtypeMatches(c, s.typ[1], firstArg) + result = sigmatch.argtypeMatches(c, s.typ.firstParamType, firstArg) else: result = false @@ -476,13 +515,13 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) var t = typ while t != nil: suggestSymList(c, t.n, field, n.info, outputs) - t = t[0] + t = t.baseClass elif typ.kind == tyObject: var t = typ while true: suggestObject(c, t.n, field, n.info, outputs) - if t[0] == nil: break - t = skipTypes(t[0], skipPtrs) + if t.baseClass == nil: break + t = skipTypes(t.baseClass, skipPtrs) elif typ.kind == tyTuple and typ.n != nil: # All tuple fields are in scope # So go through each field and add it to the suggestions (If it passes the filter) @@ -523,6 +562,16 @@ proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool = else: result = false +proc isTracked*(current, trackPos: TinyLineInfo, tokenLen: int): bool = + if current.line==trackPos.line: + let col = trackPos.col + if col >= current.col and col <= current.col+tokenLen-1: + result = true + else: + result = false + else: + result = false + when defined(nimsuggest): # Since TLineInfo defined a == operator that doesn't include the column, # we map TLineInfo to a unique int here for this lookup table: @@ -573,7 +622,7 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i ## misnamed: should be 'symDeclared' let conf = g.config when defined(nimsuggest): - g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add SymInfoPair(sym: s, info: info, isDecl: isDecl) + g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl), optIdeExceptionInlayHints in g.config.globalOptions if conf.suggestVersion == 0: if s.allUsages.len == 0: @@ -647,7 +696,7 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) = else: inc i -proc markUsed(c: PContext; info: TLineInfo; s: PSym) = +proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true) = let conf = c.config incl(s.flags, sfUsed) if s.kind == skEnumField and s.owner != nil: @@ -664,7 +713,8 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) = if sfError in s.flags: userError(conf, info, s) when defined(nimsuggest): suggestSym(c.graph, info, s, c.graph.usageSym, false) - styleCheckUse(c, info, s) + if checkStyle: + styleCheckUse(c, info, s) markOwnerModuleAsUsed(c, s) proc safeSemExpr*(c: PContext, n: PNode): PNode = @@ -761,11 +811,11 @@ proc suggestPragmas*(c: PContext, n: PNode) = # Now show suggestions for user pragmas for pragma in c.userPragmas: - var pm = default(PrefixMatch) - if filterSym(pragma, n, pm): - outputs &= symToSuggest(c.graph, pragma, isLocal=true, ideSug, info, - pragma.getQuality, pm, c.inTypeContext > 0, 0, - extractDocs=false) + var pm = default(PrefixMatch) + if filterSym(pragma, n, pm): + outputs &= symToSuggest(c.graph, pragma, isLocal=true, ideSug, info, + pragma.getQuality, pm, c.inTypeContext > 0, 0, + extractDocs=false) produceOutput(outputs, c.config) if outputs.len > 0: diff --git a/compiler/suggestsymdb.nim b/compiler/suggestsymdb.nim new file mode 100644 index 000000000..e1e67afbe --- /dev/null +++ b/compiler/suggestsymdb.nim @@ -0,0 +1,212 @@ +import std/[intsets, tables, algorithm, assertions] +import ast, lineinfos, msgs + +type + PackedBoolArray* = object + s: IntSet + len: int + + TinyLineInfo* = object + line*: uint16 + col*: int16 + + SymInfoPair* = object + sym*: PSym + info*: TLineInfo + caughtExceptions*: seq[PType] + caughtExceptionsSet*: bool + isDecl*: bool + + SuggestFileSymbolDatabase* = object + lineInfo*: seq[TinyLineInfo] + sym*: seq[PSym] + caughtExceptions*: seq[seq[PType]] + caughtExceptionsSet*: PackedBoolArray + isDecl*: PackedBoolArray + fileIndex*: FileIndex + trackCaughtExceptions*: bool + isSorted*: bool + + SuggestSymbolDatabase* = Table[FileIndex, SuggestFileSymbolDatabase] + + +func newPackedBoolArray*(): PackedBoolArray = + PackedBoolArray( + s: initIntSet(), + len: 0 + ) + +func low*(s: PackedBoolArray): int = + 0 + +func high*(s: PackedBoolArray): int = + s.len - 1 + +func `[]`*(s: PackedBoolArray; idx: int): bool = + s.s.contains(idx) + +proc `[]=`*(s: var PackedBoolArray; idx: int; v: bool) = + if v: + s.s.incl(idx) + else: + s.s.excl(idx) + +proc add*(s: var PackedBoolArray; v: bool) = + inc(s.len) + if v: + s.s.incl(s.len - 1) + +proc reverse*(s: var PackedBoolArray) = + var + reversedSet = initIntSet() + for i in 0..s.high: + if s.s.contains(i): + reversedSet.incl(s.high - i) + s.s = reversedSet + +proc getSymInfoPair*(s: SuggestFileSymbolDatabase; idx: int): SymInfoPair = + SymInfoPair( + sym: s.sym[idx], + info: TLineInfo( + line: s.lineInfo[idx].line, + col: s.lineInfo[idx].col, + fileIndex: s.fileIndex + ), + caughtExceptions: + if s.trackCaughtExceptions: + s.caughtExceptions[idx] + else: + @[], + caughtExceptionsSet: + if s.trackCaughtExceptions: + s.caughtExceptionsSet[idx] + else: + false, + isDecl: s.isDecl[idx] + ) + +proc reverse*(s: var SuggestFileSymbolDatabase) = + s.lineInfo.reverse() + s.sym.reverse() + s.caughtExceptions.reverse() + s.caughtExceptionsSet.reverse() + s.isDecl.reverse() + +proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions: bool): SuggestFileSymbolDatabase = + SuggestFileSymbolDatabase( + lineInfo: @[], + sym: @[], + caughtExceptions: @[], + caughtExceptionsSet: newPackedBoolArray(), + isDecl: newPackedBoolArray(), + fileIndex: aFileIndex, + trackCaughtExceptions: aTrackCaughtExceptions, + isSorted: true + ) + +proc exactEquals*(a, b: TinyLineInfo): bool = + result = a.line == b.line and a.col == b.col + +proc `==`*(a, b: SymInfoPair): bool = + result = a.sym == b.sym and a.info.exactEquals(b.info) + +func cmp*(a: TinyLineInfo; b: TinyLineInfo): int = + result = cmp(a.line, b.line) + if result == 0: + result = cmp(a.col, b.col) + +func compare*(s: var SuggestFileSymbolDatabase; i, j: int): int = + result = cmp(s.lineInfo[i], s.lineInfo[j]) + if result == 0: + result = cmp(s.isDecl[i], s.isDecl[j]) + +proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) = + if i == j: + return + var tmp1 = s.lineInfo[i] + s.lineInfo[i] = s.lineInfo[j] + s.lineInfo[j] = tmp1 + if s.trackCaughtExceptions: + var tmp2 = s.caughtExceptions[i] + s.caughtExceptions[i] = s.caughtExceptions[j] + s.caughtExceptions[j] = tmp2 + var tmp3 = s.caughtExceptionsSet[i] + s.caughtExceptionsSet[i] = s.caughtExceptionsSet[j] + s.caughtExceptionsSet[j] = tmp3 + var tmp4 = s.isDecl[i] + s.isDecl[i] = s.isDecl[j] + s.isDecl[j] = tmp4 + var tmp5 = s.sym[i] + s.sym[i] = s.sym[j] + s.sym[j] = tmp5 + +proc quickSort(s: var SuggestFileSymbolDatabase; ll, rr: int) = + var + i, j, pivotIdx: int + l = ll + r = rr + while true: + i = l + j = r + pivotIdx = l + ((r - l) shr 1) + while true: + while (i < pivotIdx) and (s.compare(pivotIdx, i) > 0): + inc i + while (j > pivotIdx) and (s.compare(pivotIdx, j) < 0): + dec j + if i < j: + s.exchange(i, j) + if pivotIdx == i: + pivotIdx = j + inc i + elif pivotIdx == j: + pivotIdx = i + dec j + else: + inc i + dec j + else: + break + if (pivotIdx - l) < (r - pivotIdx): + if (l + 1) < pivotIdx: + s.quickSort(l, pivotIdx - 1) + l = pivotIdx + 1 + else: + if (pivotIdx + 1) < r: + s.quickSort(pivotIdx + 1, r) + if (l + 1) < pivotIdx: + r = pivotIdx - 1 + else: + break + if l >= r: + break + +proc sort*(s: var SuggestFileSymbolDatabase) = + s.quickSort(s.lineInfo.low, s.lineInfo.high) + s.isSorted = true + +proc add*(s: var SuggestFileSymbolDatabase; v: SymInfoPair) = + doAssert(v.info.fileIndex == s.fileIndex) + s.lineInfo.add(TinyLineInfo( + line: v.info.line, + col: v.info.col + )) + s.sym.add(v.sym) + s.isDecl.add(v.isDecl) + if s.trackCaughtExceptions: + s.caughtExceptions.add(v.caughtExceptions) + s.caughtExceptionsSet.add(v.caughtExceptionsSet) + s.isSorted = false + +proc add*(s: var SuggestSymbolDatabase; v: SymInfoPair; trackCaughtExceptions: bool) = + s.mgetOrPut(v.info.fileIndex, newSuggestFileSymbolDatabase(v.info.fileIndex, trackCaughtExceptions)).add(v) + +proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo): int = + doAssert(li.fileIndex == s.fileIndex) + if not s.isSorted: + s.sort() + var q = TinyLineInfo( + line: li.line, + col: li.col + ) + result = binarySearch(s.lineInfo, q, cmp) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index ef6b1da58..6b325c77f 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -60,7 +60,7 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache else: inc(i, 2) while i < line.len and line[i] in Whitespace: inc(i) - var p: Parser + var p: Parser = default(Parser) openParser(p, filename, llStreamOpen(substr(line, i)), cache, config) result = parseAll(p) closeParser(p) diff --git a/compiler/transf.nim b/compiler/transf.nim index edae6b847..8dd24e090 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -18,6 +18,8 @@ # * performs lambda lifting for closure support # * transforms 'defer' into a 'try finally' statement +import std / tables + import options, ast, astalgo, trees, msgs, idents, renderer, types, semfold, magicsys, cgmeth, @@ -38,7 +40,7 @@ import closureiters, lambdalifting type PTransCon = ref object # part of TContext; stackable - mapping: TIdNodeTable # mapping from symbols to nodes + mapping: Table[ItemId, PNode] # mapping from symbols to nodes owner: PSym # current owner forStmt: PNode # current for stmt forLoopBody: PNode # transformed for loop body @@ -54,6 +56,7 @@ type contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly: bool isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) + inAddr: bool flags: TransformFlags graph: ModuleGraph idgen: IdGenerator @@ -76,7 +79,7 @@ proc newTransNode(kind: TNodeKind, n: PNode, proc newTransCon(owner: PSym): PTransCon = assert owner != nil - result = PTransCon(mapping: initIdNodeTable(), owner: owner) + result = PTransCon(mapping: initTable[ItemId, PNode](), owner: owner) proc pushTransCon(c: PTransf, t: PTransCon) = t.next = c.transCon @@ -95,7 +98,7 @@ proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode = r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink}) incl(r.flags, sfFromGeneric) let owner = getCurrOwner(c) - if owner.isIterator and not c.tooEarly: + if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"): result = freshVarForClosureIter(c.graph, r, c.idgen, owner) else: result = newSymNode(r) @@ -159,7 +162,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = else: b = n while tc != nil: - result = idNodeTableGet(tc.mapping, b.sym) + result = getOrDefault(tc.mapping, b.sym.itemId) if result != nil: # this slightly convoluted way ensures the line info stays correct: if result.kind == nkSym: @@ -174,7 +177,7 @@ proc transformSym(c: PTransf, n: PNode): PNode = proc freshVar(c: PTransf; v: PSym): PNode = let owner = getCurrOwner(c) - if owner.isIterator and not c.tooEarly: + if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"): result = freshVarForClosureIter(c.graph, v, c.idgen, owner) else: var newVar = copySym(v, c.idgen) @@ -194,7 +197,7 @@ proc transformVarSection(c: PTransf, v: PNode): PNode = if vn.kind == nkSym: internalAssert(c.graph.config, it.len == 3) let x = freshVar(c, vn.sym) - idNodeTablePut(c.transCon.mapping, vn.sym, x) + c.transCon.mapping[vn.sym.itemId] = x var defs = newTransNode(nkIdentDefs, it.info, 3) if importantComments(c.graph.config): # keep documentation information: @@ -215,7 +218,7 @@ proc transformVarSection(c: PTransf, v: PNode): PNode = for j in 0..<it.len-2: if it[j].kind == nkSym: let x = freshVar(c, it[j].sym) - idNodeTablePut(c.transCon.mapping, it[j].sym, x) + c.transCon.mapping[it[j].sym.itemId] = x defs[j] = x else: defs[j] = transform(c, it[j]) @@ -256,7 +259,7 @@ proc transformBlock(c: PTransf, n: PNode): PNode = var labl: PSym if c.inlining > 0: labl = newLabel(c, n[0]) - idNodeTablePut(c.transCon.mapping, n[0].sym, newSymNode(labl)) + c.transCon.mapping[n[0].sym.itemId] = newSymNode(labl) else: labl = if n[0].kind != nkEmpty: @@ -328,7 +331,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode = of nkProcDef: # todo optimize nosideeffects? result = newTransNode(n) let x = newSymNode(copySym(n[namePos].sym, c.idgen)) - idNodeTablePut(c.transCon.mapping, n[namePos].sym, x) + c.transCon.mapping[n[namePos].sym.itemId] = x result[namePos] = x # we have to copy proc definitions for iters for i in 1..<n.len: result[i] = introduceNewLocalVars(c, n[i]) @@ -413,9 +416,15 @@ proc transformYield(c: PTransf, n: PNode): PNode = result.add transform(c, v) for i in 0..<c.transCon.forStmt.len - 2: - let lhs = c.transCon.forStmt[i] - let rhs = transform(c, newTupleAccess(c.graph, tmp, i)) - result.add(asgnTo(lhs, rhs)) + if c.transCon.forStmt[i].kind == nkVarTuple: + for j in 0..<c.transCon.forStmt[i].len-1: + let lhs = c.transCon.forStmt[i][j] + let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, tmp, i), j)) + result.add(asgnTo(lhs, rhs)) + else: + let lhs = c.transCon.forStmt[i] + let rhs = transform(c, newTupleAccess(c.graph, tmp, i)) + result.add(asgnTo(lhs, rhs)) else: for i in 0..<c.transCon.forStmt.len - 2: let lhs = c.transCon.forStmt[i] @@ -454,6 +463,14 @@ proc transformYield(c: PTransf, n: PNode): PNode = let rhs = transform(c, e) result.add(asgnTo(lhs, rhs)) + + # bug #23536; note that the info of forLoopBody should't change + for idx in 0 ..< result.len: + var changeNode = result[idx] + changeNode.info = c.transCon.forStmt.info + for i, child in changeNode: + child.info = changeNode.info + inc(c.transCon.yieldStmts) if c.transCon.yieldStmts <= 1: # common case @@ -464,15 +481,10 @@ proc transformYield(c: PTransf, n: PNode): PNode = result.add(introduceNewLocalVars(c, c.transCon.forLoopBody)) c.isIntroducingNewLocalVars = false - for idx in 0 ..< result.len: - var changeNode = result[idx] - changeNode.info = c.transCon.forStmt.info - for i, child in changeNode: - child.info = changeNode.info - proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode = result = transformSons(c, n) - if c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags: return + # inlining of 'var openarray' iterators; bug #19977 + if n.typ.kind != tyOpenArray and (c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags): return var n = result case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: @@ -496,7 +508,15 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode = elif n.typ.skipTypes(abstractInst).kind in {tyVar}: result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen) else: - if n[0].kind in kinds: + if n[0].kind in kinds and + not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and + n[0][0].typ.skipTypes(abstractVar).kind == tyTuple + ) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and + n.typ.kind == tyVar and + n.typ.skipTypes(abstractVar).kind == tyOpenArray and + n[0][0].typ.skipTypes(abstractVar).kind == tyString) + : # elimination is harmful to `for tuple unpack` because of newTupleAccess + # it is also harmful to openArrayLoc (var openArray) for strings # addr ( deref ( x )) --> x result = n[0][0] if n.typ.skipTypes(abstractVar).kind != tyOpenArray: @@ -508,7 +528,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode = # we cannot generate a proper thunk here for GC-safety reasons # (see internal documentation): - if c.graph.config.backend == backendJs: return prc + if jsNoLambdaLifting in c.graph.config.legacyFeatures and c.graph.config.backend == backendJs: return prc result = newNodeIT(nkClosure, prc.info, dest) var conv = newNodeIT(nkHiddenSubConv, prc.info, dest) conv.add(newNodeI(nkEmpty, prc.info)) @@ -632,9 +652,11 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto = case arg.kind of nkEmpty..nkNilLit: result = paDirectMapping - of nkDotExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr: + of nkDotExpr, nkDerefExpr, nkHiddenDeref: + result = putArgInto(arg[0], formal) + of nkAddr, nkHiddenAddr: result = putArgInto(arg[0], formal) - #if result == paViaIndirection: result = paFastAsgn + if result == paViaIndirection: result = paFastAsgn of nkCurly, nkBracket: for i in 0..<arg.len: if putArgInto(arg[i], formal) != paDirectMapping: @@ -758,7 +780,7 @@ proc transformFor(c: PTransf, n: PNode): PNode = let pa = putArgInto(arg, formal.typ) case pa of paDirectMapping: - idNodeTablePut(newC.mapping, formal, arg) + newC.mapping[formal.itemId] = arg of paFastAsgn, paFastAsgnTakeTypeFromArg: var t = formal.typ if pa == paFastAsgnTakeTypeFromArg: @@ -772,10 +794,10 @@ proc transformFor(c: PTransf, n: PNode): PNode = #incl(temp.sym.flags, sfCursor) addVar(v, temp) stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true)) - idNodeTablePut(newC.mapping, formal, temp) + newC.mapping[formal.itemId] = temp of paVarAsgn: assert(skipTypes(formal.typ, abstractInst).kind in {tyVar, tyLent}) - idNodeTablePut(newC.mapping, formal, arg) + newC.mapping[formal.itemId] = arg # XXX BUG still not correct if the arg has a side effect! of paViaIndirection: let t = formal.typ @@ -786,13 +808,13 @@ proc transformFor(c: PTransf, n: PNode): PNode = var addrExp = newNodeIT(nkHiddenAddr, formal.info, makeVarType(t.owner, t, c.idgen, tyPtr)) addrExp.add(arg) stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, addrExp, true)) - idNodeTablePut(newC.mapping, formal, newDeref(temp)) + newC.mapping[formal.itemId] = newDeref(temp) of paComplexOpenarray: # arrays will deep copy here (pretty bad). var temp = newTemp(c, arg.typ, formal.info) addVar(v, temp) stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true)) - idNodeTablePut(newC.mapping, formal, temp) + newC.mapping[formal.itemId] = temp let body = transformBody(c.graph, c.idgen, iter, {useCache}+c.flags) pushInfoContext(c.graph.config, n.info) @@ -894,10 +916,6 @@ proc transformCall(c: PTransf, n: PNode): PNode = inc(j) result.add(a) if result.len == 2: result = result[1] - elif magic == mAddr: - result = newTransNode(nkAddr, n, 1) - result[0] = n[1] - result = transformAddrDeref(c, result, {nkDerefExpr, nkHiddenDeref}) elif magic in {mNBindSym, mTypeOf, mRunnableExamples}: # for bindSym(myconst) we MUST NOT perform constant folding: result = n @@ -1061,7 +1079,10 @@ proc transform(c: PTransf, n: PNode): PNode = of nkHiddenAddr: result = transformAddrDeref(c, n, {nkHiddenDeref}) of nkAddr: + let oldInAddr = c.inAddr + c.inAddr = true result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}) + c.inAddr = oldInAddr of nkDerefExpr: result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr}) of nkHiddenDeref: @@ -1141,7 +1162,7 @@ proc transform(c: PTransf, n: PNode): PNode = let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and n.typ != nil and n.typ.kind == tyPointer - if not exprIsPointerCast: + if not exprIsPointerCast and not c.inAddr: var cnst = getConstExpr(c.module, result, c.idgen, c.graph) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): diff --git a/compiler/trees.nim b/compiler/trees.nim index e39cbafe6..41b54eb09 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -116,7 +116,7 @@ proc isDeepConstExpr*(n: PNode; preventInheritance = false): bool = let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned}) if t.kind in {tyRef, tyPtr} or tfUnion in t.flags: return false if t.kind == tyObject: - if preventInheritance and t[0] != nil: + if preventInheritance and t.baseClass != nil: result = false elif isCaseObj(t.n): result = false @@ -226,8 +226,7 @@ proc stupidStmtListExpr*(n: PNode): bool = proc dontInlineConstant*(orig, cnst: PNode): bool {.inline.} = # symbols that expand to a complex constant (array, etc.) should not be # inlined, unless it's the empty array: - result = orig.kind != cnst.kind and - cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and + result = cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and cnst.len > ord(cnst.kind == nkObjConstr) proc isRunnableExamples*(n: PNode): bool = diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 483e55bc9..39193a42d 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -27,6 +27,7 @@ type taProcContextIsNotMacro taIsCastable taIsDefaultField + taVoid # only allow direct void fields of objects/tuples TTypeAllowedFlags* = set[TTypeAllowedFlag] @@ -60,6 +61,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if typ == nil: return nil if containsOrIncl(marker, typ.id): return nil var t = skipTypes(typ, abstractInst-{tyTypeDesc, tySink}) + + let flags = if t.kind == tyVoid: flags else: flags-{taVoid} case t.kind of tyVar, tyLent: if kind in {skProc, skFunc, skConst} and (views notin c.features): @@ -73,7 +76,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, elif isOutParam(t) and kind != skParam: result = t else: - var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc, tySink}) + var t2 = skipTypes(t.elementType, abstractInst-{tyTypeDesc, tySink}) case t2.kind of tyVar, tyLent: if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap @@ -96,11 +99,11 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, # only closure iterators may be assigned to anything. result = t let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags - for i in 1..<t.len: + for _, a in t.paramTypes: if result != nil: break - result = typeAllowedAux(marker, t[i], skParam, c, f-{taIsOpenArray}) - if result.isNil and t[0] != nil: - result = typeAllowedAux(marker, t[0], skResult, c, flags) + result = typeAllowedAux(marker, a, skParam, c, f-{taIsOpenArray}) + if result.isNil and t.returnType != nil: + result = typeAllowedAux(marker, t.returnType, skResult, c, flags) of tyTypeDesc: if kind in {skVar, skLet, skConst} and taProcContextIsNotMacro in flags: result = t @@ -115,12 +118,12 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyStatic: if kind notin {skParam}: result = t of tyVoid: - if taField notin flags: result = t + if taVoid notin flags: result = t of tyTypeClasses: if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags: discard elif t.isResolvedUserTypeClass: - result = typeAllowedAux(marker, t.lastSon, kind, c, flags) + result = typeAllowedAux(marker, t.last, kind, c, flags) elif kind notin {skParam, skResult}: result = t of tyGenericBody, tyGenericParam, tyGenericInvocation, @@ -133,74 +136,77 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyOrdinal: if kind != skParam: result = t of tyGenericInst, tyDistinct, tyAlias, tyInferred: - result = typeAllowedAux(marker, lastSon(t), kind, c, flags) + result = typeAllowedAux(marker, skipModifier(t), kind, c, flags) of tyRange: - if skipTypes(t[0], abstractInst-{tyTypeDesc}).kind notin + if skipTypes(t.elementType, abstractInst-{tyTypeDesc}).kind notin {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64, tyRange}: result = t of tyOpenArray: # you cannot nest openArrays/sinks/etc. if (kind != skParam or taIsOpenArray in flags) and views notin c.features: result = t else: - result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray}) + result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taIsOpenArray}) of tyVarargs: # you cannot nest openArrays/sinks/etc. if kind != skParam or taIsOpenArray in flags: result = t else: - result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray}) + result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taIsOpenArray}) of tySink: # you cannot nest openArrays/sinks/etc. - if kind != skParam or taIsOpenArray in flags or t[0].kind in {tySink, tyLent, tyVar}: + if kind != skParam or taIsOpenArray in flags or t.elementType.kind in {tySink, tyLent, tyVar}: result = t else: - result = typeAllowedAux(marker, t[0], kind, c, flags) + result = typeAllowedAux(marker, t.elementType, kind, c, flags) of tyUncheckedArray: if kind != skParam and taHeap notin flags: result = t else: - result = typeAllowedAux(marker, lastSon(t), kind, c, flags-{taHeap}) + result = typeAllowedAux(marker, elementType(t), kind, c, flags-{taHeap}) of tySequence: - if t[0].kind != tyEmpty: - result = typeAllowedAux(marker, t[0], kind, c, flags+{taHeap}) + if t.elementType.kind != tyEmpty: + result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap}) elif kind in {skVar, skLet}: - result = t[0] + result = t.elementType of tyArray: - if t[1].kind == tyTypeDesc: - result = t[1] - elif t[1].kind != tyEmpty: - result = typeAllowedAux(marker, t[1], kind, c, flags) + if t.elementType.kind == tyTypeDesc: + result = t.elementType + elif t.elementType.kind != tyEmpty: + result = typeAllowedAux(marker, t.elementType, kind, c, flags) elif kind in {skVar, skLet}: - result = t[1] + result = t.elementType of tyRef: if kind == skConst and taIsDefaultField notin flags: result = t - else: result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap}) + else: result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap}) of tyPtr: - result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap}) + result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap}) of tySet: - for i in 0..<t.len: - result = typeAllowedAux(marker, t[i], kind, c, flags) - if result != nil: break - of tyObject, tyTuple: + result = typeAllowedAux(marker, t.elementType, kind, c, flags) + of tyObject: if kind in {skProc, skFunc, skConst} and - t.kind == tyObject and t[0] != nil and taIsDefaultField notin flags: + t.baseClass != nil and taIsDefaultField notin flags: result = t else: - let flags = flags+{taField} - for i in 0..<t.len: - result = typeAllowedAux(marker, t[i], kind, c, flags) - if result != nil: break + let flags = flags+{taField, taVoid} + result = typeAllowedAux(marker, t.baseClass, kind, c, flags) if result.isNil and t.n != nil: result = typeAllowedNode(marker, t.n, kind, c, flags) + of tyTuple: + let flags = flags+{taField, taVoid} + for a in t.kids: + result = typeAllowedAux(marker, a, kind, c, flags) + if result != nil: break + if result.isNil and t.n != nil: + result = typeAllowedNode(marker, t.n, kind, c, flags) of tyEmpty: if kind in {skVar, skLet}: result = t - of tyProxy: + of tyError: # for now same as error node; we say it's a valid type as it should # prevent cascading errors: result = nil of tyOwned: - if t.len == 1 and t[0].skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}: - result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap}) + if t.hasElementType and t.skipModifier.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}: + result = typeAllowedAux(marker, t.skipModifier, kind, c, flags+{taHeap}) else: result = t of tyConcept: @@ -247,23 +253,23 @@ proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind = result = immutableView of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned, tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic: - result = classifyViewTypeAux(marker, lastSon(t)) + result = classifyViewTypeAux(marker, skipModifier(t)) of tyFromExpr: - if t.len > 0: - result = classifyViewTypeAux(marker, lastSon(t)) + if t.hasElementType: + result = classifyViewTypeAux(marker, skipModifier(t)) else: result = noView of tyTuple: result = noView - for i in 0..<t.len: - result.combine classifyViewTypeAux(marker, t[i]) + for a in t.kids: + result.combine classifyViewTypeAux(marker, a) if result == mutableView: break of tyObject: result = noView if t.n != nil: result = classifyViewTypeNode(marker, t.n) - if t[0] != nil: - result.combine classifyViewTypeAux(marker, t[0]) + if t.baseClass != nil: + result.combine classifyViewTypeAux(marker, t.baseClass) else: # it doesn't matter what these types contain, 'ptr openArray' is not a # view type! @@ -281,7 +287,7 @@ proc directViewType*(t: PType): ViewTypeKind = of tyLent, tyOpenArray: result = immutableView of abstractInst-{tyTypeDesc}: - result = directViewType(t.lastSon) + result = directViewType(t.skipModifier) else: result = noView diff --git a/compiler/types.nim b/compiler/types.nim index f10d5aa86..a441b0ea2 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -31,6 +31,7 @@ type # most useful, shows: symbol + resolved symbols if it differs, e.g.: # tuple[a: MyInt{int}, b: float] preferInlayHint, + preferInferredEffects, TTypeRelation* = enum # order is important! isNone, isConvertible, @@ -65,19 +66,13 @@ proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) = template `$`*(typ: PType): string = typeToString(typ) -proc base*(t: PType): PType = - result = t[0] - # ------------------- type iterator: ---------------------------------------- type TTypeIter* = proc (t: PType, closure: RootRef): bool {.nimcall.} # true if iteration should stop - TTypeMutator* = proc (t: PType, closure: RootRef): PType {.nimcall.} # copy t and mutate it TTypePredicate* = proc (t: PType): bool {.nimcall.} proc iterOverType*(t: PType, iter: TTypeIter, closure: RootRef): bool # Returns result of `iter`. -proc mutateType*(t: PType, iter: TTypeMutator, closure: RootRef): PType - # Returns result of `iter`. type TParamsEquality* = enum # they are equal, but their @@ -109,18 +104,18 @@ const typedescInst* = abstractInst + {tyTypeDesc, tyOwned, tyUserTypeClass} proc invalidGenericInst*(f: PType): bool = - result = f.kind == tyGenericInst and lastSon(f) == nil + result = f.kind == tyGenericInst and skipModifier(f) == nil proc isPureObject*(typ: PType): bool = var t = typ - while t.kind == tyObject and t[0] != nil: - t = t[0].skipTypes(skipPtrs) + while t.kind == tyObject and t.baseClass != nil: + t = t.baseClass.skipTypes(skipPtrs) result = t.sym != nil and sfPure in t.sym.flags proc isUnsigned*(t: PType): bool = t.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64} -proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 = +proc getOrdValueAux*(n: PNode, err: var bool): Int128 = var k = n.kind if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}: k = nkUIntLit @@ -136,13 +131,22 @@ proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 = toInt128(n.intVal) of nkNilLit: int128.Zero - of nkHiddenStdConv: getOrdValue(n[1], onError) + of nkHiddenStdConv: + getOrdValueAux(n[1], err) else: - # XXX: The idea behind the introduction of int128 was to finally - # have all calculations numerically far away from any - # overflows. This command just introduces such overflows and - # should therefore really be revisited. - onError + err = true + int128.Zero + +proc getOrdValue*(n: PNode): Int128 = + var err: bool = false + result = getOrdValueAux(n, err) + #assert err == false + +proc getOrdValue*(n: PNode, onError: Int128): Int128 = + var err = false + result = getOrdValueAux(n, err) + if err: + result = onError proc getFloatValue*(n: PNode): BiggestFloat = case n.kind @@ -184,10 +188,10 @@ proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferNa proc elemType*(t: PType): PType = assert(t != nil) case t.kind - of tyGenericInst, tyDistinct, tyAlias, tySink: result = elemType(lastSon(t)) - of tyArray: result = t[1] + of tyGenericInst, tyDistinct, tyAlias, tySink: result = elemType(skipModifier(t)) + of tyArray: result = t.elementType of tyError: result = t - else: result = t.lastSon + else: result = t.elementType assert(result != nil) proc enumHasHoles*(t: PType): bool = @@ -200,7 +204,7 @@ proc isOrdinalType*(t: PType, allowEnumWithHoles: bool = false): bool = baseKinds = {tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyEnum} parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tySink, tyDistinct} result = (t.kind in baseKinds and (not t.enumHasHoles or allowEnumWithHoles)) or - (t.kind in parentKinds and isOrdinalType(t.lastSon, allowEnumWithHoles)) + (t.kind in parentKinds and isOrdinalType(t.skipModifier, allowEnumWithHoles)) proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter, closure: RootRef): bool @@ -228,11 +232,15 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter, if result: return if not containsOrIncl(marker, t.id): case t.kind - of tyGenericInst, tyGenericBody, tyAlias, tySink, tyInferred: - result = iterOverTypeAux(marker, lastSon(t), iter, closure) + of tyGenericBody: + # treat as atomic, containsUnresolvedType wants always false, + # containsGenericType always gives true + discard + of tyGenericInst, tyAlias, tySink, tyInferred: + result = iterOverTypeAux(marker, skipModifier(t), iter, closure) else: - for i in 0..<t.len: - result = iterOverTypeAux(marker, t[i], iter, closure) + for a in t.kids: + result = iterOverTypeAux(marker, a, iter, closure) if result: return if t.n != nil and t.kind != tyProc: result = iterOverNode(marker, t.n, iter, closure) @@ -275,14 +283,14 @@ proc searchTypeForAux(t: PType, predicate: TTypePredicate, if result: return case t.kind of tyObject: - if t[0] != nil: - result = searchTypeForAux(t[0].skipTypes(skipPtrs), predicate, marker) + if t.baseClass != nil: + result = searchTypeForAux(t.baseClass.skipTypes(skipPtrs), predicate, marker) if not result: result = searchTypeNodeForAux(t.n, predicate, marker) of tyGenericInst, tyDistinct, tyAlias, tySink: - result = searchTypeForAux(lastSon(t), predicate, marker) + result = searchTypeForAux(skipModifier(t), predicate, marker) of tyArray, tySet, tyTuple: - for i in 0..<t.len: - result = searchTypeForAux(t[i], predicate, marker) + for a in t.kids: + result = searchTypeForAux(a, predicate, marker) if result: return else: discard @@ -298,7 +306,7 @@ proc containsObject*(t: PType): bool = result = searchTypeFor(t, isObjectPredicate) proc isObjectWithTypeFieldPredicate(t: PType): bool = - result = t.kind == tyObject and t[0] == nil and + result = t.kind == tyObject and t.baseClass == nil and not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and tfFinal notin t.flags @@ -310,7 +318,6 @@ type proc analyseObjectWithTypeFieldAux(t: PType, marker: var IntSet): TTypeFieldResult = - var res: TTypeFieldResult result = frNone if t == nil: return case t.kind @@ -318,20 +325,19 @@ proc analyseObjectWithTypeFieldAux(t: PType, if t.n != nil: if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker): return frEmbedded - for i in 0..<t.len: - var x = t[i] - if x != nil: x = x.skipTypes(skipPtrs) - res = analyseObjectWithTypeFieldAux(x, marker) - if res == frEmbedded: - return frEmbedded - if res == frHeader: result = frHeader + var x = t.baseClass + if x != nil: x = x.skipTypes(skipPtrs) + let res = analyseObjectWithTypeFieldAux(x, marker) + if res == frEmbedded: + return frEmbedded + if res == frHeader: result = frHeader if result == frNone: if isObjectWithTypeFieldPredicate(t): result = frHeader of tyGenericInst, tyDistinct, tyAlias, tySink: - result = analyseObjectWithTypeFieldAux(lastSon(t), marker) + result = analyseObjectWithTypeFieldAux(skipModifier(t), marker) of tyArray, tyTuple: - for i in 0..<t.len: - res = analyseObjectWithTypeFieldAux(t[i], marker) + for a in t.kids: + let res = analyseObjectWithTypeFieldAux(a, marker) if res != frNone: return frEmbedded else: @@ -408,9 +414,7 @@ proc canFormAcycleAux(g: ModuleGraph, marker: var IntSet, typ: PType, orig: PTyp if withRef and sameBackendType(t, orig): result = true elif not containsOrIncl(marker, t.id): - for i in 0..<t.len: - result = canFormAcycleAux(g, marker, t[i], orig, withRef or t.kind != tyUncheckedArray, hasTrace) - if result: return + result = canFormAcycleAux(g, marker, t.elementType, orig, withRef or t.kind != tyUncheckedArray, hasTrace) of tyObject: if withRef and sameBackendType(t, orig): result = true @@ -419,8 +423,8 @@ proc canFormAcycleAux(g: ModuleGraph, marker: var IntSet, typ: PType, orig: PTyp let op = getAttachedOp(g, t.skipTypes({tyRef}), attachedTrace) if op != nil and sfOverridden in op.flags: hasTrace = true - for i in 0..<t.len: - result = canFormAcycleAux(g, marker, t[i], orig, withRef, hasTrace) + if t.baseClass != nil: + result = canFormAcycleAux(g, marker, t.baseClass, orig, withRef, hasTrace) if result: return if t.n != nil: result = canFormAcycleNode(g, marker, t.n, orig, withRef, hasTrace) # Inheritance can introduce cyclic types, however this is not relevant @@ -429,13 +433,18 @@ proc canFormAcycleAux(g: ModuleGraph, marker: var IntSet, typ: PType, orig: PTyp if tfFinal notin t.flags: # damn inheritance may introduce cycles: result = true - of tyTuple, tySequence, tyArray, tyOpenArray, tyVarargs: + of tyTuple: if withRef and sameBackendType(t, orig): result = true elif not containsOrIncl(marker, t.id): - for i in 0..<t.len: - result = canFormAcycleAux(g, marker, t[i], orig, withRef, hasTrace) + for a in t.kids: + result = canFormAcycleAux(g, marker, a, orig, withRef, hasTrace) if result: return + of tySequence, tyArray, tyOpenArray, tyVarargs: + if withRef and sameBackendType(t, orig): + result = true + elif not containsOrIncl(marker, t.id): + result = canFormAcycleAux(g, marker, t.elementType, orig, withRef, hasTrace) of tyProc: result = typ.callConv == ccClosure else: discard @@ -448,37 +457,6 @@ proc canFormAcycle*(g: ModuleGraph, typ: PType): bool = let t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc}) result = canFormAcycleAux(g, marker, t, t, false, false) -proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator, - closure: RootRef): PType -proc mutateNode(marker: var IntSet, n: PNode, iter: TTypeMutator, - closure: RootRef): PNode = - result = nil - if n != nil: - result = copyNode(n) - result.typ = mutateTypeAux(marker, n.typ, iter, closure) - case n.kind - of nkNone..nkNilLit: - # a leaf - discard - else: - for i in 0..<n.len: - result.add mutateNode(marker, n[i], iter, closure) - -proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator, - closure: RootRef): PType = - result = nil - if t == nil: return - result = iter(t, closure) - if not containsOrIncl(marker, t.id): - for i in 0..<t.len: - result[i] = mutateTypeAux(marker, result[i], iter, closure) - if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure) - assert(result != nil) - -proc mutateType(t: PType, iter: TTypeMutator, closure: RootRef): PType = - var marker = initIntSet() - result = mutateTypeAux(marker, t, iter, closure) - proc valueToString(a: PNode): string = case a.kind of nkCharLit, nkUIntLit..nkUInt64Lit: @@ -513,7 +491,7 @@ const "void", "iterable"] const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, - preferGenericArg, preferResolved, preferMixed, preferInlayHint} + preferGenericArg, preferResolved, preferMixed, preferInlayHint, preferInferredEffects} template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) = tc.add concrete @@ -521,7 +499,7 @@ template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) = # TODO: It would be a good idea to kill the special state of a resolved # concept by switching to tyAlias within the instantiated procs. -# Currently, tyAlias is always skipped with lastSon, which means that +# Currently, tyAlias is always skipped with skipModifier, which means that # we can store information about the matched concept in another position. # Then builtInFieldAccess can be modified to properly read the derived # consts and types stored within the concept. @@ -551,31 +529,27 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = t.sym.name.s else: result = t.sym.name.s & " literal(" & $t.n.intVal & ")" - elif t.kind == tyAlias and t[0].kind != tyAlias: - result = typeToString(t[0]) + elif t.kind == tyAlias and t.elementType.kind != tyAlias: + result = typeToString(t.elementType) elif prefer in {preferResolved, preferMixed}: case t.kind of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}: result = typeToStr[t.kind] of tyGenericBody: - result = typeToString(t.lastSon) + result = typeToString(t.last) of tyCompositeTypeClass: # avoids showing `A[any]` in `proc fun(a: A)` with `A = object[T]` - result = typeToString(t.lastSon.lastSon) + result = typeToString(t.last.last) else: result = t.sym.name.s if prefer == preferMixed and result != t.sym.name.s: result = t.sym.name.s & "{" & result & "}" - elif prefer in {preferName, preferTypeName, preferInlayHint} or t.sym.owner.isNil: + elif prefer in {preferName, preferTypeName, preferInlayHint, preferInferredEffects} or t.sym.owner.isNil: # note: should probably be: {preferName, preferTypeName, preferGenericArg} result = t.sym.name.s - if t.kind == tyGenericParam and t.len > 0: + if t.kind == tyGenericParam and t.genericParamHasConstraints: result.add ": " - var first = true - for son in t: - if not first: result.add " or " - result.add son.typeToString - first = false + result.add t.elementType.typeToString else: result = t.sym.owner.name.s & '.' & t.sym.name.s result.addTypeFlags(t) @@ -592,35 +566,42 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = "int" else: result = "int literal(" & $t.n.intVal & ")" - of tyGenericInst, tyGenericInvocation: - result = typeToString(t[0]) & '[' - for i in 1..<t.len-ord(t.kind != tyGenericInvocation): - if i > 1: result.add(", ") - result.add(typeToString(t[i], preferGenericArg)) + of tyGenericInst: + result = typeToString(t.genericHead) & '[' + for needsComma, a in t.genericInstParams: + if needsComma: result.add(", ") + result.add(typeToString(a, preferGenericArg)) + result.add(']') + of tyGenericInvocation: + result = typeToString(t.genericHead) & '[' + for needsComma, a in t.genericInvocationParams: + if needsComma: result.add(", ") + result.add(typeToString(a, preferGenericArg)) result.add(']') of tyGenericBody: - result = typeToString(t.lastSon) & '[' - for i in 0..<t.len-1: + result = typeToString(t.typeBodyImpl) & '[' + for i, a in t.genericBodyParams: if i > 0: result.add(", ") - result.add(typeToString(t[i], preferTypeName)) + result.add(typeToString(a, preferTypeName)) result.add(']') of tyTypeDesc: - if t[0].kind == tyNone: result = "typedesc" - else: result = "typedesc[" & typeToString(t[0]) & "]" + if t.elementType.kind == tyNone: result = "typedesc" + else: result = "typedesc[" & typeToString(t.elementType) & "]" of tyStatic: if prefer == preferGenericArg and t.n != nil: result = t.n.renderTree else: - result = "static[" & (if t.len > 0: typeToString(t[0]) else: "") & "]" + result = "static[" & (if t.hasElementType: typeToString(t.skipModifier) else: "") & "]" if t.n != nil: result.add "(" & renderTree(t.n) & ")" of tyUserTypeClass: if t.sym != nil and t.sym.owner != nil: - if t.isResolvedUserTypeClass: return typeToString(t.lastSon) + if t.isResolvedUserTypeClass: return typeToString(t.last) return t.sym.owner.name.s else: result = "<invalid tyUserTypeClass>" of tyBuiltInTypeClass: - result = case t.base.kind + result = + case t.base.kind of tyVar: "var" of tyRef: "ref" of tyPtr: "ptr" @@ -641,22 +622,20 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyUserTypeClassInst: let body = t.base result = body.sym.name.s & "[" - for i in 1..<t.len - 1: - if i > 1: result.add(", ") - result.add(typeToString(t[i])) + for needsComma, a in t.userTypeClassInstParams: + if needsComma: result.add(", ") + result.add(typeToString(a)) result.add "]" of tyAnd: - for i, son in t: + for i, son in t.ikids: + if i > 0: result.add(" and ") result.add(typeToString(son)) - if i < t.len - 1: - result.add(" and ") of tyOr: - for i, son in t: + for i, son in t.ikids: + if i > 0: result.add(" or ") result.add(typeToString(son)) - if i < t.len - 1: - result.add(" or ") of tyNot: - result = "not " & typeToString(t[0]) + result = "not " & typeToString(t.elementType) of tyUntyped: #internalAssert t.len == 0 result = "untyped" @@ -667,80 +646,75 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = "typeof(" & renderTree(t.n) & ")" of tyArray: result = "array" - if t.len > 0: - if t[0].kind == tyRange: - result &= "[" & rangeToStr(t[0].n) & ", " & - typeToString(t[1]) & ']' + if t.hasElementType: + if t.indexType.kind == tyRange: + result &= "[" & rangeToStr(t.indexType.n) & ", " & + typeToString(t.elementType) & ']' else: - result &= "[" & typeToString(t[0]) & ", " & - typeToString(t[1]) & ']' + result &= "[" & typeToString(t.indexType) & ", " & + typeToString(t.elementType) & ']' of tyUncheckedArray: result = "UncheckedArray" - if t.len > 0: - result &= "[" & typeToString(t[0]) & ']' + if t.hasElementType: + result &= "[" & typeToString(t.elementType) & ']' of tySequence: if t.sym != nil and prefer != preferResolved: result = t.sym.name.s else: result = "seq" - if t.len > 0: - result &= "[" & typeToString(t[0]) & ']' + if t.hasElementType: + result &= "[" & typeToString(t.elementType) & ']' of tyOrdinal: result = "ordinal" - if t.len > 0: - result &= "[" & typeToString(t[0]) & ']' + if t.hasElementType: + result &= "[" & typeToString(t.skipModifier) & ']' of tySet: result = "set" - if t.len > 0: - result &= "[" & typeToString(t[0]) & ']' + if t.hasElementType: + result &= "[" & typeToString(t.elementType) & ']' of tyOpenArray: result = "openArray" - if t.len > 0: - result &= "[" & typeToString(t[0]) & ']' + if t.hasElementType: + result &= "[" & typeToString(t.elementType) & ']' of tyDistinct: - result = "distinct " & typeToString(t[0], + result = "distinct " & typeToString(t.elementType, if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName) of tyIterable: # xxx factor this pattern result = "iterable" - if t.len > 0: - result &= "[" & typeToString(t[0]) & ']' + if t.hasElementType: + result &= "[" & typeToString(t.skipModifier) & ']' of tyTuple: # we iterate over t.sons here, because t.n may be nil if t.n != nil: result = "tuple[" - assert(t.n.len == t.len) for i in 0..<t.n.len: assert(t.n[i].kind == nkSym) - result.add(t.n[i].sym.name.s & ": " & typeToString(t[i])) + result.add(t.n[i].sym.name.s & ": " & typeToString(t.n[i].sym.typ)) if i < t.n.len - 1: result.add(", ") result.add(']') - elif t.len == 0: + elif t.isEmptyTupleType: result = "tuple[]" + elif t.isSingletonTupleType: + result = "(" + for son in t.kids: + result.add(typeToString(son)) + result.add(",)") else: result = "(" - for i in 0..<t.len: - result.add(typeToString(t[i])) - if i < t.len - 1: result.add(", ") - elif t.len == 1: result.add(",") + for i, son in t.ikids: + if i > 0: result.add ", " + result.add(typeToString(son)) result.add(')') of tyPtr, tyRef, tyVar, tyLent: result = if isOutParam(t): "out " else: typeToStr[t.kind] - if t.len >= 2: - setLen(result, result.len-1) - result.add '[' - for i in 0..<t.len: - result.add(typeToString(t[i])) - if i < t.len - 1: result.add(", ") - result.add ']' - else: - result.add typeToString(t[0]) + result.add typeToString(t.elementType) of tyRange: result = "range " if t.n != nil and t.n.kind == nkRange: result.add rangeToStr(t.n) if prefer != preferExported: - result.add("(" & typeToString(t[0]) & ")") + result.add("(" & typeToString(t.elementType) & ")") of tyProc: result = if tfIterator in t.flags: "iterator " elif t.owner != nil: @@ -753,15 +727,17 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = "proc " if tfUnresolved in t.flags: result.add "[*missing parameters*]" result.add "(" - for i in 1..<t.len: - if t.n != nil and i < t.n.len and t.n[i].kind == nkSym: - result.add(t.n[i].sym.name.s) + for i, a in t.paramTypes: + if i > FirstParamAt: result.add(", ") + let j = paramTypeToNodeIndex(i) + if t.n != nil and j < t.n.len and t.n[j].kind == nkSym: + result.add(t.n[j].sym.name.s) result.add(": ") - result.add(typeToString(t[i])) - if i < t.len - 1: result.add(", ") + result.add(typeToString(a)) result.add(')') - if t.len > 0 and t[0] != nil: result.add(": " & typeToString(t[0])) + if t.returnType != nil: result.add(": " & typeToString(t.returnType)) var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv + var hasImplicitRaises = false if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos: let pragmasNode = t.owner.ast[pragmasPos] let raisesSpec = effectSpec(pragmasNode, wRaises) @@ -769,20 +745,44 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = addSep(prag) prag.add("raises: ") prag.add($raisesSpec) - + hasImplicitRaises = true if tfNoSideEffect in t.flags: addSep(prag) prag.add("noSideEffect") if tfThread in t.flags: addSep(prag) prag.add("gcsafe") + var effectsOfStr = "" + for i, a in t.paramTypes: + let j = paramTypeToNodeIndex(i) + if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags: + addSep(effectsOfStr) + effectsOfStr.add(t.n[j].sym.name.s) + if effectsOfStr != "": + addSep(prag) + prag.add("effectsOf: ") + prag.add(effectsOfStr) + if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0): + let effects = t.owner.typ.n[0] + if effects.kind == nkEffectList and effects.len == effectListLen: + var inferredRaisesStr = "" + let effs = effects[exceptionEffects] + if not isNil(effs): + for eff in items(effs): + if not isNil(eff): + addSep(inferredRaisesStr) + inferredRaisesStr.add($eff.typ) + addSep(prag) + prag.add("raises: <inferred> [") + prag.add(inferredRaisesStr) + prag.add("]") if prag.len != 0: result.add("{." & prag & ".}") of tyVarargs: - result = typeToStr[t.kind] % typeToString(t[0]) + result = typeToStr[t.kind] % typeToString(t.elementType) of tySink: - result = "sink " & typeToString(t[0]) + result = "sink " & typeToString(t.skipModifier) of tyOwned: - result = "owned " & typeToString(t[0]) + result = "owned " & typeToString(t.elementType) else: result = typeToStr[t.kind] result.addTypeFlags(t) @@ -790,10 +790,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = proc firstOrd*(conf: ConfigRef; t: PType): Int128 = case t.kind - of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy: + of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyError: result = Zero - of tySet, tyVar: result = firstOrd(conf, t[0]) - of tyArray: result = firstOrd(conf, t[0]) + of tySet, tyVar: result = firstOrd(conf, t.elementType) + of tyArray: result = firstOrd(conf, t.indexType) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) @@ -815,8 +815,8 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = of tyUInt..tyUInt64: result = Zero of tyEnum: # if basetype <> nil then return firstOrd of basetype - if t.len > 0 and t[0] != nil: - result = firstOrd(conf, t[0]) + if t.baseClass != nil: + result = firstOrd(conf, t.baseClass) else: if t.n.len > 0: assert(t.n[0].kind == nkSym) @@ -824,18 +824,20 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = else: result = Zero of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, - tyStatic, tyInferred, tyUserTypeClasses, tyLent: - result = firstOrd(conf, lastSon(t)) + tyStatic, tyInferred, tyLent: + result = firstOrd(conf, skipModifier(t)) + of tyUserTypeClasses: + result = firstOrd(conf, last(t)) of tyOrdinal: - if t.len > 0: result = firstOrd(conf, lastSon(t)) + if t.hasElementType: result = firstOrd(conf, skipModifier(t)) else: result = Zero - internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') + fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')') of tyUncheckedArray, tyCstring: result = Zero else: result = Zero - internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') + fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')') proc firstFloat*(t: PType): BiggestFloat = case t.kind @@ -844,10 +846,12 @@ proc firstFloat*(t: PType): BiggestFloat = assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) getFloatValue(t.n[0]) - of tyVar: firstFloat(t[0]) + of tyVar: firstFloat(t.elementType) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, - tyStatic, tyInferred, tyUserTypeClasses: - firstFloat(lastSon(t)) + tyStatic, tyInferred: + firstFloat(skipModifier(t)) + of tyUserTypeClasses: + firstFloat(last(t)) else: internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')') NaN @@ -879,8 +883,8 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = case t.kind of tyBool: result = toInt128(1'u) of tyChar: result = toInt128(255'u) - of tySet, tyVar: result = lastOrd(conf, t[0]) - of tyArray: result = lastOrd(conf, t[0]) + of tySet, tyVar: result = lastOrd(conf, t.elementType) + of tyArray: result = lastOrd(conf, t.indexType) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) @@ -915,31 +919,35 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = else: result = Zero of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, - tyStatic, tyInferred, tyUserTypeClasses, tyLent: - result = lastOrd(conf, lastSon(t)) - of tyProxy: result = Zero + tyStatic, tyInferred, tyLent: + result = lastOrd(conf, skipModifier(t)) + of tyUserTypeClasses: + result = lastOrd(conf, last(t)) + of tyError: result = Zero of tyOrdinal: - if t.len > 0: result = lastOrd(conf, lastSon(t)) + if t.hasElementType: result = lastOrd(conf, skipModifier(t)) else: result = Zero - internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') + fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')') of tyUncheckedArray: result = Zero else: result = Zero - internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') + fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')') proc lastFloat*(t: PType): BiggestFloat = case t.kind of tyFloat..tyFloat128: Inf - of tyVar: lastFloat(t[0]) + of tyVar: lastFloat(t.elementType) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) getFloatValue(t.n[1]) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, - tyStatic, tyInferred, tyUserTypeClasses: - lastFloat(lastSon(t)) + tyStatic, tyInferred: + lastFloat(skipModifier(t)) + of tyUserTypeClasses: + lastFloat(last(t)) else: internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')') NaN @@ -953,17 +961,19 @@ proc floatRangeCheck*(x: BiggestFloat, t: PType): bool = of tyRange: x in firstFloat(t)..lastFloat(t) of tyVar: - floatRangeCheck(x, t[0]) + floatRangeCheck(x, t.elementType) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, - tyStatic, tyInferred, tyUserTypeClasses: - floatRangeCheck(x, lastSon(t)) + tyStatic, tyInferred: + floatRangeCheck(x, skipModifier(t)) + of tyUserTypeClasses: + floatRangeCheck(x, last(t)) else: internalError(newPartialConfigRef(), "invalid kind for floatRangeCheck:" & $t.kind) false proc lengthOrd*(conf: ConfigRef; t: PType): Int128 = if t.skipTypes(tyUserTypeClasses).kind == tyDistinct: - result = lengthOrd(conf, t[0]) + result = lengthOrd(conf, t.skipModifier) else: let last = lastOrd(conf, t) let first = firstOrd(conf, t) @@ -988,6 +998,8 @@ type AllowCommonBase PickyCAliases # be picky about the distinction between 'cint' and 'int32' IgnoreFlags # used for borrowed functions and methods; ignores the tfVarIsPtr flag + PickyBackendAliases # be picky about different aliases + IgnoreRangeShallow TTypeCmpFlags* = set[TTypeCmpFlag] @@ -1085,11 +1097,11 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = # two tuples are equivalent iff the names, types and positions are the same; # however, both types may not have any field names (t.n may be nil) which # complicates the matter a bit. - if a.len == b.len: + if sameTupleLengths(a, b): result = true - for i in 0..<a.len: - var x = a[i] - var y = b[i] + for i, aa, bb in tupleTypePairs(a, b): + var x = aa + var y = bb if IgnoreTupleFields in c.flags: x = skipTypes(x, {tyRange, tyGenericInst, tyAlias}) y = skipTypes(y, {tyRange, tyGenericInst, tyAlias}) @@ -1176,32 +1188,30 @@ proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool = result = false proc sameObjectStructures(a, b: PType, c: var TSameTypeClosure): bool = - # check base types: - if a.len != b.len: return - for i in 0..<a.len: - if not sameTypeOrNilAux(a[i], b[i], c): return - if not sameObjectTree(a.n, b.n, c): return + if not sameTypeOrNilAux(a.baseClass, b.baseClass, c): return false + if not sameObjectTree(a.n, b.n, c): return false result = true proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool = - if a.len != b.len: return false + if not sameTupleLengths(a, b): return false + # XXX This is not tuple specific. result = true - for i in 0..<a.len: - result = sameTypeOrNilAux(a[i], b[i], c) + for _, x, y in tupleTypePairs(a, b): + result = sameTypeOrNilAux(x, y, c) if not result: return proc isGenericAlias*(t: PType): bool = - return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst + return t.kind == tyGenericInst and t.skipModifier.kind == tyGenericInst proc genericAliasDepth*(t: PType): int = result = 0 var it = t while it.isGenericAlias: - it = it.lastSon + it = it.skipModifier inc result proc skipGenericAlias*(t: PType): PType = - return if t.isGenericAlias: t.lastSon else: t + return if t.isGenericAlias: t.skipModifier else: t proc sameFlags*(a, b: PType): bool {.inline.} = result = eqTypeFlags*a.flags == eqTypeFlags*b.flags @@ -1218,26 +1228,40 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = inc c.recCheck else: if containsOrIncl(c, a, b): return true + template maybeSkipRange(x: set[TTypeKind]): set[TTypeKind] = + if IgnoreRangeShallow in c.flags: + x + {tyRange} + else: + x + + template withoutShallowFlags(body) = + let oldFlags = c.flags + c.flags.excl IgnoreRangeShallow + body + c.flags = oldFlags if x == y: return true - var a = skipTypes(x, {tyAlias}) + let aliasSkipSet = maybeSkipRange({tyAlias}) + var a = skipTypes(x, aliasSkipSet) while a.kind == tyUserTypeClass and tfResolved in a.flags: - a = skipTypes(a[^1], {tyAlias}) - var b = skipTypes(y, {tyAlias}) + a = skipTypes(a.last, aliasSkipSet) + var b = skipTypes(y, aliasSkipSet) while b.kind == tyUserTypeClass and tfResolved in b.flags: - b = skipTypes(b[^1], {tyAlias}) + b = skipTypes(b.last, aliasSkipSet) assert(a != nil) assert(b != nil) - if a.kind != b.kind: - case c.cmp - of dcEq: return false - of dcEqIgnoreDistinct: - a = a.skipTypes({tyDistinct, tyGenericInst}) - b = b.skipTypes({tyDistinct, tyGenericInst}) - if a.kind != b.kind: return false - of dcEqOrDistinctOf: - a = a.skipTypes({tyDistinct, tyGenericInst}) - if a.kind != b.kind: return false + case c.cmp + of dcEq: + if a.kind != b.kind: return false + of dcEqIgnoreDistinct: + let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst}) + a = a.skipTypes(distinctSkipSet) + b = b.skipTypes(distinctSkipSet) + if a.kind != b.kind: return false + of dcEqOrDistinctOf: + let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst}) + a = a.skipTypes(distinctSkipSet) + if a.kind != b.kind: return false #[ The following code should not run in the case either side is an generic alias, @@ -1245,16 +1269,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = objects ie `type A[T] = SomeObject` ]# # this is required by tunique_type but makes no sense really: - if tyDistinct notin {x.kind, y.kind} and x.kind == tyGenericInst and IgnoreTupleFields notin c.flags: + if c.cmp == dcEq and x.kind == tyGenericInst and + IgnoreTupleFields notin c.flags and tyDistinct != y.kind: let lhs = x.skipGenericAlias rhs = y.skipGenericAlias - if rhs.kind != tyGenericInst or lhs.base != rhs.base: + if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen: return false - for i in 1..<lhs.len - 1: - let ff = rhs[i] - let aa = lhs[i] - if not sameTypeAux(ff, aa, c): return false + withoutShallowFlags: + for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1): + if not sameTypeAux(ff, aa, c): return false return true case a.kind @@ -1268,32 +1292,39 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = let symFlagsB = if b.sym != nil: b.sym.flags else: {} if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}: result = symFlagsA == symFlagsB + elif result and PickyBackendAliases in c.flags: + let symFlagsA = if a.sym != nil: a.sym.flags else: {} + let symFlagsB = if b.sym != nil: b.sym.flags else: {} + if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}: + result = a.id == b.id of tyStatic, tyFromExpr: result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) - if result and a.len == b.len and a.len == 1: + if result and sameTupleLengths(a, b) and a.hasElementType: cycleCheck() - result = sameTypeAux(a[0], b[0], c) + result = sameTypeAux(a.skipModifier, b.skipModifier, c) of tyObject: - ifFastObjectTypeCheckFailed(a, b): - cycleCheck() - result = sameObjectStructures(a, b, c) and sameFlags(a, b) + withoutShallowFlags: + ifFastObjectTypeCheckFailed(a, b): + cycleCheck() + result = sameObjectStructures(a, b, c) and sameFlags(a, b) of tyDistinct: cycleCheck() if c.cmp == dcEq: if sameFlags(a, b): ifFastObjectTypeCheckFailed(a, b): - result = sameTypeAux(a[0], b[0], c) + result = sameTypeAux(a.elementType, b.elementType, c) else: - result = sameTypeAux(a[0], b[0], c) and sameFlags(a, b) + result = sameTypeAux(a.elementType, b.elementType, c) and sameFlags(a, b) of tyEnum, tyForward: # XXX generic enums do not make much sense, but require structural checking result = a.id == b.id and sameFlags(a, b) of tyError: result = b.kind == tyError of tyTuple: - cycleCheck() - result = sameTuple(a, b, c) and sameFlags(a, b) + withoutShallowFlags: + cycleCheck() + result = sameTuple(a, b, c) and sameFlags(a, b) of tyTypeDesc: if c.cmp == dcEqIgnoreDistinct: result = false elif ExactTypeDescValues in c.flags: @@ -1306,22 +1337,19 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}: result = a.sym.position == b.sym.position of tyBuiltInTypeClass: - assert a.len == 1 - assert a[0].len == 0 - assert b.len == 1 - assert b[0].len == 0 - result = a[0].kind == b[0].kind and sameFlags(a[0], b[0]) - if result and a[0].kind == tyProc and IgnoreCC notin c.flags: - let ecc = a[0].flags * {tfExplicitCallConv} - result = ecc == b[0].flags * {tfExplicitCallConv} and - (ecc == {} or a[0].callConv == b[0].callConv) + result = a.elementType.kind == b.elementType.kind and sameFlags(a.elementType, b.elementType) + if result and a.elementType.kind == tyProc and IgnoreCC notin c.flags: + let ecc = a.elementType.flags * {tfExplicitCallConv} + result = ecc == b.elementType.flags * {tfExplicitCallConv} and + (ecc == {} or a.elementType.callConv == b.elementType.callConv) of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink, tyUncheckedArray, tyArray, tyProc, tyVarargs, tyOrdinal, tyCompositeTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyAnd, tyOr, tyNot, tyAnything, tyOwned: cycleCheck() if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n - result = sameChildrenAux(a, b, c) + withoutShallowFlags: + result = sameChildrenAux(a, b, c) if result and IgnoreFlags notin c.flags: if IgnoreTupleFields in c.flags: result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam} @@ -1334,12 +1362,22 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = ((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n)) of tyRange: cycleCheck() - result = sameTypeOrNilAux(a[0], b[0], c) and - sameValue(a.n[0], b.n[0]) and + result = sameTypeOrNilAux(a.elementType, b.elementType, c) + if result and IgnoreRangeShallow notin c.flags: + result = sameValue(a.n[0], b.n[0]) and sameValue(a.n[1], b.n[1]) - of tyGenericInst, tyAlias, tyInferred, tyIterable: + of tyAlias, tyInferred, tyIterable: cycleCheck() - result = sameTypeAux(a.lastSon, b.lastSon, c) + result = sameTypeAux(a.skipModifier, b.skipModifier, c) + of tyGenericInst: + # BUG #23445 + # The type system must distinguish between `T[int] = object #[empty]#` + # and `T[float] = object #[empty]#`! + cycleCheck() + withoutShallowFlags: + for ff, aa in underspecifiedPairs(a, b, 1, -1): + if not sameTypeAux(ff, aa, c): return false + result = sameTypeAux(a.skipModifier, b.skipModifier, c) of tyNone: result = false of tyConcept: result = exprStructuralEquivalent(a.n, b.n) @@ -1350,6 +1388,19 @@ proc sameBackendType*(x, y: PType): bool = c.cmp = dcEqIgnoreDistinct result = sameTypeAux(x, y, c) +proc sameBackendTypeIgnoreRange*(x, y: PType): bool = + var c = initSameTypeClosure() + c.flags.incl IgnoreTupleFields + c.flags.incl IgnoreRangeShallow + c.cmp = dcEqIgnoreDistinct + result = sameTypeAux(x, y, c) + +proc sameBackendTypePickyAliases*(x, y: PType): bool = + var c = initSameTypeClosure() + c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases} + c.cmp = dcEqIgnoreDistinct + result = sameTypeAux(x, y, c) + proc compareTypes*(x, y: PType, cmp: TDistinctCompare = dcEq, flags: TTypeCmpFlags = {}): bool = @@ -1374,14 +1425,14 @@ proc inheritanceDiff*(a, b: PType): int = while x != nil: x = skipTypes(x, skipPtrs) if sameObjectTypes(x, b): return - x = x[0] + x = x.baseClass dec(result) var y = b result = 0 while y != nil: y = skipTypes(y, skipPtrs) if sameObjectTypes(y, a): return - y = y[0] + y = y.baseClass inc(result) result = high(int) @@ -1399,7 +1450,7 @@ proc commonSuperclass*(a, b: PType): PType = while x != nil: x = skipTypes(x, skipPtrs) ancestors.incl(x.id) - x = x[0] + x = x.baseClass var y = b while y != nil: var t = y # bug #7818, save type before skip @@ -1408,17 +1459,10 @@ proc commonSuperclass*(a, b: PType): PType = # bug #7818, defer the previous skipTypes if t.kind != tyGenericInst: t = y return t - y = y[0] - -proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], - last: TTypeKind): bool = - var a = a - for k, i in pattern.items: - if a.kind != k: return false - if i >= a.len or a[i] == nil: return false - a = a[i] - result = a.kind == last + y = y.baseClass +proc lacksMTypeField*(typ: PType): bool {.inline.} = + (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags include sizealignoffsetimpl @@ -1429,7 +1473,7 @@ proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt = proc getReturnType*(s: PSym): PType = # Obtains the return type of a iterator/proc/macro/template assert s.kind in skProcKinds - result = s.typ[0] + result = s.typ.returnType proc getAlign*(conf: ConfigRef; typ: PType): BiggestInt = computeSizeAlign(conf, typ) @@ -1455,9 +1499,26 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool = proc containsGenericType*(t: PType): bool = result = iterOverType(t, containsGenericTypeIter, nil) +proc containsUnresolvedTypeIter(t: PType, closure: RootRef): bool = + if tfUnresolved in t.flags: return true + case t.kind + of tyStatic: + return t.n == nil + of tyTypeDesc: + if t.base.kind == tyNone: return true + if containsUnresolvedTypeIter(t.base, closure): return true + return false + of tyGenericInvocation, tyGenericParam, tyFromExpr, tyAnything: + return true + else: + return false + +proc containsUnresolvedType*(t: PType): bool = + result = iterOverType(t, containsUnresolvedTypeIter, nil) + proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType = if t.kind == tyDistinct: - result = t[0] + result = t.elementType else: result = copyType(t, idgen, t.owner) copyTypeProps(g, idgen.module, result, t) @@ -1465,7 +1526,7 @@ proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType = var it = result while it.kind in {tyPtr, tyRef, tyOwned}: parent = it - it = it.lastSon + it = it.elementType if it.kind == tyDistinct and parent != nil: parent[0] = it[0] @@ -1570,8 +1631,8 @@ proc isCompileTimeOnly*(t: PType): bool {.inline.} = proc containsCompileTimeOnly*(t: PType): bool = if isCompileTimeOnly(t): return true - for i in 0..<t.len: - if t[i] != nil and isCompileTimeOnly(t[i]): + for a in t.kids: + if a != nil and isCompileTimeOnly(a): return true return false @@ -1580,7 +1641,7 @@ proc safeSkipTypes*(t: PType, kinds: TTypeKinds): PType = result = t var seen = initIntSet() while result.kind in kinds and not containsOrIncl(seen, result.id): - result = lastSon(result) + result = skipModifier(result) type OrdinalType* = enum @@ -1629,10 +1690,9 @@ proc skipConvTakeType*(n: PNode): PNode = proc isEmptyContainer*(t: PType): bool = case t.kind of tyUntyped, tyNil: result = true - of tyArray: result = t[1].kind == tyEmpty - of tySet, tySequence, tyOpenArray, tyVarargs: - result = t[0].kind == tyEmpty - of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon) + of tyArray, tySet, tySequence, tyOpenArray, tyVarargs: + result = t.elementType.kind == tyEmpty + of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.skipModifier) else: result = false proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType = @@ -1739,6 +1799,13 @@ proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, co of efTagsIllegal: msg.add "\n.notTag catched an illegal effect" +proc typeNameAndDesc*(t: PType): string = + result = typeToString(t) + let desc = typeToString(t, preferDesc) + if result != desc: + result.add(" = ") + result.add(desc) + proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) = if formal.kind != tyError and actual.kind != tyError: let actualStr = typeToString(actual) @@ -1780,13 +1847,15 @@ proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool = of tyTuple: result = false var cycleDetectorCopy: IntSet - for i in 0..<t.len: + for a in t.kids: cycleDetectorCopy = cycleDetector - if isTupleRecursive(t[i], cycleDetectorCopy): + if isTupleRecursive(a, cycleDetectorCopy): return true - of tyAlias, tyRef, tyPtr, tyGenericInst, tyVar, tyLent, tySink, + of tyRef, tyPtr, tyVar, tyLent, tySink, tyArray, tyUncheckedArray, tySequence, tyDistinct: - return isTupleRecursive(t.lastSon, cycleDetector) + return isTupleRecursive(t.elementType, cycleDetector) + of tyAlias, tyGenericInst: + return isTupleRecursive(t.skipModifier, cycleDetector) else: return false @@ -1801,8 +1870,8 @@ proc isException*(t: PType): bool = var t = t.skipTypes(abstractInst) while t.kind == tyObject: if t.sym != nil and t.sym.magic == mException: return true - if t[0] == nil: break - t = skipTypes(t[0], abstractPtrs) + if t.baseClass == nil: break + t = skipTypes(t.baseClass, abstractPtrs) return false proc isDefectException*(t: PType): bool = @@ -1812,8 +1881,8 @@ proc isDefectException*(t: PType): bool = sfSystemModule in t.sym.owner.flags and t.sym.name.s == "Defect": return true - if t[0] == nil: break - t = skipTypes(t[0], abstractPtrs) + if t.baseClass == nil: break + t = skipTypes(t.baseClass, abstractPtrs) return false proc isDefectOrCatchableError*(t: PType): bool = @@ -1824,8 +1893,8 @@ proc isDefectOrCatchableError*(t: PType): bool = (t.sym.name.s == "Defect" or t.sym.name.s == "CatchableError"): return true - if t[0] == nil: break - t = skipTypes(t[0], abstractPtrs) + if t.baseClass == nil: break + t = skipTypes(t.baseClass, abstractPtrs) return false proc isSinkTypeForParam*(t: PType): bool = @@ -1847,25 +1916,22 @@ proc lookupFieldAgain*(ty: PType; field: PSym): PSym = assert(ty.kind in {tyTuple, tyObject}) result = lookupInRecord(ty.n, field.name) if result != nil: break - ty = ty[0] + ty = ty.baseClass if result == nil: result = field proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool = let t = t.skipTypes(abstractInst) if t.kind == tyPtr: - let pointsTo = t[0].skipTypes(abstractInst) + let pointsTo = t.elementType.skipTypes(abstractInst) case pointsTo.kind of tyUncheckedArray: - result = pointsTo[0].kind == tyChar + result = pointsTo.elementType.kind == tyChar of tyArray: - result = pointsTo[1].kind == tyChar and firstOrd(nil, pointsTo[0]) == 0 and - skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} + result = pointsTo.elementType.kind == tyChar and firstOrd(nil, pointsTo.indexType) == 0 and + skipTypes(pointsTo.indexType, {tyRange}).kind in {tyInt..tyInt64} of tyChar: result = allowPointerToChar else: result = false else: result = false - -proc lacksMTypeField*(typ: PType): bool {.inline.} = - (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 957497bb6..1711fea46 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -106,6 +106,7 @@ type unanalysableMutation: bool inAsgnSource, inConstructor, inNoSideEffectSection: int inConditional, inLoop: int + inConvHasDestructor: int owner: PSym g: ModuleGraph @@ -400,15 +401,14 @@ proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) = if typ != nil: typ = skipTypes(typ, abstractInst) if typ.kind != tyProc: typ = nil - else: assert(typ.len == typ.n.len) for i in 1 ..< n.len: let it = n[i] - if typ != nil and i < typ.len: + if typ != nil and i < typ.n.len: assert(typ.n[i].kind == nkSym) let paramType = typ.n[i].typ - if not paramType.isCompileTimeOnly and not typ[0].isEmptyType and - canAlias(paramType, typ[0]): + if not paramType.isCompileTimeOnly and not typ.returnType.isEmptyType and + canAlias(paramType, typ.returnType): allRoots(it, result, RootEscapes) else: allRoots(it, result, RootEscapes) @@ -428,16 +428,28 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = # primitive literals including the empty are harmless: discard - of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv: + of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast: destMightOwn(c, dest, n[1]) + of nkConv: + if hasDestructor(n.typ): + inc c.inConvHasDestructor + destMightOwn(c, dest, n[1]) + dec c.inConvHasDestructor + else: + destMightOwn(c, dest, n[1]) + of nkIfStmt, nkIfExpr: for i in 0..<n.len: + inc c.inConditional destMightOwn(c, dest, n[i].lastSon) + dec c.inConditional of nkCaseStmt: for i in 1..<n.len: + inc c.inConditional destMightOwn(c, dest, n[i].lastSon) + dec c.inConditional of nkStmtList, nkStmtListExpr: if n.len > 0: @@ -482,7 +494,7 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = of nkCallKinds: if n.typ != nil: - if hasDestructor(n.typ): + if hasDestructor(n.typ) or c.inConvHasDestructor > 0: # calls do construct, what we construct must be destroyed, # so dest cannot be a cursor: dest.flags.incl ownsData @@ -490,8 +502,17 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = # we know the result is derived from the first argument: var roots: seq[(PSym, int)] = @[] allRoots(n[1], roots, RootEscapes) - for r in roots: - connect(c, dest.sym, r[0], n[1].info) + if roots.len == 0 and c.inConditional > 0: + # when in a conditional expression, + # to ensure that the first argument isn't outlived + # by the lvalue, we need find the root, otherwise + # it is probably a local temporary + # (e.g. a return value from a call), + # we should prevent cursorfication + dest.flags.incl preventCursor + else: + for r in roots: + connect(c, dest.sym, r[0], n[1].info) else: let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone @@ -707,7 +728,7 @@ proc traverse(c: var Partitions; n: PNode) = for child in n: traverse(c, child) let parameters = n[0].typ - let L = if parameters != nil: parameters.len else: 0 + let L = if parameters != nil: parameters.signatureLen else: 0 let m = getMagic(n) if m == mEnsureMove and n[1].kind == nkSym: @@ -858,7 +879,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = for child in n: computeLiveRanges(c, child) let parameters = n[0].typ - let L = if parameters != nil: parameters.len else: 0 + let L = if parameters != nil: parameters.signatureLen else: 0 for i in 1..<n.len: let it = n[i] @@ -986,7 +1007,9 @@ proc computeCursors*(s: PSym; n: PNode; g: ModuleGraph) = if v.flags * {ownsData, preventCursor, isConditionallyReassigned} == {} and v.sym.kind notin {skParam, skResult} and v.sym.flags * {sfThread, sfGlobal} == {} and hasDestructor(v.sym.typ) and - v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned: + v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned and + (getAttachedOp(g, v.sym.typ, attachedAsgn) == nil or + sfError notin getAttachedOp(g, v.sym.typ, attachedAsgn).flags): let rid = root(par, i) if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]): discard "cannot cursor into a graph that is mutated" diff --git a/compiler/vm.nim b/compiler/vm.nim index 8824eca37..161b025a6 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -507,7 +507,7 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = setLen(node.sons, newLen) if oldLen < newLen: for i in oldLen..<newLen: - node[i] = getNullValue(typ[0], info, c.config) + node[i] = getNullValue(c, typ.elementType, info, c.config) const errNilAccess = "attempt to access a nil address" @@ -527,7 +527,7 @@ template maybeHandlePtr(node2: PNode, reg: TFullReg, isAssign2: bool): bool = if nfIsPtr in node.flags or (typ != nil and typ.kind == tyPtr): assert node.kind == nkIntLit, $(node.kind) assert typ != nil - let typ2 = if typ.kind == tyPtr: typ[0] else: typ + let typ2 = if typ.kind == tyPtr: typ.elementType else: typ if not derefPtrToReg(node.intVal, typ2, reg, isAssign = isAssign2): # tyObject not supported in this context stackTrace(c, tos, pc, "deref unsupported ptr type: " & $(typeToString(typ), typ.kind)) @@ -609,7 +609,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcYldVal: assert false of opcAsgnInt: decodeB(rkInt) - regs[ra].intVal = regs[rb].intVal + if regs[rb].kind == rkInt: + regs[ra].intVal = regs[rb].intVal + else: + stackTrace(c, tos, pc, "opcAsgnInt: got " & $regs[rb].kind) of opcAsgnFloat: decodeB(rkFloat) regs[ra].floatVal = regs[rb].floatVal @@ -639,6 +642,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = cast[int](regs[rb].node.intVal) of rkNodeAddr: regs[ra].intVal = cast[int](regs[rb].nodeAddr) + of rkRegisterAddr: + regs[ra].intVal = cast[int](regs[rb].regAddr) of rkInt: regs[ra].intVal = regs[rb].intVal else: @@ -674,16 +679,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: assert regs[rb].kind == rkNode let nb = regs[rb].node - case nb.kind - of nkCharLit..nkUInt64Lit: - ensureKind(rkInt) - regs[ra].intVal = nb.intVal - of nkFloatLit..nkFloat64Lit: - ensureKind(rkFloat) - regs[ra].floatVal = nb.floatVal + if nb == nil: + stackTrace(c, tos, pc, errNilAccess) else: - ensureKind(rkNode) - regs[ra].node = nb + case nb.kind + of nkCharLit..nkUInt64Lit: + ensureKind(rkInt) + regs[ra].intVal = nb.intVal + of nkFloatLit..nkFloat64Lit: + ensureKind(rkFloat) + regs[ra].floatVal = nb.floatVal + else: + ensureKind(rkNode) + regs[ra].node = nb of opcSlice: # A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits # nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken. @@ -813,6 +821,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # a[b] = c decodeBC(rkNode) let idx = regs[rb].intVal.int + assert regs[ra].kind == rkNode let arr = regs[ra].node case arr.kind of nkTupleConstr: # refer to `opcSlice` @@ -826,7 +835,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of nkStrKinds: src.strVal[int(realIndex)] = char(regs[rc].intVal) of nkBracket: - src[int(realIndex)] = regs[rc].node + if regs[rc].kind == rkInt: + src[int(realIndex)] = newIntNode(nkIntLit, regs[rc].intVal) + else: + assert regs[rc].kind == rkNode + src[int(realIndex)] = regs[rc].node else: stackTrace(c, tos, pc, "opcWrArr internal error") else: @@ -843,25 +856,30 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdObj: # a = b.c decodeBC(rkNode) - let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[] - case src.kind - of nkEmpty..nkNilLit: - # for nkPtrLit, this could be supported in the future, use something like: - # derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false) - # where we compute the offset in bytes for field rc - stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc)) - of nkObjConstr: - let n = src[rc + 1].skipColon - regs[ra].node = n - of nkTupleConstr: - let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags: - src[rc] - else: - src[rc].skipColon - regs[ra].node = n + if rb >= regs.len or regs[rb].kind == rkNone or + (regs[rb].kind == rkNode and regs[rb].node == nil) or + (regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil): + stackTrace(c, tos, pc, errNilAccess) else: - let n = src[rc] - regs[ra].node = n + let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[] + case src.kind + of nkEmpty..nkNilLit: + # for nkPtrLit, this could be supported in the future, use something like: + # derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false) + # where we compute the offset in bytes for field rc + stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc)) + of nkObjConstr: + let n = src[rc + 1].skipColon + regs[ra].node = n + of nkTupleConstr: + let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags: + src[rc] + else: + src[rc].skipColon + regs[ra].node = n + else: + let n = src[rc] + regs[ra].node = n of opcLdObjAddr: # a = addr(b.c) decodeBC(rkNodeAddr) @@ -887,8 +905,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, errNilAccess) elif dest[shiftedRb].kind == nkExprColonExpr: writeField(dest[shiftedRb][1], regs[rc]) + dest[shiftedRb][1].flags.incl nfSkipFieldChecking else: writeField(dest[shiftedRb], regs[rc]) + dest[shiftedRb].flags.incl nfSkipFieldChecking of opcWrStrIdx: decodeBC(rkNode) let idx = regs[rb].intVal.int @@ -1356,7 +1376,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: internalError(c.config, c.debug[pc], "opcParseFloat: Incorrectly created openarray") else: - regs[ra].intVal = parseBiggestFloat(regs[ra].node.strVal, rcAddr.floatVal) + regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal, rcAddr.floatVal) of opcRangeChck: let rb = instr.regB @@ -1371,7 +1391,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rb = instr.regB let rc = instr.regC let bb = regs[rb].node + if bb.kind == nkNilLit: + stackTrace(c, tos, pc, "attempt to call nil closure") let isClosure = bb.kind == nkTupleConstr + if isClosure and bb[0].kind == nkNilLit: + stackTrace(c, tos, pc, "attempt to call nil closure") let prc = if not isClosure: bb.sym else: bb[0].sym if prc.offset < -1: # it's a callback: @@ -1410,8 +1434,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = #echo "new pc ", newPc, " calling: ", prc.name.s var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) newSeq(newFrame.slots, prc.offset+ord(isClosure)) - if not isEmptyType(prc.typ[0]): - putIntoReg(newFrame.slots[0], getNullValue(prc.typ[0], prc.info, c.config)) + if not isEmptyType(prc.typ.returnType): + putIntoReg(newFrame.slots[0], getNullValue(c, prc.typ.returnType, prc.info, c.config)) for i in 1..rc-1: newFrame.slots[i] = regs[rb+i] if isClosure: @@ -1544,7 +1568,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNew: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] - regs[ra].node = getNullValue(typ, c.debug[pc], c.config) + regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config) regs[ra].node.flags.incl nfIsRef of opcNewSeq: let typ = c.types[instr.regBx - wordExcess] @@ -1556,7 +1580,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.typ = typ newSeq(regs[ra].node.sons, count) for i in 0..<count: - regs[ra].node[i] = getNullValue(typ[0], c.debug[pc], c.config) + regs[ra].node[i] = getNullValue(c, typ.elementType, c.debug[pc], c.config) of opcNewStr: decodeB(rkNode) regs[ra].node = newNodeI(nkStrLit, c.debug[pc]) @@ -1568,7 +1592,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdNull: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] - regs[ra].node = getNullValue(typ, c.debug[pc], c.config) + regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config) # opcLdNull really is the gist of the VM's problems: should it load # a fresh null to regs[ra].node or to regs[ra].node[]? This really # depends on whether regs[ra] represents the variable itself or whether @@ -1618,7 +1642,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = node2.flags.incl nfIsPtr regs[ra].node = node2 elif not derefPtrToReg(node.intVal, typ, regs[ra], isAssign = false): - stackTrace(c, tos, pc, "opcLdDeref unsupported type: " & $(typeToString(typ), typ[0].kind)) + stackTrace(c, tos, pc, "opcLdDeref unsupported type: " & $(typeToString(typ), typ.elementType.kind)) of opcLdGlobalAddrDerefFFI: let rb = instr.regBx - wordExcess - 1 let node = c.globals[rb] @@ -1925,6 +1949,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) + + regs[ra].node = newNode(nkEmpty) if error.len > 0: c.errorFlag = error elif ast.len != 1: @@ -1942,6 +1968,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = error = formatMsg(conf, info, msg, arg)) if error.len > 0: c.errorFlag = error + regs[ra].node = newNode(nkEmpty) else: regs[ra].node = ast of opcQueryErrorFlag: @@ -2264,7 +2291,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNode) var typ = regs[rb].node.typ internalAssert c.config, typ != nil - while typ.kind == tyTypeDesc and typ.len > 0: typ = typ[0] + while typ.kind == tyTypeDesc and typ.hasElementType: typ = typ.skipModifier createStr regs[ra] regs[ra].node.strVal = typ.typeToString(preferExported) @@ -2280,11 +2307,11 @@ proc execute(c: PCtx, start: int): PNode = proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = c.loopIterations = c.config.maxLoopIterationsVM if sym.kind in routineKinds: - if sym.typ.len-1 != args.len: + if sym.typ.paramsLen != args.len: result = nil localError(c.config, sym.info, "NimScript: expected $# arguments, but got $#" % [ - $(sym.typ.len-1), $args.len]) + $(sym.typ.paramsLen), $args.len]) else: let start = genProc(c, sym) @@ -2293,11 +2320,11 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = newSeq(tos.slots, maxSlots) # setup parameters: - if not isEmptyType(sym.typ[0]) or sym.kind == skMacro: - putIntoReg(tos.slots[0], getNullValue(sym.typ[0], sym.info, c.config)) + if not isEmptyType(sym.typ.returnType) or sym.kind == skMacro: + putIntoReg(tos.slots[0], getNullValue(c, sym.typ.returnType, sym.info, c.config)) # XXX We could perform some type checking here. - for i in 1..<sym.typ.len: - putIntoReg(tos.slots[i], args[i-1]) + for i in 0..<sym.typ.paramsLen: + putIntoReg(tos.slots[i+1], args[i]) result = rawExecute(c, start, tos).regToNode else: @@ -2435,7 +2462,7 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = let gp = macroSym.ast[genericParamsPos] for i in 0..<gp.len: let genericParam = gp[i].sym - let posInCall = macroSym.typ.len + i + let posInCall = macroSym.typ.signatureLen + i if posInCall < call.len: yield (genericParam, call[posInCall]) @@ -2458,9 +2485,10 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC # immediate macros can bypass any type and arity checking so we check the # arity here too: - if sym.typ.len > n.safeLen and sym.typ.len > 1: + let sl = sym.typ.signatureLen + if sl > n.safeLen and sl > 1: globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [ - n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)]) + n.renderTree, $(n.safeLen-1), $(sym.typ.paramsLen)]) setupGlobalCtx(module, g, idgen) var c = PCtx g.vm @@ -2485,12 +2513,12 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC tos.slots[0] = TFullReg(kind: rkNode, node: newNodeI(nkEmpty, n.info)) # setup parameters: - for i in 1..<sym.typ.len: - tos.slots[i] = setupMacroParam(n[i], sym.typ[i]) + for i, param in paramTypes(sym.typ): + tos.slots[i-FirstParamAt+1] = setupMacroParam(n[i-FirstParamAt+1], param) let gp = sym.ast[genericParamsPos] for i in 0..<gp.len: - let idx = sym.typ.len + i + let idx = sym.typ.signatureLen + i if idx < n.len: tos.slots[idx] = setupMacroParam(n[idx], gp[i].sym.typ) else: diff --git a/compiler/vmconv.nim b/compiler/vmconv.nim index 394fb838b..45d925df0 100644 --- a/compiler/vmconv.nim +++ b/compiler/vmconv.nim @@ -1,4 +1,5 @@ -import ast, idents, lineinfos, astalgo +import ast except elementType +import idents, lineinfos, astalgo import vmdef import std/times diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index ed3ca68ac..294aaaa79 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -49,13 +49,13 @@ proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; inf inst=false): PNode = result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) result.add atomicTypeX(cache, name, m, t, info, idgen) - for i in 0..<t.len: - if t[i] == nil: + for a in t.kids: + if a == nil: let voidt = atomicTypeX(cache, "void", mVoid, t, info, idgen) voidt.typ = newType(tyVoid, idgen, t.owner) result.add voidt else: - result.add mapTypeToAstX(cache, t[i], info, idgen, inst) + result.add mapTypeToAstX(cache, a, info, idgen, inst) proc objectNode(cache: IdentCache; n: PNode; idgen: IdGenerator): PNode = if n.kind == nkSym: @@ -74,9 +74,9 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; var allowRecursion = allowRecursionX template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info, idgen) template atomicType(s): untyped = atomicTypeX(s, info) - template mapTypeToAst(t,info): untyped = mapTypeToAstX(cache, t, info, idgen, inst) - template mapTypeToAstR(t,info): untyped = mapTypeToAstX(cache, t, info, idgen, inst, true) - template mapTypeToAst(t,i,info): untyped = + template mapTypeToAst(t, info): untyped = mapTypeToAstX(cache, t, info, idgen, inst) + template mapTypeToAstR(t, info): untyped = mapTypeToAstX(cache, t, info, idgen, inst, true) + template mapTypeToAst(t, i, info): untyped = if i<t.len and t[i]!=nil: mapTypeToAstX(cache, t[i], info, idgen, inst) else: newNodeI(nkEmpty, info) template mapTypeToBracket(name, m, t, info): untyped = @@ -107,19 +107,19 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyUncheckedArray: result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) result.add atomicType("UncheckedArray", mUncheckedArray) - result.add mapTypeToAst(t[0], info) + result.add mapTypeToAst(t.elementType, info) of tyArray: result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) result.add atomicType("array", mArray) - if inst and t[0].kind == tyRange: + if inst and t.indexType.kind == tyRange: var rng = newNodeX(nkInfix) rng.add newIdentNode(getIdent(cache, ".."), info) - rng.add t[0].n[0].copyTree - rng.add t[0].n[1].copyTree + rng.add t.indexType.n[0].copyTree + rng.add t.indexType.n[1].copyTree result.add rng else: - result.add mapTypeToAst(t[0], info) - result.add mapTypeToAst(t[1], info) + result.add mapTypeToAst(t.indexType, info) + result.add mapTypeToAst(t.elementType, info) of tyTypeDesc: if t.base != nil: result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) @@ -129,37 +129,37 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; result = atomicType("typeDesc", mTypeDesc) of tyGenericInvocation: result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) - for i in 0..<t.len: - result.add mapTypeToAst(t[i], info) + for a in t.kids: + result.add mapTypeToAst(a, info) of tyGenericInst: if inst: if allowRecursion: - result = mapTypeToAstR(t.lastSon, info) + result = mapTypeToAstR(t.skipModifier, info) # keep original type info for getType calls on the output node: result.typ = t else: result = newNodeX(nkBracketExpr) - #result.add mapTypeToAst(t.lastSon, info) - result.add mapTypeToAst(t[0], info) - for i in 1..<t.len-1: - result.add mapTypeToAst(t[i], info) + #result.add mapTypeToAst(t.last, info) + result.add mapTypeToAst(t.genericHead, info) + for _, a in t.genericInstParams: + result.add mapTypeToAst(a, info) else: - result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion) + result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion) # keep original type info for getType calls on the output node: result.typ = t of tyGenericBody: if inst: - result = mapTypeToAstR(t.lastSon, info) + result = mapTypeToAstR(t.typeBodyImpl, info) else: - result = mapTypeToAst(t.lastSon, info) + result = mapTypeToAst(t.typeBodyImpl, info) of tyAlias: - result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion) + result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion) of tyOrdinal: - result = mapTypeToAst(t.lastSon, info) + result = mapTypeToAst(t.skipModifier, info) of tyDistinct: if inst: result = newNodeX(nkDistinctTy) - result.add mapTypeToAst(t[0], info) + result.add mapTypeToAst(t.skipModifier, info) else: if allowRecursion or t.sym == nil: result = mapTypeToBracket("distinct", mDistinct, t, info) @@ -174,11 +174,11 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; if objectDef.kind == nkRefTy: objectDef = objectDef[0] result.add objectDef[0].copyTree # copy object pragmas - if t[0] == nil: + if t.baseClass == nil: result.add newNodeI(nkEmpty, info) else: # handle parent object var nn = newNodeX(nkOfInherit) - nn.add mapTypeToAst(t[0], info) + nn.add mapTypeToAst(t.baseClass, info) result.add nn if t.n.len > 0: result.add objectNode(cache, t.n, idgen) @@ -188,10 +188,10 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; if allowRecursion or t.sym == nil: result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t) result.add newNodeI(nkEmpty, info) - if t[0] == nil: + if t.baseClass == nil: result.add newNodeI(nkEmpty, info) else: - result.add mapTypeToAst(t[0], info) + result.add mapTypeToAst(t.baseClass, info) result.add copyTree(t.n) else: result = atomicType(t.sym) @@ -205,7 +205,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; # only named tuples have a node, unnamed tuples don't if t.n.isNil: result = newNodeX(nkTupleConstr) - for subType in t: + for subType in t.kids: result.add mapTypeToAst(subType, info) else: result = newNodeX(nkTupleTy) @@ -217,19 +217,19 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyPtr: if inst: result = newNodeX(nkPtrTy) - result.add mapTypeToAst(t[0], info) + result.add mapTypeToAst(t.elementType, info) else: result = mapTypeToBracket("ptr", mPtr, t, info) of tyRef: if inst: result = newNodeX(nkRefTy) - result.add mapTypeToAst(t[0], info) + result.add mapTypeToAst(t.elementType, info) else: result = mapTypeToBracket("ref", mRef, t, info) of tyVar: if inst: result = newNodeX(nkVarTy) - result.add mapTypeToAst(t[0], info) + result.add mapTypeToAst(t.elementType, info) else: result = mapTypeToBracket("var", mVar, t, info) of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info) @@ -237,17 +237,24 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) of tyProc: if inst: - result = newNodeX(nkProcTy) + result = newNodeX(if tfIterator in t.flags: nkIteratorTy else: nkProcTy) var fp = newNodeX(nkFormalParams) - if t[0] == nil: + if t.returnType == nil: fp.add newNodeI(nkEmpty, info) else: - fp.add mapTypeToAst(t[0], t.n[0].info) - for i in 1..<t.len: + fp.add mapTypeToAst(t.returnType, t.n[0].info) + for i in FirstParamAt..<t.kidsLen: fp.add newIdentDefs(t.n[i], t[i]) result.add fp - result.add if t.n[0].len > 0: t.n[0][pragmasEffects].copyTree - else: newNodeI(nkEmpty, info) + var prag = + if t.n[0].len > 0: + t.n[0][pragmasEffects].copyTree + else: + newNodeI(nkEmpty, info) + if t.callConv != ccClosure or tfExplicitCallConv in t.flags: + if prag.kind == nkEmpty: prag = newNodeI(nkPragma, info) + prag.add newIdentNode(getIdent(cache, $t.callConv), info) + result.add prag else: result = mapTypeToBracket("proc", mNone, t, info) of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info) @@ -282,12 +289,12 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyUInt32: result = atomicType("uint32", mUInt32) of tyUInt64: result = atomicType("uint64", mUInt64) of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info) - of tyProxy: result = atomicType("error", mNone) + of tyError: result = atomicType("error", mNone) of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", mNone, t, info) of tyUserTypeClass, tyUserTypeClassInst: if t.isResolvedUserTypeClass: - result = mapTypeToAst(t.lastSon, info) + result = mapTypeToAst(t.last, info) else: result = mapTypeToBracket("concept", mNone, t, info) result.add t.n.copyTree @@ -298,7 +305,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyNot: result = mapTypeToBracket("not", mNot, t, info) of tyIterable: result = mapTypeToBracket("iterable", mIterableType, t, info) of tyAnything: result = atomicType("anything", mNone) - of tyInferred: result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion) + of tyInferred: result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion) of tyStatic, tyFromExpr: if inst: if t.n != nil: result = t.n.copyTree diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 53b2974ba..0c7a49984 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -53,6 +53,7 @@ type gfNode # Affects how variables are loaded - always loads as rkNode gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr gfIsParam # do not deepcopy parameters, they are immutable + gfIsSinkParam # deepcopy sink parameters TGenFlags = set[TGenFlag] proc debugInfo(c: PCtx; info: TLineInfo): string = @@ -245,7 +246,7 @@ proc getTemp(cc: PCtx; tt: PType): TRegister = proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc - if c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}: + if r < c.regInfo.len and c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}: # this seems to cause https://github.com/nim-lang/Nim/issues/10647 c.regInfo[r].inUse = false @@ -357,12 +358,13 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) = #if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}: if i != dest: when not defined(release): - if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown, - slotTempInt, - slotTempFloat, - slotTempStr, - slotTempComplex}: - raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind + if c.config.cmd != cmdCheck: + if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown, + slotTempInt, + slotTempFloat, + slotTempStr, + slotTempComplex}: + raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind c.prc.regInfo[i] = (inUse: false, kind: slotEmpty) c.clearDest(n, dest) @@ -619,10 +621,17 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) = let fntyp = skipTypes(n[0].typ, abstractInst) for i in 0..<n.len: var r: TRegister = x+i - c.gen(n[i], r, {gfIsParam}) - if i >= fntyp.len: + if i >= fntyp.signatureLen: + c.gen(n[i], r, {gfIsParam}) internalAssert c.config, tfVarargs in fntyp.flags c.gABx(n, opcSetType, r, c.genType(n[i].typ)) + else: + if fntyp[i] != nil and fntyp[i].kind == tySink and + fntyp[i].skipTypes({tySink}).kind in {tyObject, tyString, tySequence}: + c.gen(n[i], r, {gfIsSinkParam}) + else: + c.gen(n[i], r, {gfIsParam}) + if dest < 0: c.gABC(n, opcIndCall, 0, x, n.len) else: @@ -696,6 +705,9 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = let dest = c.genx(le, {gfNodeAddr}) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + if sameBackendType(le.typ, le[1].typ): + genAsgnPatch(c, le[1], value) else: discard @@ -868,7 +880,7 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) c.genNarrow(n, dest) -proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = +proc genConv(c: PCtx; n, arg: PNode; dest: var TDest, flags: TGenFlags = {}; opc=opcConv) = let t2 = n.typ.skipTypes({tyDistinct}) let targ2 = arg.typ.skipTypes({tyDistinct}) @@ -882,7 +894,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = result = false if implicitConv(): - gen(c, arg, dest) + gen(c, arg, dest, flags) return let tmp = c.genx(arg) @@ -900,9 +912,15 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = + template isSigned(typ: PType): bool {.dirty.} = + typ.kind == tyEnum and firstOrd(c.config, typ) < 0 or + typ.kind in {tyInt..tyInt64} + template isUnsigned(typ: PType): bool {.dirty.} = + typ.kind == tyEnum and firstOrd(c.config, typ) >= 0 or + typ.kind in {tyUInt..tyUInt64, tyChar, tyBool} + const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool} - var signedIntegers = {tyInt..tyInt64} - var unsignedIntegers = {tyUInt..tyUInt64, tyChar, tyEnum, tyBool} + let src = n[1].typ.skipTypes(abstractRange)#.kind let dst = n[0].typ.skipTypes(abstractRange)#.kind let srcSize = getSize(c.config, src) @@ -914,12 +932,12 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = if dest < 0: dest = c.getTemp(n[0].typ) c.gABC(n, opcAsgnInt, dest, tmp) if dstSize != sizeof(BiggestInt): # don't do anything on biggest int types - if dst.kind in signedIntegers: # we need to do sign extensions + if isSigned(dst): # we need to do sign extensions if dstSize <= srcSize: # Sign extension can be omitted when the size increases. c.gABC(n, opcSignExtend, dest, TRegister(dstSize*8)) - elif dst.kind in unsignedIntegers: - if src.kind in signedIntegers or dstSize < srcSize: + elif isUnsigned(dst): + if isSigned(src) or dstSize < srcSize: # Cast from signed to unsigned always needs narrowing. Cast # from unsigned to unsigned only needs narrowing when target # is smaller than source. @@ -947,7 +965,7 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = if dest < 0: dest = c.getTemp(n[0].typ) if src.kind == tyFloat32: c.gABC(n, opcCastFloatToInt32, dest, tmp) - if dst.kind in unsignedIntegers: + if isUnsigned(dst): # integers are sign extended by default. # since there is no opcCastFloatToUInt32, narrowing should do the trick. c.gABC(n, opcNarrowU, dest, TRegister(32)) @@ -1044,7 +1062,7 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode = else: (if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex) -proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = +proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, dest) of mOr: c.genAndOr(n, opcTJmp, dest) @@ -1182,8 +1200,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = let size = getSize(c.config, t) if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): c.gABC(n, opcNarrowU, dest, TRegister(size*8)) - of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: - genConv(c, n, n[1], dest) + of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr: + genConv(c, n, n[1], dest, flags) of mEqStr: genBinaryABC(c, n, dest, opcEqStr) of mEqCString: genBinaryABC(c, n, dest, opcEqCString) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) @@ -1229,13 +1247,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(tmp1) c.genAsgnPatch(d2AsNode, d2) c.freeTemp(d2) - of mReset: - unused(c, n, dest) - var d = c.genx(n[1]) - # XXX use ldNullOpcode() here? - c.gABx(n, opcLdNull, d, c.genType(n[1].typ)) - c.gABC(n, opcNodeToReg, d, d) - c.genAsgnPatch(n[1], d) of mDefault, mZeroDefault: if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ)) @@ -1530,7 +1541,11 @@ proc setSlot(c: PCtx; v: PSym) = if v.position == 0: v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1) -proc cannotEval(c: PCtx; n: PNode) {.noinline.} = +template cannotEval(c: PCtx; n: PNode) = + if c.config.cmd == cmdCheck: + localError(c.config, n.info, "cannot evaluate at compile time: " & + n.renderTree) + return globalError(c.config, n.info, "cannot evaluate at compile time: " & n.renderTree) @@ -1653,6 +1668,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = c.freeTemp(cc) else: gen(c, ri, dest) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + if sameBackendType(le.typ, le[1].typ): + genAsgn(c, le[1], ri, requiresCopy) else: let dest = c.genx(le, {gfNodeAddr}) genAsgn(c, dest, ri, requiresCopy) @@ -1691,10 +1709,10 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) = localError(c.config, info, "cannot 'importc' variable at compile time; " & s.name.s) -proc getNullValue*(typ: PType, info: TLineInfo; conf: ConfigRef): PNode +proc getNullValue*(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = - c.globals.add(getNullValue(s.typ, n.info, c.config)) + c.globals.add(getNullValue(c, s.typ, n.info, c.config)) s.position = c.globals.len # This is rather hard to support, due to the laziness of the VM code # generator. See tests/compile/tmacro2 for why this is necessary: @@ -1730,6 +1748,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = c.gABx(n, opcLdGlobalAddr, dest, s.position) elif isImportcVar: c.gABx(n, opcLdGlobalDerefFFI, dest, s.position) + elif gfIsSinkParam in flags: + genAsgn(c, dest, n, requiresCopy = true) elif fitsRegister(s.typ) and gfNode notin flags: var cc = c.getTemp(n.typ) c.gABx(n, opcLdGlobal, cc, s.position) @@ -1743,7 +1763,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = s.kind in {skParam, skResult}): if dest < 0: dest = s.position + ord(s.kind == skParam) - internalAssert(c.config, c.prc.regInfo[dest].kind < slotSomeTemp) + internalAssert(c.config, c.prc.regInfo.len > dest and c.prc.regInfo[dest].kind < slotSomeTemp) else: # we need to generate an assignment: let requiresCopy = c.prc.regInfo[dest].kind >= slotSomeTemp and @@ -1873,21 +1893,21 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr genArrAccessOpcode(c, n, dest, opc, flags) -proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) = - if t != nil and t.len > 0 and t[0] != nil: - let b = skipTypes(t[0], skipPtrs) - getNullValueAux(b, b.n, result, conf, currPosition) +proc getNullValueAux(c: PCtx; t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) = + if t != nil and t.baseClass != nil: + let b = skipTypes(t.baseClass, skipPtrs) + getNullValueAux(c, b, b.n, result, conf, currPosition) case obj.kind of nkRecList: - for i in 0..<obj.len: getNullValueAux(nil, obj[i], result, conf, currPosition) + for i in 0..<obj.len: getNullValueAux(c, nil, obj[i], result, conf, currPosition) of nkRecCase: - getNullValueAux(nil, obj[0], result, conf, currPosition) + getNullValueAux(c, nil, obj[0], result, conf, currPosition) for i in 1..<obj.len: - getNullValueAux(nil, lastSon(obj[i]), result, conf, currPosition) + getNullValueAux(c, nil, lastSon(obj[i]), result, conf, currPosition) of nkSym: let field = newNodeI(nkExprColonExpr, result.info) field.add(obj) - let value = getNullValue(obj.sym.typ, result.info, conf) + let value = getNullValue(c, obj.sym.typ, result.info, conf) value.flags.incl nfSkipFieldChecking field.add(value) result.add field @@ -1895,7 +1915,7 @@ proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currP inc currPosition else: globalError(conf, result.info, "cannot create null element for: " & $obj) -proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = +proc getNullValue(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode = var t = skipTypes(typ, abstractRange+{tyStatic, tyOwned}-{tyTypeDesc}) case t.kind of tyBool, tyEnum, tyChar, tyInt..tyInt64: @@ -1915,22 +1935,22 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result = newNodeIT(nkNilLit, info, t) else: result = newNodeIT(nkTupleConstr, info, t) - result.add(newNodeIT(nkNilLit, info, t)) - result.add(newNodeIT(nkNilLit, info, t)) + result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer))) + result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer))) of tyObject: result = newNodeIT(nkObjConstr, info, t) result.add(newNodeIT(nkEmpty, info, t)) # initialize inherited fields, and all in the correct order: var currPosition = 0 - getNullValueAux(t, t.n, result, conf, currPosition) + getNullValueAux(c, t, t.n, result, conf, currPosition) of tyArray: result = newNodeIT(nkBracket, info, t) for i in 0..<toInt(lengthOrd(conf, t)): - result.add getNullValue(elemType(t), info, conf) + result.add getNullValue(c, elemType(t), info, conf) of tyTuple: result = newNodeIT(nkTupleConstr, info, t) - for i in 0..<t.len: - result.add getNullValue(t[i], info, conf) + for a in t.kids: + result.add getNullValue(c, a, info, conf) of tySet: result = newNodeIT(nkCurly, info, t) of tySequence, tyOpenArray: @@ -1958,7 +1978,7 @@ proc genVarSection(c: PCtx; n: PNode) = if s.position == 0: if importcCond(c, s): c.importcSym(a.info, s) else: - let sa = getNullValue(s.typ, a.info, c.config) + let sa = getNullValue(c, s.typ, a.info, c.config) #if s.ast.isNil: getNullValue(s.typ, a.info) #else: s.ast assert sa.kind != nkCall @@ -1980,7 +2000,7 @@ proc genVarSection(c: PCtx; n: PNode) = # the problem is that closure types are tuples in VM, but the types of its children # shouldn't have the same type as closure types. let tmp = c.genx(a[0], {gfNodeAddr}) - let sa = getNullValue(s.typ, a.info, c.config) + let sa = getNullValue(c, s.typ, a.info, c.config) let val = c.genx(sa) c.genAdditionalCopy(sa, opcWrDeref, tmp, 0, val) c.freeTemp(val) @@ -2050,10 +2070,12 @@ proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) = c.freeTemp(a) proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = + if tfUnion in n.typ.flags: # bug #22708 # bug #13481 + globalError(c.config, n.info, "object with '{.union.}' pragmas is not supported by VM") if dest < 0: dest = c.getTemp(n.typ) let t = n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}) if t.kind == tyRef: - c.gABx(n, opcNew, dest, c.genType(t[0])) + c.gABx(n, opcNew, dest, c.genType(t.elementType)) else: c.gABx(n, opcLdNull, dest, c.genType(n.typ)) for i in 1..<n.len: @@ -2124,7 +2146,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genRdVar(c, n, dest, flags) of skParam: if s.typ.kind == tyTypeDesc: - genTypeLit(c, s.typ, dest) + genTypeLit(c, s.typ.skipTypes({tyTypeDesc}), dest) else: genRdVar(c, n, dest, flags) of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator: @@ -2163,7 +2185,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = if n[0].kind == nkSym: let s = n[0].sym if s.magic != mNone: - genMagic(c, n, dest, s.magic) + genMagic(c, n, dest, flags, s.magic) elif s.kind == skMethod: localError(c.config, n.info, "cannot call method " & s.name.s & " at compile time") @@ -2181,7 +2203,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genLit(c, n, dest) of nkUIntLit..pred(nkNilLit): genLit(c, n, dest) of nkNilLit: - if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest) + if not n.typ.isEmptyType: genLit(c, getNullValue(c, n.typ, n.info, c.config), dest) else: unused(c, n, dest) of nkAsgn, nkFastAsgn, nkSinkAsgn: unused(c, n, dest) @@ -2220,11 +2242,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = unused(c, n, dest) gen(c, n[0]) of nkHiddenStdConv, nkHiddenSubConv, nkConv: - genConv(c, n, n[1], dest) + genConv(c, n, n[1], dest, flags) of nkObjDownConv: - genConv(c, n, n[0], dest) + genConv(c, n, n[0], dest, flags) of nkObjUpConv: - genConv(c, n, n[0], dest) + genConv(c, n, n[0], dest, flags) of nkVarSection, nkLetSection: unused(c, n, dest) genVarSection(c, n) @@ -2233,18 +2255,21 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = #discard genProc(c, s) genLit(c, newSymNode(n[namePos].sym), dest) of nkChckRangeF, nkChckRange64, nkChckRange: - let - tmp0 = c.genx(n[0]) - tmp1 = c.genx(n[1]) - tmp2 = c.genx(n[2]) - c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2) - c.freeTemp(tmp1) - c.freeTemp(tmp2) - if dest >= 0: - gABC(c, n, whichAsgnOpc(n), dest, tmp0) - c.freeTemp(tmp0) + if skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64}: + genConv(c, n, n[0], dest, flags) else: - dest = tmp0 + let + tmp0 = c.genx(n[0]) + tmp1 = c.genx(n[1]) + tmp2 = c.genx(n[2]) + c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2) + c.freeTemp(tmp1) + c.freeTemp(tmp2) + if dest >= 0: + gABC(c, n, whichAsgnOpc(n), dest, tmp0) + c.freeTemp(tmp0) + else: + dest = tmp0 of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef: @@ -2257,7 +2282,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest) of nkCast: if allowCast in c.features: - genConv(c, n, n[1], dest, opcCast) + genConv(c, n, n[1], dest, flags, opcCast) else: genCastIntFloat(c, n, dest) of nkTypeOfExpr: diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index 8ce113369..0e67ededa 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -82,11 +82,11 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; s.add("]") of tyTuple: s.add("{") - for i in 0..<t.len: + for i, ti in t.ikids: if i > 0: s.add(", ") s.add("\"Field" & $i) s.add("\": ") - storeAny(s, t[i], a[i].skipColon, stored, conf) + storeAny(s, ti, a[i].skipColon, stored, conf) s.add("}") of tyObject: s.add("{") @@ -98,17 +98,17 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; if i > 0: s.add(", ") if a[i].kind == nkRange: var x = copyNode(a[i][0]) - storeAny(s, t.lastSon, x, stored, conf) + storeAny(s, t.elementType, x, stored, conf) inc x.intVal while x.intVal <= a[i][1].intVal: s.add(", ") - storeAny(s, t.lastSon, x, stored, conf) + storeAny(s, t.elementType, x, stored, conf) inc x.intVal else: - storeAny(s, t.lastSon, a[i], stored, conf) + storeAny(s, t.elementType, a[i], stored, conf) s.add("]") of tyRange, tyGenericInst, tyAlias, tySink: - storeAny(s, t.lastSon, a, stored, conf) + storeAny(s, t.skipModifier, a, stored, conf) of tyEnum: # we need a slow linear search because of enums with holes: for e in items(t.n): @@ -127,7 +127,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; s.add("[") s.add($x.ptrToInt) s.add(", ") - storeAny(s, t.lastSon, a, stored, conf) + storeAny(s, t.elementType, a, stored, conf) s.add("]") of tyString, tyCstring: if a.kind == nkNilLit: s.add("null") @@ -208,11 +208,12 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkTupleConstr) var i = 0 + let tupleLen = t.kidsLen while p.kind != jsonObjectEnd and p.kind != jsonEof: if p.kind != jsonString: raiseParseErr(p, "string expected for a field name") next(p) - if i >= t.len: + if i >= tupleLen: raiseParseErr(p, "too many fields to tuple type " & typeToString(t)) result.add loadAny(p, t[i], tab, cache, conf, idgen) inc i @@ -245,7 +246,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkCurly) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.lastSon, tab, cache, conf, idgen) + result.add loadAny(p, t.elementType, tab, cache, conf, idgen) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") of tyPtr, tyRef: @@ -264,7 +265,7 @@ proc loadAny(p: var JsonParser, t: PType, if p.kind == jsonInt: let idx = p.getInt next(p) - result = loadAny(p, t.lastSon, tab, cache, conf, idgen) + result = loadAny(p, t.elementType, tab, cache, conf, idgen) tab[idx] = result else: raiseParseErr(p, "index for ref type expected") if p.kind == jsonArrayEnd: next(p) @@ -300,14 +301,14 @@ proc loadAny(p: var JsonParser, t: PType, result = nil raiseParseErr(p, "float expected") of tyRange, tyGenericInst, tyAlias, tySink: - result = loadAny(p, t.lastSon, tab, cache, conf, idgen) + result = loadAny(p, t.skipModifier, tab, cache, conf, idgen) else: result = nil internalError conf, "cannot marshal at compile-time " & t.typeToString proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator): PNode = var tab = initTable[BiggestInt, PNode]() - var p: JsonParser + var p: JsonParser = default(JsonParser) open(p, newStringStream(s), "unknown file") next(p) result = loadAny(p, t, tab, cache, conf, idgen) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 23b41fd2e..45194e633 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -27,6 +27,7 @@ from std/envvars import getEnv, existsEnv, delEnv, putEnv, envPairs from std/os import getAppFilename from std/private/oscommon import dirExists, fileExists from std/private/osdirs import walkDir, createDir +from std/private/ospaths2 import getCurrentDir from std/times import cpuTime from std/hashes import hash @@ -341,8 +342,8 @@ proc registerAdditionalOps*(c: PCtx) = ## reproducible builds and users need to understand that this runs at CT. ## Note that `staticExec` can already do equal amount of damage so it's more ## of a semantic issue than a security issue. - registerCallback c, "stdlib.os.getCurrentDir", proc (a: VmArgs) {.nimcall.} = - setResult(a, os.getCurrentDir()) + registerCallback c, "stdlib.ospaths2.getCurrentDir", proc (a: VmArgs) {.nimcall.} = + setResult(a, getCurrentDir()) registerCallback c, "stdlib.osproc.execCmdEx", proc (a: VmArgs) {.nimcall.} = let options = getNode(a, 1).fromLit(set[osproc.ProcessOption]) a.setResult osproc.execCmdEx(getString(a, 0), options).toLit diff --git a/compiler/vtables.nim b/compiler/vtables.nim index f57b59eae..928c64dd5 100644 --- a/compiler/vtables.nim +++ b/compiler/vtables.nim @@ -1,167 +1,167 @@ -import ast, modulegraphs, magicsys, lineinfos, options, cgmeth, types -import std/[algorithm, tables, intsets, assertions] - - - -proc genVTableDispatcher(g: ModuleGraph; methods: seq[PSym]; index: int): PSym = -#[ -proc dispatch(x: Base, params: ...) = - cast[proc bar(x: Base, params: ...)](x.vTable[index])(x, params) -]# - var base = methods[0].ast[dispatcherPos].sym - result = base - var paramLen = base.typ.len - var body = newNodeI(nkStmtList, base.info) - - var disp = newNodeI(nkIfStmt, base.info) - - var vTableAccess = newNodeIT(nkBracketExpr, base.info, base.typ) - let nimGetVTableSym = getCompilerProc(g, "nimGetVTable") - let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ - - var nTyp = base.typ.n[1].sym.typ - var dispatchObject = newSymNode(base.typ.n[1].sym) - if nTyp.kind == tyObject: - dispatchObject = newTree(nkAddr, dispatchObject) - else: - if g.config.backend != backendCpp: # TODO: maybe handle ptr? - if nTyp.kind == tyVar and nTyp.skipTypes({tyVar}).kind != tyObject: - dispatchObject = newTree(nkDerefExpr, dispatchObject) - - var getVTableCall = newTree(nkCall, - newSymNode(nimGetVTableSym), - dispatchObject, - newIntNode(nkIntLit, index) - ) - getVTableCall.typ = base.typ - var vTableCall = newNodeIT(nkCall, base.info, base.typ[0]) - var castNode = newTree(nkCast, - newNodeIT(nkType, base.info, base.typ), - getVTableCall) - - castNode.typ = base.typ - vTableCall.add castNode - for col in 1..<paramLen: - let param = base.typ.n[col].sym - vTableCall.add newSymNode(param) - - var ret: PNode - if base.typ[0] != nil: - var a = newNodeI(nkFastAsgn, base.info) - a.add newSymNode(base.ast[resultPos].sym) - a.add vTableCall - ret = newNodeI(nkReturnStmt, base.info) - ret.add a - else: - ret = vTableCall - - if base.typ.n[1].sym.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: - let ifBranch = newNodeI(nkElifBranch, base.info) - let boolType = getSysType(g, unknownLineInfo, tyBool) - var isNil = getSysMagic(g, unknownLineInfo, "isNil", mIsNil) - let checkSelf = newNodeIT(nkCall, base.info, boolType) - checkSelf.add newSymNode(isNil) - checkSelf.add newSymNode(base.typ.n[1].sym) - ifBranch.add checkSelf - ifBranch.add newTree(nkCall, - newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(base.typ.n[1].sym)) - let elseBranch = newTree(nkElifBranch, ret) - disp.add ifBranch - disp.add elseBranch - else: - disp = ret - - body.add disp - body.flags.incl nfTransf # should not be further transformed - result.ast[bodyPos] = body - -proc containGenerics(base: PType, s: seq[tuple[depth: int, value: PType]]): bool = - result = tfHasMeta in base.flags - for i in s: - if tfHasMeta in i.value.flags: - result = true - break - -proc collectVTableDispatchers*(g: ModuleGraph) = - var itemTable = initTable[ItemId, seq[LazySym]]() - var rootTypeSeq = newSeq[PType]() - var rootItemIdCount = initCountTable[ItemId]() - for bucket in 0..<g.methods.len: - var relevantCols = initIntSet() - if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1) - sortBucket(g.methods[bucket].methods, relevantCols) - let base = g.methods[bucket].methods[^1] - let baseType = base.typ[1].skipTypes(skipPtrs-{tyTypeDesc}) - if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]): - let methodIndexLen = g.bucketTable[baseType.itemId] - if baseType.itemId notin itemTable: # once is enough - rootTypeSeq.add baseType - itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen) - - sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int = - if x.depth >= y.depth: 1 - else: -1 - ) - - for item in g.objectTree[baseType.itemId]: - if item.value.itemId notin itemTable: - itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen) - - var mIndex = 0 # here is the correpsonding index - if baseType.itemId notin rootItemIdCount: - rootItemIdCount[baseType.itemId] = 1 - else: - mIndex = rootItemIdCount[baseType.itemId] - rootItemIdCount.inc(baseType.itemId) - for idx in 0..<g.methods[bucket].methods.len: - let obj = g.methods[bucket].methods[idx].typ[1].skipTypes(skipPtrs) - itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx]) - g.addDispatchers genVTableDispatcher(g, g.methods[bucket].methods, mIndex) - else: # if the base object doesn't have this method - g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, g.idgen) - -proc sortVTableDispatchers*(g: ModuleGraph) = - var itemTable = initTable[ItemId, seq[LazySym]]() - var rootTypeSeq = newSeq[ItemId]() - var rootItemIdCount = initCountTable[ItemId]() - for bucket in 0..<g.methods.len: - var relevantCols = initIntSet() - if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1) - sortBucket(g.methods[bucket].methods, relevantCols) - let base = g.methods[bucket].methods[^1] - let baseType = base.typ[1].skipTypes(skipPtrs-{tyTypeDesc}) - if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]): - let methodIndexLen = g.bucketTable[baseType.itemId] - if baseType.itemId notin itemTable: # once is enough - rootTypeSeq.add baseType.itemId - itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen) - - sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int = - if x.depth >= y.depth: 1 - else: -1 - ) - - for item in g.objectTree[baseType.itemId]: - if item.value.itemId notin itemTable: - itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen) - - var mIndex = 0 # here is the correpsonding index - if baseType.itemId notin rootItemIdCount: - rootItemIdCount[baseType.itemId] = 1 - else: - mIndex = rootItemIdCount[baseType.itemId] - rootItemIdCount.inc(baseType.itemId) - for idx in 0..<g.methods[bucket].methods.len: - let obj = g.methods[bucket].methods[idx].typ[1].skipTypes(skipPtrs) - itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx]) - - for baseType in rootTypeSeq: - g.setMethodsPerType(baseType, itemTable[baseType]) - for item in g.objectTree[baseType]: - let typ = item.value.skipTypes(skipPtrs) - let idx = typ.itemId - for mIndex in 0..<itemTable[idx].len: - if itemTable[idx][mIndex].sym == nil: - let parentIndex = typ[0].skipTypes(skipPtrs).itemId - itemTable[idx][mIndex] = itemTable[parentIndex][mIndex] - g.setMethodsPerType(idx, itemTable[idx]) +import ast, modulegraphs, magicsys, lineinfos, options, cgmeth, types +import std/[algorithm, tables, intsets, assertions] + + + +proc genVTableDispatcher(g: ModuleGraph; methods: seq[PSym]; index: int): PSym = +#[ +proc dispatch(x: Base, params: ...) = + cast[proc bar(x: Base, params: ...)](x.vTable[index])(x, params) +]# + var base = methods[0].ast[dispatcherPos].sym + result = base + var paramLen = base.typ.signatureLen + var body = newNodeI(nkStmtList, base.info) + + var disp = newNodeI(nkIfStmt, base.info) + + var vTableAccess = newNodeIT(nkBracketExpr, base.info, base.typ) + let nimGetVTableSym = getCompilerProc(g, "nimGetVTable") + let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ + + var nTyp = base.typ.n[1].sym.typ + var dispatchObject = newSymNode(base.typ.n[1].sym) + if nTyp.kind == tyObject: + dispatchObject = newTree(nkAddr, dispatchObject) + else: + if g.config.backend != backendCpp: # TODO: maybe handle ptr? + if nTyp.kind == tyVar and nTyp.skipTypes({tyVar}).kind != tyObject: + dispatchObject = newTree(nkDerefExpr, dispatchObject) + + var getVTableCall = newTree(nkCall, + newSymNode(nimGetVTableSym), + dispatchObject, + newIntNode(nkIntLit, index) + ) + getVTableCall.typ = base.typ + var vTableCall = newNodeIT(nkCall, base.info, base.typ.returnType) + var castNode = newTree(nkCast, + newNodeIT(nkType, base.info, base.typ), + getVTableCall) + + castNode.typ = base.typ + vTableCall.add castNode + for col in 1..<paramLen: + let param = base.typ.n[col].sym + vTableCall.add newSymNode(param) + + var ret: PNode + if base.typ.returnType != nil: + var a = newNodeI(nkFastAsgn, base.info) + a.add newSymNode(base.ast[resultPos].sym) + a.add vTableCall + ret = newNodeI(nkReturnStmt, base.info) + ret.add a + else: + ret = vTableCall + + if base.typ.n[1].sym.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: + let ifBranch = newNodeI(nkElifBranch, base.info) + let boolType = getSysType(g, unknownLineInfo, tyBool) + var isNil = getSysMagic(g, unknownLineInfo, "isNil", mIsNil) + let checkSelf = newNodeIT(nkCall, base.info, boolType) + checkSelf.add newSymNode(isNil) + checkSelf.add newSymNode(base.typ.n[1].sym) + ifBranch.add checkSelf + ifBranch.add newTree(nkCall, + newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(base.typ.n[1].sym)) + let elseBranch = newTree(nkElifBranch, ret) + disp.add ifBranch + disp.add elseBranch + else: + disp = ret + + body.add disp + body.flags.incl nfTransf # should not be further transformed + result.ast[bodyPos] = body + +proc containGenerics(base: PType, s: seq[tuple[depth: int, value: PType]]): bool = + result = tfHasMeta in base.flags + for i in s: + if tfHasMeta in i.value.flags: + result = true + break + +proc collectVTableDispatchers*(g: ModuleGraph) = + var itemTable = initTable[ItemId, seq[LazySym]]() + var rootTypeSeq = newSeq[PType]() + var rootItemIdCount = initCountTable[ItemId]() + for bucket in 0..<g.methods.len: + var relevantCols = initIntSet() + if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1) + sortBucket(g.methods[bucket].methods, relevantCols) + let base = g.methods[bucket].methods[^1] + let baseType = base.typ.firstParamType.skipTypes(skipPtrs-{tyTypeDesc}) + if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]): + let methodIndexLen = g.bucketTable[baseType.itemId] + if baseType.itemId notin itemTable: # once is enough + rootTypeSeq.add baseType + itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen) + + sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int = + if x.depth >= y.depth: 1 + else: -1 + ) + + for item in g.objectTree[baseType.itemId]: + if item.value.itemId notin itemTable: + itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen) + + var mIndex = 0 # here is the correpsonding index + if baseType.itemId notin rootItemIdCount: + rootItemIdCount[baseType.itemId] = 1 + else: + mIndex = rootItemIdCount[baseType.itemId] + rootItemIdCount.inc(baseType.itemId) + for idx in 0..<g.methods[bucket].methods.len: + let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs) + itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx]) + g.addDispatchers genVTableDispatcher(g, g.methods[bucket].methods, mIndex) + else: # if the base object doesn't have this method + g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, g.idgen) + +proc sortVTableDispatchers*(g: ModuleGraph) = + var itemTable = initTable[ItemId, seq[LazySym]]() + var rootTypeSeq = newSeq[ItemId]() + var rootItemIdCount = initCountTable[ItemId]() + for bucket in 0..<g.methods.len: + var relevantCols = initIntSet() + if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1) + sortBucket(g.methods[bucket].methods, relevantCols) + let base = g.methods[bucket].methods[^1] + let baseType = base.typ.firstParamType.skipTypes(skipPtrs-{tyTypeDesc}) + if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]): + let methodIndexLen = g.bucketTable[baseType.itemId] + if baseType.itemId notin itemTable: # once is enough + rootTypeSeq.add baseType.itemId + itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen) + + sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int = + if x.depth >= y.depth: 1 + else: -1 + ) + + for item in g.objectTree[baseType.itemId]: + if item.value.itemId notin itemTable: + itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen) + + var mIndex = 0 # here is the correpsonding index + if baseType.itemId notin rootItemIdCount: + rootItemIdCount[baseType.itemId] = 1 + else: + mIndex = rootItemIdCount[baseType.itemId] + rootItemIdCount.inc(baseType.itemId) + for idx in 0..<g.methods[bucket].methods.len: + let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs) + itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx]) + + for baseType in rootTypeSeq: + g.setMethodsPerType(baseType, itemTable[baseType]) + for item in g.objectTree[baseType]: + let typ = item.value.skipTypes(skipPtrs) + let idx = typ.itemId + for mIndex in 0..<itemTable[idx].len: + if itemTable[idx][mIndex].sym == nil: + let parentIndex = typ.baseClass.skipTypes(skipPtrs).itemId + itemTable[idx][mIndex] = itemTable[parentIndex][mIndex] + g.setMethodsPerType(idx, itemTable[idx]) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 55a8921af..39e0b2e25 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -84,7 +84,7 @@ type wComputedGoto = "computedGoto", wExperimental = "experimental", wDoctype = "doctype", wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty", wInheritable = "inheritable", wThreadVar = "threadvar", wEmit = "emit", - wAsmNoStackFrame = "asmNoStackFrame", wImplicitStatic = "implicitStatic", + wAsmNoStackFrame = "asmNoStackFrame", wAsmSyntax = "asmSyntax", wImplicitStatic = "implicitStatic", wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked", wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", wSystemRaisesDefect = "systemRaisesDefect", diff --git a/config/config.nims b/config/config.nims index 4b8582d9c..b8979e8e3 100644 --- a/config/config.nims +++ b/config/config.nims @@ -20,4 +20,4 @@ when defined(nimStrictMode): switch("hintAsError", "ConvFromXtoItselfNotNeeded") # future work: XDeclaredButNotUsed -switch("define", "nimVersion:" & NimVersion) +switch("define", "nimVersion:" & NimVersion) # deadcode diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 9535aa384..99751f79d 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -226,12 +226,16 @@ doc.listing_end = "</pre>" doc.file = """<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>$title</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -255,9 +259,6 @@ doc.file = """<?xml version="1.0" encoding="utf-8" ?> </div> </div> $analytics - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> """ diff --git a/copying.txt b/copying.txt index ae3a162a7..819330be3 100644 --- a/copying.txt +++ b/copying.txt @@ -1,7 +1,7 @@ ===================================================== Nim -- a Compiler for Nim. https://nim-lang.org/ -Copyright (C) 2006-2023 Andreas Rumpf. All rights reserved. +Copyright (C) 2006-2024 Andreas Rumpf. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/doc/backends.md b/doc/backends.md index 27b654890..9f0c54835 100644 --- a/doc/backends.md +++ b/doc/backends.md @@ -250,6 +250,8 @@ which will likely make your program crash at runtime. The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch. Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`. +When compiling to static or dynamic libraries, they don't call destructors of global variables as normal Nim programs would do. A C API `NimDestroyGlobals` is provided to call these global destructors. + ### Nim invocation example from C @@ -371,11 +373,7 @@ The manual mentions that [Nim strings are implicitly convertible to cstrings](manual.html#types-cstring-type) which makes interaction usually painless. Most C functions accepting a Nim string converted to a `cstring` will likely not need to keep this string around and by the time -they return the string won't be needed anymore. However, for the rare cases -where a Nim string has to be preserved and made available to the C backend -as a `cstring`, you will need to manually prevent the string data -from being freed with [GC_ref](system.html#GC_ref,string) and [GC_unref]( -system.html#GC_unref,string). +they return the string won't be needed anymore. A similar thing happens with C code invoking Nim code which returns a `cstring`. Consider the following proc: diff --git a/doc/grammar.txt b/doc/grammar.txt index f1484bb0b..51b3e0053 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -187,7 +187,7 @@ objectCase = 'case' declColonEquals ':'? COMMENT? objectPart = IND{>} objectPart^+IND{=} DED / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart -conceptParam = ('var' | 'out')? symbol +conceptParam = ('var' | 'out' | 'ptr' | 'ref' | 'static' | 'type')? symbol conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? &IND{>} stmt typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue diff --git a/doc/lib.md b/doc/lib.md index a76b29151..1507bbaac 100644 --- a/doc/lib.md +++ b/doc/lib.md @@ -578,7 +578,7 @@ Modules for the JavaScript backend The wrapper of core JavaScript functions. For most purposes, you should be using the `math`, `json`, and `times` stdlib modules instead of this module. -* [jsfetch](jshttp.html) +* [jsfetch](jsfetch.html) Wrapper for `fetch`. * [jsffi](jsffi.html) diff --git a/doc/manual.md b/doc/manual.md index 0e167be04..5c36a0a7b 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -267,7 +267,7 @@ You can also use the [discard statement](#statements-and-expressions-discard-sta ``` This was how multiline comments were done before version 0.13.0, -and it is used to provide specifications to [testament](testament.html#writing-unitests) test framework. +and it is used to provide specifications to [testament](testament.html#writing-unit-tests) test framework. Identifiers & Keywords @@ -2619,13 +2619,20 @@ An expression `b` can be assigned to an expression `a` iff `a` is an Overload resolution =================== -In a call `p(args)` the routine `p` that matches best is selected. If -multiple routines match equally well, the ambiguity is reported during -semantic analysis. +In a call `p(args)` where `p` may refer to more than one +candidate, it is said to be a symbol choice. Overload resolution will attempt to +find the best candidate, thus transforming the symbol choice into a resolved symbol. +The routine `p` that matches best is selected following a series of trials explained below. +In order: Catagory matching, Hierarchical Order Comparison, and finally, Complexity Analysis. -Every arg in args needs to match. There are multiple different categories how an -argument can match. Let `f` be the formal parameter's type and `a` the type -of the argument. +If multiple candidates match equally well after all trials have been tested, the ambiguity +is reported during semantic analysis. + +First Trial: Catagory matching +-------------------------------- + +Every arg in `args` needs to match and there are multiple different categories of matches. +Let `f` be the formal parameter's type and `a` the type of the argument. 1. Exact match: `a` and `f` are of the same type. 2. Literal match: `a` is an integer literal of value `v` @@ -2643,16 +2650,16 @@ of the argument. 6. Conversion match: `a` is convertible to `f`, possibly via a user defined `converter`. +Each operand may fall into one of the categories above; the operand's +highest priority category. The list above is in order or priority. +If a candidate has more priority matches than all other candidates, it is selected as the +resolved symbol. -There are two major methods of selecting the best matching candidate, namely -counting and disambiguation. Counting takes precedence to disambiguation. In counting, -each parameter is given a category and the number of parameters in each category is counted. -The categories are listed above and are in order of precedence. For example, if -a candidate with one exact match is compared to a candidate with multiple generic matches -and zero exact matches, the candidate with an exact match will win. +For example, if a candidate with one exact match is compared to a candidate with multiple +generic matches and zero exact matches, the candidate with an exact match will win. -In the following, `count(p, m)` counts the number of matches of the matching category `m` -for the routine `p`. +Below is a pseudocode interpretation of category matching, `count(p, m)` counts the number +of matches of the matching category `m` for the routine `p`. A routine `p` matches better than a routine `q` if the following algorithm returns true: @@ -2669,13 +2676,51 @@ algorithm returns true: return "ambiguous" ``` -When counting is ambiguous, disambiguation begins. Parameters are iterated -by position and these parameter pairs are compared for their type relation. The general goal -of this comparison is to determine which parameter is more specific. The types considered are -not of the inputs from the callsite, but of the competing candidates' parameters. +Second Trial: Hierarchical Order Comparison +---------------------------------------------- + +The hierarchical order of a type is analogous to its relative specificity. Consider the type defined: + +```nim +type A[T] = object +``` + +Matching formals for this type include `T`, `object`, `A`, `A[...]` and `A[C]` where `C` is a concrete type, `A[...]` +is a generic typeclass composition and `T` is an unconstrained generic type variable. This list is in order of +specificity with respect to `A` as each subsequent category narrows the set of types that are members of their match set. + +In this trail, the formal parameters of candidates are compared in order (1st parameter, 2nd parameter, etc.) to search for +a candidate that has an unrivaled specificity. If such a formal parameter is found, the candidate it belongs to is chosen +as the resolved symbol. + +Third Trial: Complexity Analysis +---------------------------------- + +A slight clarification: While category matching digests all the formal parameters of a candidate at once (order doesn't matter), +specificity comparison and complexity analysis operate on each formal parameter at a time. The following +is the final trial to disambiguate a symbol choice when a pair of formal parameters have the same hierarchical order. + +The complexity of a type is essentially its number of modifiers and depth of shape. The definition with the *highest* +complexity wins. Consider the following types: + +```nim +type + A[T] = object + B[T, H] = object +``` + +Note: The below examples are not exhaustive. + +We shall say that: +1. `A[T]` has a higher complexity than `A` +2. `var A[T]` has a higher complexity than `A[T]` +3. `A[A[T]]` has a higher complexity than `A[T]` +4. `B[T, H]` has a higher complexity than `A[T]` (`A` and `B` are not compatible here, but convoluted versions of this exist) +5. `B[ptr T, H]` has a higher complexity than `B[T, H]` -Some examples: +Some Examples +--------------- ```nim proc takesInt(x: int) = echo "int" @@ -2692,7 +2737,6 @@ Some examples: ``` -If this algorithm returns "ambiguous" further disambiguation is performed: If the argument `a` matches both the parameter type `f` of `p` and `g` of `q` via a subtyping relation, the inheritance depth is taken into account: @@ -2734,6 +2778,23 @@ matches) is preferred: gen(ri) # "ref T" ``` +Type variables match +---------------------- + +When overload resolution is considering candidates, the type variable's definition +is not overlooked as it is used to define the formal parameter's type via variable substitution. + +For example: +```nim +type A +proc p[T: A](param: T) +proc p[T: object](param: T) +``` + +These signatures are not ambiguous for a concrete type of `A` even though the formal parameters match ("T" == "T"). +Instead `T` is treated as a variable in that (`T` ?= `T`) depending on the bound type of `T` at the time of +overload resolution. + Overloading based on 'var T' -------------------------------------- @@ -3159,6 +3220,15 @@ A const section declares constants whose values are constant expressions: Once declared, a constant's symbol can be used as a constant expression. +The value part of a constant declaration opens a new scope for each constant, +so no symbols declared in the constant value are accessible outside of it. + + ```nim + const foo = (var a = 1; a) + const bar = a # error + let baz = a # error + ``` + See [Constants and Constant Expressions] for details. Static statement/expression @@ -3727,9 +3797,6 @@ type conversions to unsigned integers and between unsigned integers. The rationale for this is mostly better interoperability with the C Programming language when algorithms are ported from C to Nim. -Exception: Values that are converted to an unsigned type at compile time -are checked so that code like `byte(-1)` does not compile. - **Note**: Historically the operations were unchecked and the conversions were sometimes checked but starting with the revision 1.0.4 of this document and the language implementation the @@ -4396,7 +4463,42 @@ as an example: Overloading of the subscript operator ------------------------------------- -The `[]` subscript operator for arrays/openarrays/sequences can be overloaded. +The `[]` subscript operator for arrays/openarrays/sequences can be overloaded +for any type (with some exceptions) by defining a routine with the name `[]`. + + ```nim + type Foo = object + data: seq[int] + + proc `[]`(foo: Foo, i: int): int = + result = foo.data[i] + + let foo = Foo(data: @[1, 2, 3]) + echo foo[1] # 2 + ``` + +Assignment to subscripts can also be overloaded by naming a routine `[]=`, +which has precedence over assigning to the result of `[]`. + + ```nim + type Foo = object + data: seq[int] + + proc `[]`(foo: Foo, i: int): int = + result = foo.data[i] + proc `[]=`(foo: var Foo, i: int, val: int) = + foo.data[i] = val + + var foo = Foo(data: @[1, 2, 3]) + echo foo[1] # 2 + foo[1] = 5 + echo foo.data # @[1, 5, 3] + echo foo[1] # 5 + ``` + +Overloads of the subscript operator cannot be applied to routine or type +symbols themselves, as this conflicts with the syntax for instantiating +generic parameters, i.e. `foo[int](1, 2, 3)` or `Foo[int]`. Methods @@ -4612,7 +4714,6 @@ Closure iterators and inline iterators have some restrictions: (but rarely useful) and ends the iteration. 3. Inline iterators cannot be recursive. 4. Neither inline nor closure iterators have the special `result` variable. -5. Closure iterators are not supported by the JS backend. Iterators that are neither marked `{.closure.}` nor `{.inline.}` explicitly default to being inline, but this may change in future versions of the @@ -5359,6 +5460,8 @@ To override the compiler's side effect analysis a `{.noSideEffect.}` **Side effects are usually inferred. The inference for side effects is analogous to the inference for exception tracking.** +When the compiler cannot infer side effects, as is the case for imported +functions, one can annotate them with the `sideEffect` pragma. GC safety effect ---------------- @@ -5424,6 +5527,7 @@ Generics are Nim's means to parametrize procs, iterators or types with `type parameters`:idx:. Depending on the context, the brackets are used either to introduce type parameters or to instantiate a generic proc, iterator, or type. + The following example shows how a generic binary tree can be modeled: ```nim test = "nim c $1" @@ -5801,7 +5905,7 @@ at definition and the context at instantiation are considered: echo a == b # works! ``` -In the example, the generic `==` for tuples (as defined in the system module) +In the example, the [generic `==` for tuples](system.html#%3D%3D%2CT%2CT_2) (as defined in the system module) uses the `==` operators of the tuple's components. However, the `==` for the `Index` type is defined *after* the `==` for tuples; yet the example compiles as the instantiation takes the currently defined symbols into account @@ -6153,9 +6257,12 @@ scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas: `gensym`'ed symbols are not exposed but `inject`'ed symbols are. The default for symbols of entity `type`, `var`, `let` and `const` -is `gensym` and for `proc`, `iterator`, `converter`, `template`, -`macro` is `inject`. However, if the name of the entity is passed as a -template parameter, it is an `inject`'ed symbol: +is `gensym`. For `proc`, `iterator`, `converter`, `template`, +`macro`, the default is `inject`, but if a `gensym` symbol with the same name +is defined in the same syntax-level scope, it will be `gensym` by default. +This can be overriden by marking the routine as `inject`. + +If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol: ```nim template withFile(f, fn, mode: untyped, actions: untyped): untyped = @@ -6956,25 +7063,47 @@ All identifiers of a module are valid from the point of declaration until the end of the module. Identifiers from indirectly dependent modules are *not* available. The `system`:idx: module is automatically imported in every module. -If a module imports an identifier by two different modules, each occurrence of -the identifier has to be qualified unless it is an overloaded procedure or -iterator in which case the overloading resolution takes place: +If a module imports the same identifier from two different modules, the +identifier is considered ambiguous, which can be resolved in the following ways: + +* Qualifying the identifier as `module.identifier` resolves ambiguity + between modules. (See below for the case that the module name itself + is ambiguous.) +* Calling the identifier as a routine makes overload resolution take place, + which resolves ambiguity in the case that one overload matches stronger + than the others. +* Using the identifier in a context where the compiler can infer the type + of the identifier resolves ambiguity in the case that one definition + matches the type stronger than the others. ```nim # Module A var x*: string + proc foo*(a: string) = + echo "A: ", a ``` ```nim # Module B var x*: int + proc foo*(b: int) = + echo "B: ", b ``` ```nim # Module C import A, B + + foo("abc") # A: abc + foo(123) # B: 123 + let inferred: proc (x: string) = foo + foo("def") # A: def + write(stdout, x) # error: x is ambiguous write(stdout, A.x) # no error: qualifier used + + proc bar(a: int): int = a + 1 + assert bar(x) == x + 1 # no error: only A.x of type int matches var x = 4 write(stdout, x) # not ambiguous: uses the module C's x @@ -6998,7 +7127,7 @@ proc fb* = echo "buzz" import A/C import B/C -C.fb() # Error: ambiguous identifier: 'fb' +C.fb() # Error: ambiguous identifier: 'C' ``` @@ -8553,8 +8682,62 @@ Byref pragma The `byref` pragma can be applied to an object or tuple type or a proc param. When applied to a type it instructs the compiler to pass the type by reference (hidden pointer) to procs. When applied to a param it will take precedence, even -if the the type was marked as `bycopy`. When using the Cpp backend, params marked -as byref will translate to cpp references `&`. +if the the type was marked as `bycopy`. When an `importc` type has a `byref` pragma or +parameters are marked as `byref` in an `importc` proc, these params translate to pointers. +When an `importcpp` type has a `byref` pragma, these params translate to +C++ references `&`. + + ```Nim + {.emit: """/*TYPESECTION*/ + typedef struct { + int x; + } CStruct; + """.} + + {.emit: """ + #ifdef __cplusplus + extern "C" + #endif + int takesCStruct(CStruct* x) { + return x->x; + } + """.} + + type + CStruct {.importc, byref.} = object + x: cint + + proc takesCStruct(x: CStruct): cint {.importc.} + ``` + + or + + + ```Nim + type + CStruct {.importc.} = object + x: cint + + proc takesCStruct(x {.byref.}: CStruct): cint {.importc.} + ``` + + ```Nim + {.emit: """/*TYPESECTION*/ + struct CppStruct { + int x; + + int takesCppStruct(CppStruct& y) { + return x + y.x; + } + }; + """.} + + type + CppStruct {.importcpp, byref.} = object + x: cint + + proc takesCppStruct(x, y: CppStruct): cint {.importcpp.} + ``` Varargs pragma -------------- @@ -8564,7 +8747,7 @@ after the last specified parameter. Nim string values will be converted to C strings automatically: ```Nim - proc printf(formatstr: cstring) {.nodecl, varargs.} + proc printf(formatstr: cstring) {.header: "<stdio.h>", varargs.} printf("hallo %s", "world") # "world" will be passed as C string ``` diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index d20695933..da51d59ad 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -560,12 +560,14 @@ Not nil annotation `{.experimental: "notnil".}`. All types for which `nil` is a valid value can be annotated with the -`not nil` annotation to exclude `nil` as a valid value: +`not nil` annotation to exclude `nil` as a valid value. Note that only local +symbols are checked. ```nim {.experimental: "notnil".} type + TObj = object PObject = ref TObj not nil TProc = (proc (x, y: int)) not nil @@ -576,8 +578,11 @@ All types for which `nil` is a valid value can be annotated with the p(nil) # and also this: - var x: PObject - p(x) + proc foo = + var x: PObject + p(x) + + foo() ``` The compiler ensures that every code path initializes variables which contain @@ -2445,7 +2450,7 @@ main() Will produce: -```c++ +```cpp struct Test { Foo foo; @@ -2515,3 +2520,150 @@ NimFunctor()(1) ``` Notice we use the overload of `()` to have the same semantics in Nim, but on the `importcpp` we import the functor as a function. This allows to easy interop with functions that accepts for example a `const` operator in its signature. + + +Injected symbols in generic procs and templates +=============================================== + +With the experimental option `openSym`, captured symbols in generic routine and +template bodies may be replaced by symbols injected locally by templates/macros +at instantiation time. `bind` may be used to keep the captured symbols over the +injected ones regardless of enabling the options, but other methods like +renaming the captured symbols should be used instead so that the code is not +affected by context changes. + +Since this change may affect runtime behavior, the experimental switch +`openSym` needs to be enabled; and a warning is given in the case where an +injected symbol would replace a captured symbol not bound by `bind` and +the experimental switch isn't enabled. + +```nim +const value = "captured" +template foo(x: int, body: untyped): untyped = + let value {.inject.} = "injected" + body + +proc old[T](): string = + foo(123): + return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym` +echo old[int]() # "captured" + +template oldTempl(): string = + block: + foo(123): + value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym` +echo oldTempl() # "captured" + +{.experimental: "openSym".} + +proc bar[T](): string = + foo(123): + return value +assert bar[int]() == "injected" # previously it would be "captured" + +proc baz[T](): string = + bind value + foo(123): + return value +assert baz[int]() == "captured" + +template barTempl(): string = + block: + foo(123): + value +assert barTempl() == "injected" # previously it would be "captured" + +template bazTempl(): string = + bind value + block: + foo(123): + value +assert bazTempl() == "captured" +``` + +This option also generates a new node kind `nnkOpenSym` which contains +exactly 1 `nnkSym` node. In the future this might be merged with a slightly +modified `nnkOpenSymChoice` node but macros that want to support the +experimental feature should still handle `nnkOpenSym`, as the node kind would +simply not be generated as opposed to being removed. + +Another experimental switch `genericsOpenSym` exists that enables this behavior +at instantiation time, meaning templates etc can enable it specifically when +they are being called. However this does not generate `nnkOpenSym` nodes +(unless the other switch is enabled) and so doesn't reflect the regular +behavior of the switch. + +```nim +const value = "captured" +template foo(x: int, body: untyped): untyped = + let value {.inject.} = "injected" + {.push experimental: "genericsOpenSym".} + body + {.pop.} + +proc bar[T](): string = + foo(123): + return value +echo bar[int]() # "injected" + +template barTempl(): string = + block: + var res: string + foo(123): + res = value + res +assert barTempl() == "injected" +``` + + +VTable for methods +================== + +Methods now support implementations based on a VTable by using `--experimental:vtables`. Note that the option needs to enabled +globally. The virtual method table is stored in the type info of +an object, which is an array of function pointers. + +```nim +method foo(x: Base, ...) {.base.} +method foo(x: Derived, ...) {.base.} +``` + +It roughly generates a dispatcher like + +```nim +proc foo_dispatch(x: Base, ...) = + x.typeinfo.vtable[method_index](x, ...) # method_index is the index of the sorted order of a method +``` + +Methods are required to be in the same module where their type has been defined. + +```nim +# types.nim +type + Base* = ref object +``` + +```nim +import types + +method foo(x: Base) {.base.} = discard +``` + +It gives an error: method `foo` can be defined only in the same module with its type (Base). + + +asmSyntax pragma +================ + +The `asmSyntax` pragma is used to specify target inline assembler syntax in an `asm` statement. + +It prevents compiling code with different of the target CC inline asm syntax, i.e. it will not allow gcc inline asm code to be compiled with vcc. + +```nim +proc nothing() = + asm {.asmSyntax: "gcc".}""" + nop + """ +``` + +The current C(C++) backend implementation cannot generate code for gcc and for vcc at the same time. For example, `{.asmSyntax: "vcc".}` with the ICC compiler will not generate code with intel asm syntax, even though ICC can use both gcc-like and vcc-like asm. diff --git a/doc/nep1.md b/doc/nep1.md index 0c62e3f9c..3d2a0cef3 100644 --- a/doc/nep1.md +++ b/doc/nep1.md @@ -256,36 +256,46 @@ Coding Conventions Conventions for multi-line statements and expressions ----------------------------------------------------- -- Tuples which are longer than one line should indent their parameters to - align with the parameters above it. +- Tuples which are longer than one line should indent their parameters. ```nim type - LongTupleA = tuple[wordyTupleMemberOne: int, wordyTupleMemberTwo: string, - wordyTupleMemberThree: float] + LongTupleA = tuple[ + wordyTupleMemberOne: int, wordyTupleMemberTwo: string, + wordyTupleMemberThree: float] ``` - Similarly, any procedure and procedure type declarations that are longer - than one line should do the same thing. + than one line should do the same thing. Double indent may be used to + distinguish them from the body that follows - this applies to all constructs + with a body (if, while, etc). ```nim type - EventCallback = proc (timeReceived: Time, errorCode: int, event: Event, - output: var string) - - proc lotsOfArguments(argOne: string, argTwo: int, argThree: float, - argFour: proc(), argFive: bool): int - {.heyLookALongPragma.} = + EventCallback = proc( + timeReceived: Time, errorCode: int, event: Event, + output: var string) + + proc lotsOfArguments( + argOne: string, argTwo: int, argThree: float, + argFour: proc(), argFive: bool, argSix: int + ): GenericType[int, string] {.heyLookALongPragma.} = + discard ``` -- Multi-line procedure calls should continue on the same column as the opening - parenthesis (like multi-line procedure declarations). +- Multi-line procedure calls should continue indented (like multi-line procedure + declarations). ```nim - startProcess(nimExecutable, currentDirectory, compilerArguments - environment, processOptions) + startProcess( + nimExecutable, currentDirectory, compilerArguments + environment, processOptions) ``` +Previous versions of this guide advocated vertical alignment along the opening +brace / parenthesis - both styles are permissible with a preference for the +current style in new code. + Miscellaneous ------------- diff --git a/doc/nimc.md b/doc/nimc.md index 08bd016e1..38558454b 100644 --- a/doc/nimc.md +++ b/doc/nimc.md @@ -481,6 +481,28 @@ They are: 5. nl_types. No headers for this. 6. As mmap is not supported, the nimAllocPagesViaMalloc option has to be used. +GPU Compilation +=============== + +Compiling for GPU computation can be achieved with `--cc:nvcc` for CUDA with nvcc, or with `--cc:hipcc` for AMD GPUs with HIP. Both compilers require building for C++ with `nim cpp`. + +Here's a very simple CUDA kernel example using emit, which can be compiled with `nim cpp --cc:nvcc --define:"useMalloc" hello_kernel.nim` assuming you have the CUDA toolkit installed. + +```nim +{.emit: """ +__global__ void add(int a, int b) { + int c; + c = a + b; +} +""".} + +proc main() = + {.emit: """ + add<<<1,1>>>(2,7); + """.} + +main() +``` DLL generation ============== @@ -503,9 +525,6 @@ To link against ``nimrtl.dll`` use the command: nim c -d:useNimRtl myprog.nim ``` -**Note**: Currently the creation of ``nimrtl.dll`` with thread support has -never been tested and is unlikely to work! - Additional compilation switches =============================== @@ -552,6 +571,13 @@ Define Effect `globalSymbols` Load all `{.dynlib.}` libraries with the `RTLD_GLOBAL`:c: flag on Posix systems to resolve symbols in subsequently loaded libraries. +`lto` Enable link-time optimization in the backend compiler and + linker. +`lto_incremental` Enable link-time optimization and additionally enable + incremental linking for compilers that support it. + Currently only clang and vcc. +`strip` Strip debug symbols added by the backend compiler from + the executable. ====================== ========================================================= @@ -677,11 +703,11 @@ additional flags to both the Nim compiler and the C compiler and/or linker to optimize the build for size. For example, the following flags can be used when targeting a gcc compiler: -`--opt:size --passC:-flto --passL:-flto`:option: +`--opt:size -d:lto -d:strip`:option: The `--opt:size`:option: flag instructs Nim to optimize code generation for small -size (with the help of the C compiler), the `-flto`:option: flags enable link-time -optimization in the compiler and linker. +size (with the help of the C compiler), the `-d:lto`:option: flags enable link-time +optimization in the compiler and linker, the `-d:strip`:option: strips debug symbols. Check the [Cross-compilation] section for instructions on how to compile the program for your target. diff --git a/doc/nimdoc.css b/doc/nimdoc.css index a9e4ac9c6..0c399e4c1 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -623,8 +623,8 @@ pre { table.line-nums-table { border-radius: 4px; - border: 1px solid #cccccc; - background-color: ghostwhite; + border: 1px solid var(--border); + background-color: var(--secondary-background); border-collapse: separate; margin-top: 15px; margin-bottom: 25px; } @@ -660,6 +660,9 @@ table { border-collapse: collapse; border-color: var(--third-background); border-spacing: 0; +} + +table:not(.line-nums-table) { font-size: 0.9em; } @@ -678,7 +681,7 @@ table th.docinfo-name { text-align: right; } -table tr:hover { +table:not(.line-nums-table) tr:hover { background-color: var(--third-background); } diff --git a/doc/nims.md b/doc/nims.md index 42cc6e124..987cc2096 100644 --- a/doc/nims.md +++ b/doc/nims.md @@ -61,43 +61,44 @@ Standard library modules At least the following standard library modules are available: -* [macros](macros.html) -* [os](os.html) -* [strutils](strutils.html) -* [math](math.html) -* [distros](distros.html) -* [sugar](sugar.html) * [algorithm](algorithm.html) * [base64](base64.html) * [bitops](bitops.html) * [chains](chains.html) * [colors](colors.html) * [complex](complex.html) +* [distros](distros.html) +* [std/editdistance](editdistance.html) * [htmlgen](htmlgen.html) +* [htmlparser](htmlparser.html) * [httpcore](httpcore.html) +* [json](json.html) * [lenientops](lenientops.html) +* [macros](macros.html) +* [math](math.html) * [options](options.html) +* [os](os.html) +* [parsecfg](parsecfg.html) +* [parsecsv](parsecsv.html) +* [parsejson](parsejson.html) +* [parsesql](parsesql.html) * [parseutils](parseutils.html) * [punycode](punycode.html) * [random](random.html) +* [ropes](ropes.html) +* [std/setutils](setutils.html) * [stats](stats.html) * [strformat](strformat.html) * [strmisc](strmisc.html) * [strscans](strscans.html) +* [strtabs](strtabs.html) +* [strutils](strutils.html) +* [sugar](sugar.html) * [unicode](unicode.html) +* [unidecode](unidecode.html) * [uri](uri.html) -* [std/editdistance](editdistance.html) * [std/wordwrap](wordwrap.html) -* [parsecsv](parsecsv.html) -* [parsecfg](parsecfg.html) -* [parsesql](parsesql.html) * [xmlparser](xmlparser.html) -* [htmlparser](htmlparser.html) -* [ropes](ropes.html) -* [json](json.html) -* [parsejson](parsejson.html) -* [strtabs](strtabs.html) -* [unidecode](unidecode.html) In addition to the standard Nim syntax ([system](system.html) module), NimScripts support the procs and templates defined in the diff --git a/doc/tut2.md b/doc/tut2.md index 4b9049082..1b59288d5 100644 --- a/doc/tut2.md +++ b/doc/tut2.md @@ -166,7 +166,7 @@ An example: n.strVal = "" ``` -As can been seen from the example, an advantage to an object hierarchy is that +As can be seen from the example, an advantage to an object hierarchy is that no conversion between different object types is needed. Yet, access to invalid object fields raises an exception. diff --git a/doc/tut3.md b/doc/tut3.md index 67f49c879..3a55d4790 100644 --- a/doc/tut3.md +++ b/doc/tut3.md @@ -322,6 +322,36 @@ used to get this output. raise newException(AssertionDefect, $a & " != " & $b) ``` + +Going further +------------- + +It is possible to create more complex macros by combining different +`NimNode` symbols with `quote do:` expressions. For example, you may +use `newStmtList` to build your macro iteratively, and `ident` in cases +in which you wish to create an identifier from a string, as shown below. + + ```nim + import std/macros + + macro createProcedures() = + result = newStmtList() + + for i in 0..<10: + let name = ident("myProc" & $i) + let content = newLit("I am procedure number #" & $i) + + result.add quote do: + proc `name`() = + echo `content` + + createProcedures() + myProc7() + ``` + +The call to `myProc7` will echo `I am procedure number #7`. + + With Power Comes Responsibility ------------------------------- diff --git a/koch.nim b/koch.nim index 708b65ab6..77bc2299f 100644 --- a/koch.nim +++ b/koch.nim @@ -1,19 +1,20 @@ # # # Maintenance program for Nim -# (c) Copyright 2017 Andreas Rumpf +# (c) Copyright 2024 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# See doc/koch.txt for documentation. +# See doc/koch.md for documentation. # const # examples of possible values for repos: Head, ea82b54 - NimbleStableCommit = "a1fdbe8912a0e3dfd30cef030bbabef218d84687" # master - AtlasStableCommit = "7b780811a168f3f32bff4822369dda46a7f87f9a" - ChecksumsStableCommit = "025bcca3915a1b9f19878cea12ad68f9884648fc" + NimbleStableCommit = "4fb6f8e6c33963f6f510fe82d09ad2a61b5e4265" # 0.16.1 + AtlasStableCommit = "5faec3e9a33afe99a7d22377dd1b45a5391f5504" + ChecksumsStableCommit = "bd9bf4eaea124bf8d01e08f92ac1b14c6879d8d3" + SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01" # examples of possible values for fusion: #head, #ea82b54, 1.2.3 FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014" @@ -51,7 +52,7 @@ const +-----------------------------------------------------------------+ | Maintenance program for Nim | | Version $1| -| (c) 2017 Andreas Rumpf | +| (c) 2024 Andreas Rumpf | +-----------------------------------------------------------------+ Build time: $2, $3 @@ -76,6 +77,7 @@ Possible Commands: doesn't require network connectivity nimble builds the Nimble tool atlas builds the Atlas tool + checksums installs the checksums dependency fusion installs fusion via Nimble Boot options: @@ -159,17 +161,21 @@ proc bundleNimbleExe(latest: bool, args: string) = commit = commit, allowBundled = true) cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/checksums.git", commit = ChecksumsStableCommit, allowBundled = true) # or copy it from dist? + cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/sat.git", + commit = SatStableCommit, allowBundled = true) # installer.ini expects it under $nim/bin nimCompile("dist/nimble/src/nimble.nim", - options = "-d:release --noNimblePath " & args) + options = "-d:release -d:nimNimbleBootstrap --noNimblePath " & args) proc bundleAtlasExe(latest: bool, args: string) = let commit = if latest: "HEAD" else: AtlasStableCommit cloneDependency(distDir, "https://github.com/nim-lang/atlas.git", commit = commit, allowBundled = true) + cloneDependency(distDir / "atlas" / distDir, "https://github.com/nim-lang/sat.git", + commit = SatStableCommit, allowBundled = true) # installer.ini expects it under $nim/bin nimCompile("dist/atlas/src/atlas.nim", - options = "-d:release --noNimblePath " & args) + options = "-d:release --noNimblePath -d:nimAtlasBootstrap " & args) proc bundleNimsuggest(args: string) = nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim", @@ -339,8 +345,7 @@ proc boot(args: string, skipIntegrityCheck: bool) = let smartNimcache = (if "release" in args or "danger" in args: "nimcache/r_" else: "nimcache/d_") & hostOS & "_" & hostCPU - if not dirExists("dist/checksums"): - bundleChecksums(false) + bundleChecksums(false) let usingLibFFI = "nimHasLibFFI" in args if usingLibFFI and not dirExists("dist/libffi"): @@ -503,8 +508,7 @@ proc temp(args: string) = result[1].add " " & quoteShell(args[i]) inc i - if not dirExists("dist/checksums"): - bundleChecksums(false) + bundleChecksums(false) let d = getAppDir() let output = d / "compiler" / "nim".exe diff --git a/lib/core/locks.nim b/lib/core/locks.nim index ad0bff44d..523727479 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -37,7 +37,7 @@ proc initLock*(lock: var Lock) {.inline.} = when not defined(js): initSysLock(lock) -proc deinitLock*(lock: var Lock) {.inline.} = +proc deinitLock*(lock: Lock) {.inline.} = ## Frees the resources associated with the lock. deinitSys(lock) @@ -60,7 +60,7 @@ proc initCond*(cond: var Cond) {.inline.} = ## Initializes the given condition variable. initSysCond(cond) -proc deinitCond*(cond: var Cond) {.inline.} = +proc deinitCond*(cond: Cond) {.inline.} = ## Frees the resources associated with the condition variable. deinitSysCond(cond) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 01a654b6c..7646b165c 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -93,6 +93,8 @@ type nnkFuncDef, nnkTupleConstr, nnkError, ## erroneous AST node + nnkModuleRef, nnkReplayAction, nnkNilRodNode ## internal IC nodes + nnkOpenSym NimNodeKinds* = set[NimNodeKind] NimTypeKind* = enum # some types are no longer used, see ast.nim @@ -277,7 +279,7 @@ when (NimMajor, NimMinor, NimPatch) >= (1, 3, 5) or defined(nimSymImplTransform) ## note that code transformations are implementation dependent and subject to change. ## See an example in `tests/macros/tmacros_various.nim`. -proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.} +proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect, deprecated.} ## Accepts a node of kind `nnkSym` and returns its owner's symbol. ## The meaning of 'owner' depends on `sym`'s `NimSymKind` and declaration ## context. For top level declarations this is an `nskModule` symbol, @@ -347,8 +349,7 @@ proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} = newLit(x.getTypeImpl.repr) let t = """ object - arr: array[0 .. 3, float32] -""" + arr: array[0 .. 3, float32]""" doAssert(dumpTypeImpl(a) == t) doAssert(dumpTypeImpl(b) == t) doAssert(dumpTypeImpl(c) == t) @@ -427,7 +428,12 @@ proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} = let x = 12 echo x -proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.} +when defined(nimHasNoReturnError): + {.pragma: errorNoReturn, noreturn.} +else: + {.pragma: errorNoReturn.} + +proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign, errorNoReturn.} ## Writes an error message at compile time. The optional `n: NimNode` ## parameter is used as the source for file and line number information in ## the compilation error message. @@ -1403,7 +1409,7 @@ proc `$`*(node: NimNode): string = result = node.basename.strVal & "*" of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent: result = node.strVal - of nnkOpenSymChoice, nnkClosedSymChoice: + of nnkOpenSymChoice, nnkClosedSymChoice, nnkOpenSym: result = $node[0] of nnkAccQuoted: result = "" diff --git a/lib/core/rlocks.nim b/lib/core/rlocks.nim index bee5c1655..8cb0cef05 100644 --- a/lib/core/rlocks.nim +++ b/lib/core/rlocks.nim @@ -31,7 +31,7 @@ proc initRLock*(lock: var RLock) {.inline.} = else: initSysLock(lock) -proc deinitRLock*(lock: var RLock) {.inline.} = +proc deinitRLock*(lock: RLock) {.inline.} = ## Frees the resources associated with the lock. deinitSys(lock) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 5075c8458..f2fee91c4 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -129,7 +129,9 @@ when not defined(gcDestructors): else: proc nimNewObj(size, align: int): pointer {.importCompilerProc.} proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc.} - proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. + proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. + importCompilerProc.} + proc zeroNewElements(len: int; p: pointer; addlen, elemSize, elemAlign: int) {. importCompilerProc.} template `+!!`(a, b): untyped = cast[pointer](cast[int](a) + b) @@ -159,16 +161,6 @@ proc newAny(value: pointer, rawType: PNimType): Any {.inline.} = result.value = value result.rawType = rawType -when declared(system.VarSlot): - proc toAny*(x: VarSlot): Any {.inline.} = - ## Constructs an `Any` object from a variable slot `x`. - ## This captures `x`'s address, so `x` can be modified with its - ## `Any` wrapper! The caller needs to ensure that the wrapper - ## **does not** live longer than `x`! - ## This is provided for easier reflection capabilities of a debugger. - result.value = x.address - result.rawType = x.typ - proc toAny*[T](x: var T): Any {.inline.} = ## Constructs an `Any` object from `x`. This captures `x`'s address, so ## `x` can be modified with its `Any` wrapper! The caller needs to ensure @@ -221,7 +213,8 @@ proc extendSeq*(x: Any) = var s = cast[ptr NimSeqV2Reimpl](x.value) let elem = x.rawType.base if s.p == nil or s.p.cap < s.len+1: - s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align)) + s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAddUninit(s.len, s.p, 1, elem.size, elem.align)) + zeroNewElements(s.len, s.p, 1, elem.size, elem.align) inc s.len else: var y = cast[ptr PGenSeq](x.value)[] diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim index 045d1e6b5..9b043f3e5 100644 --- a/lib/js/asyncjs.nim +++ b/lib/js/asyncjs.nim @@ -243,7 +243,7 @@ since (1, 5, 1): else: type A = impl(onSuccess(default(T))) var ret: A - asm "`ret` = `future`.then(`onSuccess`, `onReject`)" + {.emit: "`ret` = `future`.then(`onSuccess`, `onReject`);".} return ret proc catch*[T](future: Future[T], onReject: OnReject): Future[void] = @@ -266,4 +266,4 @@ since (1, 5, 1): discard main() - asm "`result` = `future`.catch(`onReject`)" + {.emit: "`result` = `future`.catch(`onReject`);".} diff --git a/lib/js/dom.nim b/lib/js/dom.nim index eb05308bb..be2a34db1 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1655,7 +1655,7 @@ proc item*(list: TouchList, i: int): Touch proc clearData*(dt: DataTransfer, format: cstring) proc getData*(dt: DataTransfer, format: cstring): cstring proc setData*(dt: DataTransfer, format: cstring, data: cstring) -proc setDragImage*(dt: DataTransfer, img: Element, xOffset: int64, yOffset: int64) +proc setDragImage*(dt: DataTransfer, img: Element, xOffset: int, yOffset: int) # DataTransferItem "methods" proc getAsFile*(dti: DataTransferItem): File diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 08b1c6db9..d50d58ae5 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -468,7 +468,7 @@ proc replaceSyms(n: NimNode): NimNode = for i in 0..<n.len: result[i] = replaceSyms(n[i]) -macro bindMethod*(procedure: typed): auto = +macro bindMethod*(procedure: typed): auto {.deprecated: "Don't use it with closures".} = ## Takes the name of a procedure and wraps it into a lambda missing the first ## argument, which passes the JavaScript builtin `this` as the first ## argument to the procedure. Returns the resulting lambda. diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index 69bd75c3b..2d931eb20 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -58,7 +58,7 @@ func contains*(pattern: cstring; self: RegExp): bool = assert jsregex in r"abc" assert jsregex notin r"abcd" assert "xabc".contains jsregex - asm "`result` = `self`.test(`pattern`);" + {.emit: "`result` = `self`.test(`pattern`);".} func startsWith*(pattern: cstring; self: RegExp): bool = ## Tests if string starts with given RegExp diff --git a/lib/nimbase.h b/lib/nimbase.h index 3a1289b6f..cf0c8002b 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -14,6 +14,8 @@ __GNUC__ __TINYC__ __clang__ __AVR__ +__arm__ +__EMSCRIPTEN__ */ @@ -177,12 +179,13 @@ __AVR__ # define N_THISCALL_PTR(rettype, name) rettype (__thiscall *name) # define N_SAFECALL_PTR(rettype, name) rettype (__stdcall *name) -# ifdef __cplusplus -# define N_LIB_EXPORT NIM_EXTERNC __declspec(dllexport) +# ifdef __EMSCRIPTEN__ +# define N_LIB_EXPORT NIM_EXTERNC __declspec(dllexport) __attribute__((used)) +# define N_LIB_EXPORT_VAR __declspec(dllexport) __attribute__((used)) # else # define N_LIB_EXPORT NIM_EXTERNC __declspec(dllexport) +# define N_LIB_EXPORT_VAR __declspec(dllexport) # endif -# define N_LIB_EXPORT_VAR __declspec(dllexport) # define N_LIB_IMPORT extern __declspec(dllimport) #else # define N_LIB_PRIVATE __attribute__((visibility("hidden"))) @@ -211,8 +214,13 @@ __AVR__ # define N_FASTCALL_PTR(rettype, name) rettype (*name) # define N_SAFECALL_PTR(rettype, name) rettype (*name) # endif -# define N_LIB_EXPORT NIM_EXTERNC __attribute__((visibility("default"))) -# define N_LIB_EXPORT_VAR __attribute__((visibility("default"))) +# ifdef __EMSCRIPTEN__ +# define N_LIB_EXPORT NIM_EXTERNC __attribute__((visibility("default"), used)) +# define N_LIB_EXPORT_VAR __attribute__((visibility("default"), used)) +# else +# define N_LIB_EXPORT NIM_EXTERNC __attribute__((visibility("default"))) +# define N_LIB_EXPORT_VAR __attribute__((visibility("default"))) +# endif # define N_LIB_IMPORT extern #endif @@ -265,11 +273,15 @@ __AVR__ #elif defined(__cplusplus) #define NIM_STATIC_ASSERT(x, msg) static_assert((x), msg) #else -#define NIM_STATIC_ASSERT(x, msg) typedef int NIM_STATIC_ASSERT_AUX[(x) ? 1 : -1]; +#define _NIM_STATIC_ASSERT_FINAL(x, append_name) typedef int NIM_STATIC_ASSERT_AUX ## append_name[(x) ? 1 : -1]; +#define _NIM_STATIC_ASSERT_STAGE_3(x, line) _NIM_STATIC_ASSERT_FINAL(x, _AT_LINE_##line) +#define _NIM_STATIC_ASSERT_STAGE_2(x, line) _NIM_STATIC_ASSERT_STAGE_3(x, line) +#define NIM_STATIC_ASSERT(x, msg) _NIM_STATIC_ASSERT_STAGE_2(x,__LINE__) // On failure, your C compiler will say something like: -// "error: 'NIM_STATIC_ASSERT_AUX' declared as an array with a negative size" -// we could use a better fallback to also show line number, using: -// http://www.pixelbeat.org/programming/gcc/static_assert.html +// "error: 'NIM_STATIC_ASSERT_AUX_AT_LINE_XXX' declared as an array with a negative size" +// Adding the line number helps to avoid redefinitions which are not allowed in +// old GCC versions, however the order of evaluation for __LINE__ is a little tricky, +// hence all the helper macros. See https://stackoverflow.com/a/3385694 for more info. #endif /* C99 compiler? */ @@ -466,7 +478,7 @@ typedef char* NCSTRING; /* declared size of a sequence/variable length array: */ #if defined(__cplusplus) && defined(__clang__) # define SEQ_DECL_SIZE 1 -#elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) +#elif defined(__GNUC__) || defined(_MSC_VER) # define SEQ_DECL_SIZE /* empty is correct! */ #else # define SEQ_DECL_SIZE 1000000 @@ -573,9 +585,16 @@ NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "P #define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res) #if NIM_INTBITS == 32 - #define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res) - #define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res) - #define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res) + #if defined(__arm__) && defined(__GNUC__) + /* arm-none-eabi-gcc targets defines int32_t as long int */ + #define nimAddInt(a, b, res) __builtin_saddl_overflow(a, b, res) + #define nimSubInt(a, b, res) __builtin_ssubl_overflow(a, b, res) + #define nimMulInt(a, b, res) __builtin_smull_overflow(a, b, res) + #else + #define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res) + #define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res) + #define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res) + #endif #else /* map it to the 'long long' variant */ #define nimAddInt(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 342ce0108..706c50689 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -380,8 +380,8 @@ type kind: FootnoteType # discriminator number: int # valid for fnManualNumber (always) and fnAutoNumber, # fnAutoNumberLabel after resolveSubs is called - autoNumIdx: int # order of occurence: fnAutoNumber, fnAutoNumberLabel - autoSymIdx: int # order of occurence: fnAutoSymbol + autoNumIdx: int # order of occurrence: fnAutoNumber, fnAutoNumberLabel + autoSymIdx: int # order of occurrence: fnAutoSymbol label: string # valid for fnAutoNumberLabel RstFileTable* = object filenameToIdx*: Table[string, FileIndex] @@ -1526,7 +1526,7 @@ proc parseMarkdownCodeblockFields(p: var RstParser): PRstNode = result = nil else: result = newRstNode(rnFieldList) - while currentTok(p).kind != tkIndent: + while currentTok(p).kind notin {tkIndent, tkEof}: if currentTok(p).kind == tkWhite: inc p.idx else: @@ -1603,6 +1603,7 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode = else: args = nil var n = newLeaf("") + var isFirstLine = true while true: if currentTok(p).kind == tkEof: rstMessage(p, meMissingClosing, @@ -1614,7 +1615,8 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode = inc p.idx, 2 break elif currentTok(p).kind == tkIndent: - n.text.add "\n" + if not isFirstLine: + n.text.add "\n" if currentTok(p).ival > baseCol: n.text.add " ".repeat(currentTok(p).ival - baseCol) elif currentTok(p).ival < baseCol: @@ -1624,6 +1626,7 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode = else: n.text.add(currentTok(p).symbol) inc p.idx + isFirstLine = false result.sons[0] = args if result.sons[2] == nil: var lb = newRstNode(rnLiteralBlock) @@ -3652,114 +3655,114 @@ proc preparePass2*(s: var PRstSharedState, mainNode: PRstNode, importdoc = true) loadIdxFile(s, origFilename) proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode = - # Associate this link alias with its target and change node kind to - # rnHyperlink or rnInternalRef appropriately. - var desc, alias: PRstNode - if n.kind == rnPandocRef: # link like [desc][alias] - desc = n.sons[0] - alias = n.sons[1] - else: # n.kind == rnRstRef, link like `desc=alias`_ - desc = n - alias = n - type LinkDef = object - ar: AnchorRule - priority: int - tooltip: string - target: PRstNode - info: TLineInfo - externFilename: string - # when external anchor: origin filename where anchor was defined - isTitle: bool - proc cmp(x, y: LinkDef): int = - result = cmp(x.priority, y.priority) - if result == 0: - result = cmp(x.target, y.target) - var foundLinks: seq[LinkDef] - let refn = rstnodeToRefname(alias) - var hyperlinks = findRef(s, refn) - for y in hyperlinks: - foundLinks.add LinkDef(ar: arHyperlink, priority: refPriority(y.kind), - target: y.value, info: y.info, - tooltip: "(" & $y.kind & ")") - let substRst = findMainAnchorRst(s, alias.addNodes, n.info) - template getExternFilename(subst: AnchorSubst): string = - if subst.kind == arExternalRst or - (subst.kind == arNim and subst.external): - getFilename(s, subst) - else: "" - for subst in substRst: - var refname, fullRefname: string - if subst.kind == arInternalRst: - refname = subst.target.anchor - fullRefname = refname - else: # arExternalRst - refname = subst.refnameExt - fullRefname = s.idxImports[getFilename(s, subst)].linkRelPath & - "/" & refname - let anchorType = - if subst.kind == arInternalRst: subst.anchorType - else: subst.anchorTypeExt # arExternalRst + # Associate this link alias with its target and change node kind to + # rnHyperlink or rnInternalRef appropriately. + var desc, alias: PRstNode + if n.kind == rnPandocRef: # link like [desc][alias] + desc = n.sons[0] + alias = n.sons[1] + else: # n.kind == rnRstRef, link like `desc=alias`_ + desc = n + alias = n + type LinkDef = object + ar: AnchorRule + priority: int + tooltip: string + target: PRstNode + info: TLineInfo + externFilename: string + # when external anchor: origin filename where anchor was defined + isTitle: bool + proc cmp(x, y: LinkDef): int = + result = cmp(x.priority, y.priority) + if result == 0: + result = cmp(x.target, y.target) + var foundLinks: seq[LinkDef] + let refn = rstnodeToRefname(alias) + var hyperlinks = findRef(s, refn) + for y in hyperlinks: + foundLinks.add LinkDef(ar: arHyperlink, priority: refPriority(y.kind), + target: y.value, info: y.info, + tooltip: "(" & $y.kind & ")") + let substRst = findMainAnchorRst(s, alias.addNodes, n.info) + template getExternFilename(subst: AnchorSubst): string = + if subst.kind == arExternalRst or + (subst.kind == arNim and subst.external): + getFilename(s, subst) + else: "" + for subst in substRst: + var refname, fullRefname: string + if subst.kind == arInternalRst: + refname = subst.target.anchor + fullRefname = refname + else: # arExternalRst + refname = subst.refnameExt + fullRefname = s.idxImports[getFilename(s, subst)].linkRelPath & + "/" & refname + let anchorType = + if subst.kind == arInternalRst: subst.anchorType + else: subst.anchorTypeExt # arExternalRst + foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority, + target: newLeaf(fullRefname), + info: subst.info, + externFilename: getExternFilename(subst), + isTitle: isDocumentationTitle(refname), + tooltip: "(" & $anchorType & ")") + # find anchors automatically generated from Nim symbols + if roNimFile in s.options or s.nimFileImported: + let substNim = findMainAnchorNim(s, signature=alias, n.info) + for subst in substNim: + let fullRefname = + if subst.external: + s.idxImports[getFilename(s, subst)].linkRelPath & + "/" & subst.refname + else: subst.refname foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority, target: newLeaf(fullRefname), - info: subst.info, externFilename: getExternFilename(subst), - isTitle: isDocumentationTitle(refname), - tooltip: "(" & $anchorType & ")") - # find anchors automatically generated from Nim symbols - if roNimFile in s.options or s.nimFileImported: - let substNim = findMainAnchorNim(s, signature=alias, n.info) - for subst in substNim: - let fullRefname = - if subst.external: - s.idxImports[getFilename(s, subst)].linkRelPath & - "/" & subst.refname - else: subst.refname - foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority, - target: newLeaf(fullRefname), - externFilename: getExternFilename(subst), - isTitle: isDocumentationTitle(subst.refname), - info: subst.info, tooltip: subst.tooltip) - foundLinks.sort(cmp = cmp, order = Descending) - let aliasStr = addNodes(alias) - if foundLinks.len >= 1: + isTitle: isDocumentationTitle(subst.refname), + info: subst.info, tooltip: subst.tooltip) + foundLinks.sort(cmp = cmp, order = Descending) + let aliasStr = addNodes(alias) + if foundLinks.len >= 1: + if foundLinks[0].externFilename != "": + s.idxImports[foundLinks[0].externFilename].used = true + let kind = if foundLinks[0].ar in {arHyperlink, arExternalRst}: rnHyperlink + elif foundLinks[0].ar == arNim: + if foundLinks[0].externFilename == "": rnNimdocRef + else: rnHyperlink + else: rnInternalRef + result = newRstNode(kind) + let documentName = # filename without ext for `.nim`, title for `.md` + if foundLinks[0].ar == arNim: + changeFileExt(foundLinks[0].externFilename.extractFilename, "") + elif foundLinks[0].externFilename != "": + s.idxImports[foundLinks[0].externFilename].title + else: foundLinks[0].externFilename.extractFilename + let linkText = if foundLinks[0].externFilename != "": - s.idxImports[foundLinks[0].externFilename].used = true - let kind = if foundLinks[0].ar in {arHyperlink, arExternalRst}: rnHyperlink - elif foundLinks[0].ar == arNim: - if foundLinks[0].externFilename == "": rnNimdocRef - else: rnHyperlink - else: rnInternalRef - result = newRstNode(kind) - let documentName = # filename without ext for `.nim`, title for `.md` - if foundLinks[0].ar == arNim: - changeFileExt(foundLinks[0].externFilename.extractFilename, "") - elif foundLinks[0].externFilename != "": - s.idxImports[foundLinks[0].externFilename].title - else: foundLinks[0].externFilename.extractFilename - let linkText = - if foundLinks[0].externFilename != "": - if foundLinks[0].isTitle: newLeaf(addNodes(desc)) - else: newLeaf(documentName & ": " & addNodes(desc)) - else: - newRstNode(rnInner, desc.sons) - result.sons = @[linkText, foundLinks[0].target] - if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip - if foundLinks.len > 1: # report ambiguous link - var targets = newSeq[string]() - for l in foundLinks: - var t = " " - if s.filenames.len > 1: - t.add getFilename(s.filenames, l.info.fileIndex) - let n = l.info.line - let c = l.info.col + ColRstOffset - t.add "($1, $2): $3" % [$n, $c, l.tooltip] - targets.add t - rstMessage(s.filenames, s.msgHandler, n.info, mwAmbiguousLink, - "`$1`\n clash:\n$2" % [ - aliasStr, targets.join("\n")]) - else: # nothing found - result = n - rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, aliasStr) + if foundLinks[0].isTitle: newLeaf(addNodes(desc)) + else: newLeaf(documentName & ": " & addNodes(desc)) + else: + newRstNode(rnInner, desc.sons) + result.sons = @[linkText, foundLinks[0].target] + if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip + if foundLinks.len > 1: # report ambiguous link + var targets = newSeq[string]() + for l in foundLinks: + var t = " " + if s.filenames.len > 1: + t.add getFilename(s.filenames, l.info.fileIndex) + let n = l.info.line + let c = l.info.col + ColRstOffset + t.add "($1, $2): $3" % [$n, $c, l.tooltip] + targets.add t + rstMessage(s.filenames, s.msgHandler, n.info, mwAmbiguousLink, + "`$1`\n clash:\n$2" % [ + aliasStr, targets.join("\n")]) + else: # nothing found + result = n + rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, aliasStr) proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = ## Makes pass 2 of RST parsing. diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim index 7bc1504e5..575accc18 100644 --- a/lib/posix/inotify.nim +++ b/lib/posix/inotify.nim @@ -20,7 +20,7 @@ type mask* {.importc: "mask".}: uint32 ## Watch mask. cookie* {.importc: "cookie".}: uint32 ## Cookie to synchronize two events. len* {.importc: "len".}: uint32 ## Length (including NULs) of name. - name* {.importc: "name".}: char ## Name. + name* {.importc: "name".}: UncheckedArray[char] ## Name. # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. const @@ -67,7 +67,8 @@ proc inotify_init*(): FileHandle {.cdecl, importc: "inotify_init", proc inotify_init1*(flags: cint): FileHandle {.cdecl, importc: "inotify_init1", header: "<sys/inotify.h>".} - ## Create and initialize inotify instance. + ## Like `inotify_init<#inotify_init>`_ , + ## but has a flags argument that provides access to some extra functionality. proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint {.cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".} @@ -79,11 +80,18 @@ proc inotify_rm_watch*(fd: cint; wd: cint): cint {.cdecl, iterator inotify_events*(evs: pointer, n: int): ptr InotifyEvent = ## Abstract the packed buffer interface to yield event object pointers. - ## ```Nim - ## var evs = newSeq[byte](8192) # Already did inotify_init+add_watch - ## while (let n = read(fd, evs[0].addr, 8192); n) > 0: # read forever - ## for e in inotify_events(evs[0].addr, n): echo e[].len # echo name lens - ## ``` + runnableExamples("-r:off"): + when defined(linux): + import std/posix # needed for FileHandle read procedure + const MaxWatches = 8192 + + let inotifyFd = inotify_init() # create new inotify instance and get it's FileHandle + let wd = inotifyFd.inotify_add_watch("/tmp", IN_CREATE or IN_DELETE) # Add new watch + + var events: array[MaxWatches, byte] # event buffer + while (let n = read(inotifyFd, addr events, MaxWatches); n) > 0: # blocks until any events have been read + for e in inotify_events(addr events, n): + echo (e[].wd, e[].mask, cast[cstring](addr e[].name)) # echo watch id, mask, and name value of each event var ev: ptr InotifyEvent = cast[ptr InotifyEvent](evs) var n = n while n > 0: @@ -94,8 +102,10 @@ iterator inotify_events*(evs: pointer, n: int): ptr InotifyEvent = runnableExamples: when defined(linux): - let inoty: FileHandle = inotify_init() ## Create 1 Inotify. - doAssert inoty >= 0 ## Check for errors (FileHandle is alias to cint). - let watchdoge: cint = inotify_add_watch(inoty, ".", IN_ALL_EVENTS) ## Add directory to watchdog. - doAssert watchdoge >= 0 ## Check for errors. - doAssert inotify_rm_watch(inoty, watchdoge) >= 0 ## Remove directory from the watchdog + let inotifyFd = inotify_init() # create and get new inotify FileHandle + doAssert inotifyFd >= 0 # check for errors + + let wd = inotifyFd.inotify_add_watch("/tmp", IN_CREATE or IN_DELETE) # Add new watch + doAssert wd >= 0 # check for errors + + discard inotifyFd.inotify_rm_watch(wd) # remove watch diff --git a/lib/posix/posix_macos_amd64.nim b/lib/posix/posix_macos_amd64.nim index d6b5834cb..a4b64ed62 100644 --- a/lib/posix/posix_macos_amd64.nim +++ b/lib/posix/posix_macos_amd64.nim @@ -122,10 +122,14 @@ type ## used for block sizes Clock* {.importc: "clock_t", header: "<sys/types.h>".} = int ClockId* {.importc: "clockid_t", header: "<sys/types.h>".} = int - Dev* {.importc: "dev_t", header: "<sys/types.h>".} = int32 + Dev* {.importc: "dev_t", header: "<sys/types.h>".} = ( + when defined(freebsd): + uint32 + else: + int32) Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = int Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = int - Gid* {.importc: "gid_t", header: "<sys/types.h>".} = int32 + Gid* {.importc: "gid_t", header: "<sys/types.h>".} = uint32 Id* {.importc: "id_t", header: "<sys/types.h>".} = int Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int Key* {.importc: "key_t", header: "<sys/types.h>".} = int @@ -135,7 +139,7 @@ type else: uint16 ) - Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int16 + Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = uint16 Off* {.importc: "off_t", header: "<sys/types.h>".} = int64 Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32 Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>".} = int @@ -167,7 +171,7 @@ type Trace_event_set* {.importc: "trace_event_set_t", header: "<sys/types.h>".} = int Trace_id* {.importc: "trace_id_t", header: "<sys/types.h>".} = int - Uid* {.importc: "uid_t", header: "<sys/types.h>".} = int32 + Uid* {.importc: "uid_t", header: "<sys/types.h>".} = uint32 Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = int Utsname* {.importc: "struct utsname", @@ -462,9 +466,9 @@ type header: "<netinet/in.h>".} = object ## struct sockaddr_in6 sin6_family*: TSa_Family ## AF_INET6. sin6_port*: InPort ## Port number. - sin6_flowinfo*: int32 ## IPv6 traffic class and flow information. + sin6_flowinfo*: uint32 ## IPv6 traffic class and flow information. sin6_addr*: In6Addr ## IPv6 address. - sin6_scope_id*: int32 ## Set of interfaces for a scope. + sin6_scope_id*: uint32 ## Set of interfaces for a scope. Tipv6_mreq* {.importc: "struct ipv6_mreq", pure, final, header: "<netinet/in.h>".} = object ## struct ipv6_mreq @@ -491,7 +495,7 @@ type ## alternative network names, terminated by a ## null pointer. n_addrtype*: cint ## The address type of the network. - n_net*: int32 ## The network number, in host byte order. + n_net*: uint32 ## The network number, in host byte order. Protoent* {.importc: "struct protoent", pure, final, header: "<netdb.h>".} = object ## struct protoent diff --git a/lib/posix/posix_openbsd_amd64.nim b/lib/posix/posix_openbsd_amd64.nim index 1ef4a4182..184cd89c0 100644 --- a/lib/posix/posix_openbsd_amd64.nim +++ b/lib/posix/posix_openbsd_amd64.nim @@ -131,15 +131,19 @@ type ## used for block sizes Clock* {.importc: "clock_t", header: "<sys/types.h>".} = int ClockId* {.importc: "clockid_t", header: "<sys/types.h>".} = int - Dev* {.importc: "dev_t", header: "<sys/types.h>".} = int32 + Dev* {.importc: "dev_t", header: "<sys/types.h>".} = ( + when defined(freebsd): + uint32 + else: + int32) Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = int Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = int - Gid* {.importc: "gid_t", header: "<sys/types.h>".} = int32 + Gid* {.importc: "gid_t", header: "<sys/types.h>".} = uint32 Id* {.importc: "id_t", header: "<sys/types.h>".} = int Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int Key* {.importc: "key_t", header: "<sys/types.h>".} = int Mode* {.importc: "mode_t", header: "<sys/types.h>".} = uint32 - Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int16 + Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = uint32 Off* {.importc: "off_t", header: "<sys/types.h>".} = int64 Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32 Pthread_attr* {.importc: "pthread_attr_t", header: "<pthread.h>".} = int @@ -171,7 +175,7 @@ type Trace_event_set* {.importc: "trace_event_set_t", header: "<sys/types.h>".} = int Trace_id* {.importc: "trace_id_t", header: "<sys/types.h>".} = int - Uid* {.importc: "uid_t", header: "<sys/types.h>".} = int32 + Uid* {.importc: "uid_t", header: "<sys/types.h>".} = uint32 Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = int Utsname* {.importc: "struct utsname", @@ -446,9 +450,9 @@ type header: "<netinet/in.h>".} = object ## struct sockaddr_in6 sin6_family*: TSa_Family ## AF_INET6. sin6_port*: InPort ## Port number. - sin6_flowinfo*: int32 ## IPv6 traffic class and flow information. + sin6_flowinfo*: uint32 ## IPv6 traffic class and flow information. sin6_addr*: In6Addr ## IPv6 address. - sin6_scope_id*: int32 ## Set of interfaces for a scope. + sin6_scope_id*: uint32 ## Set of interfaces for a scope. Tipv6_mreq* {.importc: "struct ipv6_mreq", pure, final, header: "<netinet/in.h>".} = object ## struct ipv6_mreq @@ -475,7 +479,7 @@ type ## alternative network names, terminated by a ## null pointer. n_addrtype*: cint ## The address type of the network. - n_net*: int32 ## The network number, in host byte order. + n_net*: uint32 ## The network number, in host byte order. Protoent* {.importc: "struct protoent", pure, final, header: "<netdb.h>".} = object ## struct protoent diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 65402e9fb..b12ed7cdd 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -387,7 +387,7 @@ func sort*[T](a: var openArray[T], ## ``` ## ## You can inline adhoc comparison procs with the `do notation - ## <manual_experimental.html#do-notation>`_. Example: + ## <manual.html#procedures-do-notation>`_. Example: ## ## ```nim ## people.sort do (x, y: Person) -> int: diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index edb4e14d3..29ebf8f89 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -import std/[os, tables, strutils, times, heapqueue, options, deques, cstrutils] +import std/[os, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits] import system/stacktraces @@ -193,7 +193,7 @@ proc add(callbacks: var CallbackList, function: CallbackFunc) = last = last.next last.next = newCallback -proc completeImpl[T, U](future: Future[T], val: U, isVoid: static bool) = +proc completeImpl[T, U](future: Future[T], val: sink U, isVoid: static bool) = #assert(not future.finished, "Future already finished, cannot finish twice.") checkFinished(future) assert(future.error == nil) @@ -203,7 +203,7 @@ proc completeImpl[T, U](future: Future[T], val: U, isVoid: static bool) = future.callbacks.call() when isFutureLoggingEnabled: logFutureFinish(future) -proc complete*[T](future: Future[T], val: T) = +proc complete*[T](future: Future[T], val: sink T) = ## Completes `future` with value `val`. completeImpl(future, val, false) @@ -219,7 +219,7 @@ proc complete*[T](future: FutureVar[T]) = fut.callbacks.call() when isFutureLoggingEnabled: logFutureFinish(Future[T](future)) -proc complete*[T](future: FutureVar[T], val: T) = +proc complete*[T](future: FutureVar[T], val: sink T) = ## Completes a `FutureVar` with value `val`. ## ## Any previously stored value will be overwritten. @@ -370,11 +370,7 @@ proc injectStacktrace[T](future: Future[T]) = # newMsg.add "\n" & $entry future.error.msg = newMsg -proc read*[T](future: Future[T] | FutureVar[T]): T = - ## Retrieves the value of `future`. Future must be finished otherwise - ## this function will fail with a `ValueError` exception. - ## - ## If the result of the future is an error then that error will be raised. +template readImpl(future, T) = when future is Future[T]: let fut {.cursor.} = future else: @@ -384,11 +380,21 @@ proc read*[T](future: Future[T] | FutureVar[T]): T = injectStacktrace(fut) raise fut.error when T isnot void: - result = fut.value + result = distinctBase(future).value else: # TODO: Make a custom exception type for this? raise newException(ValueError, "Future still in progress.") +proc read*[T](future: Future[T] | FutureVar[T]): lent T = + ## Retrieves the value of `future`. Future must be finished otherwise + ## this function will fail with a `ValueError` exception. + ## + ## If the result of the future is an error then that error will be raised. + readImpl(future, T) + +proc read*(future: Future[void] | FutureVar[void]) = + readImpl(future, void) + proc readError*[T](future: Future[T]): ref Exception = ## Retrieves the exception stored in `future`. ## diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index d8dc7a798..ee07e599e 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -687,7 +687,7 @@ when defined(posix) and not useNimNetLite: var socketAddr = makeUnixAddr(path) let ret = socket.fd.connect(cast[ptr SockAddr](addr socketAddr), - (sizeof(socketAddr.sun_family) + path.len).SockLen) + (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) if ret == 0: # Request to connect completed immediately. retFuture.complete() @@ -705,7 +705,7 @@ when defined(posix) and not useNimNetLite: when not defined(nimdoc): var socketAddr = makeUnixAddr(path) if socket.fd.bindAddr(cast[ptr SockAddr](addr socketAddr), - (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32: + (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) != 0'i32: raiseOSError(osLastError()) elif defined(nimdoc): diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 6af5345f2..591d22cc0 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -244,7 +244,7 @@ proc decode*(s: string): string = inputLen = s.len inputEnds = 0 # strip trailing characters - while s[inputLen - 1] in {'\n', '\r', ' ', '='}: + while inputLen > 0 and s[inputLen - 1] in {'\n', '\r', ' ', '='}: dec inputLen # hot loop: read 4 characters at at time inputEnds = inputLen - 4 diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim index 5708582e1..59e2078df 100644 --- a/lib/pure/browsers.nim +++ b/lib/pure/browsers.nim @@ -12,7 +12,7 @@ ## ## Unstable API. -import std/private/since +import std/private/since # used by the deprecated `openDefaultBrowser()` import std/strutils @@ -40,7 +40,7 @@ proc prepare(s: string): string = else: result = "file://" & absolutePath(s) -proc openDefaultBrowserImplPrep(url: string) = +proc openDefaultBrowserRaw(url: string) = ## note the url argument should be alreadly prepared, i.e. the url is passed "AS IS" when defined(windows): @@ -60,9 +60,6 @@ proc openDefaultBrowserImplPrep(url: string) = except OSError: discard -proc openDefaultBrowserImpl(url: string) = - openDefaultBrowserImplPrep(prepare url) - proc openDefaultBrowser*(url: string) = ## Opens `url` with the user's default browser. This does not block. ## The URL must not be empty string, to open on a blank page see `openDefaultBrowser()`. @@ -78,16 +75,30 @@ proc openDefaultBrowser*(url: string) = ## block: openDefaultBrowser("https://nim-lang.org") ## ``` doAssert url.len > 0, "URL must not be empty string" - openDefaultBrowserImpl(url) + openDefaultBrowserRaw(url) -proc openDefaultBrowser*() {.since: (1, 1).} = - ## Opens the user's default browser without any `url` (blank page). This does not block. - ## Implements IETF RFC-6694 Section 3, "about:blank" must be reserved for a blank page. +proc openDefaultBrowser*() {.since: (1, 1), deprecated: + "not implemented, please open with a specific url instead".} = + ## Intends to open the user's default browser without any `url` (blank page). + ## This does not block. + ## Intends to implement IETF RFC-6694 Section 3, + ## ("about:blank" is reserved for a blank page). ## - ## Under Windows, `ShellExecute` is used. Under Mac OS X the `open` - ## command is used. Under Unix, it is checked if `xdg-open` exists and - ## used if it does. Otherwise the environment variable `BROWSER` is - ## used to determine the default browser to use. + ## Beware that this intended behavior is **not** implemented and + ## considered not worthy to implement here. + ## + ## The following describes the behavior of current implementation: + ## + ## - Under Windows, this will only cause a pop-up dialog \ + ## asking the assocated application with `about` \ + ## (as Windows simply treats `about:` as a protocol like `http`). + ## - Under Mac OS X the `open "about:blank"` command is used. + ## - Under Unix, it is checked if `xdg-open` exists and used \ + ## if it does and open the application assocated with `text/html` mime \ + ## (not `x-scheme-handler/http`, so maybe html-viewer \ + ## other than your default browser is opened). \ + ## Otherwise the environment variable `BROWSER` is used \ + ## to determine the default browser to use. ## ## This proc doesn't raise an exception on error, beware. ## @@ -98,4 +109,4 @@ proc openDefaultBrowser*() {.since: (1, 1).} = ## **See also:** ## ## * https://tools.ietf.org/html/rfc6694#section-3 - openDefaultBrowserImplPrep("about:blank") # See IETF RFC-6694 Section 3. + openDefaultBrowserRaw("about:blank") # See IETF RFC-6694 Section 3. diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index b07138e84..d2b0099f2 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -50,7 +50,7 @@ runnableExamples: import std/private/since -import std/math +import std/[assertions, hashes, math] type Deque*[T] = object @@ -59,20 +59,29 @@ type ## To initialize an empty deque, ## use the `initDeque proc <#initDeque,int>`_. data: seq[T] - head, tail, count, mask: int + + # `head` and `tail` are masked only when accessing an element of `data` + # so that `tail - head == data.len` when the deque is full. + # They are uint so that incrementing/decrementing them doesn't cause + # over/underflow. You can get a number of items with `tail - head` + # even if `tail` or `head` is wraps around and `tail < head`, because + # `tail - head == (uint.high + 1 + tail) - head` when `tail < head`. + head, tail: uint const defaultInitialSize* = 4 template initImpl(result: typed, initialSize: int) = let correctSize = nextPowerOfTwo(initialSize) - result.mask = correctSize - 1 newSeq(result.data, correctSize) template checkIfInitialized(deq: typed) = - if deq.mask == 0: + if deq.data.len == 0: initImpl(deq, defaultInitialSize) +func mask[T](deq: Deque[T]): uint {.inline.} = + uint(deq.data.len) - 1 + proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] = ## Creates a new empty deque. ## @@ -85,22 +94,22 @@ proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] = ## * `toDeque proc <#toDeque,openArray[T]>`_ result.initImpl(initialSize) -proc len*[T](deq: Deque[T]): int {.inline.} = +func len*[T](deq: Deque[T]): int {.inline.} = ## Returns the number of elements of `deq`. - result = deq.count + int(deq.tail - deq.head) template emptyCheck(deq) = # Bounds check for the regular deque access. when compileOption("boundChecks"): - if unlikely(deq.count < 1): + if unlikely(deq.len < 1): raise newException(IndexDefect, "Empty deque.") template xBoundsCheck(deq, i) = # Bounds check for the array like accesses. when compileOption("boundChecks"): # `-d:danger` or `--checks:off` should disable this. - if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter + if unlikely(i >= deq.len): # x < deq.low is taken care by the Natural parameter raise newException(IndexDefect, - "Out of bounds: " & $i & " > " & $(deq.count - 1)) + "Out of bounds: " & $i & " > " & $(deq.len - 1)) if unlikely(i < 0): # when used with BackwardsIndex raise newException(IndexDefect, "Out of bounds: " & $i & " < 0") @@ -114,7 +123,7 @@ proc `[]`*[T](deq: Deque[T], i: Natural): lent T {.inline.} = doAssertRaises(IndexDefect, echo a[8]) xBoundsCheck(deq, i) - return deq.data[(deq.head + i) and deq.mask] + return deq.data[(deq.head + i.uint) and deq.mask] proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = ## Accesses the `i`-th element of `deq` and returns a mutable @@ -125,7 +134,7 @@ proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = assert a[0] == 11 xBoundsCheck(deq, i) - return deq.data[(deq.head + i) and deq.mask] + return deq.data[(deq.head + i.uint) and deq.mask] proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} = ## Sets the `i`-th element of `deq` to `val`. @@ -137,7 +146,7 @@ proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} = checkIfInitialized(deq) xBoundsCheck(deq, i) - deq.data[(deq.head + i) and deq.mask] = val + deq.data[(deq.head + i.uint) and deq.mask] = val proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): lent T {.inline.} = ## Accesses the backwards indexed `i`-th element. @@ -190,10 +199,8 @@ iterator items*[T](deq: Deque[T]): lent T = let a = [10, 20, 30, 40, 50].toDeque assert toSeq(a.items) == @[10, 20, 30, 40, 50] - var i = deq.head - for c in 0 ..< deq.count: - yield deq.data[i] - i = (i + 1) and deq.mask + for c in 0 ..< deq.len: + yield deq.data[(deq.head + c.uint) and deq.mask] iterator mitems*[T](deq: var Deque[T]): var T = ## Yields every element of `deq`, which can be modified. @@ -207,10 +214,8 @@ iterator mitems*[T](deq: var Deque[T]): var T = x = 5 * x - 1 assert $a == "[49, 99, 149, 199, 249]" - var i = deq.head - for c in 0 ..< deq.count: - yield deq.data[i] - i = (i + 1) and deq.mask + for c in 0 ..< deq.len: + yield deq.data[(deq.head + c.uint) and deq.mask] iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] = ## Yields every `(position, value)`-pair of `deq`. @@ -220,10 +225,8 @@ iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] = let a = [10, 20, 30].toDeque assert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)] - var i = deq.head - for c in 0 ..< deq.count: - yield (c, deq.data[i]) - i = (i + 1) and deq.mask + for c in 0 ..< deq.len: + yield (c, deq.data[(deq.head + c.uint) and deq.mask]) proc contains*[T](deq: Deque[T], item: T): bool {.inline.} = ## Returns true if `item` is in `deq` or false if not found. @@ -242,8 +245,9 @@ proc contains*[T](deq: Deque[T], item: T): bool {.inline.} = proc expandIfNeeded[T](deq: var Deque[T]) = checkIfInitialized(deq) - var cap = deq.mask + 1 - if unlikely(deq.count >= cap): + let cap = deq.data.len + assert deq.len <= cap + if unlikely(deq.len == cap): var n = newSeq[T](cap * 2) var i = 0 for x in mitems(deq): @@ -251,8 +255,7 @@ proc expandIfNeeded[T](deq: var Deque[T]) = else: n[i] = move(x) inc i deq.data = move(n) - deq.mask = cap * 2 - 1 - deq.tail = deq.count + deq.tail = cap.uint deq.head = 0 proc addFirst*[T](deq: var Deque[T], item: sink T) = @@ -267,9 +270,8 @@ proc addFirst*[T](deq: var Deque[T], item: sink T) = assert $a == "[50, 40, 30, 20, 10]" expandIfNeeded(deq) - inc deq.count - deq.head = (deq.head - 1) and deq.mask - deq.data[deq.head] = item + dec deq.head + deq.data[deq.head and deq.mask] = item proc addLast*[T](deq: var Deque[T], item: sink T) = ## Adds an `item` to the end of `deq`. @@ -283,9 +285,8 @@ proc addLast*[T](deq: var Deque[T], item: sink T) = assert $a == "[10, 20, 30, 40, 50]" expandIfNeeded(deq) - inc deq.count - deq.data[deq.tail] = item - deq.tail = (deq.tail + 1) and deq.mask + deq.data[deq.tail and deq.mask] = item + inc deq.tail proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} = ## Creates a new deque that contains the elements of `x` (in the same order). @@ -314,7 +315,7 @@ proc peekFirst*[T](deq: Deque[T]): lent T {.inline.} = assert len(a) == 5 emptyCheck(deq) - result = deq.data[deq.head] + result = deq.data[deq.head and deq.mask] proc peekLast*[T](deq: Deque[T]): lent T {.inline.} = ## Returns the last element of `deq`, but does not remove it from the deque. @@ -344,7 +345,7 @@ proc peekFirst*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} = assert $a == "[99, 20, 30, 40, 50]" emptyCheck(deq) - result = deq.data[deq.head] + result = deq.data[deq.head and deq.mask] proc peekLast*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} = ## Returns a mutable reference to the last element of `deq`, @@ -377,9 +378,8 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = assert $a == "[20, 30, 40, 50]" emptyCheck(deq) - dec deq.count - result = move deq.data[deq.head] - deq.head = (deq.head + 1) and deq.mask + result = move deq.data[deq.head and deq.mask] + inc deq.head proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = ## Removes and returns the last element of the `deq`. @@ -394,9 +394,8 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = assert $a == "[10, 20, 30, 40]" emptyCheck(deq) - dec deq.count - deq.tail = (deq.tail - 1) and deq.mask - result = move deq.data[deq.tail] + dec deq.tail + result = move deq.data[deq.tail and deq.mask] proc clear*[T](deq: var Deque[T]) {.inline.} = ## Resets the deque so that it is empty. @@ -410,7 +409,6 @@ proc clear*[T](deq: var Deque[T]) {.inline.} = assert len(a) == 0 for el in mitems(deq): destroy(el) - deq.count = 0 deq.tail = deq.head proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = @@ -430,19 +428,17 @@ proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = a.shrink(fromFirst = 2, fromLast = 1) assert $a == "[30, 40]" - if fromFirst + fromLast > deq.count: + if fromFirst + fromLast > deq.len: clear(deq) return for i in 0 ..< fromFirst: - destroy(deq.data[deq.head]) - deq.head = (deq.head + 1) and deq.mask + destroy(deq.data[deq.head and deq.mask]) + inc deq.head for i in 0 ..< fromLast: destroy(deq.data[(deq.tail - 1) and deq.mask]) - deq.tail = (deq.tail - 1) and deq.mask - - dec deq.count, fromFirst + fromLast + dec deq.tail proc `$`*[T](deq: Deque[T]): string = ## Turns a deque into its string representation. @@ -455,3 +451,30 @@ proc `$`*[T](deq: Deque[T]): string = if result.len > 1: result.add(", ") result.addQuoted(x) result.add("]") + +func `==`*[T](deq1, deq2: Deque[T]): bool = + ## The `==` operator for Deque. + ## Returns `true` if both deques contains the same values in the same order. + runnableExamples: + var a, b = initDeque[int]() + a.addFirst(2) + a.addFirst(1) + b.addLast(1) + b.addLast(2) + doAssert a == b + + if deq1.len != deq2.len: + return false + + for i in 0 ..< deq1.len: + if deq1.data[(deq1.head + i.uint) and deq1.mask] != deq2.data[(deq2.head + i.uint) and deq2.mask]: + return false + + true + +func hash*[T](deq: Deque[T]): Hash = + ## Hashing of Deque. + var h: Hash = 0 + for x in deq: + h = h !& hash(x) + !$h diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim index 8fd4c6e08..17785c8c7 100644 --- a/lib/pure/collections/hashcommon.nim +++ b/lib/pure/collections/hashcommon.nim @@ -58,7 +58,10 @@ proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} = template genHashImpl(key, hc: typed) = hc = hash(key) if hc == 0: # This almost never taken branch should be very predictable. - hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine. + when sizeof(int) < 4: + hc = 31415 # Value doesn't matter; Any non-zero favorite is fine <= 16-bit. + else: + hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine. template genHash(key: typed): Hash = var res: Hash diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index bcfdf37c2..96f9b4430 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -47,6 +47,9 @@ runnableExamples: import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + type HeapQueue*[T] = object ## A heap queue, commonly known as a priority queue. data: seq[T] @@ -73,6 +76,13 @@ proc `[]`*[T](heap: HeapQueue[T], i: Natural): lent T {.inline.} = ## Accesses the i-th element of `heap`. heap.data[i] +iterator items*[T](heap: HeapQueue[T]): lent T {.inline, since: (2, 1, 1).} = + ## Iterates over each item of `heap`. + let L = len(heap) + for i in 0 .. high(heap.data): + yield heap.data[i] + assert(len(heap) == L, "the length of the HeapQueue changed while iterating over it") + proc heapCmp[T](x, y: T): bool {.inline.} = x < y proc siftup[T](heap: var HeapQueue[T], startpos, p: int) = @@ -178,6 +188,11 @@ proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} = for i in 0 ..< heap.len: if heap[i] == x: return i +proc contains*[T](heap: HeapQueue[T], x: T): bool {.since: (2, 1, 1).} = + ## Returns true if `x` is in `heap` or false if not found. This is a shortcut + ## for `find(heap, x) >= 0`. + result = find(heap, x) >= 0 + proc del*[T](heap: var HeapQueue[T], index: Natural) = ## Removes the element at `index` from `heap`, maintaining the heap invariant. runnableExamples: diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index b9d5c48eb..6b88747ef 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -384,9 +384,7 @@ proc prependMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} = assert s == [0, 1, 0, 1, 0, 1] b.addMoved(a) - when defined(js): # XXX: swap broken in js; bug #16771 - (b, a) = (a, b) - else: swap a, b + swap a, b proc add*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). @@ -983,6 +981,17 @@ func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: ( for elem in elems.items: result.add(elem) +func toSinglyLinkedRing*[T](elems: openArray[T]): SinglyLinkedRing[T] = + ## Creates a new `SinglyLinkedRing` from the members of `elems`. + runnableExamples: + from std/sequtils import toSeq + let a = [1, 2, 3, 4, 5].toSinglyLinkedRing + assert a.toSeq == [1, 2, 3, 4, 5] + + result = initSinglyLinkedRing[T]() + for elem in elems.items: + result.add(elem) + func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = ## Creates a new `DoublyLinkedList` from the members of `elems`. runnableExamples: @@ -993,3 +1002,14 @@ func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: ( result = initDoublyLinkedList[T]() for elem in elems.items: result.add(elem) + +func toDoublyLinkedRing*[T](elems: openArray[T]): DoublyLinkedRing[T] = + ## Creates a new `DoublyLinkedRing` from the members of `elems`. + runnableExamples: + from std/sequtils import toSeq + let a = [1, 2, 3, 4, 5].toDoublyLinkedRing + assert a.toSeq == [1, 2, 3, 4, 5] + + result = initDoublyLinkedRing[T]() + for elem in elems.items: + result.add(elem) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 8e4a3c35f..d414caeed 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -278,6 +278,7 @@ proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] = let a = initTable[int, string]() b = initTable[char, seq[int]]() + result = default(Table[A, B]) initImpl(result, initialSize) proc `[]=`*[A, B](t: var Table[A, B], key: A, val: sink B) = @@ -1346,6 +1347,7 @@ proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A, let a = initOrderedTable[int, string]() b = initOrderedTable[char, seq[int]]() + result = default(OrderedTable[A, B]) initImpl(result, initialSize) proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) = @@ -1437,7 +1439,7 @@ proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = doAssert a.hasKey('a') == true doAssert a.hasKey('z') == false - var hc: Hash + var hc: Hash = default(Hash) result = rawGet(t, key, hc) >= 0 proc contains*[A, B](t: OrderedTable[A, B], key: A): bool = @@ -2336,6 +2338,7 @@ proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] = ## * `toCountTable proc<#toCountTable,openArray[A]>`_ ## * `newCountTable proc<#newCountTable>`_ for creating a ## `CountTableRef` + result = default(CountTable[A]) initImpl(result, initialSize) proc toCountTable*[A](keys: openArray[A]): CountTable[A] = diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 2ea29a4f9..b48811eae 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -15,9 +15,6 @@ runnableExamples: from std/math import almostEqual, sqrt - func almostEqual(a, b: Complex): bool = - almostEqual(a.re, b.re) and almostEqual(a.im, b.im) - let z1 = complex(1.0, 2.0) z2 = complex(3.0, -4.0) @@ -81,7 +78,7 @@ func abs2*[T](z: Complex[T]): T = ## that is the squared distance from (0, 0) to `z`. ## This is more efficient than `abs(z) ^ 2`. result = z.re * z.re + z.im * z.im - + func sgn*[T](z: Complex[T]): Complex[T] = ## Returns the phase of `z` as a unit complex number, ## or 0 if `z` is 0. @@ -249,10 +246,31 @@ func pow*[T](x, y: Complex[T]): Complex[T] = else: result.re = 0.0 result.im = 0.0 - elif y.re == 1.0 and y.im == 0.0: - result = x - elif y.re == -1.0 and y.im == 0.0: - result = T(1.0) / x + elif y.im == 0.0: + if y.re == 1.0: + result = x + elif y.re == -1.0: + result = T(1.0) / x + elif y.re == 2.0: + result = x * x + elif y.re == 0.5: + result = sqrt(x) + elif x.im == 0.0: + # Revert to real pow when both base and exponent are real + result.re = pow(x.re, y.re) + result.im = 0.0 + else: + # Special case when the exponent is real + let + rho = abs(x) + theta = arctan2(x.im, x.re) + s = pow(rho, y.re) + r = y.re * theta + result.re = s * cos(r) + result.im = s * sin(r) + elif x.im == 0.0 and x.re == E: + # Special case Euler's formula + result = exp(y) else: let rho = abs(x) @@ -391,6 +409,24 @@ func rect*[T](r, phi: T): Complex[T] = ## * `polar func<#polar,Complex[T]>`_ for the inverse operation complex(r * cos(phi), r * sin(phi)) +func almostEqual*[T: SomeFloat](x, y: Complex[T]; unitsInLastPlace: Natural = 4): bool = + ## Checks if two complex values are almost equal, using the + ## [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon). + ## + ## Two complex values are considered almost equal if their real and imaginary + ## components are almost equal. + ## + ## `unitsInLastPlace` is the max number of + ## [units in the last place](https://en.wikipedia.org/wiki/Unit_in_the_last_place) + ## difference tolerated when comparing two numbers. The larger the value, the + ## more error is allowed. A `0` value means that two numbers must be exactly the + ## same to be considered equal. + ## + ## The machine epsilon has to be scaled to the magnitude of the values used + ## and multiplied by the desired precision in ULPs unless the difference is + ## subnormal. + almostEqual(x.re, y.re, unitsInLastPlace = unitsInLastPlace) and + almostEqual(x.im, y.im, unitsInLastPlace = unitsInLastPlace) func `$`*(z: Complex): string = ## Returns `z`'s string representation as `"(re, im)"`. diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim index c7b881bc5..818f1b37a 100644 --- a/lib/pure/concurrency/atomics.nim +++ b/lib/pure/concurrency/atomics.nim @@ -10,6 +10,9 @@ ## Types and operations for atomic operations and lockless algorithms. ## ## Unstable API. +## +## By default, C++ uses C11 atomic primitives. To use C++ `std::atomic`, +## `-d:nimUseCppAtomics` can be defined. runnableExamples: # Atomic @@ -50,8 +53,7 @@ runnableExamples: flag.clear(moRelaxed) assert not flag.testAndSet - -when defined(cpp) or defined(nimdoc): +when (defined(cpp) and defined(nimUseCppAtomics)) or defined(nimdoc): # For the C++ backend, types and operations map directly to C++11 atomics. {.push, header: "<atomic>".} @@ -274,10 +276,17 @@ else: cast[T](interlockedXor(addr(location.value), cast[nonAtomicType(T)](value))) else: - {.push, header: "<stdatomic.h>".} + when defined(cpp): + {.push, header: "<atomic>".} + template maybeWrapStd(x: string): string = + "std::" & x + else: + {.push, header: "<stdatomic.h>".} + template maybeWrapStd(x: string): string = + x type - MemoryOrder* {.importc: "memory_order".} = enum + MemoryOrder* {.importc: "memory_order".maybeWrapStd.} = enum moRelaxed moConsume moAcquire @@ -285,16 +294,25 @@ else: moAcquireRelease moSequentiallyConsistent - type - # Atomic*[T] {.importcpp: "_Atomic('0)".} = object + when defined(cpp): + type + # Atomic*[T] {.importcpp: "_Atomic('0)".} = object + + AtomicInt8 {.importc: "std::atomic<NI8>".} = int8 + AtomicInt16 {.importc: "std::atomic<NI16>".} = int16 + AtomicInt32 {.importc: "std::atomic<NI32>".} = int32 + AtomicInt64 {.importc: "std::atomic<NI64>".} = int64 + else: + type + # Atomic*[T] {.importcpp: "_Atomic('0)".} = object - AtomicInt8 {.importc: "_Atomic NI8".} = int8 - AtomicInt16 {.importc: "_Atomic NI16".} = int16 - AtomicInt32 {.importc: "_Atomic NI32".} = int32 - AtomicInt64 {.importc: "_Atomic NI64".} = int64 + AtomicInt8 {.importc: "_Atomic NI8".} = int8 + AtomicInt16 {.importc: "_Atomic NI16".} = int16 + AtomicInt32 {.importc: "_Atomic NI32".} = int32 + AtomicInt64 {.importc: "_Atomic NI64".} = int64 type - AtomicFlag* {.importc: "atomic_flag", size: 1.} = object + AtomicFlag* {.importc: "atomic_flag".maybeWrapStd, size: 1.} = object Atomic*[T] = object when T is Trivial: @@ -308,27 +326,27 @@ else: guard: AtomicFlag #proc init*[T](location: var Atomic[T]; value: T): T {.importcpp: "atomic_init(@)".} - proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc.} - proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc.} - proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.} - proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.} - proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.} + proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc: "atomic_load_explicit".maybeWrapStd.} + proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_store_explicit".maybeWrapStd.} + proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_exchange_explicit".maybeWrapStd.} + proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_strong_explicit".maybeWrapStd.} + proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_weak_explicit".maybeWrapStd.} # Numerical operations - proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.} - proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.} - proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.} - proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.} - proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.} + proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_add_explicit".maybeWrapStd.} + proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_sub_explicit".maybeWrapStd.} + proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_and_explicit".maybeWrapStd.} + proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_or_explicit".maybeWrapStd.} + proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_xor_explicit".maybeWrapStd.} # Flag operations # var ATOMIC_FLAG_INIT {.importc, nodecl.}: AtomicFlag # proc init*(location: var AtomicFlag) {.inline.} = location = ATOMIC_FLAG_INIT - proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".} - proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".} + proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".maybeWrapStd.} + proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".maybeWrapStd.} - proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".} - proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".} + proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".maybeWrapStd.} + proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".maybeWrapStd.} {.pop.} diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index 0b2c915b2..9bc3fd579 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -15,75 +15,96 @@ runnableExamples: include "system/inclrtl" -when defined(posix) and not (defined(macosx) or defined(bsd)): - import std/posix +when defined(js): + import std/jsffi + proc countProcessorsImpl(): int = + when defined(nodejs): + let jsOs = require("os") + let jsObj = jsOs.cpus().length + else: + # `navigator.hardwareConcurrency` + # works on browser as well as deno. + let navigator{.importcpp.}: JsObject + let jsObj = navigator.hardwareConcurrency + result = jsObj.to int +else: + when defined(posix) and not (defined(macosx) or defined(bsd)): + import std/posix -when defined(windows): - import std/private/win_getsysteminfo + when defined(windows): + import std/private/win_getsysteminfo + + when defined(freebsd) or defined(macosx): + {.emit: "#include <sys/types.h>".} + + when defined(openbsd) or defined(netbsd): + {.emit: "#include <sys/param.h>".} -when defined(freebsd) or defined(macosx): - {.emit: "#include <sys/types.h>".} + when defined(macosx) or defined(bsd): + # we HAVE to emit param.h before sysctl.h so we cannot use .header here + # either. The amount of archaic bullshit in Poonix based OSes is just insane. + {.emit: "#include <sys/sysctl.h>".} + {.push nodecl.} + when defined(macosx): + proc sysctlbyname(name: cstring, + oldp: pointer, oldlenp: var csize_t, + newp: pointer, newlen: csize_t): cint {.importc.} + let + CTL_HW{.importc.}: cint + HW_NCPU{.importc.}: cint + proc sysctl[I: static[int]](name: var array[I, cint], namelen: cuint, + oldp: pointer, oldlenp: var csize_t, + newp: pointer, newlen: csize_t): cint {.importc.} + {.pop.} -when defined(openbsd) or defined(netbsd): - {.emit: "#include <sys/param.h>".} + when defined(genode): + import genode/env -when defined(macosx) or defined(bsd): - # we HAVE to emit param.h before sysctl.h so we cannot use .header here - # either. The amount of archaic bullshit in Poonix based OSes is just insane. - {.emit: "#include <sys/sysctl.h>".} - const - CTL_HW = 6 - HW_AVAILCPU = 25 - HW_NCPU = 3 - proc sysctl(x: ptr array[0..3, cint], y: cint, z: pointer, - a: var csize_t, b: pointer, c: csize_t): cint {. - importc: "sysctl", nodecl.} + proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {. + importcpp: "@->cpu().affinity_space().total()".} -when defined(genode): - import genode/env + when defined(haiku): + type + SystemInfo {.importc: "system_info", header: "<OS.h>".} = object + cpuCount {.importc: "cpu_count".}: uint32 - proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {. - importcpp: "@->cpu().affinity_space().total()".} + proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info", + header: "<OS.h>".} + + proc countProcessorsImpl(): int {.inline.} = + when defined(windows): + var + si: SystemInfo + getSystemInfo(addr si) + result = int(si.dwNumberOfProcessors) + elif defined(macosx) or defined(bsd): + let dest = addr result + var len = sizeof(result).csize_t + when defined(macosx): + # alias of "hw.activecpu" + if sysctlbyname("hw.logicalcpu", dest, len, nil, 0) == 0: + return + var mib = [CTL_HW, HW_NCPU] + if sysctl(mib, 2, dest, len, nil, 0) == 0: + return + elif defined(hpux): + result = mpctl(MPC_GETNUMSPUS, nil, nil) + elif defined(irix): + var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint + result = sysconf(SC_NPROC_ONLN) + elif defined(genode): + result = runtimeEnv.affinitySpaceTotal().int + elif defined(haiku): + var sysinfo: SystemInfo + if getSystemInfo(addr sysinfo) == 0: + result = sysinfo.cpuCount.int + else: + result = sysconf(SC_NPROCESSORS_ONLN) + if result < 0: result = 0 -when defined(haiku): - type - SystemInfo {.importc: "system_info", header: "<OS.h>".} = object - cpuCount {.importc: "cpu_count".}: uint32 - proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info", - header: "<OS.h>".} proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = ## Returns the number of the processors/cores the machine has. ## Returns 0 if it cannot be detected. - when defined(windows): - var - si: SystemInfo - getSystemInfo(addr si) - result = int(si.dwNumberOfProcessors) - elif defined(macosx) or defined(bsd): - var - mib: array[0..3, cint] - numCPU: int - mib[0] = CTL_HW - mib[1] = HW_AVAILCPU - var len = sizeof(numCPU).csize_t - discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0) - if numCPU < 1: - mib[1] = HW_NCPU - discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0) - result = numCPU - elif defined(hpux): - result = mpctl(MPC_GETNUMSPUS, nil, nil) - elif defined(irix): - var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint - result = sysconf(SC_NPROC_ONLN) - elif defined(genode): - result = runtimeEnv.affinitySpaceTotal().int - elif defined(haiku): - var sysinfo: SystemInfo - if getSystemInfo(addr sysinfo) == 0: - result = sysinfo.cpuCount.int - else: - result = sysconf(SC_NPROCESSORS_ONLN) - if result <= 0: result = 0 + countProcessorsImpl() diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index 58eacf633..9e71d4ce0 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -127,6 +127,7 @@ type BSD FreeBSD + NetBSD OpenBSD DragonFlyBSD @@ -168,7 +169,7 @@ proc detectOsImpl(d: Distribution): bool = else: when defined(bsd): case d - of Distribution.FreeBSD, Distribution.OpenBSD: + of Distribution.FreeBSD, Distribution.NetBSD, Distribution.OpenBSD: result = $d in uname() else: result = false @@ -251,7 +252,7 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) = result = ("nix-env -i " & p, false) elif detectOs(Solaris) or detectOs(FreeBSD): result = ("pkg install " & p, true) - elif detectOs(OpenBSD): + elif detectOs(NetBSD) or detectOs(OpenBSD): result = ("pkg_add " & p, true) elif detectOs(PCLinuxOS): result = ("rpm -ivh " & p, true) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index c8683ee40..bbadca655 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -342,7 +342,7 @@ proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = ## Raises `EncodingError` if it cannot fulfill the request. when not defined(windows): result = iconvOpen(destEncoding, srcEncoding) - if result == nil: + if result == cast[EncodingConverter](-1): raise newException(EncodingError, "cannot create encoding converter from " & srcEncoding & " to " & destEncoding) diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim index ddb6a1a0c..1d96fd6be 100644 --- a/lib/pure/fenv.nim +++ b/lib/pure/fenv.nim @@ -12,7 +12,7 @@ ## The types, vars and procs are bindings for the C standard library ## [<fenv.h>](https://en.cppreference.com/w/c/numeric/fenv) header. -when defined(posix) and not defined(genode): +when defined(posix) and not defined(genode) and not defined(macosx): {.passl: "-lm".} var diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 6d246d5b9..1038d55a1 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -190,7 +190,7 @@ proc hashData*(data: pointer, size: int): Hash = var h: Hash = 0 when defined(js): var p: cstring - asm """`p` = `Data`""" + {.emit: """`p` = `Data`;""".} else: var p = cast[cstring](data) var i = 0 @@ -217,7 +217,7 @@ else: when defined(js): var objectID = 0 proc getObjectId(x: pointer): int = - asm """ + {.emit: """ if (typeof `x` == "object") { if ("_NimID" in `x`) `result` = `x`["_NimID"]; @@ -226,7 +226,7 @@ when defined(js): `x`["_NimID"] = `result`; } } - """ + """.} proc hash*(x: pointer): Hash {.inline.} = ## Efficient `hash` overload. @@ -379,6 +379,158 @@ proc hashVmImplChar(x: openArray[char], sPos, ePos: int): Hash = proc hashVmImplByte(x: openArray[byte], sPos, ePos: int): Hash = raiseAssert "implementation override in compiler/vmops.nim" +const k0 = 0xc3a5c85c97cb3127u64 # Primes on (2^63, 2^64) for various uses +const k1 = 0xb492b66fbe98f273u64 +const k2 = 0x9ae16a3b2f90404fu64 + +proc load4e(s: openArray[byte], o=0): uint32 {.inline.} = + uint32(s[o + 3]) shl 24 or uint32(s[o + 2]) shl 16 or + uint32(s[o + 1]) shl 8 or uint32(s[o + 0]) + +proc load8e(s: openArray[byte], o=0): uint64 {.inline.} = + uint64(s[o + 7]) shl 56 or uint64(s[o + 6]) shl 48 or + uint64(s[o + 5]) shl 40 or uint64(s[o + 4]) shl 32 or + uint64(s[o + 3]) shl 24 or uint64(s[o + 2]) shl 16 or + uint64(s[o + 1]) shl 8 or uint64(s[o + 0]) + +proc load4(s: openArray[byte], o=0): uint32 {.inline.} = + when nimvm: result = load4e(s, o) + else: + when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof + else: result = load4e(s, o) + +proc load8(s: openArray[byte], o=0): uint64 {.inline.} = + when nimvm: result = load8e(s, o) + else: + when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof + else: result = load8e(s, o) + +proc lenU(s: openArray[byte]): uint64 {.inline.} = s.len.uint64 + +proc shiftMix(v: uint64): uint64 {.inline.} = v xor (v shr 47) + +proc rotR(v: uint64; bits: cint): uint64 {.inline.} = + (v shr bits) or (v shl (64 - bits)) + +proc len16(u: uint64; v: uint64; mul: uint64): uint64 {.inline.} = + var a = (u xor v)*mul + a = a xor (a shr 47) + var b = (v xor a)*mul + b = b xor (b shr 47) + b*mul + +proc len0_16(s: openArray[byte]): uint64 {.inline.} = + if s.len >= 8: + let mul = k2 + 2*s.lenU + let a = load8(s) + k2 + let b = load8(s, s.len - 8) + let c = rotR(b, 37)*mul + a + let d = (rotR(a, 25) + b)*mul + len16 c, d, mul + elif s.len >= 4: + let mul = k2 + 2*s.lenU + let a = load4(s).uint64 + len16 s.lenU + (a shl 3), load4(s, s.len - 4), mul + elif s.len > 0: + let a = uint32(s[0]) + let b = uint32(s[s.len shr 1]) + let c = uint32(s[s.len - 1]) + let y = a + (b shl 8) + let z = s.lenU + (c shl 2) + shiftMix(y*k2 xor z*k0)*k2 + else: k2 # s.len == 0 + +proc len17_32(s: openArray[byte]): uint64 {.inline.} = + let mul = k2 + 2*s.lenU + let a = load8(s)*k1 + let b = load8(s, 8) + let c = load8(s, s.len - 8)*mul + let d = load8(s, s.len - 16)*k2 + len16 rotR(a + b, 43) + rotR(c, 30) + d, a + rotR(b + k2, 18) + c, mul + +proc len33_64(s: openArray[byte]): uint64 {.inline.} = + let mul = k2 + 2*s.lenU + let a = load8(s)*k2 + let b = load8(s, 8) + let c = load8(s, s.len - 8)*mul + let d = load8(s, s.len - 16)*k2 + let y = rotR(a + b, 43) + rotR(c, 30) + d + let z = len16(y, a + rotR(b + k2, 18) + c, mul) + let e = load8(s, 16)*mul + let f = load8(s, 24) + let g = (y + load8(s, s.len - 32))*mul + let h = (z + load8(s, s.len - 24))*mul + len16 rotR(e + f, 43) + rotR(g, 30) + h, e + rotR(f + a, 18) + g, mul + +type Pair = tuple[first, second: uint64] + +proc weakLen32withSeeds2(w, x, y, z, a, b: uint64): Pair {.inline.} = + var a = a + w + var b = rotR(b + a + z, 21) + let c = a + a += x + a += y + b += rotR(a, 44) + result[0] = a + z + result[1] = b + c + +proc weakLen32withSeeds(s: openArray[byte]; o: int; a,b: uint64): Pair {.inline.} = + weakLen32withSeeds2 load8(s, o ), load8(s, o + 8), + load8(s, o + 16), load8(s, o + 24), a, b + +proc hashFarm(s: openArray[byte]): uint64 {.inline.} = + if s.len <= 16: return len0_16(s) + if s.len <= 32: return len17_32(s) + if s.len <= 64: return len33_64(s) + const seed = 81u64 # not const to use input `h` + var + o = 0 # s[] ptr arith -> variable origin variable `o` + x = seed + y = seed*k1 + 113 + z = shiftMix(y*k2 + 113)*k2 + v, w: Pair + x = x*k2 + load8(s) + let eos = ((s.len - 1) div 64)*64 + let last64 = eos + ((s.len - 1) and 63) - 63 + while true: + x = rotR(x + y + v[0] + load8(s, o+8), 37)*k1 + y = rotR(y + v[1] + load8(s, o+48), 42)*k1 + x = x xor w[1] + y += v[0] + load8(s, o+40) + z = rotR(z + w[0], 33)*k1 + v = weakLen32withSeeds(s, o+0 , v[1]*k1, x + w[0]) + w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16)) + swap z, x + inc o, 64 + if o == eos: break + let mul = k1 + ((z and 0xff) shl 1) + o = last64 + w[0] += (s.lenU - 1) and 63 + v[0] += w[0] + w[0] += v[0] + x = rotR(x + y + v[0] + load8(s, o+8), 37)*mul + y = rotR(y + v[1] + load8(s, o+48), 42)*mul + x = x xor w[1]*9 + y += v[0]*9 + load8(s, o+40) + z = rotR(z + w[0], 33)*mul + v = weakLen32withSeeds(s, o+0 , v[1]*mul, x + w[0]) + w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16)) + swap z, x + len16 len16(v[0],w[0],mul) + shiftMix(y)*k0 + z, len16(v[1],w[1],mul) + x, mul + +template jsNoInt64: untyped = + when defined js: + when compiles(compileOption("jsbigint64")): + when not compileOption("jsbigint64"): true + else: false + else: false + else: false +const sHash2 = (when defined(nimStringHash2) or jsNoInt64(): true else: false) + +template maybeFailJS_Number = + when jsNoInt64() and not defined(nimStringHash2): + {.error: "Must use `-d:nimStringHash2` when using `--jsbigint64:off`".} + proc hash*(x: string): Hash = ## Efficient hashing of strings. ## @@ -387,11 +539,14 @@ proc hash*(x: string): Hash = ## * `hashIgnoreCase <#hashIgnoreCase,string>`_ runnableExamples: doAssert hash("abracadabra") != hash("AbracadabrA") - - when nimvm: - result = hashVmImpl(x, 0, high(x)) + maybeFailJS_Number() + when not sHash2: + result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high))) else: - result = murmurHash(toOpenArrayByte(x, 0, high(x))) + #when nimvm: + # result = hashVmImpl(x, 0, high(x)) + when true: + result = murmurHash(toOpenArrayByte(x, 0, high(x))) proc hash*(x: cstring): Hash = ## Efficient hashing of null-terminated strings. @@ -400,14 +555,22 @@ proc hash*(x: cstring): Hash = doAssert hash(cstring"AbracadabrA") == hash("AbracadabrA") doAssert hash(cstring"abracadabra") != hash(cstring"AbracadabrA") - when nimvm: - hashVmImpl(x, 0, high(x)) - else: - when not defined(js): - murmurHash(toOpenArrayByte(x, 0, x.high)) - else: + maybeFailJS_Number() + when not sHash2: + when defined js: let xx = $x - murmurHash(toOpenArrayByte(xx, 0, high(xx))) + result = cast[Hash](hashFarm(toOpenArrayByte(xx, 0, xx.high))) + else: + result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high))) + else: + #when nimvm: + # result = hashVmImpl(x, 0, high(x)) + when true: + when not defined(js): + result = murmurHash(toOpenArrayByte(x, 0, x.high)) + else: + let xx = $x + result = murmurHash(toOpenArrayByte(xx, 0, high(xx))) proc hash*(sBuf: string, sPos, ePos: int): Hash = ## Efficient hashing of a string buffer, from starting @@ -418,7 +581,11 @@ proc hash*(sBuf: string, sPos, ePos: int): Hash = var a = "abracadabra" doAssert hash(a, 0, 3) == hash(a, 7, 10) - murmurHash(toOpenArrayByte(sBuf, sPos, ePos)) + maybeFailJS_Number() + when not sHash2: + result = cast[Hash](hashFarm(toOpenArrayByte(sBuf, sPos, ePos))) + else: + murmurHash(toOpenArrayByte(sBuf, sPos, ePos)) proc hashIgnoreStyle*(x: string): Hash = ## Efficient hashing of strings; style is ignored. @@ -553,12 +720,18 @@ proc hash*[A](x: openArray[A]): Hash = ## Efficient hashing of arrays and sequences. ## There must be a `hash` proc defined for the element type `A`. when A is byte: - result = murmurHash(x) + when not sHash2: + result = cast[Hash](hashFarm(x)) + else: + result = murmurHash(x) elif A is char: - when nimvm: - result = hashVmImplChar(x, 0, x.high) + when not sHash2: + result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high))) else: - result = murmurHash(toOpenArrayByte(x, 0, x.high)) + #when nimvm: + # result = hashVmImplChar(x, 0, x.high) + when true: + result = murmurHash(toOpenArrayByte(x, 0, x.high)) else: result = 0 for a in x: @@ -574,17 +747,24 @@ proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = runnableExamples: let a = [1, 2, 5, 1, 2, 6] doAssert hash(a, 0, 1) == hash(a, 3, 4) - when A is byte: - when nimvm: - result = hashVmImplByte(aBuf, sPos, ePos) + maybeFailJS_Number() + when not sHash2: + result = cast[Hash](hashFarm(toOpenArray(aBuf, sPos, ePos))) else: - result = murmurHash(toOpenArray(aBuf, sPos, ePos)) + #when nimvm: + # result = hashVmImplByte(aBuf, sPos, ePos) + when true: + result = murmurHash(toOpenArray(aBuf, sPos, ePos)) elif A is char: - when nimvm: - result = hashVmImplChar(aBuf, sPos, ePos) + maybeFailJS_Number() + when not sHash2: + result = cast[Hash](hashFarm(toOpenArrayByte(aBuf, sPos, ePos))) else: - result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos)) + #when nimvm: + # result = hashVmImplChar(aBuf, sPos, ePos) + when true: + result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos)) else: for i in sPos .. ePos: result = result !& hash(aBuf[i]) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index 45365c185..5ccab379c 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -164,7 +164,8 @@ func `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues = ## To access multiple values of a key, use the overloaded `[]` below or ## to get all of them access the `table` field directly. {.cast(noSideEffect).}: - return headers.table[headers.toCaseInsensitive(key)].HttpHeaderValues + let tmp = headers.table[headers.toCaseInsensitive(key)] + return HttpHeaderValues(tmp) converter toString*(values: HttpHeaderValues): string = return seq[string](values)[0] diff --git a/lib/pure/includes/unicode_ranges.nim b/lib/pure/includes/unicode_ranges.nim index 74c8c6399..04ccfb747 100644 --- a/lib/pure/includes/unicode_ranges.nim +++ b/lib/pure/includes/unicode_ranges.nim @@ -2,1988 +2,1979 @@ const toLowerRanges = [ - 0x00041, 0x0005A, 532, - 0x000C0, 0x000D6, 532, - 0x000D8, 0x000DE, 532, - 0x00189, 0x0018A, 705, - 0x001B1, 0x001B2, 717, - 0x00388, 0x0038A, 537, - 0x0038E, 0x0038F, 563, - 0x00391, 0x003A1, 532, - 0x003A3, 0x003AB, 532, - 0x003FD, 0x003FF, 370, - 0x00400, 0x0040F, 580, - 0x00410, 0x0042F, 532, - 0x00531, 0x00556, 548, - 0x010A0, 0x010C5, 7764, - 0x013A0, 0x013EF, 39364, - 0x013F0, 0x013F5, 508, - 0x01C90, 0x01CBA, -2508, - 0x01CBD, 0x01CBF, -2508, - 0x01F08, 0x01F0F, 492, - 0x01F18, 0x01F1D, 492, - 0x01F28, 0x01F2F, 492, - 0x01F38, 0x01F3F, 492, - 0x01F48, 0x01F4D, 492, - 0x01F68, 0x01F6F, 492, - 0x01F88, 0x01F8F, 492, - 0x01F98, 0x01F9F, 492, - 0x01FA8, 0x01FAF, 492, - 0x01FB8, 0x01FB9, 492, - 0x01FBA, 0x01FBB, 426, - 0x01FC8, 0x01FCB, 414, - 0x01FD8, 0x01FD9, 492, - 0x01FDA, 0x01FDB, 400, - 0x01FE8, 0x01FE9, 492, - 0x01FEA, 0x01FEB, 388, - 0x01FF8, 0x01FF9, 372, - 0x01FFA, 0x01FFB, 374, - 0x02C00, 0x02C2E, 548, - 0x02C7E, 0x02C7F, -10315, - 0x0FF21, 0x0FF3A, 532, - 0x10400, 0x10427, 540, - 0x104B0, 0x104D3, 540, - 0x10C80, 0x10CB2, 564, - 0x118A0, 0x118BF, 532, - 0x16E40, 0x16E5F, 532, - 0x1E900, 0x1E921, 534, + 0x00041'i32, 0x0005A'i32, 532, + 0x000C0'i32, 0x000D6'i32, 532, + 0x000D8'i32, 0x000DE'i32, 532, + 0x00189'i32, 0x0018A'i32, 705, + 0x001B1'i32, 0x001B2'i32, 717, + 0x00388'i32, 0x0038A'i32, 537, + 0x0038E'i32, 0x0038F'i32, 563, + 0x00391'i32, 0x003A1'i32, 532, + 0x003A3'i32, 0x003AB'i32, 532, + 0x003FD'i32, 0x003FF'i32, 370, + 0x00400'i32, 0x0040F'i32, 580, + 0x00410'i32, 0x0042F'i32, 532, + 0x00531'i32, 0x00556'i32, 548, + 0x010A0'i32, 0x010C5'i32, 7764, + 0x013A0'i32, 0x013EF'i32, 39364, + 0x013F0'i32, 0x013F5'i32, 508, + 0x01C90'i32, 0x01CBA'i32, -2508, + 0x01CBD'i32, 0x01CBF'i32, -2508, + 0x01F08'i32, 0x01F0F'i32, 492, + 0x01F18'i32, 0x01F1D'i32, 492, + 0x01F28'i32, 0x01F2F'i32, 492, + 0x01F38'i32, 0x01F3F'i32, 492, + 0x01F48'i32, 0x01F4D'i32, 492, + 0x01F68'i32, 0x01F6F'i32, 492, + 0x01F88'i32, 0x01F8F'i32, 492, + 0x01F98'i32, 0x01F9F'i32, 492, + 0x01FA8'i32, 0x01FAF'i32, 492, + 0x01FB8'i32, 0x01FB9'i32, 492, + 0x01FBA'i32, 0x01FBB'i32, 426, + 0x01FC8'i32, 0x01FCB'i32, 414, + 0x01FD8'i32, 0x01FD9'i32, 492, + 0x01FDA'i32, 0x01FDB'i32, 400, + 0x01FE8'i32, 0x01FE9'i32, 492, + 0x01FEA'i32, 0x01FEB'i32, 388, + 0x01FF8'i32, 0x01FF9'i32, 372, + 0x01FFA'i32, 0x01FFB'i32, 374, + 0x02C00'i32, 0x02C2E'i32, 548, + 0x02C7E'i32, 0x02C7F'i32, -10315, + 0x0FF21'i32, 0x0FF3A'i32, 532, + 0x10400'i32, 0x10427'i32, 540, + 0x104B0'i32, 0x104D3'i32, 540, + 0x10C80'i32, 0x10CB2'i32, 564, + 0x118A0'i32, 0x118BF'i32, 532, + 0x16E40'i32, 0x16E5F'i32, 532, + 0x1E900'i32, 0x1E921'i32, 534, ] toLowerSinglets = [ - 0x00100, 501, - 0x00102, 501, - 0x00104, 501, - 0x00106, 501, - 0x00108, 501, - 0x0010A, 501, - 0x0010C, 501, - 0x0010E, 501, - 0x00110, 501, - 0x00112, 501, - 0x00114, 501, - 0x00116, 501, - 0x00118, 501, - 0x0011A, 501, - 0x0011C, 501, - 0x0011E, 501, - 0x00120, 501, - 0x00122, 501, - 0x00124, 501, - 0x00126, 501, - 0x00128, 501, - 0x0012A, 501, - 0x0012C, 501, - 0x0012E, 501, - 0x00130, 301, - 0x00132, 501, - 0x00134, 501, - 0x00136, 501, - 0x00139, 501, - 0x0013B, 501, - 0x0013D, 501, - 0x0013F, 501, - 0x00141, 501, - 0x00143, 501, - 0x00145, 501, - 0x00147, 501, - 0x0014A, 501, - 0x0014C, 501, - 0x0014E, 501, - 0x00150, 501, - 0x00152, 501, - 0x00154, 501, - 0x00156, 501, - 0x00158, 501, - 0x0015A, 501, - 0x0015C, 501, - 0x0015E, 501, - 0x00160, 501, - 0x00162, 501, - 0x00164, 501, - 0x00166, 501, - 0x00168, 501, - 0x0016A, 501, - 0x0016C, 501, - 0x0016E, 501, - 0x00170, 501, - 0x00172, 501, - 0x00174, 501, - 0x00176, 501, - 0x00178, 379, - 0x00179, 501, - 0x0017B, 501, - 0x0017D, 501, - 0x00181, 710, - 0x00182, 501, - 0x00184, 501, - 0x00186, 706, - 0x00187, 501, - 0x0018B, 501, - 0x0018E, 579, - 0x0018F, 702, - 0x00190, 703, - 0x00191, 501, - 0x00193, 705, - 0x00194, 707, - 0x00196, 711, - 0x00197, 709, - 0x00198, 501, - 0x0019C, 711, - 0x0019D, 713, - 0x0019F, 714, - 0x001A0, 501, - 0x001A2, 501, - 0x001A4, 501, - 0x001A6, 718, - 0x001A7, 501, - 0x001A9, 718, - 0x001AC, 501, - 0x001AE, 718, - 0x001AF, 501, - 0x001B3, 501, - 0x001B5, 501, - 0x001B7, 719, - 0x001B8, 501, - 0x001BC, 501, - 0x001C4, 502, - 0x001C5, 501, - 0x001C7, 502, - 0x001C8, 501, - 0x001CA, 502, - 0x001CB, 501, - 0x001CD, 501, - 0x001CF, 501, - 0x001D1, 501, - 0x001D3, 501, - 0x001D5, 501, - 0x001D7, 501, - 0x001D9, 501, - 0x001DB, 501, - 0x001DE, 501, - 0x001E0, 501, - 0x001E2, 501, - 0x001E4, 501, - 0x001E6, 501, - 0x001E8, 501, - 0x001EA, 501, - 0x001EC, 501, - 0x001EE, 501, - 0x001F1, 502, - 0x001F2, 501, - 0x001F4, 501, - 0x001F6, 403, - 0x001F7, 444, - 0x001F8, 501, - 0x001FA, 501, - 0x001FC, 501, - 0x001FE, 501, - 0x00200, 501, - 0x00202, 501, - 0x00204, 501, - 0x00206, 501, - 0x00208, 501, - 0x0020A, 501, - 0x0020C, 501, - 0x0020E, 501, - 0x00210, 501, - 0x00212, 501, - 0x00214, 501, - 0x00216, 501, - 0x00218, 501, - 0x0021A, 501, - 0x0021C, 501, - 0x0021E, 501, - 0x00220, 370, - 0x00222, 501, - 0x00224, 501, - 0x00226, 501, - 0x00228, 501, - 0x0022A, 501, - 0x0022C, 501, - 0x0022E, 501, - 0x00230, 501, - 0x00232, 501, - 0x0023A, 11295, - 0x0023B, 501, - 0x0023D, 337, - 0x0023E, 11292, - 0x00241, 501, - 0x00243, 305, - 0x00244, 569, - 0x00245, 571, - 0x00246, 501, - 0x00248, 501, - 0x0024A, 501, - 0x0024C, 501, - 0x0024E, 501, - 0x00370, 501, - 0x00372, 501, - 0x00376, 501, - 0x0037F, 616, - 0x00386, 538, - 0x0038C, 564, - 0x003CF, 508, - 0x003D8, 501, - 0x003DA, 501, - 0x003DC, 501, - 0x003DE, 501, - 0x003E0, 501, - 0x003E2, 501, - 0x003E4, 501, - 0x003E6, 501, - 0x003E8, 501, - 0x003EA, 501, - 0x003EC, 501, - 0x003EE, 501, - 0x003F4, 440, - 0x003F7, 501, - 0x003F9, 493, - 0x003FA, 501, - 0x00460, 501, - 0x00462, 501, - 0x00464, 501, - 0x00466, 501, - 0x00468, 501, - 0x0046A, 501, - 0x0046C, 501, - 0x0046E, 501, - 0x00470, 501, - 0x00472, 501, - 0x00474, 501, - 0x00476, 501, - 0x00478, 501, - 0x0047A, 501, - 0x0047C, 501, - 0x0047E, 501, - 0x00480, 501, - 0x0048A, 501, - 0x0048C, 501, - 0x0048E, 501, - 0x00490, 501, - 0x00492, 501, - 0x00494, 501, - 0x00496, 501, - 0x00498, 501, - 0x0049A, 501, - 0x0049C, 501, - 0x0049E, 501, - 0x004A0, 501, - 0x004A2, 501, - 0x004A4, 501, - 0x004A6, 501, - 0x004A8, 501, - 0x004AA, 501, - 0x004AC, 501, - 0x004AE, 501, - 0x004B0, 501, - 0x004B2, 501, - 0x004B4, 501, - 0x004B6, 501, - 0x004B8, 501, - 0x004BA, 501, - 0x004BC, 501, - 0x004BE, 501, - 0x004C0, 515, - 0x004C1, 501, - 0x004C3, 501, - 0x004C5, 501, - 0x004C7, 501, - 0x004C9, 501, - 0x004CB, 501, - 0x004CD, 501, - 0x004D0, 501, - 0x004D2, 501, - 0x004D4, 501, - 0x004D6, 501, - 0x004D8, 501, - 0x004DA, 501, - 0x004DC, 501, - 0x004DE, 501, - 0x004E0, 501, - 0x004E2, 501, - 0x004E4, 501, - 0x004E6, 501, - 0x004E8, 501, - 0x004EA, 501, - 0x004EC, 501, - 0x004EE, 501, - 0x004F0, 501, - 0x004F2, 501, - 0x004F4, 501, - 0x004F6, 501, - 0x004F8, 501, - 0x004FA, 501, - 0x004FC, 501, - 0x004FE, 501, - 0x00500, 501, - 0x00502, 501, - 0x00504, 501, - 0x00506, 501, - 0x00508, 501, - 0x0050A, 501, - 0x0050C, 501, - 0x0050E, 501, - 0x00510, 501, - 0x00512, 501, - 0x00514, 501, - 0x00516, 501, - 0x00518, 501, - 0x0051A, 501, - 0x0051C, 501, - 0x0051E, 501, - 0x00520, 501, - 0x00522, 501, - 0x00524, 501, - 0x00526, 501, - 0x00528, 501, - 0x0052A, 501, - 0x0052C, 501, - 0x0052E, 501, - 0x010C7, 7764, - 0x010CD, 7764, - 0x01E00, 501, - 0x01E02, 501, - 0x01E04, 501, - 0x01E06, 501, - 0x01E08, 501, - 0x01E0A, 501, - 0x01E0C, 501, - 0x01E0E, 501, - 0x01E10, 501, - 0x01E12, 501, - 0x01E14, 501, - 0x01E16, 501, - 0x01E18, 501, - 0x01E1A, 501, - 0x01E1C, 501, - 0x01E1E, 501, - 0x01E20, 501, - 0x01E22, 501, - 0x01E24, 501, - 0x01E26, 501, - 0x01E28, 501, - 0x01E2A, 501, - 0x01E2C, 501, - 0x01E2E, 501, - 0x01E30, 501, - 0x01E32, 501, - 0x01E34, 501, - 0x01E36, 501, - 0x01E38, 501, - 0x01E3A, 501, - 0x01E3C, 501, - 0x01E3E, 501, - 0x01E40, 501, - 0x01E42, 501, - 0x01E44, 501, - 0x01E46, 501, - 0x01E48, 501, - 0x01E4A, 501, - 0x01E4C, 501, - 0x01E4E, 501, - 0x01E50, 501, - 0x01E52, 501, - 0x01E54, 501, - 0x01E56, 501, - 0x01E58, 501, - 0x01E5A, 501, - 0x01E5C, 501, - 0x01E5E, 501, - 0x01E60, 501, - 0x01E62, 501, - 0x01E64, 501, - 0x01E66, 501, - 0x01E68, 501, - 0x01E6A, 501, - 0x01E6C, 501, - 0x01E6E, 501, - 0x01E70, 501, - 0x01E72, 501, - 0x01E74, 501, - 0x01E76, 501, - 0x01E78, 501, - 0x01E7A, 501, - 0x01E7C, 501, - 0x01E7E, 501, - 0x01E80, 501, - 0x01E82, 501, - 0x01E84, 501, - 0x01E86, 501, - 0x01E88, 501, - 0x01E8A, 501, - 0x01E8C, 501, - 0x01E8E, 501, - 0x01E90, 501, - 0x01E92, 501, - 0x01E94, 501, - 0x01E9E, -7115, - 0x01EA0, 501, - 0x01EA2, 501, - 0x01EA4, 501, - 0x01EA6, 501, - 0x01EA8, 501, - 0x01EAA, 501, - 0x01EAC, 501, - 0x01EAE, 501, - 0x01EB0, 501, - 0x01EB2, 501, - 0x01EB4, 501, - 0x01EB6, 501, - 0x01EB8, 501, - 0x01EBA, 501, - 0x01EBC, 501, - 0x01EBE, 501, - 0x01EC0, 501, - 0x01EC2, 501, - 0x01EC4, 501, - 0x01EC6, 501, - 0x01EC8, 501, - 0x01ECA, 501, - 0x01ECC, 501, - 0x01ECE, 501, - 0x01ED0, 501, - 0x01ED2, 501, - 0x01ED4, 501, - 0x01ED6, 501, - 0x01ED8, 501, - 0x01EDA, 501, - 0x01EDC, 501, - 0x01EDE, 501, - 0x01EE0, 501, - 0x01EE2, 501, - 0x01EE4, 501, - 0x01EE6, 501, - 0x01EE8, 501, - 0x01EEA, 501, - 0x01EEC, 501, - 0x01EEE, 501, - 0x01EF0, 501, - 0x01EF2, 501, - 0x01EF4, 501, - 0x01EF6, 501, - 0x01EF8, 501, - 0x01EFA, 501, - 0x01EFC, 501, - 0x01EFE, 501, - 0x01F59, 492, - 0x01F5B, 492, - 0x01F5D, 492, - 0x01F5F, 492, - 0x01FBC, 491, - 0x01FCC, 491, - 0x01FEC, 493, - 0x01FFC, 491, - 0x02126, -7017, - 0x0212A, -7883, - 0x0212B, -7762, - 0x02132, 528, - 0x02183, 501, - 0x02C60, 501, - 0x02C62, -10243, - 0x02C63, -3314, - 0x02C64, -10227, - 0x02C67, 501, - 0x02C69, 501, - 0x02C6B, 501, - 0x02C6D, -10280, - 0x02C6E, -10249, - 0x02C6F, -10283, - 0x02C70, -10282, - 0x02C72, 501, - 0x02C75, 501, - 0x02C80, 501, - 0x02C82, 501, - 0x02C84, 501, - 0x02C86, 501, - 0x02C88, 501, - 0x02C8A, 501, - 0x02C8C, 501, - 0x02C8E, 501, - 0x02C90, 501, - 0x02C92, 501, - 0x02C94, 501, - 0x02C96, 501, - 0x02C98, 501, - 0x02C9A, 501, - 0x02C9C, 501, - 0x02C9E, 501, - 0x02CA0, 501, - 0x02CA2, 501, - 0x02CA4, 501, - 0x02CA6, 501, - 0x02CA8, 501, - 0x02CAA, 501, - 0x02CAC, 501, - 0x02CAE, 501, - 0x02CB0, 501, - 0x02CB2, 501, - 0x02CB4, 501, - 0x02CB6, 501, - 0x02CB8, 501, - 0x02CBA, 501, - 0x02CBC, 501, - 0x02CBE, 501, - 0x02CC0, 501, - 0x02CC2, 501, - 0x02CC4, 501, - 0x02CC6, 501, - 0x02CC8, 501, - 0x02CCA, 501, - 0x02CCC, 501, - 0x02CCE, 501, - 0x02CD0, 501, - 0x02CD2, 501, - 0x02CD4, 501, - 0x02CD6, 501, - 0x02CD8, 501, - 0x02CDA, 501, - 0x02CDC, 501, - 0x02CDE, 501, - 0x02CE0, 501, - 0x02CE2, 501, - 0x02CEB, 501, - 0x02CED, 501, - 0x02CF2, 501, - 0x0A640, 501, - 0x0A642, 501, - 0x0A644, 501, - 0x0A646, 501, - 0x0A648, 501, - 0x0A64A, 501, - 0x0A64C, 501, - 0x0A64E, 501, - 0x0A650, 501, - 0x0A652, 501, - 0x0A654, 501, - 0x0A656, 501, - 0x0A658, 501, - 0x0A65A, 501, - 0x0A65C, 501, - 0x0A65E, 501, - 0x0A660, 501, - 0x0A662, 501, - 0x0A664, 501, - 0x0A666, 501, - 0x0A668, 501, - 0x0A66A, 501, - 0x0A66C, 501, - 0x0A680, 501, - 0x0A682, 501, - 0x0A684, 501, - 0x0A686, 501, - 0x0A688, 501, - 0x0A68A, 501, - 0x0A68C, 501, - 0x0A68E, 501, - 0x0A690, 501, - 0x0A692, 501, - 0x0A694, 501, - 0x0A696, 501, - 0x0A698, 501, - 0x0A69A, 501, - 0x0A722, 501, - 0x0A724, 501, - 0x0A726, 501, - 0x0A728, 501, - 0x0A72A, 501, - 0x0A72C, 501, - 0x0A72E, 501, - 0x0A732, 501, - 0x0A734, 501, - 0x0A736, 501, - 0x0A738, 501, - 0x0A73A, 501, - 0x0A73C, 501, - 0x0A73E, 501, - 0x0A740, 501, - 0x0A742, 501, - 0x0A744, 501, - 0x0A746, 501, - 0x0A748, 501, - 0x0A74A, 501, - 0x0A74C, 501, - 0x0A74E, 501, - 0x0A750, 501, - 0x0A752, 501, - 0x0A754, 501, - 0x0A756, 501, - 0x0A758, 501, - 0x0A75A, 501, - 0x0A75C, 501, - 0x0A75E, 501, - 0x0A760, 501, - 0x0A762, 501, - 0x0A764, 501, - 0x0A766, 501, - 0x0A768, 501, - 0x0A76A, 501, - 0x0A76C, 501, - 0x0A76E, 501, - 0x0A779, 501, - 0x0A77B, 501, - 0x0A77D, -34832, - 0x0A77E, 501, - 0x0A780, 501, - 0x0A782, 501, - 0x0A784, 501, - 0x0A786, 501, - 0x0A78B, 501, - 0x0A78D, -41780, - 0x0A790, 501, - 0x0A792, 501, - 0x0A796, 501, - 0x0A798, 501, - 0x0A79A, 501, - 0x0A79C, 501, - 0x0A79E, 501, - 0x0A7A0, 501, - 0x0A7A2, 501, - 0x0A7A4, 501, - 0x0A7A6, 501, - 0x0A7A8, 501, - 0x0A7AA, -41808, - 0x0A7AB, -41819, - 0x0A7AC, -41815, - 0x0A7AD, -41805, - 0x0A7AE, -41808, - 0x0A7B0, -41758, - 0x0A7B1, -41782, - 0x0A7B2, -41761, - 0x0A7B3, 1428, - 0x0A7B4, 501, - 0x0A7B6, 501, - 0x0A7B8, 501, - 0x0A7BA, 501, - 0x0A7BC, 501, - 0x0A7BE, 501, - 0x0A7C2, 501, - 0x0A7C4, 452, - 0x0A7C5, -41807, - 0x0A7C6, -34884, + 0x00100'i32, 501, + 0x00102'i32, 501, + 0x00104'i32, 501, + 0x00106'i32, 501, + 0x00108'i32, 501, + 0x0010A'i32, 501, + 0x0010C'i32, 501, + 0x0010E'i32, 501, + 0x00110'i32, 501, + 0x00112'i32, 501, + 0x00114'i32, 501, + 0x00116'i32, 501, + 0x00118'i32, 501, + 0x0011A'i32, 501, + 0x0011C'i32, 501, + 0x0011E'i32, 501, + 0x00120'i32, 501, + 0x00122'i32, 501, + 0x00124'i32, 501, + 0x00126'i32, 501, + 0x00128'i32, 501, + 0x0012A'i32, 501, + 0x0012C'i32, 501, + 0x0012E'i32, 501, + 0x00130'i32, 301, + 0x00132'i32, 501, + 0x00134'i32, 501, + 0x00136'i32, 501, + 0x00139'i32, 501, + 0x0013B'i32, 501, + 0x0013D'i32, 501, + 0x0013F'i32, 501, + 0x00141'i32, 501, + 0x00143'i32, 501, + 0x00145'i32, 501, + 0x00147'i32, 501, + 0x0014A'i32, 501, + 0x0014C'i32, 501, + 0x0014E'i32, 501, + 0x00150'i32, 501, + 0x00152'i32, 501, + 0x00154'i32, 501, + 0x00156'i32, 501, + 0x00158'i32, 501, + 0x0015A'i32, 501, + 0x0015C'i32, 501, + 0x0015E'i32, 501, + 0x00160'i32, 501, + 0x00162'i32, 501, + 0x00164'i32, 501, + 0x00166'i32, 501, + 0x00168'i32, 501, + 0x0016A'i32, 501, + 0x0016C'i32, 501, + 0x0016E'i32, 501, + 0x00170'i32, 501, + 0x00172'i32, 501, + 0x00174'i32, 501, + 0x00176'i32, 501, + 0x00178'i32, 379, + 0x00179'i32, 501, + 0x0017B'i32, 501, + 0x0017D'i32, 501, + 0x00181'i32, 710, + 0x00182'i32, 501, + 0x00184'i32, 501, + 0x00186'i32, 706, + 0x00187'i32, 501, + 0x0018B'i32, 501, + 0x0018E'i32, 579, + 0x0018F'i32, 702, + 0x00190'i32, 703, + 0x00191'i32, 501, + 0x00193'i32, 705, + 0x00194'i32, 707, + 0x00196'i32, 711, + 0x00197'i32, 709, + 0x00198'i32, 501, + 0x0019C'i32, 711, + 0x0019D'i32, 713, + 0x0019F'i32, 714, + 0x001A0'i32, 501, + 0x001A2'i32, 501, + 0x001A4'i32, 501, + 0x001A6'i32, 718, + 0x001A7'i32, 501, + 0x001A9'i32, 718, + 0x001AC'i32, 501, + 0x001AE'i32, 718, + 0x001AF'i32, 501, + 0x001B3'i32, 501, + 0x001B5'i32, 501, + 0x001B7'i32, 719, + 0x001B8'i32, 501, + 0x001BC'i32, 501, + 0x001C4'i32, 502, + 0x001C5'i32, 501, + 0x001C7'i32, 502, + 0x001C8'i32, 501, + 0x001CA'i32, 502, + 0x001CB'i32, 501, + 0x001CD'i32, 501, + 0x001CF'i32, 501, + 0x001D1'i32, 501, + 0x001D3'i32, 501, + 0x001D5'i32, 501, + 0x001D7'i32, 501, + 0x001D9'i32, 501, + 0x001DB'i32, 501, + 0x001DE'i32, 501, + 0x001E0'i32, 501, + 0x001E2'i32, 501, + 0x001E4'i32, 501, + 0x001E6'i32, 501, + 0x001E8'i32, 501, + 0x001EA'i32, 501, + 0x001EC'i32, 501, + 0x001EE'i32, 501, + 0x001F1'i32, 502, + 0x001F2'i32, 501, + 0x001F4'i32, 501, + 0x001F6'i32, 403, + 0x001F7'i32, 444, + 0x001F8'i32, 501, + 0x001FA'i32, 501, + 0x001FC'i32, 501, + 0x001FE'i32, 501, + 0x00200'i32, 501, + 0x00202'i32, 501, + 0x00204'i32, 501, + 0x00206'i32, 501, + 0x00208'i32, 501, + 0x0020A'i32, 501, + 0x0020C'i32, 501, + 0x0020E'i32, 501, + 0x00210'i32, 501, + 0x00212'i32, 501, + 0x00214'i32, 501, + 0x00216'i32, 501, + 0x00218'i32, 501, + 0x0021A'i32, 501, + 0x0021C'i32, 501, + 0x0021E'i32, 501, + 0x00220'i32, 370, + 0x00222'i32, 501, + 0x00224'i32, 501, + 0x00226'i32, 501, + 0x00228'i32, 501, + 0x0022A'i32, 501, + 0x0022C'i32, 501, + 0x0022E'i32, 501, + 0x00230'i32, 501, + 0x00232'i32, 501, + 0x0023A'i32, 11295, + 0x0023B'i32, 501, + 0x0023D'i32, 337, + 0x0023E'i32, 11292, + 0x00241'i32, 501, + 0x00243'i32, 305, + 0x00244'i32, 569, + 0x00245'i32, 571, + 0x00246'i32, 501, + 0x00248'i32, 501, + 0x0024A'i32, 501, + 0x0024C'i32, 501, + 0x0024E'i32, 501, + 0x00370'i32, 501, + 0x00372'i32, 501, + 0x00376'i32, 501, + 0x0037F'i32, 616, + 0x00386'i32, 538, + 0x0038C'i32, 564, + 0x003CF'i32, 508, + 0x003D8'i32, 501, + 0x003DA'i32, 501, + 0x003DC'i32, 501, + 0x003DE'i32, 501, + 0x003E0'i32, 501, + 0x003E2'i32, 501, + 0x003E4'i32, 501, + 0x003E6'i32, 501, + 0x003E8'i32, 501, + 0x003EA'i32, 501, + 0x003EC'i32, 501, + 0x003EE'i32, 501, + 0x003F4'i32, 440, + 0x003F7'i32, 501, + 0x003F9'i32, 493, + 0x003FA'i32, 501, + 0x00460'i32, 501, + 0x00462'i32, 501, + 0x00464'i32, 501, + 0x00466'i32, 501, + 0x00468'i32, 501, + 0x0046A'i32, 501, + 0x0046C'i32, 501, + 0x0046E'i32, 501, + 0x00470'i32, 501, + 0x00472'i32, 501, + 0x00474'i32, 501, + 0x00476'i32, 501, + 0x00478'i32, 501, + 0x0047A'i32, 501, + 0x0047C'i32, 501, + 0x0047E'i32, 501, + 0x00480'i32, 501, + 0x0048A'i32, 501, + 0x0048C'i32, 501, + 0x0048E'i32, 501, + 0x00490'i32, 501, + 0x00492'i32, 501, + 0x00494'i32, 501, + 0x00496'i32, 501, + 0x00498'i32, 501, + 0x0049A'i32, 501, + 0x0049C'i32, 501, + 0x0049E'i32, 501, + 0x004A0'i32, 501, + 0x004A2'i32, 501, + 0x004A4'i32, 501, + 0x004A6'i32, 501, + 0x004A8'i32, 501, + 0x004AA'i32, 501, + 0x004AC'i32, 501, + 0x004AE'i32, 501, + 0x004B0'i32, 501, + 0x004B2'i32, 501, + 0x004B4'i32, 501, + 0x004B6'i32, 501, + 0x004B8'i32, 501, + 0x004BA'i32, 501, + 0x004BC'i32, 501, + 0x004BE'i32, 501, + 0x004C0'i32, 515, + 0x004C1'i32, 501, + 0x004C3'i32, 501, + 0x004C5'i32, 501, + 0x004C7'i32, 501, + 0x004C9'i32, 501, + 0x004CB'i32, 501, + 0x004CD'i32, 501, + 0x004D0'i32, 501, + 0x004D2'i32, 501, + 0x004D4'i32, 501, + 0x004D6'i32, 501, + 0x004D8'i32, 501, + 0x004DA'i32, 501, + 0x004DC'i32, 501, + 0x004DE'i32, 501, + 0x004E0'i32, 501, + 0x004E2'i32, 501, + 0x004E4'i32, 501, + 0x004E6'i32, 501, + 0x004E8'i32, 501, + 0x004EA'i32, 501, + 0x004EC'i32, 501, + 0x004EE'i32, 501, + 0x004F0'i32, 501, + 0x004F2'i32, 501, + 0x004F4'i32, 501, + 0x004F6'i32, 501, + 0x004F8'i32, 501, + 0x004FA'i32, 501, + 0x004FC'i32, 501, + 0x004FE'i32, 501, + 0x00500'i32, 501, + 0x00502'i32, 501, + 0x00504'i32, 501, + 0x00506'i32, 501, + 0x00508'i32, 501, + 0x0050A'i32, 501, + 0x0050C'i32, 501, + 0x0050E'i32, 501, + 0x00510'i32, 501, + 0x00512'i32, 501, + 0x00514'i32, 501, + 0x00516'i32, 501, + 0x00518'i32, 501, + 0x0051A'i32, 501, + 0x0051C'i32, 501, + 0x0051E'i32, 501, + 0x00520'i32, 501, + 0x00522'i32, 501, + 0x00524'i32, 501, + 0x00526'i32, 501, + 0x00528'i32, 501, + 0x0052A'i32, 501, + 0x0052C'i32, 501, + 0x0052E'i32, 501, + 0x010C7'i32, 7764, + 0x010CD'i32, 7764, + 0x01E00'i32, 501, + 0x01E02'i32, 501, + 0x01E04'i32, 501, + 0x01E06'i32, 501, + 0x01E08'i32, 501, + 0x01E0A'i32, 501, + 0x01E0C'i32, 501, + 0x01E0E'i32, 501, + 0x01E10'i32, 501, + 0x01E12'i32, 501, + 0x01E14'i32, 501, + 0x01E16'i32, 501, + 0x01E18'i32, 501, + 0x01E1A'i32, 501, + 0x01E1C'i32, 501, + 0x01E1E'i32, 501, + 0x01E20'i32, 501, + 0x01E22'i32, 501, + 0x01E24'i32, 501, + 0x01E26'i32, 501, + 0x01E28'i32, 501, + 0x01E2A'i32, 501, + 0x01E2C'i32, 501, + 0x01E2E'i32, 501, + 0x01E30'i32, 501, + 0x01E32'i32, 501, + 0x01E34'i32, 501, + 0x01E36'i32, 501, + 0x01E38'i32, 501, + 0x01E3A'i32, 501, + 0x01E3C'i32, 501, + 0x01E3E'i32, 501, + 0x01E40'i32, 501, + 0x01E42'i32, 501, + 0x01E44'i32, 501, + 0x01E46'i32, 501, + 0x01E48'i32, 501, + 0x01E4A'i32, 501, + 0x01E4C'i32, 501, + 0x01E4E'i32, 501, + 0x01E50'i32, 501, + 0x01E52'i32, 501, + 0x01E54'i32, 501, + 0x01E56'i32, 501, + 0x01E58'i32, 501, + 0x01E5A'i32, 501, + 0x01E5C'i32, 501, + 0x01E5E'i32, 501, + 0x01E60'i32, 501, + 0x01E62'i32, 501, + 0x01E64'i32, 501, + 0x01E66'i32, 501, + 0x01E68'i32, 501, + 0x01E6A'i32, 501, + 0x01E6C'i32, 501, + 0x01E6E'i32, 501, + 0x01E70'i32, 501, + 0x01E72'i32, 501, + 0x01E74'i32, 501, + 0x01E76'i32, 501, + 0x01E78'i32, 501, + 0x01E7A'i32, 501, + 0x01E7C'i32, 501, + 0x01E7E'i32, 501, + 0x01E80'i32, 501, + 0x01E82'i32, 501, + 0x01E84'i32, 501, + 0x01E86'i32, 501, + 0x01E88'i32, 501, + 0x01E8A'i32, 501, + 0x01E8C'i32, 501, + 0x01E8E'i32, 501, + 0x01E90'i32, 501, + 0x01E92'i32, 501, + 0x01E94'i32, 501, + 0x01E9E'i32, -7115, + 0x01EA0'i32, 501, + 0x01EA2'i32, 501, + 0x01EA4'i32, 501, + 0x01EA6'i32, 501, + 0x01EA8'i32, 501, + 0x01EAA'i32, 501, + 0x01EAC'i32, 501, + 0x01EAE'i32, 501, + 0x01EB0'i32, 501, + 0x01EB2'i32, 501, + 0x01EB4'i32, 501, + 0x01EB6'i32, 501, + 0x01EB8'i32, 501, + 0x01EBA'i32, 501, + 0x01EBC'i32, 501, + 0x01EBE'i32, 501, + 0x01EC0'i32, 501, + 0x01EC2'i32, 501, + 0x01EC4'i32, 501, + 0x01EC6'i32, 501, + 0x01EC8'i32, 501, + 0x01ECA'i32, 501, + 0x01ECC'i32, 501, + 0x01ECE'i32, 501, + 0x01ED0'i32, 501, + 0x01ED2'i32, 501, + 0x01ED4'i32, 501, + 0x01ED6'i32, 501, + 0x01ED8'i32, 501, + 0x01EDA'i32, 501, + 0x01EDC'i32, 501, + 0x01EDE'i32, 501, + 0x01EE0'i32, 501, + 0x01EE2'i32, 501, + 0x01EE4'i32, 501, + 0x01EE6'i32, 501, + 0x01EE8'i32, 501, + 0x01EEA'i32, 501, + 0x01EEC'i32, 501, + 0x01EEE'i32, 501, + 0x01EF0'i32, 501, + 0x01EF2'i32, 501, + 0x01EF4'i32, 501, + 0x01EF6'i32, 501, + 0x01EF8'i32, 501, + 0x01EFA'i32, 501, + 0x01EFC'i32, 501, + 0x01EFE'i32, 501, + 0x01F59'i32, 492, + 0x01F5B'i32, 492, + 0x01F5D'i32, 492, + 0x01F5F'i32, 492, + 0x01FBC'i32, 491, + 0x01FCC'i32, 491, + 0x01FEC'i32, 493, + 0x01FFC'i32, 491, + 0x02126'i32, -7017, + 0x0212A'i32, -7883, + 0x0212B'i32, -7762, + 0x02132'i32, 528, + 0x02183'i32, 501, + 0x02C60'i32, 501, + 0x02C62'i32, -10243, + 0x02C63'i32, -3314, + 0x02C64'i32, -10227, + 0x02C67'i32, 501, + 0x02C69'i32, 501, + 0x02C6B'i32, 501, + 0x02C6D'i32, -10280, + 0x02C6E'i32, -10249, + 0x02C6F'i32, -10283, + 0x02C70'i32, -10282, + 0x02C72'i32, 501, + 0x02C75'i32, 501, + 0x02C80'i32, 501, + 0x02C82'i32, 501, + 0x02C84'i32, 501, + 0x02C86'i32, 501, + 0x02C88'i32, 501, + 0x02C8A'i32, 501, + 0x02C8C'i32, 501, + 0x02C8E'i32, 501, + 0x02C90'i32, 501, + 0x02C92'i32, 501, + 0x02C94'i32, 501, + 0x02C96'i32, 501, + 0x02C98'i32, 501, + 0x02C9A'i32, 501, + 0x02C9C'i32, 501, + 0x02C9E'i32, 501, + 0x02CA0'i32, 501, + 0x02CA2'i32, 501, + 0x02CA4'i32, 501, + 0x02CA6'i32, 501, + 0x02CA8'i32, 501, + 0x02CAA'i32, 501, + 0x02CAC'i32, 501, + 0x02CAE'i32, 501, + 0x02CB0'i32, 501, + 0x02CB2'i32, 501, + 0x02CB4'i32, 501, + 0x02CB6'i32, 501, + 0x02CB8'i32, 501, + 0x02CBA'i32, 501, + 0x02CBC'i32, 501, + 0x02CBE'i32, 501, + 0x02CC0'i32, 501, + 0x02CC2'i32, 501, + 0x02CC4'i32, 501, + 0x02CC6'i32, 501, + 0x02CC8'i32, 501, + 0x02CCA'i32, 501, + 0x02CCC'i32, 501, + 0x02CCE'i32, 501, + 0x02CD0'i32, 501, + 0x02CD2'i32, 501, + 0x02CD4'i32, 501, + 0x02CD6'i32, 501, + 0x02CD8'i32, 501, + 0x02CDA'i32, 501, + 0x02CDC'i32, 501, + 0x02CDE'i32, 501, + 0x02CE0'i32, 501, + 0x02CE2'i32, 501, + 0x02CEB'i32, 501, + 0x02CED'i32, 501, + 0x02CF2'i32, 501, + 0x0A640'i32, 501, + 0x0A642'i32, 501, + 0x0A644'i32, 501, + 0x0A646'i32, 501, + 0x0A648'i32, 501, + 0x0A64A'i32, 501, + 0x0A64C'i32, 501, + 0x0A64E'i32, 501, + 0x0A650'i32, 501, + 0x0A652'i32, 501, + 0x0A654'i32, 501, + 0x0A656'i32, 501, + 0x0A658'i32, 501, + 0x0A65A'i32, 501, + 0x0A65C'i32, 501, + 0x0A65E'i32, 501, + 0x0A660'i32, 501, + 0x0A662'i32, 501, + 0x0A664'i32, 501, + 0x0A666'i32, 501, + 0x0A668'i32, 501, + 0x0A66A'i32, 501, + 0x0A66C'i32, 501, + 0x0A680'i32, 501, + 0x0A682'i32, 501, + 0x0A684'i32, 501, + 0x0A686'i32, 501, + 0x0A688'i32, 501, + 0x0A68A'i32, 501, + 0x0A68C'i32, 501, + 0x0A68E'i32, 501, + 0x0A690'i32, 501, + 0x0A692'i32, 501, + 0x0A694'i32, 501, + 0x0A696'i32, 501, + 0x0A698'i32, 501, + 0x0A69A'i32, 501, + 0x0A722'i32, 501, + 0x0A724'i32, 501, + 0x0A726'i32, 501, + 0x0A728'i32, 501, + 0x0A72A'i32, 501, + 0x0A72C'i32, 501, + 0x0A72E'i32, 501, + 0x0A732'i32, 501, + 0x0A734'i32, 501, + 0x0A736'i32, 501, + 0x0A738'i32, 501, + 0x0A73A'i32, 501, + 0x0A73C'i32, 501, + 0x0A73E'i32, 501, + 0x0A740'i32, 501, + 0x0A742'i32, 501, + 0x0A744'i32, 501, + 0x0A746'i32, 501, + 0x0A748'i32, 501, + 0x0A74A'i32, 501, + 0x0A74C'i32, 501, + 0x0A74E'i32, 501, + 0x0A750'i32, 501, + 0x0A752'i32, 501, + 0x0A754'i32, 501, + 0x0A756'i32, 501, + 0x0A758'i32, 501, + 0x0A75A'i32, 501, + 0x0A75C'i32, 501, + 0x0A75E'i32, 501, + 0x0A760'i32, 501, + 0x0A762'i32, 501, + 0x0A764'i32, 501, + 0x0A766'i32, 501, + 0x0A768'i32, 501, + 0x0A76A'i32, 501, + 0x0A76C'i32, 501, + 0x0A76E'i32, 501, + 0x0A779'i32, 501, + 0x0A77B'i32, 501, + 0x0A77D'i32, -34832, + 0x0A77E'i32, 501, + 0x0A780'i32, 501, + 0x0A782'i32, 501, + 0x0A784'i32, 501, + 0x0A786'i32, 501, + 0x0A78B'i32, 501, + 0x0A78D'i32, -41780, + 0x0A790'i32, 501, + 0x0A792'i32, 501, + 0x0A796'i32, 501, + 0x0A798'i32, 501, + 0x0A79A'i32, 501, + 0x0A79C'i32, 501, + 0x0A79E'i32, 501, + 0x0A7A0'i32, 501, + 0x0A7A2'i32, 501, + 0x0A7A4'i32, 501, + 0x0A7A6'i32, 501, + 0x0A7A8'i32, 501, + 0x0A7AA'i32, -41808, + 0x0A7AB'i32, -41819, + 0x0A7AC'i32, -41815, + 0x0A7AD'i32, -41805, + 0x0A7AE'i32, -41808, + 0x0A7B0'i32, -41758, + 0x0A7B1'i32, -41782, + 0x0A7B2'i32, -41761, + 0x0A7B3'i32, 1428, + 0x0A7B4'i32, 501, + 0x0A7B6'i32, 501, + 0x0A7B8'i32, 501, + 0x0A7BA'i32, 501, + 0x0A7BC'i32, 501, + 0x0A7BE'i32, 501, + 0x0A7C2'i32, 501, + 0x0A7C4'i32, 452, + 0x0A7C5'i32, -41807, + 0x0A7C6'i32, -34884, ] toUpperRanges = [ - 0x00061, 0x0007A, 468, - 0x000E0, 0x000F6, 468, - 0x000F8, 0x000FE, 468, - 0x0023F, 0x00240, 11315, - 0x00256, 0x00257, 295, - 0x0028A, 0x0028B, 283, - 0x0037B, 0x0037D, 630, - 0x003AD, 0x003AF, 463, - 0x003B1, 0x003C1, 468, - 0x003C3, 0x003CB, 468, - 0x003CD, 0x003CE, 437, - 0x00430, 0x0044F, 468, - 0x00450, 0x0045F, 420, - 0x00561, 0x00586, 452, - 0x010D0, 0x010FA, 3508, - 0x010FD, 0x010FF, 3508, - 0x013F8, 0x013FD, 492, - 0x01C83, 0x01C84, -5742, - 0x01F00, 0x01F07, 508, - 0x01F10, 0x01F15, 508, - 0x01F20, 0x01F27, 508, - 0x01F30, 0x01F37, 508, - 0x01F40, 0x01F45, 508, - 0x01F60, 0x01F67, 508, - 0x01F70, 0x01F71, 574, - 0x01F72, 0x01F75, 586, - 0x01F76, 0x01F77, 600, - 0x01F78, 0x01F79, 628, - 0x01F7A, 0x01F7B, 612, - 0x01F7C, 0x01F7D, 626, - 0x01F80, 0x01F87, 508, - 0x01F90, 0x01F97, 508, - 0x01FA0, 0x01FA7, 508, - 0x01FB0, 0x01FB1, 508, - 0x01FD0, 0x01FD1, 508, - 0x01FE0, 0x01FE1, 508, - 0x02C30, 0x02C5E, 452, - 0x02D00, 0x02D25, -6764, - 0x0AB70, 0x0ABBF, -38364, - 0x0FF41, 0x0FF5A, 468, - 0x10428, 0x1044F, 460, - 0x104D8, 0x104FB, 460, - 0x10CC0, 0x10CF2, 436, - 0x118C0, 0x118DF, 468, - 0x16E60, 0x16E7F, 468, - 0x1E922, 0x1E943, 466, + 0x00061'i32, 0x0007A'i32, 468, + 0x000E0'i32, 0x000F6'i32, 468, + 0x000F8'i32, 0x000FE'i32, 468, + 0x0023F'i32, 0x00240'i32, 11315, + 0x00256'i32, 0x00257'i32, 295, + 0x0028A'i32, 0x0028B'i32, 283, + 0x0037B'i32, 0x0037D'i32, 630, + 0x003AD'i32, 0x003AF'i32, 463, + 0x003B1'i32, 0x003C1'i32, 468, + 0x003C3'i32, 0x003CB'i32, 468, + 0x003CD'i32, 0x003CE'i32, 437, + 0x00430'i32, 0x0044F'i32, 468, + 0x00450'i32, 0x0045F'i32, 420, + 0x00561'i32, 0x00586'i32, 452, + 0x010D0'i32, 0x010FA'i32, 3508, + 0x010FD'i32, 0x010FF'i32, 3508, + 0x013F8'i32, 0x013FD'i32, 492, + 0x01C83'i32, 0x01C84'i32, -5742, + 0x01F00'i32, 0x01F07'i32, 508, + 0x01F10'i32, 0x01F15'i32, 508, + 0x01F20'i32, 0x01F27'i32, 508, + 0x01F30'i32, 0x01F37'i32, 508, + 0x01F40'i32, 0x01F45'i32, 508, + 0x01F60'i32, 0x01F67'i32, 508, + 0x01F70'i32, 0x01F71'i32, 574, + 0x01F72'i32, 0x01F75'i32, 586, + 0x01F76'i32, 0x01F77'i32, 600, + 0x01F78'i32, 0x01F79'i32, 628, + 0x01F7A'i32, 0x01F7B'i32, 612, + 0x01F7C'i32, 0x01F7D'i32, 626, + 0x01F80'i32, 0x01F87'i32, 508, + 0x01F90'i32, 0x01F97'i32, 508, + 0x01FA0'i32, 0x01FA7'i32, 508, + 0x01FB0'i32, 0x01FB1'i32, 508, + 0x01FD0'i32, 0x01FD1'i32, 508, + 0x01FE0'i32, 0x01FE1'i32, 508, + 0x02C30'i32, 0x02C5E'i32, 452, + 0x02D00'i32, 0x02D25'i32, -6764, + 0x0AB70'i32, 0x0ABBF'i32, -38364, + 0x0FF41'i32, 0x0FF5A'i32, 468, + 0x10428'i32, 0x1044F'i32, 460, + 0x104D8'i32, 0x104FB'i32, 460, + 0x10CC0'i32, 0x10CF2'i32, 436, + 0x118C0'i32, 0x118DF'i32, 468, + 0x16E60'i32, 0x16E7F'i32, 468, + 0x1E922'i32, 0x1E943'i32, 466, ] toUpperSinglets = [ - 0x000B5, 1243, - 0x000FF, 621, - 0x00101, 499, - 0x00103, 499, - 0x00105, 499, - 0x00107, 499, - 0x00109, 499, - 0x0010B, 499, - 0x0010D, 499, - 0x0010F, 499, - 0x00111, 499, - 0x00113, 499, - 0x00115, 499, - 0x00117, 499, - 0x00119, 499, - 0x0011B, 499, - 0x0011D, 499, - 0x0011F, 499, - 0x00121, 499, - 0x00123, 499, - 0x00125, 499, - 0x00127, 499, - 0x00129, 499, - 0x0012B, 499, - 0x0012D, 499, - 0x0012F, 499, - 0x00131, 268, - 0x00133, 499, - 0x00135, 499, - 0x00137, 499, - 0x0013A, 499, - 0x0013C, 499, - 0x0013E, 499, - 0x00140, 499, - 0x00142, 499, - 0x00144, 499, - 0x00146, 499, - 0x00148, 499, - 0x0014B, 499, - 0x0014D, 499, - 0x0014F, 499, - 0x00151, 499, - 0x00153, 499, - 0x00155, 499, - 0x00157, 499, - 0x00159, 499, - 0x0015B, 499, - 0x0015D, 499, - 0x0015F, 499, - 0x00161, 499, - 0x00163, 499, - 0x00165, 499, - 0x00167, 499, - 0x00169, 499, - 0x0016B, 499, - 0x0016D, 499, - 0x0016F, 499, - 0x00171, 499, - 0x00173, 499, - 0x00175, 499, - 0x00177, 499, - 0x0017A, 499, - 0x0017C, 499, - 0x0017E, 499, - 0x0017F, 200, - 0x00180, 695, - 0x00183, 499, - 0x00185, 499, - 0x00188, 499, - 0x0018C, 499, - 0x00192, 499, - 0x00195, 597, - 0x00199, 499, - 0x0019A, 663, - 0x0019E, 630, - 0x001A1, 499, - 0x001A3, 499, - 0x001A5, 499, - 0x001A8, 499, - 0x001AD, 499, - 0x001B0, 499, - 0x001B4, 499, - 0x001B6, 499, - 0x001B9, 499, - 0x001BD, 499, - 0x001BF, 556, - 0x001C5, 499, - 0x001C6, 498, - 0x001C8, 499, - 0x001C9, 498, - 0x001CB, 499, - 0x001CC, 498, - 0x001CE, 499, - 0x001D0, 499, - 0x001D2, 499, - 0x001D4, 499, - 0x001D6, 499, - 0x001D8, 499, - 0x001DA, 499, - 0x001DC, 499, - 0x001DD, 421, - 0x001DF, 499, - 0x001E1, 499, - 0x001E3, 499, - 0x001E5, 499, - 0x001E7, 499, - 0x001E9, 499, - 0x001EB, 499, - 0x001ED, 499, - 0x001EF, 499, - 0x001F2, 499, - 0x001F3, 498, - 0x001F5, 499, - 0x001F9, 499, - 0x001FB, 499, - 0x001FD, 499, - 0x001FF, 499, - 0x00201, 499, - 0x00203, 499, - 0x00205, 499, - 0x00207, 499, - 0x00209, 499, - 0x0020B, 499, - 0x0020D, 499, - 0x0020F, 499, - 0x00211, 499, - 0x00213, 499, - 0x00215, 499, - 0x00217, 499, - 0x00219, 499, - 0x0021B, 499, - 0x0021D, 499, - 0x0021F, 499, - 0x00223, 499, - 0x00225, 499, - 0x00227, 499, - 0x00229, 499, - 0x0022B, 499, - 0x0022D, 499, - 0x0022F, 499, - 0x00231, 499, - 0x00233, 499, - 0x0023C, 499, - 0x00242, 499, - 0x00247, 499, - 0x00249, 499, - 0x0024B, 499, - 0x0024D, 499, - 0x0024F, 499, - 0x00250, 11283, - 0x00251, 11280, - 0x00252, 11282, - 0x00253, 290, - 0x00254, 294, - 0x00259, 298, - 0x0025B, 297, - 0x0025C, 42819, - 0x00260, 295, - 0x00261, 42815, - 0x00263, 293, - 0x00265, 42780, - 0x00266, 42808, - 0x00268, 291, - 0x00269, 289, - 0x0026A, 42808, - 0x0026B, 11243, - 0x0026C, 42805, - 0x0026F, 289, - 0x00271, 11249, - 0x00272, 287, - 0x00275, 286, - 0x0027D, 11227, - 0x00280, 282, - 0x00282, 42807, - 0x00283, 282, - 0x00287, 42782, - 0x00288, 282, - 0x00289, 431, - 0x0028C, 429, - 0x00292, 281, - 0x0029D, 42761, - 0x0029E, 42758, - 0x00371, 499, - 0x00373, 499, - 0x00377, 499, - 0x003AC, 462, - 0x003C2, 469, - 0x003CC, 436, - 0x003D0, 438, - 0x003D1, 443, - 0x003D5, 453, - 0x003D6, 446, - 0x003D7, 492, - 0x003D9, 499, - 0x003DB, 499, - 0x003DD, 499, - 0x003DF, 499, - 0x003E1, 499, - 0x003E3, 499, - 0x003E5, 499, - 0x003E7, 499, - 0x003E9, 499, - 0x003EB, 499, - 0x003ED, 499, - 0x003EF, 499, - 0x003F0, 414, - 0x003F1, 420, - 0x003F2, 507, - 0x003F3, 384, - 0x003F5, 404, - 0x003F8, 499, - 0x003FB, 499, - 0x00461, 499, - 0x00463, 499, - 0x00465, 499, - 0x00467, 499, - 0x00469, 499, - 0x0046B, 499, - 0x0046D, 499, - 0x0046F, 499, - 0x00471, 499, - 0x00473, 499, - 0x00475, 499, - 0x00477, 499, - 0x00479, 499, - 0x0047B, 499, - 0x0047D, 499, - 0x0047F, 499, - 0x00481, 499, - 0x0048B, 499, - 0x0048D, 499, - 0x0048F, 499, - 0x00491, 499, - 0x00493, 499, - 0x00495, 499, - 0x00497, 499, - 0x00499, 499, - 0x0049B, 499, - 0x0049D, 499, - 0x0049F, 499, - 0x004A1, 499, - 0x004A3, 499, - 0x004A5, 499, - 0x004A7, 499, - 0x004A9, 499, - 0x004AB, 499, - 0x004AD, 499, - 0x004AF, 499, - 0x004B1, 499, - 0x004B3, 499, - 0x004B5, 499, - 0x004B7, 499, - 0x004B9, 499, - 0x004BB, 499, - 0x004BD, 499, - 0x004BF, 499, - 0x004C2, 499, - 0x004C4, 499, - 0x004C6, 499, - 0x004C8, 499, - 0x004CA, 499, - 0x004CC, 499, - 0x004CE, 499, - 0x004CF, 485, - 0x004D1, 499, - 0x004D3, 499, - 0x004D5, 499, - 0x004D7, 499, - 0x004D9, 499, - 0x004DB, 499, - 0x004DD, 499, - 0x004DF, 499, - 0x004E1, 499, - 0x004E3, 499, - 0x004E5, 499, - 0x004E7, 499, - 0x004E9, 499, - 0x004EB, 499, - 0x004ED, 499, - 0x004EF, 499, - 0x004F1, 499, - 0x004F3, 499, - 0x004F5, 499, - 0x004F7, 499, - 0x004F9, 499, - 0x004FB, 499, - 0x004FD, 499, - 0x004FF, 499, - 0x00501, 499, - 0x00503, 499, - 0x00505, 499, - 0x00507, 499, - 0x00509, 499, - 0x0050B, 499, - 0x0050D, 499, - 0x0050F, 499, - 0x00511, 499, - 0x00513, 499, - 0x00515, 499, - 0x00517, 499, - 0x00519, 499, - 0x0051B, 499, - 0x0051D, 499, - 0x0051F, 499, - 0x00521, 499, - 0x00523, 499, - 0x00525, 499, - 0x00527, 499, - 0x00529, 499, - 0x0052B, 499, - 0x0052D, 499, - 0x0052F, 499, - 0x01C80, -5754, - 0x01C81, -5753, - 0x01C82, -5744, - 0x01C85, -5743, - 0x01C86, -5736, - 0x01C87, -5681, - 0x01C88, 35766, - 0x01D79, 35832, - 0x01D7D, 4314, - 0x01D8E, 35884, - 0x01E01, 499, - 0x01E03, 499, - 0x01E05, 499, - 0x01E07, 499, - 0x01E09, 499, - 0x01E0B, 499, - 0x01E0D, 499, - 0x01E0F, 499, - 0x01E11, 499, - 0x01E13, 499, - 0x01E15, 499, - 0x01E17, 499, - 0x01E19, 499, - 0x01E1B, 499, - 0x01E1D, 499, - 0x01E1F, 499, - 0x01E21, 499, - 0x01E23, 499, - 0x01E25, 499, - 0x01E27, 499, - 0x01E29, 499, - 0x01E2B, 499, - 0x01E2D, 499, - 0x01E2F, 499, - 0x01E31, 499, - 0x01E33, 499, - 0x01E35, 499, - 0x01E37, 499, - 0x01E39, 499, - 0x01E3B, 499, - 0x01E3D, 499, - 0x01E3F, 499, - 0x01E41, 499, - 0x01E43, 499, - 0x01E45, 499, - 0x01E47, 499, - 0x01E49, 499, - 0x01E4B, 499, - 0x01E4D, 499, - 0x01E4F, 499, - 0x01E51, 499, - 0x01E53, 499, - 0x01E55, 499, - 0x01E57, 499, - 0x01E59, 499, - 0x01E5B, 499, - 0x01E5D, 499, - 0x01E5F, 499, - 0x01E61, 499, - 0x01E63, 499, - 0x01E65, 499, - 0x01E67, 499, - 0x01E69, 499, - 0x01E6B, 499, - 0x01E6D, 499, - 0x01E6F, 499, - 0x01E71, 499, - 0x01E73, 499, - 0x01E75, 499, - 0x01E77, 499, - 0x01E79, 499, - 0x01E7B, 499, - 0x01E7D, 499, - 0x01E7F, 499, - 0x01E81, 499, - 0x01E83, 499, - 0x01E85, 499, - 0x01E87, 499, - 0x01E89, 499, - 0x01E8B, 499, - 0x01E8D, 499, - 0x01E8F, 499, - 0x01E91, 499, - 0x01E93, 499, - 0x01E95, 499, - 0x01E9B, 441, - 0x01EA1, 499, - 0x01EA3, 499, - 0x01EA5, 499, - 0x01EA7, 499, - 0x01EA9, 499, - 0x01EAB, 499, - 0x01EAD, 499, - 0x01EAF, 499, - 0x01EB1, 499, - 0x01EB3, 499, - 0x01EB5, 499, - 0x01EB7, 499, - 0x01EB9, 499, - 0x01EBB, 499, - 0x01EBD, 499, - 0x01EBF, 499, - 0x01EC1, 499, - 0x01EC3, 499, - 0x01EC5, 499, - 0x01EC7, 499, - 0x01EC9, 499, - 0x01ECB, 499, - 0x01ECD, 499, - 0x01ECF, 499, - 0x01ED1, 499, - 0x01ED3, 499, - 0x01ED5, 499, - 0x01ED7, 499, - 0x01ED9, 499, - 0x01EDB, 499, - 0x01EDD, 499, - 0x01EDF, 499, - 0x01EE1, 499, - 0x01EE3, 499, - 0x01EE5, 499, - 0x01EE7, 499, - 0x01EE9, 499, - 0x01EEB, 499, - 0x01EED, 499, - 0x01EEF, 499, - 0x01EF1, 499, - 0x01EF3, 499, - 0x01EF5, 499, - 0x01EF7, 499, - 0x01EF9, 499, - 0x01EFB, 499, - 0x01EFD, 499, - 0x01EFF, 499, - 0x01F51, 508, - 0x01F53, 508, - 0x01F55, 508, - 0x01F57, 508, - 0x01FB3, 509, - 0x01FBE, -6705, - 0x01FC3, 509, - 0x01FE5, 507, - 0x01FF3, 509, - 0x0214E, 472, - 0x02184, 499, - 0x02C61, 499, - 0x02C65, -10295, - 0x02C66, -10292, - 0x02C68, 499, - 0x02C6A, 499, - 0x02C6C, 499, - 0x02C73, 499, - 0x02C76, 499, - 0x02C81, 499, - 0x02C83, 499, - 0x02C85, 499, - 0x02C87, 499, - 0x02C89, 499, - 0x02C8B, 499, - 0x02C8D, 499, - 0x02C8F, 499, - 0x02C91, 499, - 0x02C93, 499, - 0x02C95, 499, - 0x02C97, 499, - 0x02C99, 499, - 0x02C9B, 499, - 0x02C9D, 499, - 0x02C9F, 499, - 0x02CA1, 499, - 0x02CA3, 499, - 0x02CA5, 499, - 0x02CA7, 499, - 0x02CA9, 499, - 0x02CAB, 499, - 0x02CAD, 499, - 0x02CAF, 499, - 0x02CB1, 499, - 0x02CB3, 499, - 0x02CB5, 499, - 0x02CB7, 499, - 0x02CB9, 499, - 0x02CBB, 499, - 0x02CBD, 499, - 0x02CBF, 499, - 0x02CC1, 499, - 0x02CC3, 499, - 0x02CC5, 499, - 0x02CC7, 499, - 0x02CC9, 499, - 0x02CCB, 499, - 0x02CCD, 499, - 0x02CCF, 499, - 0x02CD1, 499, - 0x02CD3, 499, - 0x02CD5, 499, - 0x02CD7, 499, - 0x02CD9, 499, - 0x02CDB, 499, - 0x02CDD, 499, - 0x02CDF, 499, - 0x02CE1, 499, - 0x02CE3, 499, - 0x02CEC, 499, - 0x02CEE, 499, - 0x02CF3, 499, - 0x02D27, -6764, - 0x02D2D, -6764, - 0x0A641, 499, - 0x0A643, 499, - 0x0A645, 499, - 0x0A647, 499, - 0x0A649, 499, - 0x0A64B, 499, - 0x0A64D, 499, - 0x0A64F, 499, - 0x0A651, 499, - 0x0A653, 499, - 0x0A655, 499, - 0x0A657, 499, - 0x0A659, 499, - 0x0A65B, 499, - 0x0A65D, 499, - 0x0A65F, 499, - 0x0A661, 499, - 0x0A663, 499, - 0x0A665, 499, - 0x0A667, 499, - 0x0A669, 499, - 0x0A66B, 499, - 0x0A66D, 499, - 0x0A681, 499, - 0x0A683, 499, - 0x0A685, 499, - 0x0A687, 499, - 0x0A689, 499, - 0x0A68B, 499, - 0x0A68D, 499, - 0x0A68F, 499, - 0x0A691, 499, - 0x0A693, 499, - 0x0A695, 499, - 0x0A697, 499, - 0x0A699, 499, - 0x0A69B, 499, - 0x0A723, 499, - 0x0A725, 499, - 0x0A727, 499, - 0x0A729, 499, - 0x0A72B, 499, - 0x0A72D, 499, - 0x0A72F, 499, - 0x0A733, 499, - 0x0A735, 499, - 0x0A737, 499, - 0x0A739, 499, - 0x0A73B, 499, - 0x0A73D, 499, - 0x0A73F, 499, - 0x0A741, 499, - 0x0A743, 499, - 0x0A745, 499, - 0x0A747, 499, - 0x0A749, 499, - 0x0A74B, 499, - 0x0A74D, 499, - 0x0A74F, 499, - 0x0A751, 499, - 0x0A753, 499, - 0x0A755, 499, - 0x0A757, 499, - 0x0A759, 499, - 0x0A75B, 499, - 0x0A75D, 499, - 0x0A75F, 499, - 0x0A761, 499, - 0x0A763, 499, - 0x0A765, 499, - 0x0A767, 499, - 0x0A769, 499, - 0x0A76B, 499, - 0x0A76D, 499, - 0x0A76F, 499, - 0x0A77A, 499, - 0x0A77C, 499, - 0x0A77F, 499, - 0x0A781, 499, - 0x0A783, 499, - 0x0A785, 499, - 0x0A787, 499, - 0x0A78C, 499, - 0x0A791, 499, - 0x0A793, 499, - 0x0A794, 548, - 0x0A797, 499, - 0x0A799, 499, - 0x0A79B, 499, - 0x0A79D, 499, - 0x0A79F, 499, - 0x0A7A1, 499, - 0x0A7A3, 499, - 0x0A7A5, 499, - 0x0A7A7, 499, - 0x0A7A9, 499, - 0x0A7B5, 499, - 0x0A7B7, 499, - 0x0A7B9, 499, - 0x0A7BB, 499, - 0x0A7BD, 499, - 0x0A7BF, 499, - 0x0A7C3, 499, - 0x0AB53, -428, + 0x000B5'i32, 1243, + 0x000FF'i32, 621, + 0x00101'i32, 499, + 0x00103'i32, 499, + 0x00105'i32, 499, + 0x00107'i32, 499, + 0x00109'i32, 499, + 0x0010B'i32, 499, + 0x0010D'i32, 499, + 0x0010F'i32, 499, + 0x00111'i32, 499, + 0x00113'i32, 499, + 0x00115'i32, 499, + 0x00117'i32, 499, + 0x00119'i32, 499, + 0x0011B'i32, 499, + 0x0011D'i32, 499, + 0x0011F'i32, 499, + 0x00121'i32, 499, + 0x00123'i32, 499, + 0x00125'i32, 499, + 0x00127'i32, 499, + 0x00129'i32, 499, + 0x0012B'i32, 499, + 0x0012D'i32, 499, + 0x0012F'i32, 499, + 0x00131'i32, 268, + 0x00133'i32, 499, + 0x00135'i32, 499, + 0x00137'i32, 499, + 0x0013A'i32, 499, + 0x0013C'i32, 499, + 0x0013E'i32, 499, + 0x00140'i32, 499, + 0x00142'i32, 499, + 0x00144'i32, 499, + 0x00146'i32, 499, + 0x00148'i32, 499, + 0x0014B'i32, 499, + 0x0014D'i32, 499, + 0x0014F'i32, 499, + 0x00151'i32, 499, + 0x00153'i32, 499, + 0x00155'i32, 499, + 0x00157'i32, 499, + 0x00159'i32, 499, + 0x0015B'i32, 499, + 0x0015D'i32, 499, + 0x0015F'i32, 499, + 0x00161'i32, 499, + 0x00163'i32, 499, + 0x00165'i32, 499, + 0x00167'i32, 499, + 0x00169'i32, 499, + 0x0016B'i32, 499, + 0x0016D'i32, 499, + 0x0016F'i32, 499, + 0x00171'i32, 499, + 0x00173'i32, 499, + 0x00175'i32, 499, + 0x00177'i32, 499, + 0x0017A'i32, 499, + 0x0017C'i32, 499, + 0x0017E'i32, 499, + 0x0017F'i32, 200, + 0x00180'i32, 695, + 0x00183'i32, 499, + 0x00185'i32, 499, + 0x00188'i32, 499, + 0x0018C'i32, 499, + 0x00192'i32, 499, + 0x00195'i32, 597, + 0x00199'i32, 499, + 0x0019A'i32, 663, + 0x0019E'i32, 630, + 0x001A1'i32, 499, + 0x001A3'i32, 499, + 0x001A5'i32, 499, + 0x001A8'i32, 499, + 0x001AD'i32, 499, + 0x001B0'i32, 499, + 0x001B4'i32, 499, + 0x001B6'i32, 499, + 0x001B9'i32, 499, + 0x001BD'i32, 499, + 0x001BF'i32, 556, + 0x001C5'i32, 499, + 0x001C6'i32, 498, + 0x001C8'i32, 499, + 0x001C9'i32, 498, + 0x001CB'i32, 499, + 0x001CC'i32, 498, + 0x001CE'i32, 499, + 0x001D0'i32, 499, + 0x001D2'i32, 499, + 0x001D4'i32, 499, + 0x001D6'i32, 499, + 0x001D8'i32, 499, + 0x001DA'i32, 499, + 0x001DC'i32, 499, + 0x001DD'i32, 421, + 0x001DF'i32, 499, + 0x001E1'i32, 499, + 0x001E3'i32, 499, + 0x001E5'i32, 499, + 0x001E7'i32, 499, + 0x001E9'i32, 499, + 0x001EB'i32, 499, + 0x001ED'i32, 499, + 0x001EF'i32, 499, + 0x001F2'i32, 499, + 0x001F3'i32, 498, + 0x001F5'i32, 499, + 0x001F9'i32, 499, + 0x001FB'i32, 499, + 0x001FD'i32, 499, + 0x001FF'i32, 499, + 0x00201'i32, 499, + 0x00203'i32, 499, + 0x00205'i32, 499, + 0x00207'i32, 499, + 0x00209'i32, 499, + 0x0020B'i32, 499, + 0x0020D'i32, 499, + 0x0020F'i32, 499, + 0x00211'i32, 499, + 0x00213'i32, 499, + 0x00215'i32, 499, + 0x00217'i32, 499, + 0x00219'i32, 499, + 0x0021B'i32, 499, + 0x0021D'i32, 499, + 0x0021F'i32, 499, + 0x00223'i32, 499, + 0x00225'i32, 499, + 0x00227'i32, 499, + 0x00229'i32, 499, + 0x0022B'i32, 499, + 0x0022D'i32, 499, + 0x0022F'i32, 499, + 0x00231'i32, 499, + 0x00233'i32, 499, + 0x0023C'i32, 499, + 0x00242'i32, 499, + 0x00247'i32, 499, + 0x00249'i32, 499, + 0x0024B'i32, 499, + 0x0024D'i32, 499, + 0x0024F'i32, 499, + 0x00250'i32, 11283, + 0x00251'i32, 11280, + 0x00252'i32, 11282, + 0x00253'i32, 290, + 0x00254'i32, 294, + 0x00259'i32, 298, + 0x0025B'i32, 297, + 0x0025C'i32, 42819, + 0x00260'i32, 295, + 0x00261'i32, 42815, + 0x00263'i32, 293, + 0x00265'i32, 42780, + 0x00266'i32, 42808, + 0x00268'i32, 291, + 0x00269'i32, 289, + 0x0026A'i32, 42808, + 0x0026B'i32, 11243, + 0x0026C'i32, 42805, + 0x0026F'i32, 289, + 0x00271'i32, 11249, + 0x00272'i32, 287, + 0x00275'i32, 286, + 0x0027D'i32, 11227, + 0x00280'i32, 282, + 0x00282'i32, 42807, + 0x00283'i32, 282, + 0x00287'i32, 42782, + 0x00288'i32, 282, + 0x00289'i32, 431, + 0x0028C'i32, 429, + 0x00292'i32, 281, + 0x0029D'i32, 42761, + 0x0029E'i32, 42758, + 0x00371'i32, 499, + 0x00373'i32, 499, + 0x00377'i32, 499, + 0x003AC'i32, 462, + 0x003C2'i32, 469, + 0x003CC'i32, 436, + 0x003D0'i32, 438, + 0x003D1'i32, 443, + 0x003D5'i32, 453, + 0x003D6'i32, 446, + 0x003D7'i32, 492, + 0x003D9'i32, 499, + 0x003DB'i32, 499, + 0x003DD'i32, 499, + 0x003DF'i32, 499, + 0x003E1'i32, 499, + 0x003E3'i32, 499, + 0x003E5'i32, 499, + 0x003E7'i32, 499, + 0x003E9'i32, 499, + 0x003EB'i32, 499, + 0x003ED'i32, 499, + 0x003EF'i32, 499, + 0x003F0'i32, 414, + 0x003F1'i32, 420, + 0x003F2'i32, 507, + 0x003F3'i32, 384, + 0x003F5'i32, 404, + 0x003F8'i32, 499, + 0x003FB'i32, 499, + 0x00461'i32, 499, + 0x00463'i32, 499, + 0x00465'i32, 499, + 0x00467'i32, 499, + 0x00469'i32, 499, + 0x0046B'i32, 499, + 0x0046D'i32, 499, + 0x0046F'i32, 499, + 0x00471'i32, 499, + 0x00473'i32, 499, + 0x00475'i32, 499, + 0x00477'i32, 499, + 0x00479'i32, 499, + 0x0047B'i32, 499, + 0x0047D'i32, 499, + 0x0047F'i32, 499, + 0x00481'i32, 499, + 0x0048B'i32, 499, + 0x0048D'i32, 499, + 0x0048F'i32, 499, + 0x00491'i32, 499, + 0x00493'i32, 499, + 0x00495'i32, 499, + 0x00497'i32, 499, + 0x00499'i32, 499, + 0x0049B'i32, 499, + 0x0049D'i32, 499, + 0x0049F'i32, 499, + 0x004A1'i32, 499, + 0x004A3'i32, 499, + 0x004A5'i32, 499, + 0x004A7'i32, 499, + 0x004A9'i32, 499, + 0x004AB'i32, 499, + 0x004AD'i32, 499, + 0x004AF'i32, 499, + 0x004B1'i32, 499, + 0x004B3'i32, 499, + 0x004B5'i32, 499, + 0x004B7'i32, 499, + 0x004B9'i32, 499, + 0x004BB'i32, 499, + 0x004BD'i32, 499, + 0x004BF'i32, 499, + 0x004C2'i32, 499, + 0x004C4'i32, 499, + 0x004C6'i32, 499, + 0x004C8'i32, 499, + 0x004CA'i32, 499, + 0x004CC'i32, 499, + 0x004CE'i32, 499, + 0x004CF'i32, 485, + 0x004D1'i32, 499, + 0x004D3'i32, 499, + 0x004D5'i32, 499, + 0x004D7'i32, 499, + 0x004D9'i32, 499, + 0x004DB'i32, 499, + 0x004DD'i32, 499, + 0x004DF'i32, 499, + 0x004E1'i32, 499, + 0x004E3'i32, 499, + 0x004E5'i32, 499, + 0x004E7'i32, 499, + 0x004E9'i32, 499, + 0x004EB'i32, 499, + 0x004ED'i32, 499, + 0x004EF'i32, 499, + 0x004F1'i32, 499, + 0x004F3'i32, 499, + 0x004F5'i32, 499, + 0x004F7'i32, 499, + 0x004F9'i32, 499, + 0x004FB'i32, 499, + 0x004FD'i32, 499, + 0x004FF'i32, 499, + 0x00501'i32, 499, + 0x00503'i32, 499, + 0x00505'i32, 499, + 0x00507'i32, 499, + 0x00509'i32, 499, + 0x0050B'i32, 499, + 0x0050D'i32, 499, + 0x0050F'i32, 499, + 0x00511'i32, 499, + 0x00513'i32, 499, + 0x00515'i32, 499, + 0x00517'i32, 499, + 0x00519'i32, 499, + 0x0051B'i32, 499, + 0x0051D'i32, 499, + 0x0051F'i32, 499, + 0x00521'i32, 499, + 0x00523'i32, 499, + 0x00525'i32, 499, + 0x00527'i32, 499, + 0x00529'i32, 499, + 0x0052B'i32, 499, + 0x0052D'i32, 499, + 0x0052F'i32, 499, + 0x01C80'i32, -5754, + 0x01C81'i32, -5753, + 0x01C82'i32, -5744, + 0x01C85'i32, -5743, + 0x01C86'i32, -5736, + 0x01C87'i32, -5681, + 0x01C88'i32, 35766, + 0x01D79'i32, 35832, + 0x01D7D'i32, 4314, + 0x01D8E'i32, 35884, + 0x01E01'i32, 499, + 0x01E03'i32, 499, + 0x01E05'i32, 499, + 0x01E07'i32, 499, + 0x01E09'i32, 499, + 0x01E0B'i32, 499, + 0x01E0D'i32, 499, + 0x01E0F'i32, 499, + 0x01E11'i32, 499, + 0x01E13'i32, 499, + 0x01E15'i32, 499, + 0x01E17'i32, 499, + 0x01E19'i32, 499, + 0x01E1B'i32, 499, + 0x01E1D'i32, 499, + 0x01E1F'i32, 499, + 0x01E21'i32, 499, + 0x01E23'i32, 499, + 0x01E25'i32, 499, + 0x01E27'i32, 499, + 0x01E29'i32, 499, + 0x01E2B'i32, 499, + 0x01E2D'i32, 499, + 0x01E2F'i32, 499, + 0x01E31'i32, 499, + 0x01E33'i32, 499, + 0x01E35'i32, 499, + 0x01E37'i32, 499, + 0x01E39'i32, 499, + 0x01E3B'i32, 499, + 0x01E3D'i32, 499, + 0x01E3F'i32, 499, + 0x01E41'i32, 499, + 0x01E43'i32, 499, + 0x01E45'i32, 499, + 0x01E47'i32, 499, + 0x01E49'i32, 499, + 0x01E4B'i32, 499, + 0x01E4D'i32, 499, + 0x01E4F'i32, 499, + 0x01E51'i32, 499, + 0x01E53'i32, 499, + 0x01E55'i32, 499, + 0x01E57'i32, 499, + 0x01E59'i32, 499, + 0x01E5B'i32, 499, + 0x01E5D'i32, 499, + 0x01E5F'i32, 499, + 0x01E61'i32, 499, + 0x01E63'i32, 499, + 0x01E65'i32, 499, + 0x01E67'i32, 499, + 0x01E69'i32, 499, + 0x01E6B'i32, 499, + 0x01E6D'i32, 499, + 0x01E6F'i32, 499, + 0x01E71'i32, 499, + 0x01E73'i32, 499, + 0x01E75'i32, 499, + 0x01E77'i32, 499, + 0x01E79'i32, 499, + 0x01E7B'i32, 499, + 0x01E7D'i32, 499, + 0x01E7F'i32, 499, + 0x01E81'i32, 499, + 0x01E83'i32, 499, + 0x01E85'i32, 499, + 0x01E87'i32, 499, + 0x01E89'i32, 499, + 0x01E8B'i32, 499, + 0x01E8D'i32, 499, + 0x01E8F'i32, 499, + 0x01E91'i32, 499, + 0x01E93'i32, 499, + 0x01E95'i32, 499, + 0x01E9B'i32, 441, + 0x01EA1'i32, 499, + 0x01EA3'i32, 499, + 0x01EA5'i32, 499, + 0x01EA7'i32, 499, + 0x01EA9'i32, 499, + 0x01EAB'i32, 499, + 0x01EAD'i32, 499, + 0x01EAF'i32, 499, + 0x01EB1'i32, 499, + 0x01EB3'i32, 499, + 0x01EB5'i32, 499, + 0x01EB7'i32, 499, + 0x01EB9'i32, 499, + 0x01EBB'i32, 499, + 0x01EBD'i32, 499, + 0x01EBF'i32, 499, + 0x01EC1'i32, 499, + 0x01EC3'i32, 499, + 0x01EC5'i32, 499, + 0x01EC7'i32, 499, + 0x01EC9'i32, 499, + 0x01ECB'i32, 499, + 0x01ECD'i32, 499, + 0x01ECF'i32, 499, + 0x01ED1'i32, 499, + 0x01ED3'i32, 499, + 0x01ED5'i32, 499, + 0x01ED7'i32, 499, + 0x01ED9'i32, 499, + 0x01EDB'i32, 499, + 0x01EDD'i32, 499, + 0x01EDF'i32, 499, + 0x01EE1'i32, 499, + 0x01EE3'i32, 499, + 0x01EE5'i32, 499, + 0x01EE7'i32, 499, + 0x01EE9'i32, 499, + 0x01EEB'i32, 499, + 0x01EED'i32, 499, + 0x01EEF'i32, 499, + 0x01EF1'i32, 499, + 0x01EF3'i32, 499, + 0x01EF5'i32, 499, + 0x01EF7'i32, 499, + 0x01EF9'i32, 499, + 0x01EFB'i32, 499, + 0x01EFD'i32, 499, + 0x01EFF'i32, 499, + 0x01F51'i32, 508, + 0x01F53'i32, 508, + 0x01F55'i32, 508, + 0x01F57'i32, 508, + 0x01FB3'i32, 509, + 0x01FBE'i32, -6705, + 0x01FC3'i32, 509, + 0x01FE5'i32, 507, + 0x01FF3'i32, 509, + 0x0214E'i32, 472, + 0x02184'i32, 499, + 0x02C61'i32, 499, + 0x02C65'i32, -10295, + 0x02C66'i32, -10292, + 0x02C68'i32, 499, + 0x02C6A'i32, 499, + 0x02C6C'i32, 499, + 0x02C73'i32, 499, + 0x02C76'i32, 499, + 0x02C81'i32, 499, + 0x02C83'i32, 499, + 0x02C85'i32, 499, + 0x02C87'i32, 499, + 0x02C89'i32, 499, + 0x02C8B'i32, 499, + 0x02C8D'i32, 499, + 0x02C8F'i32, 499, + 0x02C91'i32, 499, + 0x02C93'i32, 499, + 0x02C95'i32, 499, + 0x02C97'i32, 499, + 0x02C99'i32, 499, + 0x02C9B'i32, 499, + 0x02C9D'i32, 499, + 0x02C9F'i32, 499, + 0x02CA1'i32, 499, + 0x02CA3'i32, 499, + 0x02CA5'i32, 499, + 0x02CA7'i32, 499, + 0x02CA9'i32, 499, + 0x02CAB'i32, 499, + 0x02CAD'i32, 499, + 0x02CAF'i32, 499, + 0x02CB1'i32, 499, + 0x02CB3'i32, 499, + 0x02CB5'i32, 499, + 0x02CB7'i32, 499, + 0x02CB9'i32, 499, + 0x02CBB'i32, 499, + 0x02CBD'i32, 499, + 0x02CBF'i32, 499, + 0x02CC1'i32, 499, + 0x02CC3'i32, 499, + 0x02CC5'i32, 499, + 0x02CC7'i32, 499, + 0x02CC9'i32, 499, + 0x02CCB'i32, 499, + 0x02CCD'i32, 499, + 0x02CCF'i32, 499, + 0x02CD1'i32, 499, + 0x02CD3'i32, 499, + 0x02CD5'i32, 499, + 0x02CD7'i32, 499, + 0x02CD9'i32, 499, + 0x02CDB'i32, 499, + 0x02CDD'i32, 499, + 0x02CDF'i32, 499, + 0x02CE1'i32, 499, + 0x02CE3'i32, 499, + 0x02CEC'i32, 499, + 0x02CEE'i32, 499, + 0x02CF3'i32, 499, + 0x02D27'i32, -6764, + 0x02D2D'i32, -6764, + 0x0A641'i32, 499, + 0x0A643'i32, 499, + 0x0A645'i32, 499, + 0x0A647'i32, 499, + 0x0A649'i32, 499, + 0x0A64B'i32, 499, + 0x0A64D'i32, 499, + 0x0A64F'i32, 499, + 0x0A651'i32, 499, + 0x0A653'i32, 499, + 0x0A655'i32, 499, + 0x0A657'i32, 499, + 0x0A659'i32, 499, + 0x0A65B'i32, 499, + 0x0A65D'i32, 499, + 0x0A65F'i32, 499, + 0x0A661'i32, 499, + 0x0A663'i32, 499, + 0x0A665'i32, 499, + 0x0A667'i32, 499, + 0x0A669'i32, 499, + 0x0A66B'i32, 499, + 0x0A66D'i32, 499, + 0x0A681'i32, 499, + 0x0A683'i32, 499, + 0x0A685'i32, 499, + 0x0A687'i32, 499, + 0x0A689'i32, 499, + 0x0A68B'i32, 499, + 0x0A68D'i32, 499, + 0x0A68F'i32, 499, + 0x0A691'i32, 499, + 0x0A693'i32, 499, + 0x0A695'i32, 499, + 0x0A697'i32, 499, + 0x0A699'i32, 499, + 0x0A69B'i32, 499, + 0x0A723'i32, 499, + 0x0A725'i32, 499, + 0x0A727'i32, 499, + 0x0A729'i32, 499, + 0x0A72B'i32, 499, + 0x0A72D'i32, 499, + 0x0A72F'i32, 499, + 0x0A733'i32, 499, + 0x0A735'i32, 499, + 0x0A737'i32, 499, + 0x0A739'i32, 499, + 0x0A73B'i32, 499, + 0x0A73D'i32, 499, + 0x0A73F'i32, 499, + 0x0A741'i32, 499, + 0x0A743'i32, 499, + 0x0A745'i32, 499, + 0x0A747'i32, 499, + 0x0A749'i32, 499, + 0x0A74B'i32, 499, + 0x0A74D'i32, 499, + 0x0A74F'i32, 499, + 0x0A751'i32, 499, + 0x0A753'i32, 499, + 0x0A755'i32, 499, + 0x0A757'i32, 499, + 0x0A759'i32, 499, + 0x0A75B'i32, 499, + 0x0A75D'i32, 499, + 0x0A75F'i32, 499, + 0x0A761'i32, 499, + 0x0A763'i32, 499, + 0x0A765'i32, 499, + 0x0A767'i32, 499, + 0x0A769'i32, 499, + 0x0A76B'i32, 499, + 0x0A76D'i32, 499, + 0x0A76F'i32, 499, + 0x0A77A'i32, 499, + 0x0A77C'i32, 499, + 0x0A77F'i32, 499, + 0x0A781'i32, 499, + 0x0A783'i32, 499, + 0x0A785'i32, 499, + 0x0A787'i32, 499, + 0x0A78C'i32, 499, + 0x0A791'i32, 499, + 0x0A793'i32, 499, + 0x0A794'i32, 548, + 0x0A797'i32, 499, + 0x0A799'i32, 499, + 0x0A79B'i32, 499, + 0x0A79D'i32, 499, + 0x0A79F'i32, 499, + 0x0A7A1'i32, 499, + 0x0A7A3'i32, 499, + 0x0A7A5'i32, 499, + 0x0A7A7'i32, 499, + 0x0A7A9'i32, 499, + 0x0A7B5'i32, 499, + 0x0A7B7'i32, 499, + 0x0A7B9'i32, 499, + 0x0A7BB'i32, 499, + 0x0A7BD'i32, 499, + 0x0A7BF'i32, 499, + 0x0A7C3'i32, 499, + 0x0AB53'i32, -428, ] toTitleSinglets = [ - 0x001C4, 501, - 0x001C6, 499, - 0x001C7, 501, - 0x001C9, 499, - 0x001CA, 501, - 0x001CC, 499, - 0x001F1, 501, - 0x001F3, 499, + 0x001C4'i32, 501, + 0x001C6'i32, 499, + 0x001C7'i32, 501, + 0x001C9'i32, 499, + 0x001CA'i32, 501, + 0x001CC'i32, 499, + 0x001F1'i32, 501, + 0x001F3'i32, 499, ] alphaRanges = [ - 0x00041, 0x0005A, - 0x00061, 0x0007A, - 0x000C0, 0x000D6, - 0x000D8, 0x000F6, - 0x000F8, 0x002C1, - 0x002C6, 0x002D1, - 0x002E0, 0x002E4, - 0x00370, 0x00374, - 0x00376, 0x00377, - 0x0037A, 0x0037D, - 0x00388, 0x0038A, - 0x0038E, 0x003A1, - 0x003A3, 0x003F5, - 0x003F7, 0x00481, - 0x0048A, 0x0052F, - 0x00531, 0x00556, - 0x00560, 0x00588, - 0x005D0, 0x005EA, - 0x005EF, 0x005F2, - 0x00620, 0x0064A, - 0x0066E, 0x0066F, - 0x00671, 0x006D3, - 0x006E5, 0x006E6, - 0x006EE, 0x006EF, - 0x006FA, 0x006FC, - 0x00712, 0x0072F, - 0x0074D, 0x007A5, - 0x007CA, 0x007EA, - 0x007F4, 0x007F5, - 0x00800, 0x00815, - 0x00840, 0x00858, - 0x00860, 0x0086A, - 0x008A0, 0x008B4, - 0x008B6, 0x008BD, - 0x00904, 0x00939, - 0x00958, 0x00961, - 0x00971, 0x00980, - 0x00985, 0x0098C, - 0x0098F, 0x00990, - 0x00993, 0x009A8, - 0x009AA, 0x009B0, - 0x009B6, 0x009B9, - 0x009DC, 0x009DD, - 0x009DF, 0x009E1, - 0x009F0, 0x009F1, - 0x00A05, 0x00A0A, - 0x00A0F, 0x00A10, - 0x00A13, 0x00A28, - 0x00A2A, 0x00A30, - 0x00A32, 0x00A33, - 0x00A35, 0x00A36, - 0x00A38, 0x00A39, - 0x00A59, 0x00A5C, - 0x00A72, 0x00A74, - 0x00A85, 0x00A8D, - 0x00A8F, 0x00A91, - 0x00A93, 0x00AA8, - 0x00AAA, 0x00AB0, - 0x00AB2, 0x00AB3, - 0x00AB5, 0x00AB9, - 0x00AE0, 0x00AE1, - 0x00B05, 0x00B0C, - 0x00B0F, 0x00B10, - 0x00B13, 0x00B28, - 0x00B2A, 0x00B30, - 0x00B32, 0x00B33, - 0x00B35, 0x00B39, - 0x00B5C, 0x00B5D, - 0x00B5F, 0x00B61, - 0x00B85, 0x00B8A, - 0x00B8E, 0x00B90, - 0x00B92, 0x00B95, - 0x00B99, 0x00B9A, - 0x00B9E, 0x00B9F, - 0x00BA3, 0x00BA4, - 0x00BA8, 0x00BAA, - 0x00BAE, 0x00BB9, - 0x00C05, 0x00C0C, - 0x00C0E, 0x00C10, - 0x00C12, 0x00C28, - 0x00C2A, 0x00C39, - 0x00C58, 0x00C5A, - 0x00C60, 0x00C61, - 0x00C85, 0x00C8C, - 0x00C8E, 0x00C90, - 0x00C92, 0x00CA8, - 0x00CAA, 0x00CB3, - 0x00CB5, 0x00CB9, - 0x00CE0, 0x00CE1, - 0x00CF1, 0x00CF2, - 0x00D05, 0x00D0C, - 0x00D0E, 0x00D10, - 0x00D12, 0x00D3A, - 0x00D54, 0x00D56, - 0x00D5F, 0x00D61, - 0x00D7A, 0x00D7F, - 0x00D85, 0x00D96, - 0x00D9A, 0x00DB1, - 0x00DB3, 0x00DBB, - 0x00DC0, 0x00DC6, - 0x00E01, 0x00E30, - 0x00E32, 0x00E33, - 0x00E40, 0x00E46, - 0x00E81, 0x00E82, - 0x00E86, 0x00E8A, - 0x00E8C, 0x00EA3, - 0x00EA7, 0x00EB0, - 0x00EB2, 0x00EB3, - 0x00EC0, 0x00EC4, - 0x00EDC, 0x00EDF, - 0x00F40, 0x00F47, - 0x00F49, 0x00F6C, - 0x00F88, 0x00F8C, - 0x01000, 0x0102A, - 0x01050, 0x01055, - 0x0105A, 0x0105D, - 0x01065, 0x01066, - 0x0106E, 0x01070, - 0x01075, 0x01081, - 0x010A0, 0x010C5, - 0x010D0, 0x010FA, - 0x010FC, 0x01248, - 0x0124A, 0x0124D, - 0x01250, 0x01256, - 0x0125A, 0x0125D, - 0x01260, 0x01288, - 0x0128A, 0x0128D, - 0x01290, 0x012B0, - 0x012B2, 0x012B5, - 0x012B8, 0x012BE, - 0x012C2, 0x012C5, - 0x012C8, 0x012D6, - 0x012D8, 0x01310, - 0x01312, 0x01315, - 0x01318, 0x0135A, - 0x01380, 0x0138F, - 0x013A0, 0x013F5, - 0x013F8, 0x013FD, - 0x01401, 0x0166C, - 0x0166F, 0x0167F, - 0x01681, 0x0169A, - 0x016A0, 0x016EA, - 0x016F1, 0x016F8, - 0x01700, 0x0170C, - 0x0170E, 0x01711, - 0x01720, 0x01731, - 0x01740, 0x01751, - 0x01760, 0x0176C, - 0x0176E, 0x01770, - 0x01780, 0x017B3, - 0x01820, 0x01878, - 0x01880, 0x01884, - 0x01887, 0x018A8, - 0x018B0, 0x018F5, - 0x01900, 0x0191E, - 0x01950, 0x0196D, - 0x01970, 0x01974, - 0x01980, 0x019AB, - 0x019B0, 0x019C9, - 0x01A00, 0x01A16, - 0x01A20, 0x01A54, - 0x01B05, 0x01B33, - 0x01B45, 0x01B4B, - 0x01B83, 0x01BA0, - 0x01BAE, 0x01BAF, - 0x01BBA, 0x01BE5, - 0x01C00, 0x01C23, - 0x01C4D, 0x01C4F, - 0x01C5A, 0x01C7D, - 0x01C80, 0x01C88, - 0x01C90, 0x01CBA, - 0x01CBD, 0x01CBF, - 0x01CE9, 0x01CEC, - 0x01CEE, 0x01CF3, - 0x01CF5, 0x01CF6, - 0x01D00, 0x01DBF, - 0x01E00, 0x01F15, - 0x01F18, 0x01F1D, - 0x01F20, 0x01F45, - 0x01F48, 0x01F4D, - 0x01F50, 0x01F57, - 0x01F5F, 0x01F7D, - 0x01F80, 0x01FB4, - 0x01FB6, 0x01FBC, - 0x01FC2, 0x01FC4, - 0x01FC6, 0x01FCC, - 0x01FD0, 0x01FD3, - 0x01FD6, 0x01FDB, - 0x01FE0, 0x01FEC, - 0x01FF2, 0x01FF4, - 0x01FF6, 0x01FFC, - 0x02090, 0x0209C, - 0x0210A, 0x02113, - 0x02119, 0x0211D, - 0x0212A, 0x0212D, - 0x0212F, 0x02139, - 0x0213C, 0x0213F, - 0x02145, 0x02149, - 0x02183, 0x02184, - 0x02C00, 0x02C2E, - 0x02C30, 0x02C5E, - 0x02C60, 0x02CE4, - 0x02CEB, 0x02CEE, - 0x02CF2, 0x02CF3, - 0x02D00, 0x02D25, - 0x02D30, 0x02D67, - 0x02D80, 0x02D96, - 0x02DA0, 0x02DA6, - 0x02DA8, 0x02DAE, - 0x02DB0, 0x02DB6, - 0x02DB8, 0x02DBE, - 0x02DC0, 0x02DC6, - 0x02DC8, 0x02DCE, - 0x02DD0, 0x02DD6, - 0x02DD8, 0x02DDE, - 0x03005, 0x03006, - 0x03031, 0x03035, - 0x0303B, 0x0303C, - 0x03041, 0x03096, - 0x0309D, 0x0309F, - 0x030A1, 0x030FA, - 0x030FC, 0x030FF, - 0x03105, 0x0312F, - 0x03131, 0x0318E, - 0x031A0, 0x031BA, - 0x031F0, 0x031FF, - 0x0A000, 0x0A48C, - 0x0A4D0, 0x0A4FD, - 0x0A500, 0x0A60C, - 0x0A610, 0x0A61F, - 0x0A62A, 0x0A62B, - 0x0A640, 0x0A66E, - 0x0A67F, 0x0A69D, - 0x0A6A0, 0x0A6E5, - 0x0A717, 0x0A71F, - 0x0A722, 0x0A788, - 0x0A78B, 0x0A7BF, - 0x0A7C2, 0x0A7C6, - 0x0A7F7, 0x0A801, - 0x0A803, 0x0A805, - 0x0A807, 0x0A80A, - 0x0A80C, 0x0A822, - 0x0A840, 0x0A873, - 0x0A882, 0x0A8B3, - 0x0A8F2, 0x0A8F7, - 0x0A8FD, 0x0A8FE, - 0x0A90A, 0x0A925, - 0x0A930, 0x0A946, - 0x0A960, 0x0A97C, - 0x0A984, 0x0A9B2, - 0x0A9E0, 0x0A9E4, - 0x0A9E6, 0x0A9EF, - 0x0A9FA, 0x0A9FE, - 0x0AA00, 0x0AA28, - 0x0AA40, 0x0AA42, - 0x0AA44, 0x0AA4B, - 0x0AA60, 0x0AA76, - 0x0AA7E, 0x0AAAF, - 0x0AAB5, 0x0AAB6, - 0x0AAB9, 0x0AABD, - 0x0AADB, 0x0AADD, - 0x0AAE0, 0x0AAEA, - 0x0AAF2, 0x0AAF4, - 0x0AB01, 0x0AB06, - 0x0AB09, 0x0AB0E, - 0x0AB11, 0x0AB16, - 0x0AB20, 0x0AB26, - 0x0AB28, 0x0AB2E, - 0x0AB30, 0x0AB5A, - 0x0AB5C, 0x0AB67, - 0x0AB70, 0x0ABE2, - 0x0D7B0, 0x0D7C6, - 0x0D7CB, 0x0D7FB, - 0x0F900, 0x0FA6D, - 0x0FA70, 0x0FAD9, - 0x0FB00, 0x0FB06, - 0x0FB13, 0x0FB17, - 0x0FB1F, 0x0FB28, - 0x0FB2A, 0x0FB36, - 0x0FB38, 0x0FB3C, - 0x0FB40, 0x0FB41, - 0x0FB43, 0x0FB44, - 0x0FB46, 0x0FBB1, - 0x0FBD3, 0x0FD3D, - 0x0FD50, 0x0FD8F, - 0x0FD92, 0x0FDC7, - 0x0FDF0, 0x0FDFB, - 0x0FE70, 0x0FE74, - 0x0FE76, 0x0FEFC, - 0x0FF21, 0x0FF3A, - 0x0FF41, 0x0FF5A, - 0x0FF66, 0x0FFBE, - 0x0FFC2, 0x0FFC7, - 0x0FFCA, 0x0FFCF, - 0x0FFD2, 0x0FFD7, - 0x0FFDA, 0x0FFDC, - 0x10000, 0x1000B, - 0x1000D, 0x10026, - 0x10028, 0x1003A, - 0x1003C, 0x1003D, - 0x1003F, 0x1004D, - 0x10050, 0x1005D, - 0x10080, 0x100FA, - 0x10280, 0x1029C, - 0x102A0, 0x102D0, - 0x10300, 0x1031F, - 0x1032D, 0x10340, - 0x10342, 0x10349, - 0x10350, 0x10375, - 0x10380, 0x1039D, - 0x103A0, 0x103C3, - 0x103C8, 0x103CF, - 0x10400, 0x1049D, - 0x104B0, 0x104D3, - 0x104D8, 0x104FB, - 0x10500, 0x10527, - 0x10530, 0x10563, - 0x10600, 0x10736, - 0x10740, 0x10755, - 0x10760, 0x10767, - 0x10800, 0x10805, - 0x1080A, 0x10835, - 0x10837, 0x10838, - 0x1083F, 0x10855, - 0x10860, 0x10876, - 0x10880, 0x1089E, - 0x108E0, 0x108F2, - 0x108F4, 0x108F5, - 0x10900, 0x10915, - 0x10920, 0x10939, - 0x10980, 0x109B7, - 0x109BE, 0x109BF, - 0x10A10, 0x10A13, - 0x10A15, 0x10A17, - 0x10A19, 0x10A35, - 0x10A60, 0x10A7C, - 0x10A80, 0x10A9C, - 0x10AC0, 0x10AC7, - 0x10AC9, 0x10AE4, - 0x10B00, 0x10B35, - 0x10B40, 0x10B55, - 0x10B60, 0x10B72, - 0x10B80, 0x10B91, - 0x10C00, 0x10C48, - 0x10C80, 0x10CB2, - 0x10CC0, 0x10CF2, - 0x10D00, 0x10D23, - 0x10F00, 0x10F1C, - 0x10F30, 0x10F45, - 0x10FE0, 0x10FF6, - 0x11003, 0x11037, - 0x11083, 0x110AF, - 0x110D0, 0x110E8, - 0x11103, 0x11126, - 0x11150, 0x11172, - 0x11183, 0x111B2, - 0x111C1, 0x111C4, - 0x11200, 0x11211, - 0x11213, 0x1122B, - 0x11280, 0x11286, - 0x1128A, 0x1128D, - 0x1128F, 0x1129D, - 0x1129F, 0x112A8, - 0x112B0, 0x112DE, - 0x11305, 0x1130C, - 0x1130F, 0x11310, - 0x11313, 0x11328, - 0x1132A, 0x11330, - 0x11332, 0x11333, - 0x11335, 0x11339, - 0x1135D, 0x11361, - 0x11400, 0x11434, - 0x11447, 0x1144A, - 0x11480, 0x114AF, - 0x114C4, 0x114C5, - 0x11580, 0x115AE, - 0x115D8, 0x115DB, - 0x11600, 0x1162F, - 0x11680, 0x116AA, - 0x11700, 0x1171A, - 0x11800, 0x1182B, - 0x118A0, 0x118DF, - 0x119A0, 0x119A7, - 0x119AA, 0x119D0, - 0x11A0B, 0x11A32, - 0x11A5C, 0x11A89, - 0x11AC0, 0x11AF8, - 0x11C00, 0x11C08, - 0x11C0A, 0x11C2E, - 0x11C72, 0x11C8F, - 0x11D00, 0x11D06, - 0x11D08, 0x11D09, - 0x11D0B, 0x11D30, - 0x11D60, 0x11D65, - 0x11D67, 0x11D68, - 0x11D6A, 0x11D89, - 0x11EE0, 0x11EF2, - 0x12000, 0x12399, - 0x12480, 0x12543, - 0x13000, 0x1342E, - 0x14400, 0x14646, - 0x16800, 0x16A38, - 0x16A40, 0x16A5E, - 0x16AD0, 0x16AED, - 0x16B00, 0x16B2F, - 0x16B40, 0x16B43, - 0x16B63, 0x16B77, - 0x16B7D, 0x16B8F, - 0x16E40, 0x16E7F, - 0x16F00, 0x16F4A, - 0x16F93, 0x16F9F, - 0x16FE0, 0x16FE1, - 0x18800, 0x18AF2, - 0x1B000, 0x1B11E, - 0x1B150, 0x1B152, - 0x1B164, 0x1B167, - 0x1B170, 0x1B2FB, - 0x1BC00, 0x1BC6A, - 0x1BC70, 0x1BC7C, - 0x1BC80, 0x1BC88, - 0x1BC90, 0x1BC99, - 0x1D400, 0x1D454, - 0x1D456, 0x1D49C, - 0x1D49E, 0x1D49F, - 0x1D4A5, 0x1D4A6, - 0x1D4A9, 0x1D4AC, - 0x1D4AE, 0x1D4B9, - 0x1D4BD, 0x1D4C3, - 0x1D4C5, 0x1D505, - 0x1D507, 0x1D50A, - 0x1D50D, 0x1D514, - 0x1D516, 0x1D51C, - 0x1D51E, 0x1D539, - 0x1D53B, 0x1D53E, - 0x1D540, 0x1D544, - 0x1D54A, 0x1D550, - 0x1D552, 0x1D6A5, - 0x1D6A8, 0x1D6C0, - 0x1D6C2, 0x1D6DA, - 0x1D6DC, 0x1D6FA, - 0x1D6FC, 0x1D714, - 0x1D716, 0x1D734, - 0x1D736, 0x1D74E, - 0x1D750, 0x1D76E, - 0x1D770, 0x1D788, - 0x1D78A, 0x1D7A8, - 0x1D7AA, 0x1D7C2, - 0x1D7C4, 0x1D7CB, - 0x1E100, 0x1E12C, - 0x1E137, 0x1E13D, - 0x1E2C0, 0x1E2EB, - 0x1E800, 0x1E8C4, - 0x1E900, 0x1E943, - 0x1EE00, 0x1EE03, - 0x1EE05, 0x1EE1F, - 0x1EE21, 0x1EE22, - 0x1EE29, 0x1EE32, - 0x1EE34, 0x1EE37, - 0x1EE4D, 0x1EE4F, - 0x1EE51, 0x1EE52, - 0x1EE61, 0x1EE62, - 0x1EE67, 0x1EE6A, - 0x1EE6C, 0x1EE72, - 0x1EE74, 0x1EE77, - 0x1EE79, 0x1EE7C, - 0x1EE80, 0x1EE89, - 0x1EE8B, 0x1EE9B, - 0x1EEA1, 0x1EEA3, - 0x1EEA5, 0x1EEA9, - 0x1EEAB, 0x1EEBB, - 0x2F800, 0x2FA1D, + 0x00041'i32, 0x0005A'i32, + 0x00061'i32, 0x0007A'i32, + 0x000C0'i32, 0x000D6'i32, + 0x000D8'i32, 0x000F6'i32, + 0x000F8'i32, 0x002C1'i32, + 0x002C6'i32, 0x002D1'i32, + 0x002E0'i32, 0x002E4'i32, + 0x00370'i32, 0x00374'i32, + 0x00376'i32, 0x00377'i32, + 0x0037A'i32, 0x0037D'i32, + 0x00388'i32, 0x0038A'i32, + 0x0038E'i32, 0x003A1'i32, + 0x003A3'i32, 0x003F5'i32, + 0x003F7'i32, 0x00481'i32, + 0x0048A'i32, 0x0052F'i32, + 0x00531'i32, 0x00556'i32, + 0x00560'i32, 0x00588'i32, + 0x005D0'i32, 0x005EA'i32, + 0x005EF'i32, 0x005F2'i32, + 0x00620'i32, 0x0064A'i32, + 0x0066E'i32, 0x0066F'i32, + 0x00671'i32, 0x006D3'i32, + 0x006E5'i32, 0x006E6'i32, + 0x006EE'i32, 0x006EF'i32, + 0x006FA'i32, 0x006FC'i32, + 0x00712'i32, 0x0072F'i32, + 0x0074D'i32, 0x007A5'i32, + 0x007CA'i32, 0x007EA'i32, + 0x007F4'i32, 0x007F5'i32, + 0x00800'i32, 0x00815'i32, + 0x00840'i32, 0x00858'i32, + 0x00860'i32, 0x0086A'i32, + 0x008A0'i32, 0x008B4'i32, + 0x008B6'i32, 0x008BD'i32, + 0x00904'i32, 0x00939'i32, + 0x00958'i32, 0x00961'i32, + 0x00971'i32, 0x00980'i32, + 0x00985'i32, 0x0098C'i32, + 0x0098F'i32, 0x00990'i32, + 0x00993'i32, 0x009A8'i32, + 0x009AA'i32, 0x009B0'i32, + 0x009B6'i32, 0x009B9'i32, + 0x009DC'i32, 0x009DD'i32, + 0x009DF'i32, 0x009E1'i32, + 0x009F0'i32, 0x009F1'i32, + 0x00A05'i32, 0x00A0A'i32, + 0x00A0F'i32, 0x00A10'i32, + 0x00A13'i32, 0x00A28'i32, + 0x00A2A'i32, 0x00A30'i32, + 0x00A32'i32, 0x00A33'i32, + 0x00A35'i32, 0x00A36'i32, + 0x00A38'i32, 0x00A39'i32, + 0x00A59'i32, 0x00A5C'i32, + 0x00A72'i32, 0x00A74'i32, + 0x00A85'i32, 0x00A8D'i32, + 0x00A8F'i32, 0x00A91'i32, + 0x00A93'i32, 0x00AA8'i32, + 0x00AAA'i32, 0x00AB0'i32, + 0x00AB2'i32, 0x00AB3'i32, + 0x00AB5'i32, 0x00AB9'i32, + 0x00AE0'i32, 0x00AE1'i32, + 0x00B05'i32, 0x00B0C'i32, + 0x00B0F'i32, 0x00B10'i32, + 0x00B13'i32, 0x00B28'i32, + 0x00B2A'i32, 0x00B30'i32, + 0x00B32'i32, 0x00B33'i32, + 0x00B35'i32, 0x00B39'i32, + 0x00B5C'i32, 0x00B5D'i32, + 0x00B5F'i32, 0x00B61'i32, + 0x00B85'i32, 0x00B8A'i32, + 0x00B8E'i32, 0x00B90'i32, + 0x00B92'i32, 0x00B95'i32, + 0x00B99'i32, 0x00B9A'i32, + 0x00B9E'i32, 0x00B9F'i32, + 0x00BA3'i32, 0x00BA4'i32, + 0x00BA8'i32, 0x00BAA'i32, + 0x00BAE'i32, 0x00BB9'i32, + 0x00C05'i32, 0x00C0C'i32, + 0x00C0E'i32, 0x00C10'i32, + 0x00C12'i32, 0x00C28'i32, + 0x00C2A'i32, 0x00C39'i32, + 0x00C58'i32, 0x00C5A'i32, + 0x00C60'i32, 0x00C61'i32, + 0x00C85'i32, 0x00C8C'i32, + 0x00C8E'i32, 0x00C90'i32, + 0x00C92'i32, 0x00CA8'i32, + 0x00CAA'i32, 0x00CB3'i32, + 0x00CB5'i32, 0x00CB9'i32, + 0x00CE0'i32, 0x00CE1'i32, + 0x00CF1'i32, 0x00CF2'i32, + 0x00D05'i32, 0x00D0C'i32, + 0x00D0E'i32, 0x00D10'i32, + 0x00D12'i32, 0x00D3A'i32, + 0x00D54'i32, 0x00D56'i32, + 0x00D5F'i32, 0x00D61'i32, + 0x00D7A'i32, 0x00D7F'i32, + 0x00D85'i32, 0x00D96'i32, + 0x00D9A'i32, 0x00DB1'i32, + 0x00DB3'i32, 0x00DBB'i32, + 0x00DC0'i32, 0x00DC6'i32, + 0x00E01'i32, 0x00E30'i32, + 0x00E32'i32, 0x00E33'i32, + 0x00E40'i32, 0x00E46'i32, + 0x00E81'i32, 0x00E82'i32, + 0x00E86'i32, 0x00E8A'i32, + 0x00E8C'i32, 0x00EA3'i32, + 0x00EA7'i32, 0x00EB0'i32, + 0x00EB2'i32, 0x00EB3'i32, + 0x00EC0'i32, 0x00EC4'i32, + 0x00EDC'i32, 0x00EDF'i32, + 0x00F40'i32, 0x00F47'i32, + 0x00F49'i32, 0x00F6C'i32, + 0x00F88'i32, 0x00F8C'i32, + 0x01000'i32, 0x0102A'i32, + 0x01050'i32, 0x01055'i32, + 0x0105A'i32, 0x0105D'i32, + 0x01065'i32, 0x01066'i32, + 0x0106E'i32, 0x01070'i32, + 0x01075'i32, 0x01081'i32, + 0x010A0'i32, 0x010C5'i32, + 0x010D0'i32, 0x010FA'i32, + 0x010FC'i32, 0x01248'i32, + 0x0124A'i32, 0x0124D'i32, + 0x01250'i32, 0x01256'i32, + 0x0125A'i32, 0x0125D'i32, + 0x01260'i32, 0x01288'i32, + 0x0128A'i32, 0x0128D'i32, + 0x01290'i32, 0x012B0'i32, + 0x012B2'i32, 0x012B5'i32, + 0x012B8'i32, 0x012BE'i32, + 0x012C2'i32, 0x012C5'i32, + 0x012C8'i32, 0x012D6'i32, + 0x012D8'i32, 0x01310'i32, + 0x01312'i32, 0x01315'i32, + 0x01318'i32, 0x0135A'i32, + 0x01380'i32, 0x0138F'i32, + 0x013A0'i32, 0x013F5'i32, + 0x013F8'i32, 0x013FD'i32, + 0x01401'i32, 0x0166C'i32, + 0x0166F'i32, 0x0167F'i32, + 0x01681'i32, 0x0169A'i32, + 0x016A0'i32, 0x016EA'i32, + 0x016F1'i32, 0x016F8'i32, + 0x01700'i32, 0x0170C'i32, + 0x0170E'i32, 0x01711'i32, + 0x01720'i32, 0x01731'i32, + 0x01740'i32, 0x01751'i32, + 0x01760'i32, 0x0176C'i32, + 0x0176E'i32, 0x01770'i32, + 0x01780'i32, 0x017B3'i32, + 0x01820'i32, 0x01878'i32, + 0x01880'i32, 0x01884'i32, + 0x01887'i32, 0x018A8'i32, + 0x018B0'i32, 0x018F5'i32, + 0x01900'i32, 0x0191E'i32, + 0x01950'i32, 0x0196D'i32, + 0x01970'i32, 0x01974'i32, + 0x01980'i32, 0x019AB'i32, + 0x019B0'i32, 0x019C9'i32, + 0x01A00'i32, 0x01A16'i32, + 0x01A20'i32, 0x01A54'i32, + 0x01B05'i32, 0x01B33'i32, + 0x01B45'i32, 0x01B4B'i32, + 0x01B83'i32, 0x01BA0'i32, + 0x01BAE'i32, 0x01BAF'i32, + 0x01BBA'i32, 0x01BE5'i32, + 0x01C00'i32, 0x01C23'i32, + 0x01C4D'i32, 0x01C4F'i32, + 0x01C5A'i32, 0x01C7D'i32, + 0x01C80'i32, 0x01C88'i32, + 0x01C90'i32, 0x01CBA'i32, + 0x01CBD'i32, 0x01CBF'i32, + 0x01CE9'i32, 0x01CEC'i32, + 0x01CEE'i32, 0x01CF3'i32, + 0x01CF5'i32, 0x01CF6'i32, + 0x01D00'i32, 0x01DBF'i32, + 0x01E00'i32, 0x01F15'i32, + 0x01F18'i32, 0x01F1D'i32, + 0x01F20'i32, 0x01F45'i32, + 0x01F48'i32, 0x01F4D'i32, + 0x01F50'i32, 0x01F57'i32, + 0x01F5F'i32, 0x01F7D'i32, + 0x01F80'i32, 0x01FB4'i32, + 0x01FB6'i32, 0x01FBC'i32, + 0x01FC2'i32, 0x01FC4'i32, + 0x01FC6'i32, 0x01FCC'i32, + 0x01FD0'i32, 0x01FD3'i32, + 0x01FD6'i32, 0x01FDB'i32, + 0x01FE0'i32, 0x01FEC'i32, + 0x01FF2'i32, 0x01FF4'i32, + 0x01FF6'i32, 0x01FFC'i32, + 0x02090'i32, 0x0209C'i32, + 0x0210A'i32, 0x02113'i32, + 0x02119'i32, 0x0211D'i32, + 0x0212A'i32, 0x0212D'i32, + 0x0212F'i32, 0x02139'i32, + 0x0213C'i32, 0x0213F'i32, + 0x02145'i32, 0x02149'i32, + 0x02183'i32, 0x02184'i32, + 0x02C00'i32, 0x02C2E'i32, + 0x02C30'i32, 0x02C5E'i32, + 0x02C60'i32, 0x02CE4'i32, + 0x02CEB'i32, 0x02CEE'i32, + 0x02CF2'i32, 0x02CF3'i32, + 0x02D00'i32, 0x02D25'i32, + 0x02D30'i32, 0x02D67'i32, + 0x02D80'i32, 0x02D96'i32, + 0x02DA0'i32, 0x02DA6'i32, + 0x02DA8'i32, 0x02DAE'i32, + 0x02DB0'i32, 0x02DB6'i32, + 0x02DB8'i32, 0x02DBE'i32, + 0x02DC0'i32, 0x02DC6'i32, + 0x02DC8'i32, 0x02DCE'i32, + 0x02DD0'i32, 0x02DD6'i32, + 0x02DD8'i32, 0x02DDE'i32, + 0x03005'i32, 0x03006'i32, + 0x03031'i32, 0x03035'i32, + 0x0303B'i32, 0x0303C'i32, + 0x03041'i32, 0x03096'i32, + 0x0309D'i32, 0x0309F'i32, + 0x030A1'i32, 0x030FA'i32, + 0x030FC'i32, 0x030FF'i32, + 0x03105'i32, 0x0312F'i32, + 0x03131'i32, 0x0318E'i32, + 0x031A0'i32, 0x031BA'i32, + 0x031F0'i32, 0x031FF'i32, + 0x03400'i32, 0x04DB5'i32, + 0x04E00'i32, 0x09FEF'i32, + 0x0A000'i32, 0x0A48C'i32, + 0x0A4D0'i32, 0x0A4FD'i32, + 0x0A500'i32, 0x0A60C'i32, + 0x0A610'i32, 0x0A61F'i32, + 0x0A62A'i32, 0x0A62B'i32, + 0x0A640'i32, 0x0A66E'i32, + 0x0A67F'i32, 0x0A69D'i32, + 0x0A6A0'i32, 0x0A6E5'i32, + 0x0A717'i32, 0x0A71F'i32, + 0x0A722'i32, 0x0A788'i32, + 0x0A78B'i32, 0x0A7BF'i32, + 0x0A7C2'i32, 0x0A7C6'i32, + 0x0A7F7'i32, 0x0A801'i32, + 0x0A803'i32, 0x0A805'i32, + 0x0A807'i32, 0x0A80A'i32, + 0x0A80C'i32, 0x0A822'i32, + 0x0A840'i32, 0x0A873'i32, + 0x0A882'i32, 0x0A8B3'i32, + 0x0A8F2'i32, 0x0A8F7'i32, + 0x0A8FD'i32, 0x0A8FE'i32, + 0x0A90A'i32, 0x0A925'i32, + 0x0A930'i32, 0x0A946'i32, + 0x0A960'i32, 0x0A97C'i32, + 0x0A984'i32, 0x0A9B2'i32, + 0x0A9E0'i32, 0x0A9E4'i32, + 0x0A9E6'i32, 0x0A9EF'i32, + 0x0A9FA'i32, 0x0A9FE'i32, + 0x0AA00'i32, 0x0AA28'i32, + 0x0AA40'i32, 0x0AA42'i32, + 0x0AA44'i32, 0x0AA4B'i32, + 0x0AA60'i32, 0x0AA76'i32, + 0x0AA7E'i32, 0x0AAAF'i32, + 0x0AAB5'i32, 0x0AAB6'i32, + 0x0AAB9'i32, 0x0AABD'i32, + 0x0AADB'i32, 0x0AADD'i32, + 0x0AAE0'i32, 0x0AAEA'i32, + 0x0AAF2'i32, 0x0AAF4'i32, + 0x0AB01'i32, 0x0AB06'i32, + 0x0AB09'i32, 0x0AB0E'i32, + 0x0AB11'i32, 0x0AB16'i32, + 0x0AB20'i32, 0x0AB26'i32, + 0x0AB28'i32, 0x0AB2E'i32, + 0x0AB30'i32, 0x0AB5A'i32, + 0x0AB5C'i32, 0x0AB67'i32, + 0x0AB70'i32, 0x0ABE2'i32, + 0x0AC00'i32, 0x0D7A3'i32, + 0x0D7B0'i32, 0x0D7C6'i32, + 0x0D7CB'i32, 0x0D7FB'i32, + 0x0F900'i32, 0x0FA6D'i32, + 0x0FA70'i32, 0x0FAD9'i32, + 0x0FB00'i32, 0x0FB06'i32, + 0x0FB13'i32, 0x0FB17'i32, + 0x0FB1F'i32, 0x0FB28'i32, + 0x0FB2A'i32, 0x0FB36'i32, + 0x0FB38'i32, 0x0FB3C'i32, + 0x0FB40'i32, 0x0FB41'i32, + 0x0FB43'i32, 0x0FB44'i32, + 0x0FB46'i32, 0x0FBB1'i32, + 0x0FBD3'i32, 0x0FD3D'i32, + 0x0FD50'i32, 0x0FD8F'i32, + 0x0FD92'i32, 0x0FDC7'i32, + 0x0FDF0'i32, 0x0FDFB'i32, + 0x0FE70'i32, 0x0FE74'i32, + 0x0FE76'i32, 0x0FEFC'i32, + 0x0FF21'i32, 0x0FF3A'i32, + 0x0FF41'i32, 0x0FF5A'i32, + 0x0FF66'i32, 0x0FFBE'i32, + 0x0FFC2'i32, 0x0FFC7'i32, + 0x0FFCA'i32, 0x0FFCF'i32, + 0x0FFD2'i32, 0x0FFD7'i32, + 0x0FFDA'i32, 0x0FFDC'i32, + 0x10000'i32, 0x1000B'i32, + 0x1000D'i32, 0x10026'i32, + 0x10028'i32, 0x1003A'i32, + 0x1003C'i32, 0x1003D'i32, + 0x1003F'i32, 0x1004D'i32, + 0x10050'i32, 0x1005D'i32, + 0x10080'i32, 0x100FA'i32, + 0x10280'i32, 0x1029C'i32, + 0x102A0'i32, 0x102D0'i32, + 0x10300'i32, 0x1031F'i32, + 0x1032D'i32, 0x10340'i32, + 0x10342'i32, 0x10349'i32, + 0x10350'i32, 0x10375'i32, + 0x10380'i32, 0x1039D'i32, + 0x103A0'i32, 0x103C3'i32, + 0x103C8'i32, 0x103CF'i32, + 0x10400'i32, 0x1049D'i32, + 0x104B0'i32, 0x104D3'i32, + 0x104D8'i32, 0x104FB'i32, + 0x10500'i32, 0x10527'i32, + 0x10530'i32, 0x10563'i32, + 0x10600'i32, 0x10736'i32, + 0x10740'i32, 0x10755'i32, + 0x10760'i32, 0x10767'i32, + 0x10800'i32, 0x10805'i32, + 0x1080A'i32, 0x10835'i32, + 0x10837'i32, 0x10838'i32, + 0x1083F'i32, 0x10855'i32, + 0x10860'i32, 0x10876'i32, + 0x10880'i32, 0x1089E'i32, + 0x108E0'i32, 0x108F2'i32, + 0x108F4'i32, 0x108F5'i32, + 0x10900'i32, 0x10915'i32, + 0x10920'i32, 0x10939'i32, + 0x10980'i32, 0x109B7'i32, + 0x109BE'i32, 0x109BF'i32, + 0x10A10'i32, 0x10A13'i32, + 0x10A15'i32, 0x10A17'i32, + 0x10A19'i32, 0x10A35'i32, + 0x10A60'i32, 0x10A7C'i32, + 0x10A80'i32, 0x10A9C'i32, + 0x10AC0'i32, 0x10AC7'i32, + 0x10AC9'i32, 0x10AE4'i32, + 0x10B00'i32, 0x10B35'i32, + 0x10B40'i32, 0x10B55'i32, + 0x10B60'i32, 0x10B72'i32, + 0x10B80'i32, 0x10B91'i32, + 0x10C00'i32, 0x10C48'i32, + 0x10C80'i32, 0x10CB2'i32, + 0x10CC0'i32, 0x10CF2'i32, + 0x10D00'i32, 0x10D23'i32, + 0x10F00'i32, 0x10F1C'i32, + 0x10F30'i32, 0x10F45'i32, + 0x10FE0'i32, 0x10FF6'i32, + 0x11003'i32, 0x11037'i32, + 0x11083'i32, 0x110AF'i32, + 0x110D0'i32, 0x110E8'i32, + 0x11103'i32, 0x11126'i32, + 0x11150'i32, 0x11172'i32, + 0x11183'i32, 0x111B2'i32, + 0x111C1'i32, 0x111C4'i32, + 0x11200'i32, 0x11211'i32, + 0x11213'i32, 0x1122B'i32, + 0x11280'i32, 0x11286'i32, + 0x1128A'i32, 0x1128D'i32, + 0x1128F'i32, 0x1129D'i32, + 0x1129F'i32, 0x112A8'i32, + 0x112B0'i32, 0x112DE'i32, + 0x11305'i32, 0x1130C'i32, + 0x1130F'i32, 0x11310'i32, + 0x11313'i32, 0x11328'i32, + 0x1132A'i32, 0x11330'i32, + 0x11332'i32, 0x11333'i32, + 0x11335'i32, 0x11339'i32, + 0x1135D'i32, 0x11361'i32, + 0x11400'i32, 0x11434'i32, + 0x11447'i32, 0x1144A'i32, + 0x11480'i32, 0x114AF'i32, + 0x114C4'i32, 0x114C5'i32, + 0x11580'i32, 0x115AE'i32, + 0x115D8'i32, 0x115DB'i32, + 0x11600'i32, 0x1162F'i32, + 0x11680'i32, 0x116AA'i32, + 0x11700'i32, 0x1171A'i32, + 0x11800'i32, 0x1182B'i32, + 0x118A0'i32, 0x118DF'i32, + 0x119A0'i32, 0x119A7'i32, + 0x119AA'i32, 0x119D0'i32, + 0x11A0B'i32, 0x11A32'i32, + 0x11A5C'i32, 0x11A89'i32, + 0x11AC0'i32, 0x11AF8'i32, + 0x11C00'i32, 0x11C08'i32, + 0x11C0A'i32, 0x11C2E'i32, + 0x11C72'i32, 0x11C8F'i32, + 0x11D00'i32, 0x11D06'i32, + 0x11D08'i32, 0x11D09'i32, + 0x11D0B'i32, 0x11D30'i32, + 0x11D60'i32, 0x11D65'i32, + 0x11D67'i32, 0x11D68'i32, + 0x11D6A'i32, 0x11D89'i32, + 0x11EE0'i32, 0x11EF2'i32, + 0x12000'i32, 0x12399'i32, + 0x12480'i32, 0x12543'i32, + 0x13000'i32, 0x1342E'i32, + 0x14400'i32, 0x14646'i32, + 0x16800'i32, 0x16A38'i32, + 0x16A40'i32, 0x16A5E'i32, + 0x16AD0'i32, 0x16AED'i32, + 0x16B00'i32, 0x16B2F'i32, + 0x16B40'i32, 0x16B43'i32, + 0x16B63'i32, 0x16B77'i32, + 0x16B7D'i32, 0x16B8F'i32, + 0x16E40'i32, 0x16E7F'i32, + 0x16F00'i32, 0x16F4A'i32, + 0x16F93'i32, 0x16F9F'i32, + 0x16FE0'i32, 0x16FE1'i32, + 0x17000'i32, 0x187F7'i32, + 0x18800'i32, 0x18AF2'i32, + 0x1B000'i32, 0x1B11E'i32, + 0x1B150'i32, 0x1B152'i32, + 0x1B164'i32, 0x1B167'i32, + 0x1B170'i32, 0x1B2FB'i32, + 0x1BC00'i32, 0x1BC6A'i32, + 0x1BC70'i32, 0x1BC7C'i32, + 0x1BC80'i32, 0x1BC88'i32, + 0x1BC90'i32, 0x1BC99'i32, + 0x1D400'i32, 0x1D454'i32, + 0x1D456'i32, 0x1D49C'i32, + 0x1D49E'i32, 0x1D49F'i32, + 0x1D4A5'i32, 0x1D4A6'i32, + 0x1D4A9'i32, 0x1D4AC'i32, + 0x1D4AE'i32, 0x1D4B9'i32, + 0x1D4BD'i32, 0x1D4C3'i32, + 0x1D4C5'i32, 0x1D505'i32, + 0x1D507'i32, 0x1D50A'i32, + 0x1D50D'i32, 0x1D514'i32, + 0x1D516'i32, 0x1D51C'i32, + 0x1D51E'i32, 0x1D539'i32, + 0x1D53B'i32, 0x1D53E'i32, + 0x1D540'i32, 0x1D544'i32, + 0x1D54A'i32, 0x1D550'i32, + 0x1D552'i32, 0x1D6A5'i32, + 0x1D6A8'i32, 0x1D6C0'i32, + 0x1D6C2'i32, 0x1D6DA'i32, + 0x1D6DC'i32, 0x1D6FA'i32, + 0x1D6FC'i32, 0x1D714'i32, + 0x1D716'i32, 0x1D734'i32, + 0x1D736'i32, 0x1D74E'i32, + 0x1D750'i32, 0x1D76E'i32, + 0x1D770'i32, 0x1D788'i32, + 0x1D78A'i32, 0x1D7A8'i32, + 0x1D7AA'i32, 0x1D7C2'i32, + 0x1D7C4'i32, 0x1D7CB'i32, + 0x1E100'i32, 0x1E12C'i32, + 0x1E137'i32, 0x1E13D'i32, + 0x1E2C0'i32, 0x1E2EB'i32, + 0x1E800'i32, 0x1E8C4'i32, + 0x1E900'i32, 0x1E943'i32, + 0x1EE00'i32, 0x1EE03'i32, + 0x1EE05'i32, 0x1EE1F'i32, + 0x1EE21'i32, 0x1EE22'i32, + 0x1EE29'i32, 0x1EE32'i32, + 0x1EE34'i32, 0x1EE37'i32, + 0x1EE4D'i32, 0x1EE4F'i32, + 0x1EE51'i32, 0x1EE52'i32, + 0x1EE61'i32, 0x1EE62'i32, + 0x1EE67'i32, 0x1EE6A'i32, + 0x1EE6C'i32, 0x1EE72'i32, + 0x1EE74'i32, 0x1EE77'i32, + 0x1EE79'i32, 0x1EE7C'i32, + 0x1EE80'i32, 0x1EE89'i32, + 0x1EE8B'i32, 0x1EE9B'i32, + 0x1EEA1'i32, 0x1EEA3'i32, + 0x1EEA5'i32, 0x1EEA9'i32, + 0x1EEAB'i32, 0x1EEBB'i32, + 0x20000'i32, 0x2A6D6'i32, + 0x2A700'i32, 0x2B734'i32, + 0x2B740'i32, 0x2B81D'i32, + 0x2B820'i32, 0x2CEA1'i32, + 0x2CEB0'i32, 0x2EBE0'i32, + 0x2F800'i32, 0x2FA1D'i32, ] alphaSinglets = [ - 0x000AA, - 0x000B5, - 0x000BA, - 0x002EC, - 0x002EE, - 0x0037F, - 0x00386, - 0x0038C, - 0x00559, - 0x006D5, - 0x006FF, - 0x00710, - 0x007B1, - 0x007FA, - 0x0081A, - 0x00824, - 0x00828, - 0x0093D, - 0x00950, - 0x009B2, - 0x009BD, - 0x009CE, - 0x009FC, - 0x00A5E, - 0x00ABD, - 0x00AD0, - 0x00AF9, - 0x00B3D, - 0x00B71, - 0x00B83, - 0x00B9C, - 0x00BD0, - 0x00C3D, - 0x00C80, - 0x00CBD, - 0x00CDE, - 0x00D3D, - 0x00D4E, - 0x00DBD, - 0x00E84, - 0x00EA5, - 0x00EBD, - 0x00EC6, - 0x00F00, - 0x0103F, - 0x01061, - 0x0108E, - 0x010C7, - 0x010CD, - 0x01258, - 0x012C0, - 0x017D7, - 0x017DC, - 0x018AA, - 0x01AA7, - 0x01CFA, - 0x01F59, - 0x01F5B, - 0x01F5D, - 0x01FBE, - 0x02071, - 0x0207F, - 0x02102, - 0x02107, - 0x02115, - 0x02124, - 0x02126, - 0x02128, - 0x0214E, - 0x02D27, - 0x02D2D, - 0x02D6F, - 0x02E2F, - 0x03400, - 0x04DB5, - 0x04E00, - 0x09FEF, - 0x0A8FB, - 0x0A9CF, - 0x0AA7A, - 0x0AAB1, - 0x0AAC0, - 0x0AAC2, - 0x0AC00, - 0x0D7A3, - 0x0FB1D, - 0x0FB3E, - 0x10808, - 0x1083C, - 0x10A00, - 0x10F27, - 0x11144, - 0x11176, - 0x111DA, - 0x111DC, - 0x11288, - 0x1133D, - 0x11350, - 0x1145F, - 0x114C7, - 0x11644, - 0x116B8, - 0x118FF, - 0x119E1, - 0x119E3, - 0x11A00, - 0x11A3A, - 0x11A50, - 0x11A9D, - 0x11C40, - 0x11D46, - 0x11D98, - 0x16F50, - 0x16FE3, - 0x17000, - 0x187F7, - 0x1D4A2, - 0x1D4BB, - 0x1D546, - 0x1E14E, - 0x1E94B, - 0x1EE24, - 0x1EE27, - 0x1EE39, - 0x1EE3B, - 0x1EE42, - 0x1EE47, - 0x1EE49, - 0x1EE4B, - 0x1EE54, - 0x1EE57, - 0x1EE59, - 0x1EE5B, - 0x1EE5D, - 0x1EE5F, - 0x1EE64, - 0x1EE7E, - 0x20000, - 0x2A6D6, - 0x2A700, - 0x2B734, - 0x2B740, - 0x2B81D, - 0x2B820, - 0x2CEA1, - 0x2CEB0, - 0x2EBE0, + 0x000AA'i32, + 0x000B5'i32, + 0x000BA'i32, + 0x002EC'i32, + 0x002EE'i32, + 0x0037F'i32, + 0x00386'i32, + 0x0038C'i32, + 0x00559'i32, + 0x006D5'i32, + 0x006FF'i32, + 0x00710'i32, + 0x007B1'i32, + 0x007FA'i32, + 0x0081A'i32, + 0x00824'i32, + 0x00828'i32, + 0x0093D'i32, + 0x00950'i32, + 0x009B2'i32, + 0x009BD'i32, + 0x009CE'i32, + 0x009FC'i32, + 0x00A5E'i32, + 0x00ABD'i32, + 0x00AD0'i32, + 0x00AF9'i32, + 0x00B3D'i32, + 0x00B71'i32, + 0x00B83'i32, + 0x00B9C'i32, + 0x00BD0'i32, + 0x00C3D'i32, + 0x00C80'i32, + 0x00CBD'i32, + 0x00CDE'i32, + 0x00D3D'i32, + 0x00D4E'i32, + 0x00DBD'i32, + 0x00E84'i32, + 0x00EA5'i32, + 0x00EBD'i32, + 0x00EC6'i32, + 0x00F00'i32, + 0x0103F'i32, + 0x01061'i32, + 0x0108E'i32, + 0x010C7'i32, + 0x010CD'i32, + 0x01258'i32, + 0x012C0'i32, + 0x017D7'i32, + 0x017DC'i32, + 0x018AA'i32, + 0x01AA7'i32, + 0x01CFA'i32, + 0x01F59'i32, + 0x01F5B'i32, + 0x01F5D'i32, + 0x01FBE'i32, + 0x02071'i32, + 0x0207F'i32, + 0x02102'i32, + 0x02107'i32, + 0x02115'i32, + 0x02124'i32, + 0x02126'i32, + 0x02128'i32, + 0x0214E'i32, + 0x02D27'i32, + 0x02D2D'i32, + 0x02D6F'i32, + 0x02E2F'i32, + 0x0A8FB'i32, + 0x0A9CF'i32, + 0x0AA7A'i32, + 0x0AAB1'i32, + 0x0AAC0'i32, + 0x0AAC2'i32, + 0x0FB1D'i32, + 0x0FB3E'i32, + 0x10808'i32, + 0x1083C'i32, + 0x10A00'i32, + 0x10F27'i32, + 0x11144'i32, + 0x11176'i32, + 0x111DA'i32, + 0x111DC'i32, + 0x11288'i32, + 0x1133D'i32, + 0x11350'i32, + 0x1145F'i32, + 0x114C7'i32, + 0x11644'i32, + 0x116B8'i32, + 0x118FF'i32, + 0x119E1'i32, + 0x119E3'i32, + 0x11A00'i32, + 0x11A3A'i32, + 0x11A50'i32, + 0x11A9D'i32, + 0x11C40'i32, + 0x11D46'i32, + 0x11D98'i32, + 0x16F50'i32, + 0x16FE3'i32, + 0x1D4A2'i32, + 0x1D4BB'i32, + 0x1D546'i32, + 0x1E14E'i32, + 0x1E94B'i32, + 0x1EE24'i32, + 0x1EE27'i32, + 0x1EE39'i32, + 0x1EE3B'i32, + 0x1EE42'i32, + 0x1EE47'i32, + 0x1EE49'i32, + 0x1EE4B'i32, + 0x1EE54'i32, + 0x1EE57'i32, + 0x1EE59'i32, + 0x1EE5B'i32, + 0x1EE5D'i32, + 0x1EE5F'i32, + 0x1EE64'i32, + 0x1EE7E'i32, ] spaceRanges = [ - 0x00009, 0x0000D, - 0x00020, 0x00020, - 0x00085, 0x00085, - 0x000A0, 0x000A0, - 0x01680, 0x01680, - 0x02000, 0x0200A, - 0x02028, 0x02029, - 0x0202F, 0x0202F, - 0x0205F, 0x0205F, - 0x03000, 0x03000, + 0x00009'i32, 0x0000D'i32, + 0x00020'i32, 0x00020'i32, + 0x00085'i32, 0x00085'i32, + 0x000A0'i32, 0x000A0'i32, + 0x01680'i32, 0x01680'i32, + 0x02000'i32, 0x0200A'i32, + 0x02028'i32, 0x02029'i32, + 0x0202F'i32, 0x0202F'i32, + 0x0205F'i32, 0x0205F'i32, + 0x03000'i32, 0x03000'i32, ] unicodeSpaces = [ diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim index e28218a97..513578eda 100644 --- a/lib/pure/ioselects/ioselectors_kqueue.nim +++ b/lib/pure/ioselects/ioselectors_kqueue.nim @@ -194,7 +194,9 @@ when hasThreadSupport: if s.changesLength > 0: if kevent(s.kqFD, addr(s.changes[0]), cint(s.changesLength), nil, 0, nil) == -1: - raiseIOSelectorsError(osLastError()) + let res = osLastError() + if cint(res) != ENOENT: # ignore pipes whose read end is closed + raiseIOSelectorsError(res) s.changesLength = 0 else: template modifyKQueue[T](s: Selector[T], nident: uint, nfilter: cshort, @@ -211,7 +213,9 @@ else: if length > 0: if kevent(s.kqFD, addr(s.changes[0]), length, nil, 0, nil) == -1: - raiseIOSelectorsError(osLastError()) + let res = osLastError() + if cint(res) != ENOENT: # ignore pipes whose read end is closed + raiseIOSelectorsError(res) s.changes.setLen(0) proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle, diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 11bc62b78..6c516395b 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -314,7 +314,7 @@ proc selectInto*[T](s: Selector[T], timeout: int, if timeout != -1: when defined(genode) or defined(freertos) or defined(zephyr) or defined(nuttx): - tv.tv_sec = Time(timeout div 1_000) + tv.tv_sec = posix.Time(timeout div 1_000) else: tv.tv_sec = timeout.int32 div 1_000 tv.tv_usec = (timeout.int32 %% 1_000) * 1_000 diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 704d330c5..53fa7553a 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -634,7 +634,7 @@ proc delete*(obj: JsonNode, key: string) = obj.fields.del(key) proc copy*(p: JsonNode): JsonNode = - ## Performs a deep copy of `a`. + ## Performs a deep copy of `p`. case p.kind of JString: result = newJString(p.str) @@ -991,9 +991,9 @@ when defined(js): else: assert false proc len(x: JsObject): int = - asm """ + {.emit: """ `result` = `x`.length; - """ + """.} proc convertObject(x: JsObject): JsonNode = var isRawNumber = false @@ -1004,14 +1004,15 @@ when defined(js): result.add(x[i].convertObject()) of JObject: result = newJObject() - asm """for (var property in `x`) { + {.emit: """for (var property in `x`) { if (`x`.hasOwnProperty(property)) { - """ + """.} + var nimProperty: cstring var nimValue: JsObject - asm "`nimProperty` = property; `nimValue` = `x`[property];" + {.emit: "`nimProperty` = property; `nimValue` = `x`[property];".} result[$nimProperty] = nimValue.convertObject() - asm "}}" + {.emit: "}}".} of JInt: result = newJInt(x.to(int)) of JFloat: diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index 1b6b2b3a2..1efd97b24 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -104,9 +104,9 @@ proc fillBaseLexer(L: var BaseLexer, pos: int): int = result = 0 proc handleCR*(L: var BaseLexer, pos: int): int = - ## Call this if you scanned over '\c' in the buffer; it returns the + ## Call this if you scanned over `'\c'` in the buffer; it returns the ## position to continue the scanning from. `pos` must be the position - ## of the '\c'. + ## of the `'\c'`. assert(L.buf[pos] == '\c') inc(L.lineNumber) result = fillBaseLexer(L, pos) @@ -115,9 +115,9 @@ proc handleCR*(L: var BaseLexer, pos: int): int = L.lineStart = result proc handleLF*(L: var BaseLexer, pos: int): int = - ## Call this if you scanned over '\L' in the buffer; it returns the + ## Call this if you scanned over `'\L'` in the buffer; it returns the ## position to continue the scanning from. `pos` must be the position - ## of the '\L'. + ## of the `'\L'`. assert(L.buf[pos] == '\L') inc(L.lineNumber) result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result; diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index e3f0240a2..c30f68af8 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -839,6 +839,7 @@ proc addHandler*(handler: Logger) = ## each of those threads. ## ## See also: + ## * `removeHandler proc`_ ## * `getHandlers proc<#getHandlers>`_ runnableExamples: var logger = newConsoleLogger() @@ -846,6 +847,16 @@ proc addHandler*(handler: Logger) = doAssert logger in getHandlers() handlers.add(handler) +proc removeHandler*(handler: Logger) = + ## Removes a logger from the list of registered handlers. + ## + ## Note that for n times a logger is registered, n calls to this proc + ## are required to remove that logger. + for i, hnd in handlers: + if hnd == handler: + handlers.delete(i) + return + proc getHandlers*(): seq[Logger] = ## Returns a list of all the registered handlers. ## diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 00f42ec1d..ed7d2382f 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -58,12 +58,13 @@ import std/private/since # of the standard library! import std/[bitops, fenv] +import system/countbits_impl when defined(nimPreviewSlimSystem): import std/assertions -when defined(c) or defined(cpp): +when not defined(js) and not defined(nimscript): # C proc c_isnan(x: float): bool {.importc: "isnan", header: "<math.h>".} # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. @@ -155,7 +156,7 @@ func fac*(n: int): int = {.push checks: off, line_dir: off, stack_trace: off.} -when defined(posix) and not defined(genode): +when defined(posix) and not defined(genode) and not defined(macosx): {.passl: "-lm".} const @@ -200,7 +201,7 @@ func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = template fn: untyped = result = x != x when nimvm: fn() else: - when defined(js): fn() + when defined(js) or defined(nimscript): fn() else: result = c_isnan(x) when defined(js): @@ -219,13 +220,13 @@ when defined(js): let a = newFloat64Array(buffer) let b = newUint32Array(buffer) a[0] = x - asm """ + {.emit: """ function updateBit(num, bitPos, bitVal) { return (num & ~(1 << bitPos)) | (bitVal << bitPos); } `b`[1] = updateBit(`b`[1], 31, `sgn`); - `result` = `a`[0] - """ + `result` = `a`[0]; + """.} proc signbit*(x: SomeFloat): bool {.inline, since: (1, 5, 1).} = ## Returns true if `x` is negative, false otherwise. @@ -270,7 +271,6 @@ func classify*(x: float): FloatClass = ## Classifies a floating point value. ## ## Returns `x`'s class as specified by the `FloatClass enum<#FloatClass>`_. - ## Doesn't work with `--passc:-ffast-math`. runnableExamples: doAssert classify(0.3) == fcNormal doAssert classify(0.0) == fcZero @@ -279,6 +279,7 @@ func classify*(x: float): FloatClass = doAssert classify(5.0e-324) == fcSubnormal # JavaScript and most C compilers have no classify: + if isNan(x): return fcNan if x == 0.0: if 1.0 / x == Inf: return fcZero @@ -287,7 +288,6 @@ func classify*(x: float): FloatClass = if x * 0.5 == x: if x > 0.0: return fcInf else: return fcNegInf - if x != x: return fcNan if abs(x) < MinFloatNormal: return fcSubnormal return fcNormal @@ -1230,40 +1230,42 @@ func gcd*[T](x, y: T): T = swap x, y abs x -func gcd*(x, y: SomeInteger): SomeInteger = - ## Computes the greatest common (positive) divisor of `x` and `y`, - ## using the binary GCD (aka Stein's) algorithm. - ## - ## **See also:** - ## * `gcd func <#gcd,T,T>`_ for a float version - ## * `lcm func <#lcm,T,T>`_ - runnableExamples: - doAssert gcd(12, 8) == 4 - doAssert gcd(17, 63) == 1 - - when x is SomeSignedInt: - var x = abs(x) - else: - var x = x - when y is SomeSignedInt: - var y = abs(y) - else: - var y = y - - if x == 0: - return y - if y == 0: - return x - - let shift = countTrailingZeroBits(x or y) - y = y shr countTrailingZeroBits(y) - while x != 0: - x = x shr countTrailingZeroBits(x) - if y > x: - swap y, x - x -= y - y shl shift - +when useBuiltins: + ## this func uses bitwise comparisons from C compilers, which are not always available. + func gcd*(x, y: SomeInteger): SomeInteger = + ## Computes the greatest common (positive) divisor of `x` and `y`, + ## using the binary GCD (aka Stein's) algorithm. + ## + ## **See also:** + ## * `gcd func <#gcd,T,T>`_ for a float version + ## * `lcm func <#lcm,T,T>`_ + runnableExamples: + doAssert gcd(12, 8) == 4 + doAssert gcd(17, 63) == 1 + + when x is SomeSignedInt: + var x = abs(x) + else: + var x = x + when y is SomeSignedInt: + var y = abs(y) + else: + var y = y + + if x == 0: + return y + if y == 0: + return x + + let shift = countTrailingZeroBits(x or y) + y = y shr countTrailingZeroBits(y) + while x != 0: + x = x shr countTrailingZeroBits(x) + if y > x: + swap y, x + x -= y + y shl shift + func gcd*[T](x: openArray[T]): T {.since: (1, 1).} = ## Computes the greatest common (positive) divisor of the elements of `x`. ## diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index c65a9c2da..9c3f6d51b 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -100,32 +100,19 @@ proc decode(dest: var openArray[uint8], src: openArray[uint32]) = dest[i+3] = uint8(src[j] shr 24 and 0xff'u32) inc(i, 4) -template slice(s: string, a, b): openArray[uint8] = - when nimvm: - # toOpenArray is not implemented in VM - var s2 = newSeq[uint8](s.len) - for i in 0 ..< s2.len: - s2[i] = uint8(s[i]) - s2 - else: - s.toOpenArrayByte(a, b) - template slice(s: cstring, a, b): openArray[uint8] = when nimvm: # toOpenArray is not implemented in VM - slice($s, a, b) + toOpenArrayByte($s, a, b) else: when defined(js): # toOpenArrayByte for cstring is not implemented in JS - slice($s, a, b) + toOpenArrayByte($s, a, b) else: s.toOpenArrayByte(a, b) template slice(s: openArray[uint8], a, b): openArray[uint8] = - when nimvm: - s[a .. b] - else: - s.toOpenArray(a, b) + s.toOpenArray(a, b) const useMem = declared(copyMem) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index df5b8c46f..8eec551c4 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -35,12 +35,12 @@ proc newEIO(msg: string): ref IOError = new(result) result.msg = msg -proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode = - ## Set the size of open file pointed to by `fh` to `newFileSize` if != -1. - ## Space is only allocated if that is cheaper than writing to the file. This - ## routine returns the last OSErrorCode found rather than raising to support - ## old rollback/clean-up code style. [ Should maybe move to std/osfiles. ] - if newFileSize == -1: +proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode = + ## Set the size of open file pointed to by `fh` to `newFileSize` if != -1, + ## allocating | freeing space from the file system. This routine returns the + ## last OSErrorCode found rather than raising to support old rollback/clean-up + ## code style. [ Should maybe move to std/osfiles. ] + if newFileSize < 0 or newFileSize == oldSize: return when defined(windows): var sizeHigh = int32(newFileSize shr 32) @@ -51,14 +51,18 @@ proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode = setEndOfFile(fh) == 0: result = lastErr else: - var e: cint # posix_fallocate truncates up when needed. - when declared(posix_fallocate): - while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR): - discard - if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1: - result = osLastError() # fallback arguable; Most portable, but allows SEGV - elif e != 0: - result = osLastError() + if newFileSize > oldSize: # grow the file + var e: cint # posix_fallocate truncates up when needed. + when declared(posix_fallocate): + while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR): + discard + if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1: + result = osLastError() # fallback arguable; Most portable BUT allows SEGV + elif e != 0: + result = osLastError() + else: # shrink the file + if ftruncate(fh.cint, newFileSize) == -1: + result = osLastError() type MemFile* = object ## represents a memory mapped file @@ -255,41 +259,31 @@ proc open*(filename: string, mode: FileMode = fmRead, flags = flags or O_CREAT or O_TRUNC var permissionsMode = S_IRUSR or S_IWUSR result.handle = open(filename, flags, permissionsMode) + if result.handle != -1: + if (let e = setFileSize(result.handle.FileHandle, newFileSize); + e != 0.OSErrorCode): fail(e, "error setting file size") else: result.handle = open(filename, flags) if result.handle == -1: - # XXX: errno is supposed to be set here - # Is there an exception that wraps it? fail(osLastError(), "error opening file") - if (let e = setFileSize(result.handle.FileHandle, newFileSize); - e != 0.OSErrorCode): fail(e, "error setting file size") - - if mappedSize != -1: - result.size = mappedSize - else: - var stat: Stat + if mappedSize != -1: #XXX Logic here differs from `when windows` branch .. + result.size = mappedSize #.. which always fstats&Uses min(mappedSize, st). + else: # if newFileSize!=-1: result.size=newFileSize # if trust setFileSize + var stat: Stat #^^.. BUT some FSes (eg. Linux HugeTLBfs) round to 2MiB. if fstat(result.handle, stat) != -1: - # XXX: Hmm, this could be unsafe - # Why is mmap taking int anyway? - result.size = int(stat.st_size) + result.size = stat.st_size.int # int may be 32-bit-unsafe for 2..<4 GiB else: fail(osLastError(), "error getting file size") result.flags = if mapFlags == cint(-1): MAP_SHARED else: mapFlags - #Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set + # Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set if int(result.flags and MAP_PRIVATE) == 0: result.flags = result.flags or MAP_SHARED - result.mem = mmap( - nil, - result.size, - if readonly: PROT_READ else: PROT_READ or PROT_WRITE, - result.flags, - result.handle, - offset) - + let pr = if readonly: PROT_READ else: PROT_READ or PROT_WRITE + result.mem = mmap(nil, result.size, pr, result.flags, result.handle, offset) if result.mem == cast[pointer](MAP_FAILED): fail(osLastError(), "file mapping failed") @@ -353,7 +347,7 @@ proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} = raise newException(IOError, "Cannot resize MemFile opened with allowRemap=false") if newFileSize != f.size: - if (let e = setFileSize(f.handle.FileHandle, newFileSize); + if (let e = setFileSize(f.handle.FileHandle, newFileSize, f.size); e != 0.OSErrorCode): raiseOSError(e) when defined(linux): #Maybe NetBSD, too? # On Linux this can be over 100 times faster than a munmap,mmap cycle. @@ -418,7 +412,7 @@ proc `==`*(x, y: MemSlice): bool = proc `$`*(ms: MemSlice): string {.inline.} = ## Return a Nim string built from a MemSlice. result.setLen(ms.size) - copyMem(addr(result[0]), ms.data, ms.size) + copyMem(result.cstring, ms.data, ms.size) iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline.} = ## Iterates over \[optional `eat`] `delim`-delimited slices in MemFile `mfile`. diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index e7dd2f8f2..ff639e8e5 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -38,1848 +38,704 @@ type mimes: OrderedTable[string, string] const mimes* = { - "123": "application/vnd.lotus-1-2-3", - "1km": "application/vnd.1000minds.decision-model+xml", - "323": "text/h323", - "3dm": "text/vnd.in3d.3dml", - "3dmf": "x-world/x-3dmf", - "3dml": "text/vnd.in3d.3dml", - "3ds": "image/x-3ds", - "3g2": "video/3gpp2", - "3gp": "video/3gpp", - "3gpp": "audio/3gpp", - "3gpp2": "video/3gpp2", - "3mf": "application/vnd.ms-3mfdocument", - "669": "audio/x-mod", - "726": "audio/32kadpcm", - "7z": "application/x-7z-compressed", - "a": "text/plain", - "a2l": "application/a2l", - "aa3": "audio/atrac3", - "aab": "application/x-authorware-bin", - "aac": "audio/x-aac", - "aal": "audio/atrac-advanced-lossless", - "aam": "application/x-authorware-map", - "aas": "application/x-authorware-seg", - "abc": "text/vnd.abc", - "abw": "application/x-abiword", + "ez": "application/andrew-inset", + "aw": "application/applixware", + "atom": "application/atom+xml", + "atomcat": "application/atomcat+xml", + "atomsvc": "application/atomsvc+xml", + "ccxml": "application/ccxml+xml", + "cdmia": "application/cdmi-capability", + "cdmic": "application/cdmi-container", + "cdmid": "application/cdmi-domain", + "cdmio": "application/cdmi-object", + "cdmiq": "application/cdmi-queue", + "cu": "application/cu-seeme", + "davmount": "application/davmount+xml", + "dbk": "application/docbook+xml", + "dssc": "application/dssc+der", + "xdssc": "application/dssc+xml", + "ecma": "application/ecmascript", + "emma": "application/emma+xml", + "epub": "application/epub+zip", + "exi": "application/exi", + "pfr": "application/font-tdpfr", + "gml": "application/gml+xml", + "gpx": "application/gpx+xml", + "gxf": "application/gxf", + "stk": "application/hyperstudio", + "ink": "application/inkml+xml", + "inkml": "application/inkml+xml", + "ipfix": "application/ipfix", + "jar": "application/java-archive", + "ser": "application/java-serialized-object", + "class": "application/java-vm", + "json": "application/json", + "jsonml": "application/jsonml+json", + "lostxml": "application/lost+xml", + "hqx": "application/mac-binhex40", + "cpt": "application/mac-compactpro", + "mads": "application/mads+xml", + "mrc": "application/marc", + "mrcx": "application/marcxml+xml", + "ma": "application/mathematica", + "nb": "application/mathematica", + "mb": "application/mathematica", + "mathml": "application/mathml+xml", + "mbox": "application/mbox", + "mscml": "application/mediaservercontrol+xml", + "metalink": "application/metalink+xml", + "meta4": "application/metalink4+xml", + "mets": "application/mets+xml", + "mods": "application/mods+xml", + "m21": "application/mp21", + "mp21": "application/mp21", + "mp4s": "application/mp4", + "doc": "application/msword", + "dot": "application/msword", + "mxf": "application/mxf", + "bin": "application/octet-stream", + "dms": "application/octet-stream", + "lrf": "application/octet-stream", + "mar": "application/octet-stream", + "so": "application/octet-stream", + "dist": "application/octet-stream", + "distz": "application/octet-stream", + "pkg": "application/octet-stream", + "bpk": "application/octet-stream", + "dump": "application/octet-stream", + "elc": "application/octet-stream", + "deploy": "application/octet-stream", + "oda": "application/oda", + "opf": "application/oebps-package+xml", + "ogx": "application/ogg", + "omdoc": "application/omdoc+xml", + "onetoc": "application/onenote", + "onetoc2": "application/onenote", + "onetmp": "application/onenote", + "onepkg": "application/onenote", + "oxps": "application/oxps", + "xer": "application/patch-ops-error+xml", + "pdf": "application/pdf", + "pgp": "application/pgp-encrypted", + "asc": "application/pgp-signature", + "sig": "application/pgp-signature", + "prf": "application/pics-rules", + "p10": "application/pkcs10", + "p7m": "application/pkcs7-mime", + "p7c": "application/pkcs7-mime", + "p7s": "application/pkcs7-signature", + "p8": "application/pkcs8", "ac": "application/pkix-attr-cert", - "ac3": "audio/ac3", - "acc": "application/vnd.americandynamics.acc", - "ace": "application/x-ace-compressed", - "acn": "audio/asc", - "acu": "application/vnd.acucobol", - "acutc": "application/vnd.acucorp", - "acx": "application/internet-property-stream", - "adp": "audio/adpcm", - "aep": "application/vnd.audiograph", - "afl": "video/animaflex", - "afm": "application/x-font-type1", - "afp": "application/vnd.ibm.modcap", - "ahead": "application/vnd.ahead.space", + "cer": "application/pkix-cert", + "crl": "application/pkix-crl", + "pkipath": "application/pkix-pkipath", + "pki": "application/pkixcmp", + "pls": "application/pls+xml", "ai": "application/postscript", - "aif": "audio/x-aiff", - "aifc": "audio/x-aiff", - "aiff": "audio/x-aiff", - "aim": "application/x-aim", - "aip": "text/x-audiosoft-intra", - "air": "application/vnd.adobe.air-application-installer-package+zip", - "ait": "application/vnd.dvb.ait", - "alc": "chemical/x-alchemy", - "ami": "application/vnd.amiga.ami", - "aml": "application/aml", - "amr": "audio/amr", - "ani": "application/x-navi-animation", - "anx": "application/x-annodex", - "aos": "application/x-nokia-9000-communicator-add-on-software", - "apinotes": "text/apinotes", - "apk": "application/vnd.android.package-archive", - "apkg": "application/vnd.anki", - "apng": "image/apng", - "appcache": "text/cache-manifest", - "appimage": "application/appimage", - "application": "application/x-ms-application", - "apr": "application/vnd.lotus-approach", - "aps": "application/mime", - "apxml": "application/auth-policy+xml", - "arc": "application/x-freearc", - "arj": "application/x-arj", - "art": "message/rfc822", - "asar": "binary/asar", - "asc": "text/plain", - "ascii": "text/vnd.ascii-art", - "asf": "application/vnd.ms-asf", - "asice": "application/vnd.etsi.asic-e+zip", - "asics": "application/vnd.etsi.asic-s+zip", - "asm": "text/x-asm", - "asn": "chemical/x-ncbi-asn1-spec", + "eps": "application/postscript", + "ps": "application/postscript", + "cww": "application/prs.cww", + "pskcxml": "application/pskc+xml", + "rdf": "application/rdf+xml", + "rif": "application/reginfo+xml", + "rnc": "application/relax-ng-compact-syntax", + "rl": "application/resource-lists+xml", + "rld": "application/resource-lists-diff+xml", + "rs": "application/rls-services+xml", + "gbr": "application/rpki-ghostbusters", + "mft": "application/rpki-manifest", + "roa": "application/rpki-roa", + "rsd": "application/rsd+xml", + "rss": "application/rss+xml", + "rtf": "application/rtf", + "sbml": "application/sbml+xml", + "scq": "application/scvp-cv-request", + "scs": "application/scvp-cv-response", + "spq": "application/scvp-vp-request", + "spp": "application/scvp-vp-response", + "sdp": "application/sdp", + "setpay": "application/set-payment-initiation", + "setreg": "application/set-registration-initiation", + "shf": "application/shf+xml", + "smi": "application/smil+xml", + "smil": "application/smil+xml", + "rq": "application/sparql-query", + "srx": "application/sparql-results+xml", + "gram": "application/srgs", + "grxml": "application/srgs+xml", + "sru": "application/sru+xml", + "ssdl": "application/ssdl+xml", + "ssml": "application/ssml+xml", + "tei": "application/tei+xml", + "teicorpus": "application/tei+xml", + "tfi": "application/thraud+xml", + "tsd": "application/timestamped-data", + "plb": "application/vnd.3gpp.pic-bw-large", + "psb": "application/vnd.3gpp.pic-bw-small", + "pvb": "application/vnd.3gpp.pic-bw-var", + "tcap": "application/vnd.3gpp2.tcap", + "pwn": "application/vnd.3m.post-it-notes", "aso": "application/vnd.accpac.simply.aso", - "asp": "text/asp", - "asr": "video/x-ms-asf", - "asx": "video/x-ms-asf", - "at3": "audio/atrac3", + "imp": "application/vnd.accpac.simply.imp", + "acu": "application/vnd.acucobol", "atc": "application/vnd.acucorp", - "atf": "application/atf", - "atfx": "application/atfx", - "atom": "application/atom+xml", - "atomcat": "application/atomcat+xml", - "atomdeleted": "application/atomdeleted+xml", - "atomsrv": "application/atomserv+xml", - "atomsvc": "application/atomsvc+xml", - "atx": "application/vnd.antix.game-component", - "atxml": "application/atxml", - "au": "audio/basic", - "auc": "application/tamp-apex-update-confirm", - "avi": "video/x-msvideo", - "avs": "video/avs-video", - "aw": "application/applixware", - "awb": "audio/amr-wb", - "axa": "audio/x-annodex", - "axs": "application/olescript", - "axv": "video/x-annodex", + "acutc": "application/vnd.acucorp", + "air": "application/vnd.adobe.air-application-installer-package+zip", + "fcdt": "application/vnd.adobe.formscentral.fcdt", + "fxp": "application/vnd.adobe.fxp", + "fxpl": "application/vnd.adobe.fxp", + "xdp": "application/vnd.adobe.xdp+xml", + "xfdf": "application/vnd.adobe.xfdf", + "ahead": "application/vnd.ahead.space", "azf": "application/vnd.airzip.filesecure.azf", "azs": "application/vnd.airzip.filesecure.azs", - "azv": "image/vnd.airzip.accelerator.azv", "azw": "application/vnd.amazon.ebook", - "azw3": "application/vnd.amazon.mobi8-ebook", - "b": "chemical/x-molconn-Z", - "bak": "application/x-trash", - "bar": "application/vnd.qualcomm.brew-app-res", - "bas": "text/plain", - "bash": "text/shell", - "bat": "application/x-msdos-program", - "bcpio": "application/x-bcpio", - "bdf": "application/x-font-bdf", - "bdm": "application/vnd.syncml.dm+wbxml", - "bdoc": "application/bdoc", - "bed": "application/vnd.realvnc.bed", - "bh2": "application/vnd.fujitsu.oasysprs", - "bib": "text/x-bibtex", - "bik": "video/vnd.radgamettools.bink", - "bin": "application/octet-stream", - "bk2": "video/vnd.radgamettools.bink", - "bkm": "application/vnd.nervana", - "blb": "application/x-blorb", - "blend": "binary/blender", - "blorb": "application/x-blorb", - "bm": "image/bmp", - "bmed": "multipart/vnd.bint.med-plus", + "acc": "application/vnd.americandynamics.acc", + "ami": "application/vnd.amiga.ami", + "apk": "application/vnd.android.package-archive", + "cii": "application/vnd.anser-web-certificate-issue-initiation", + "fti": "application/vnd.anser-web-funds-transfer-initiation", + "atx": "application/vnd.antix.game-component", + "mpkg": "application/vnd.apple.installer+xml", + "m3u8": "application/vnd.apple.mpegurl", + "swi": "application/vnd.aristanetworks.swi", + "iota": "application/vnd.astraea-software.iota", + "aep": "application/vnd.audiograph", + "mpm": "application/vnd.blueice.multipass", "bmi": "application/vnd.bmi", - "bmml": "application/vnd.balsamiq.bmml+xml", - "bmp": "image/bmp", - "bmpr": "application/vnd.balsamiq.bmpr", - "boo": "application/book", - "book": "application/book", - "box": "application/vnd.previewsystems.box", - "boz": "application/x-bzip2", - "bpd": "application/vnd.hbci", - "bpk": "application/octet-stream", - "brf": "text/plain", - "bsd": "chemical/x-crossfire", - "bsh": "application/x-bsh", - "bsp": "model/vnd.valve.source.compiled-map", - "btf": "image/prs.btif", - "btif": "image/prs.btif", - "bz": "application/x-bzip", - "bz2": "application/x-bzip2", - "c": "text/x-csrc", - "c++": "text/x-c++src", - "c11amc": "application/vnd.cluetrust.cartomobile-config", - "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg", - "c3d": "chemical/x-chem3d", - "c3ex": "application/cccex", + "rep": "application/vnd.businessobjects", + "cdxml": "application/vnd.chemdraw+xml", + "mmd": "application/vnd.chipnuts.karaoke-mmd", + "cdy": "application/vnd.cinderella", + "cla": "application/vnd.claymore", + "rp9": "application/vnd.cloanto.rp9", + "c4g": "application/vnd.clonk.c4group", "c4d": "application/vnd.clonk.c4group", "c4f": "application/vnd.clonk.c4group", - "c4g": "application/vnd.clonk.c4group", "c4p": "application/vnd.clonk.c4group", "c4u": "application/vnd.clonk.c4group", - "cab": "application/vnd.ms-cab-compressed", - "cac": "chemical/x-cache", - "cache": "application/x-cache", - "caf": "audio/x-caf", - "cap": "application/vnd.tcpdump.pcap", - "car": "application/vnd.curl.car", - "cascii": "chemical/x-cactvs-binary", - "cat": "application/vnd.ms-pki.seccat", - "cb7": "application/x-cbr", - "cba": "application/x-cbr", - "cbin": "chemical/x-cactvs-binary", - "cbor": "application/cbor", - "cbr": "application/x-cbr", - "cbt": "application/x-cbr", - "cbz": "application/vnd.comicbook+zip", - "cc": "text/plain", - "ccad": "application/clariscad", - "ccc": "text/vnd.net2phone.commcenter.command", - "ccmp": "application/ccmp+xml", - "cco": "application/x-cocoa", - "cct": "application/x-director", - "ccxml": "application/ccxml+xml", - "cda": "application/x-cdf", + "c11amc": "application/vnd.cluetrust.cartomobile-config", + "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg", + "csp": "application/vnd.commonspace", "cdbcmsg": "application/vnd.contact.cmsg", - "cdf": "application/x-netcdf", - "cdfx": "application/cdfx+xml", - "cdkey": "application/vnd.mediastation.cdkey", - "cdmia": "application/cdmi-capability", - "cdmic": "application/cdmi-container", - "cdmid": "application/cdmi-domain", - "cdmio": "application/cdmi-object", - "cdmiq": "application/cdmi-queue", - "cdr": "image/x-coreldraw", - "cdt": "image/x-coreldrawtemplate", - "cdx": "chemical/x-cdx", - "cdxml": "application/vnd.chemdraw+xml", - "cdy": "application/vnd.cinderella", - "cea": "application/cea", - "cef": "chemical/x-cxf", - "cellml": "application/cellml+xml", - "cer": "application/pkix-cert", - "cfg": "text/cfg", - "cfs": "application/x-cfs-compressed", - "cgm": "image/cgm", - "cha": "application/x-chat", - "chat": "application/x-chat", - "chm": "application/vnd.ms-htmlhelp", - "chrt": "application/vnd.kde.kchart", - "cif": "chemical/x-cif", - "cii": "application/vnd.anser-web-certificate-issue-initiation", - "cil": "application/vnd.ms-artgalry", - "cl": "application/simple-filter+xml", - "cla": "application/vnd.claymore", - "class": "application/java-vm", + "cmc": "application/vnd.cosmocaller", + "clkx": "application/vnd.crick.clicker", "clkk": "application/vnd.crick.clicker.keyboard", "clkp": "application/vnd.crick.clicker.palette", "clkt": "application/vnd.crick.clicker.template", "clkw": "application/vnd.crick.clicker.wordbank", - "clkx": "application/vnd.crick.clicker", - "clp": "application/x-msclip", - "cls": "text/x-tex", - "clue": "application/clue_info+xml", - "cmake": "text/cmake", - "cmc": "application/vnd.cosmocaller", - "cmdf": "chemical/x-cmdf", - "cml": "chemical/x-cml", - "cmp": "application/vnd.yellowriver-custom-menu", - "cmsc": "application/cms", - "cmx": "image/x-cmx", - "cnd": "text/jcr-cnd", - "cnf": "text/cnf", - "cod": "application/vnd.rim.cod", - "coffee": "application/vnd.coffeescript", - "com": "application/x-msdos-program", - "conf": "text/plain", - "copyright": "text/vnd.debian.copyright", - "cpa": "chemical/x-compass", - "cpio": "application/x-cpio", - "cpkg": "application/vnd.xmpie.cpkg", - "cpl": "application/cpl+xml", - "cpp": "text/x-c++src", - "cpt": "application/mac-compactpro", - "cr2": "image/x-canon-cr2", - "crd": "application/x-mscardfile", - "crl": "application/pkix-crl", - "crt": "application/x-x509-ca-cert", - "crtr": "application/vnd.multiad.creator", - "crw": "image/x-canon-crw", - "crx": "application/x-chrome-extension", - "cryptonote": "application/vnd.rig.cryptonote", - "cs": "text/c#", - "csf": "chemical/x-cache-csf", - "csh": "application/x-csh", - "csl": "application/vnd.citationstyles.style+xml", - "csm": "chemical/x-csml", - "csml": "chemical/x-csml", - "cson": "text/cson", - "csp": "application/vnd.commonspace", - "csrattrs": "application/csrattrs", - "css": "text/css", - "cst": "application/vnd.commonspace", - "csv": "text/csv", - "csvs": "text/csv-schema", - "ctab": "chemical/x-cactvs-binary", - "ctx": "chemical/x-ctx", - "cu": "application/cu-seeme", - "cub": "chemical/x-gaussian-cube", - "cuc": "application/tamp-community-update-confirm", - "curl": "text/vnd.curl", - "cw": "application/prs.cww", - "cww": "application/prs.cww", - "cxf": "chemical/x-cxf", - "cxt": "application/x-director", - "cxx": "text/plain", - "d": "text/x-dsrc", - "dae": "model/vnd.collada+xml", - "daf": "application/vnd.mobius.daf", + "wbs": "application/vnd.criticaltools.wbs+xml", + "pml": "application/vnd.ctc-posml", + "ppd": "application/vnd.cups-ppd", + "car": "application/vnd.curl.car", + "pcurl": "application/vnd.curl.pcurl", "dart": "application/vnd.dart", - "dat": "application/x-ns-proxy-autoconfig", - "dataless": "application/vnd.fdsn.seed", - "davmount": "application/davmount+xml", - "dbk": "application/docbook+xml", - "dcd": "application/dcd", - "dcf": "application/vnd.oma.drm.content", - "dcm": "application/dicom", - "dcr": "application/x-director", - "dcurl": "text/vnd.curl.dcurl", - "dd": "application/vnd.oma.dd+xml", - "dd2": "application/vnd.oma.dd2+xml", - "ddd": "application/vnd.fujixerox.ddd", - "ddf": "application/vnd.syncml.dmddf+xml", - "deb": "application/vnd.debian.binary-package", - "deepv": "application/x-deepv", - "def": "text/plain", - "deploy": "application/octet-stream", - "der": "application/x-x509-ca-cert", - "dfac": "application/vnd.dreamfactory", - "dgc": "application/x-dgc-compressed", - "dib": "image/bmp", - "dic": "text/x-c", - "dif": "video/x-dv", - "diff": "text/x-diff", - "dii": "application/dii", - "dim": "application/vnd.fastcopy-disk-image", - "dir": "application/x-director", - "dis": "application/vnd.mobius.dis", - "disposition-notification": "message/disposition-notification", - "dist": "application/vnd.apple.installer+xml", - "distz": "application/vnd.apple.installer+xml", - "dit": "application/dit", - "djv": "image/vnd.djvu", - "djvu": "image/vnd.djvu", - "dl": "video/dl", - "dll": "application/x-msdos-program", - "dls": "audio/dls", - "dm": "application/vnd.oma.drm.message", - "dmg": "application/x-apple-diskimage", - "dmp": "application/vnd.tcpdump.pcap", - "dms": "text/vnd.dmclientscript", + "rdz": "application/vnd.data-vision.rdz", + "uvf": "application/vnd.dece.data", + "uvvf": "application/vnd.dece.data", + "uvd": "application/vnd.dece.data", + "uvvd": "application/vnd.dece.data", + "uvt": "application/vnd.dece.ttml+xml", + "uvvt": "application/vnd.dece.ttml+xml", + "uvx": "application/vnd.dece.unspecified", + "uvvx": "application/vnd.dece.unspecified", + "uvz": "application/vnd.dece.zip", + "uvvz": "application/vnd.dece.zip", + "fe_launch": "application/vnd.denovo.fcselayout-link", "dna": "application/vnd.dna", - "doc": "application/msword", - "docjson": "application/vnd.document+json", - "docm": "application/vnd.ms-word.document.macroenabled.12", - "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "dor": "model/vnd.gdl", - "dot": "text/vnd.graphviz", - "dotm": "application/vnd.ms-word.template.macroenabled.12", - "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template", - "dp": "application/vnd.osgi.dp", + "mlp": "application/vnd.dolby.mlp", "dpg": "application/vnd.dpgraph", - "dpgraph": "application/vnd.dpgraph", - "dpkg": "application/vnd.xmpie.dpkg", - "dr": "application/vnd.oma.drm.rights+xml", - "dra": "audio/vnd.dra", - "drc": "application/vnd.oma.drm.rights+wbxml", - "drle": "image/dicom-rle", - "drw": "application/drafting", - "dsc": "text/prs.lines.tag", - "dsm": "application/vnd.desmume.movie", - "dssc": "application/dssc+der", - "dtb": "application/x-dtbook+xml", - "dtd": "application/xml-dtd", - "dts": "audio/vnd.dts", - "dtshd": "audio/vnd.dts.hd", - "dump": "application/octet-stream", - "dv": "video/x-dv", - "dvb": "video/vnd.dvb.file", - "dvc": "application/dvcs", - "dvi": "application/x-dvi", - "dwf": "model/vnd.dwf", - "dwg": "image/vnd.dwg", - "dx": "chemical/x-jcamp-dx", - "dxf": "image/vnd.dxf", - "dxp": "application/vnd.spotfire.dxp", - "dxr": "application/x-director", - "dzr": "application/vnd.dzr", - "ear": "binary/zip", - "ecelp4800": "audio/vnd.nuera.ecelp4800", - "ecelp7470": "audio/vnd.nuera.ecelp7470", - "ecelp9600": "audio/vnd.nuera.ecelp9600", - "ecig": "application/vnd.evolv.ecig.settings", - "ecigprofile": "application/vnd.evolv.ecig.profile", - "ecigtheme": "application/vnd.evolv.ecig.theme", - "ecma": "application/ecmascript", - "edm": "application/vnd.novadigm.edm", - "edx": "application/vnd.novadigm.edx", - "efi": "application/efi", - "efif": "application/vnd.picsel", - "ei6": "application/vnd.pg.osasli", - "ejs": "text/ejs", - "el": "text/plain", - "elc": "application/x-bytecode.elisp", - "emb": "chemical/x-embl-dl-nucleotide", - "embl": "chemical/x-embl-dl-nucleotide", - "emf": "image/emf", - "eml": "message/rfc822", - "emm": "application/vnd.ibm.electronic-media", - "emma": "application/emma+xml", - "emotionml": "application/emotionml+xml", - "emz": "application/x-msmetafile", - "ent": "text/xml-external-parsed-entity", - "entity": "application/vnd.nervana", - "env": "application/x-envoy", - "enw": "audio/evrcnw", - "eol": "audio/vnd.digital-winds", - "eot": "application/vnd.ms-fontobject", - "ep": "application/vnd.bluetooth.ep.oob", - "eps": "application/postscript", - "eps2": "application/postscript", - "eps3": "application/postscript", - "epsf": "application/postscript", - "epsi": "application/postscript", - "epub": "application/epub+zip", - "erb": "text/erb", - "erf": "image/x-epson-erf", - "es": "application/ecmascript", - "es3": "application/vnd.eszigno3+xml", - "esa": "application/vnd.osgi.subsystem", - "escn": "text/godot", + "dfac": "application/vnd.dreamfactory", + "kpxx": "application/vnd.ds-keypoint", + "ait": "application/vnd.dvb.ait", + "svc": "application/vnd.dvb.service", + "geo": "application/vnd.dynageo", + "mag": "application/vnd.ecowin.chart", + "nml": "application/vnd.enliven", "esf": "application/vnd.epson.esf", - "espass": "application/vnd.espass-espass+zip", + "msf": "application/vnd.epson.msf", + "qam": "application/vnd.epson.quickanime", + "slt": "application/vnd.epson.salt", + "ssf": "application/vnd.epson.ssf", + "es3": "application/vnd.eszigno3+xml", "et3": "application/vnd.eszigno3+xml", - "etx": "text/x-setext", - "eva": "application/x-eva", - "evb": "audio/evrcb", - "evc": "audio/evrc", - "evw": "audio/evrcwb", - "evy": "application/x-envoy", - "exe": "application/x-msdos-program", - "exi": "application/exi", - "exr": "image/aces", - "ext": "application/vnd.novadigm.ext", - "eyaml": "text/yaml", - "ez": "application/andrew-inset", "ez2": "application/vnd.ezpix-album", "ez3": "application/vnd.ezpix-package", - "f": "text/x-fortran", - "f4v": "video/x-f4v", - "f77": "text/x-fortran", - "f90": "text/plain", - "fb": "application/x-maker", - "fbdoc": "application/x-maker", - "fbs": "image/vnd.fastbidsheet", - "fbx": "model/filmbox", - "fcdt": "application/vnd.adobe.formscentral.fcdt", - "fch": "chemical/x-gaussian-checkpoint", - "fchk": "chemical/x-gaussian-checkpoint", - "fcs": "application/vnd.isac.fcs", "fdf": "application/vnd.fdf", - "fdt": "application/fdt+xml", - "fe_launch": "application/vnd.denovo.fcselayout-link", - "feature": "text/gherkin", - "fg5": "application/vnd.fujitsu.oasysgp", - "fgd": "application/x-director", - "fh": "image/x-freehand", - "fh4": "image/x-freehand", - "fh5": "image/x-freehand", - "fh7": "image/x-freehand", - "fhc": "image/x-freehand", - "fif": "image/fif", - "fig": "application/x-xfig", - "finf": "application/fastinfoset", - "fish": "text/fish", - "fit": "image/fits", - "fits": "image/fits", - "fla": "application/vnd.dtg.local.flash", - "flac": "audio/x-flac", - "fli": "video/x-fli", - "flo": "application/vnd.micrografx.flo", - "flr": "x-world/x-vrml", - "flv": "video/x-flv", - "flw": "application/vnd.kde.kivio", - "flx": "text/vnd.fmi.flexstor", - "fly": "text/vnd.fly", + "mseed": "application/vnd.fdsn.mseed", + "seed": "application/vnd.fdsn.seed", + "dataless": "application/vnd.fdsn.seed", + "gph": "application/vnd.flographit", + "ftc": "application/vnd.fluxtime.clip", "fm": "application/vnd.framemaker", - "fmf": "video/x-atomic3d-feature", - "fnc": "application/vnd.frogans.fnc", - "fo": "application/vnd.software602.filler.form+xml", - "for": "text/x-fortran", - "fpx": "image/vnd.fpx", "frame": "application/vnd.framemaker", - "frl": "application/freeloader", - "frm": "application/vnd.ufdl", + "maker": "application/vnd.framemaker", + "book": "application/vnd.framemaker", + "fnc": "application/vnd.frogans.fnc", + "ltf": "application/vnd.frogans.ltf", "fsc": "application/vnd.fsc.weblaunch", - "fst": "image/vnd.fst", - "ftc": "application/vnd.fluxtime.clip", - "fti": "application/vnd.anser-web-funds-transfer-initiation", - "fts": "image/fits", - "funk": "audio/make", - "fvt": "video/vnd.fvt", - "fxm": "video/x-javafx", - "fxp": "application/vnd.adobe.fxp", - "fxpl": "application/vnd.adobe.fxp", + "oas": "application/vnd.fujitsu.oasys", + "oa2": "application/vnd.fujitsu.oasys2", + "oa3": "application/vnd.fujitsu.oasys3", + "fg5": "application/vnd.fujitsu.oasysgp", + "bh2": "application/vnd.fujitsu.oasysprs", + "ddd": "application/vnd.fujixerox.ddd", + "xdw": "application/vnd.fujixerox.docuworks", + "xbd": "application/vnd.fujixerox.docuworks.binder", "fzs": "application/vnd.fuzzysheet", - "g": "text/plain", - "g2w": "application/vnd.geoplan", - "g3": "image/g3fax", - "g3w": "application/vnd.geospace", - "gac": "application/vnd.groove-account", - "gal": "chemical/x-gaussian-log", - "gam": "application/x-tads", - "gamin": "chemical/x-gamess-input", - "gau": "chemical/x-gaussian-input", - "gbr": "application/rpki-ghostbusters", - "gca": "application/x-gca-compressed", - "gcd": "text/x-pcs-gcd", - "gcf": "application/x-graphing-calculator", - "gcg": "chemical/x-gcg8-sequence", - "gdl": "model/vnd.gdl", - "gdoc": "application/vnd.google-apps.document", - "gemspec": "text/ruby", - "gen": "chemical/x-genbank", - "geo": "application/vnd.dynageo", - "geojson": "application/geo+json", - "gex": "application/vnd.geometry-explorer", - "gf": "application/x-tex-gf", + "txd": "application/vnd.genomatix.tuxedo", "ggb": "application/vnd.geogebra.file", + "ggs": "application/vnd.geogebra.slides", "ggt": "application/vnd.geogebra.tool", - "ghf": "application/vnd.groove-help", - "gif": "image/gif", - "gim": "application/vnd.groove-identity-message", - "gjc": "chemical/x-gaussian-input", - "gjf": "chemical/x-gaussian-input", - "gl": "video/gl", - "glb": "model/gltf-binary", - "gltf": "model/gltf+json", - "gml": "application/gml+xml", + "gex": "application/vnd.geometry-explorer", + "gre": "application/vnd.geometry-explorer", + "gxt": "application/vnd.geonext", + "g2w": "application/vnd.geoplan", + "g3w": "application/vnd.geospace", "gmx": "application/vnd.gmx", - "gnumeric": "application/x-gnumeric", - "go": "text/go", - "gotmpl": "text/gotmpl", - "gph": "application/vnd.flographit", - "gpt": "chemical/x-mopac-graph", - "gpx": "application/gpx+xml", + "kml": "application/vnd.google-earth.kml+xml", + "kmz": "application/vnd.google-earth.kmz", "gqf": "application/vnd.grafeq", "gqs": "application/vnd.grafeq", - "gradle": "text/groovy", - "gram": "application/srgs", - "gramps": "application/x-gramps-xml", - "gre": "application/vnd.geometry-explorer", - "groovy": "text/groovy", + "gac": "application/vnd.groove-account", + "ghf": "application/vnd.groove-help", + "gim": "application/vnd.groove-identity-message", "grv": "application/vnd.groove-injector", - "grxml": "application/srgs+xml", - "gsd": "audio/x-gsm", - "gsf": "application/x-font-ghostscript", - "gsheet": "application/vnd.google-apps.spreadsheet", - "gslides": "application/vnd.google-apps.presentation", - "gsm": "model/vnd.gdl", - "gsp": "application/x-gsp", - "gss": "application/x-gss", - "gtar": "application/x-gtar", "gtm": "application/vnd.groove-tool-message", - "gtw": "model/vnd.gtw", - "gv": "text/vnd.graphviz", - "gxf": "application/gxf", - "gxt": "application/vnd.geonext", - "gyb": "text/gyb", - "gyp": "text/gyp", - "gypi": "text/gyp", - "gz": "application/gzip", - "h": "text/x-chdr", - "h++": "text/x-c++hdr", - "h261": "video/h261", - "h263": "video/h263", - "h264": "video/h264", + "tpl": "application/vnd.groove-tool-template", + "vcg": "application/vnd.groove-vcard", "hal": "application/vnd.hal+xml", - "hbc": "application/vnd.hbci", + "zmm": "application/vnd.handheld-entertainment+xml", "hbci": "application/vnd.hbci", - "hbs": "text/x-handlebars-template", - "hdd": "application/x-virtualbox-hdd", - "hdf": "application/x-hdf", - "hdr": "image/vnd.radiance", - "hdt": "application/vnd.hdt", - "heic": "image/heic", - "heics": "image/heic-sequence", - "heif": "image/heif", - "heifs": "image/heif-sequence", - "help": "application/x-helpfile", - "hgl": "application/vnd.hp-hpgl", - "hh": "text/plain", - "hin": "chemical/x-hin", - "hjson": "application/hjson", - "hlb": "text/x-script", - "hlp": "application/winhlp", - "hpg": "application/vnd.hp-hpgl", + "les": "application/vnd.hhe.lesson-player", "hpgl": "application/vnd.hp-hpgl", - "hpi": "application/vnd.hp-hpid", "hpid": "application/vnd.hp-hpid", - "hpp": "text/x-c++hdr", "hps": "application/vnd.hp-hps", - "hpub": "application/prs.hpub+zip", - "hqx": "application/mac-binhex40", - "hs": "text/x-haskell", - "hta": "application/hta", - "htc": "text/x-component", - "htke": "application/vnd.kenameaapp", - "html": "text/html", - "htt": "text/webviewhtml", - "hvd": "application/vnd.yamaha.hv-dic", - "hvp": "application/vnd.yamaha.hv-voice", - "hvs": "application/vnd.yamaha.hv-script", - "hx": "text/haxe", - "hxml": "text/haxe", - "hxx": "text/plain", - "i2g": "application/vnd.intergeo", - "ic0": "application/vnd.commerce-battelle", - "ic1": "application/vnd.commerce-battelle", - "ic2": "application/vnd.commerce-battelle", - "ic3": "application/vnd.commerce-battelle", - "ic4": "application/vnd.commerce-battelle", - "ic5": "application/vnd.commerce-battelle", - "ic6": "application/vnd.commerce-battelle", - "ic7": "application/vnd.commerce-battelle", - "ic8": "application/vnd.commerce-battelle", - "ica": "application/vnd.commerce-battelle", + "jlt": "application/vnd.hp-jlyt", + "pcl": "application/vnd.hp-pcl", + "pclxl": "application/vnd.hp-pclxl", + "sfd-hdstx": "application/vnd.hydrostatix.sof-data", + "mpy": "application/vnd.ibm.minipay", + "afp": "application/vnd.ibm.modcap", + "listafp": "application/vnd.ibm.modcap", + "list3820": "application/vnd.ibm.modcap", + "irm": "application/vnd.ibm.rights-management", + "sc": "application/vnd.ibm.secure-container", "icc": "application/vnd.iccprofile", - "icd": "application/vnd.commerce-battelle", - "ice": "x-conference/x-cooltalk", - "icf": "application/vnd.commerce-battelle", "icm": "application/vnd.iccprofile", - "icns": "binary/icns", - "ico": "image/x-icon", - "ics": "text/calendar", - "icz": "text/calendar", - "idc": "text/plain", - "idl": "text/idl", - "ief": "image/ief", - "iefs": "image/ief", - "ifb": "text/calendar", - "ifm": "application/vnd.shana.informed.formdata", - "iges": "model/iges", "igl": "application/vnd.igloader", - "igm": "application/vnd.insors.igm", - "ign": "application/vnd.coreos.ignition+json", - "ignition": "application/vnd.coreos.ignition+json", - "igs": "model/iges", - "igx": "application/vnd.micrografx.igx", - "iif": "application/vnd.shana.informed.interchange", - "iii": "application/x-iphone", - "ima": "application/x-ima", - "imap": "application/x-httpd-imap", - "imf": "application/vnd.imagemeter.folder+zip", - "img": "application/octet-stream", - "imgcal": "application/vnd.3lightssoftware.imagescal", - "imi": "application/vnd.imagemeter.image+zip", - "imp": "application/vnd.accpac.simply.imp", - "ims": "application/vnd.ms-ims", - "imscc": "application/vnd.ims.imsccv1p1", - "in": "text/plain", - "inc": "text/inc", - "inf": "application/inf", - "info": "application/x-info", - "ini": "text/ini", - "ink": "application/inkml+xml", - "inkml": "application/inkml+xml", - "inp": "chemical/x-gamess-input", - "ins": "application/x-internet-signup", - "install": "application/x-install-instructions", - "iota": "application/vnd.astraea-software.iota", - "ip": "application/x-ip2", - "ipfix": "application/ipfix", - "ipk": "application/vnd.shana.informed.package", - "irm": "application/vnd.ibm.rights-management", - "irp": "application/vnd.irepository.package+xml", - "ism": "model/vnd.gdl", - "iso": "application/x-iso9660-image", - "isp": "application/x-internet-signup", - "ist": "chemical/x-isostar", - "istr": "chemical/x-isostar", - "isu": "video/x-isvideo", - "it": "audio/it", - "itp": "application/vnd.shana.informed.formtemplate", - "its": "application/its+xml", - "iv": "application/x-inventor", "ivp": "application/vnd.immervision-ivp", - "ivr": "i-world/i-vrml", "ivu": "application/vnd.immervision-ivu", - "ivy": "application/x-livescreen", - "j2": "text/jinja", - "jad": "text/vnd.sun.j2me.app-descriptor", - "jade": "text/jade", + "igm": "application/vnd.insors.igm", + "xpw": "application/vnd.intercon.formnet", + "xpx": "application/vnd.intercon.formnet", + "i2g": "application/vnd.intergeo", + "qbo": "application/vnd.intu.qbo", + "qfx": "application/vnd.intu.qfx", + "rcprofile": "application/vnd.ipunplugged.rcprofile", + "irp": "application/vnd.irepository.package+xml", + "xpr": "application/vnd.is-xpr", + "fcs": "application/vnd.isac.fcs", "jam": "application/vnd.jam", - "jar": "application/x-java-archive", - "jardiff": "application/x-java-archive-diff", - "java": "text/x-java-source", - "jcm": "application/x-java-commerce", - "jdx": "chemical/x-jcamp-dx", - "jenkinsfile": "text/groovy", - "jfif": "image/jpeg", - "jinja": "text/jinja", - "jinja2": "text/jinja", + "rms": "application/vnd.jcp.javame.midlet-rms", "jisp": "application/vnd.jisp", - "jls": "image/jls", - "jlt": "application/vnd.hp-jlyt", - "jl": "text/julia", - "jmz": "application/x-jmol", - "jng": "image/x-jng", - "jnlp": "application/x-java-jnlp-file", "joda": "application/vnd.joost.joda-archive", - "jp2": "image/jp2", - "jpe": "image/jpeg", - "jpeg": "image/jpeg", - "jpf": "image/jpx", - "jpg": "image/jpeg", - "jpg2": "image/jp2", - "jpgm": "image/jpm", - "jpgv": "video/jpeg", - "jpm": "image/jpm", - "jps": "image/x-jps", - "jpx": "image/jpx", - "jrd": "application/jrd+json", - "js": "application/javascript", - "json": "application/json", - "json-patch": "application/json-patch+json", - "json5": "application/json5", - "jsonld": "application/ld+json", - "jsonml": "application/jsonml+json", - "jsx": "text/jsx", - "jtd": "text/vnd.esmertec.theme-descriptor", - "jut": "image/jutvision", - "kar": "audio/midi", + "ktz": "application/vnd.kahootz", + "ktr": "application/vnd.kahootz", "karbon": "application/vnd.kde.karbon", - "kcm": "application/vnd.nervana", - "key": "application/pgp-keys", - "keynote": "application/vnd.apple.keynote", + "chrt": "application/vnd.kde.kchart", "kfo": "application/vnd.kde.kformula", - "kia": "application/vnd.kidspiration", - "kil": "application/x-killustrator", - "kin": "chemical/x-kinemage", - "kml": "application/vnd.google-earth.kml+xml", - "kmz": "application/vnd.google-earth.kmz", - "kne": "application/vnd.kinar", - "knp": "application/vnd.kinar", - "kom": "application/vnd.hbci", + "flw": "application/vnd.kde.kivio", "kon": "application/vnd.kde.kontour", - "koz": "audio/vnd.audikoz", "kpr": "application/vnd.kde.kpresenter", "kpt": "application/vnd.kde.kpresenter", - "kpxx": "application/vnd.ds-keypoint", - "ksh": "application/x-ksh", "ksp": "application/vnd.kde.kspread", - "kt": "text/kotlin", - "ktr": "application/vnd.kahootz", - "ktx": "image/ktx", - "ktz": "application/vnd.kahootz", "kwd": "application/vnd.kde.kword", "kwt": "application/vnd.kde.kword", - "l16": "audio/l16", - "la": "audio/nspaudio", - "lam": "audio/x-liveaudio", - "lasjson": "application/vnd.las.las+json", + "htke": "application/vnd.kenameaapp", + "kia": "application/vnd.kidspiration", + "kne": "application/vnd.kinar", + "knp": "application/vnd.kinar", + "skp": "application/vnd.koan", + "skd": "application/vnd.koan", + "skt": "application/vnd.koan", + "skm": "application/vnd.koan", + "sse": "application/vnd.kodak-descriptor", "lasxml": "application/vnd.las.las+xml", - "latex": "application/x-latex", - "lbc": "audio/ilbc", "lbd": "application/vnd.llamagraphics.life-balance.desktop", "lbe": "application/vnd.llamagraphics.life-balance.exchange+xml", - "le": "application/vnd.bluetooth.le.oob", - "les": "application/vnd.hhe.lesson-player", - "less": "text/less", - "lgr": "application/lgr+xml", - "lha": "application/octet-stream", - "lhs": "text/x-literate-haskell", - "lhx": "application/octet-stream", - "lin": "application/bbolin", - "link66": "application/vnd.route66.link66+xml", - "list": "text/plain", - "list3820": "application/vnd.ibm.modcap", - "listafp": "application/vnd.ibm.modcap", - "lmp": "model/vnd.gdl", - "lnk": "application/x-ms-shortcut", - "log": "text/plain", - "lostsyncxml": "application/lostsync+xml", - "lostxml": "application/lost+xml", - "lrf": "application/octet-stream", - "lrm": "application/vnd.ms-lrm", - "lsf": "video/x-la-asf", - "lsp": "text/x-script.lisp", - "lst": "text/plain", - "lsx": "video/x-la-asf", - "ltf": "application/vnd.frogans.ltf", - "ltx": "application/x-latex", - "lua": "text/x-lua", - "luac": "application/x-lua-bytecode", - "lvp": "audio/vnd.lucent.voice", + "123": "application/vnd.lotus-1-2-3", + "apr": "application/vnd.lotus-approach", + "pre": "application/vnd.lotus-freelance", + "nsf": "application/vnd.lotus-notes", + "org": "application/vnd.lotus-organizer", + "scm": "application/vnd.lotus-screencam", "lwp": "application/vnd.lotus-wordpro", - "lxf": "application/lxf", - "lyx": "application/x-lyx", - "lzh": "application/octet-stream", - "lzx": "application/x-lzx", - "m": "application/vnd.wolfram.mathematica.package", - "m13": "application/x-msmediaview", - "m14": "application/x-msmediaview", - "m15": "audio/x-mod", - "m1v": "video/mpeg", - "m21": "application/mp21", - "m2a": "audio/mpeg", - "m2v": "video/mpeg", - "m3a": "audio/mpeg", - "m3g": "application/m3g", - "m3u": "audio/x-mpegurl", - "m3u8": "application/vnd.apple.mpegurl", - "m4a": "audio/x-m4a", - "m4s": "video/iso.segment", - "m4u": "video/vnd.mpegurl", - "m4v": "video/x-m4v", - "ma": "application/mathematica", - "mads": "application/mads+xml", - "mag": "application/vnd.ecowin.chart", - "mail": "message/rfc822", - "maker": "application/vnd.framemaker", - "man": "application/x-troff-man", - "manifest": "text/cache-manifest", - "map": "application/x-navimap", - "mar": "text/plain", - "markdown": "text/markdown", - "mathml": "application/mathml+xml", - "mb": "application/mathematica", - "mbd": "application/mbedlet", - "mbk": "application/vnd.mobius.mbk", - "mbox": "application/mbox", - "mc$": "application/x-magic-cap-package-1.0", - "mc1": "application/vnd.medcalcdata", + "portpkg": "application/vnd.macports.portpkg", "mcd": "application/vnd.mcd", - "mcf": "image/vasa", - "mcif": "chemical/x-mmcif", - "mcm": "chemical/x-macmolecule", - "mcp": "application/netmc", - "mcurl": "text/vnd.curl.mcurl", - "md": "text/markdown", - "mdb": "application/x-msaccess", - "mdc": "application/vnd.marlin.drm.mdcf", - "mdi": "image/vnd.ms-modi", - "me": "application/x-troff-me", - "med": "audio/x-mod", - "mesh": "model/mesh", - "meta4": "application/metalink4+xml", - "metalink": "application/metalink+xml", - "mets": "application/mets+xml", - "mf4": "application/mf4", + "mc1": "application/vnd.medcalcdata", + "cdkey": "application/vnd.mediastation.cdkey", + "mwf": "application/vnd.mfer", "mfm": "application/vnd.mfmp", - "mft": "application/rpki-manifest", - "mgp": "application/vnd.osgeo.mapguide.package", - "mgz": "application/vnd.proteus.magazine", - "mht": "message/rfc822", - "mhtml": "message/rfc822", - "mib": "text/mib", - "mid": "audio/midi", - "midi": "audio/midi", - "mie": "application/x-mie", - "mif": "application/x-mif", - "mime": "message/rfc822", - "miz": "text/mizar", - "mj2": "video/mj2", - "mjf": "audio/x-vnd.audioexplosion.mjuicemediafile", - "mjp2": "video/mj2", - "mjpg": "video/x-motion-jpeg", - "mjs": "application/javascript", - "mk": "text/makefile", - "mk3d": "video/x-matroska-3d", - "mka": "audio/x-matroska", - "mkd": "text/x-markdown", - "mks": "video/x-matroska", - "mkv": "video/x-matroska", - "mlp": "application/vnd.dolby.mlp", - "mm": "application/x-freemind", - "mmd": "application/vnd.chipnuts.karaoke-mmd", - "mmdb": "application/vnd.maxmind.maxmind-db", - "mme": "application/base64", - "mmf": "application/vnd.smaf", - "mml": "text/mathml", - "mmod": "chemical/x-macromodel-input", - "mmr": "image/vnd.fujixerox.edmics-mmr", - "mms": "application/vnd.wap.mms-message", - "mng": "video/x-mng", - "mny": "application/x-msmoney", - "mobi": "application/x-mobipocket-ebook", - "moc": "text/x-moc", - "mod": "audio/x-mod", - "model-inter": "application/vnd.vd-study", - "mods": "application/mods+xml", - "modulemap": "text/modulemap", - "mol": "chemical/x-mdl-molfile", - "mol2": "chemical/x-mol2", - "moml": "model/vnd.moml+xml", - "moo": "chemical/x-mopac-out", - "moov": "video/quicktime", - "mop": "chemical/x-mopac-input", - "mopcrt": "chemical/x-mopac-input", - "mov": "video/quicktime", - "movie": "video/x-sgi-movie", - "mp1": "audio/mpeg", - "mp2": "audio/mpeg", - "mp21": "application/mp21", - "mp2a": "audio/mpeg", - "mp3": "audio/mp3", - "mp4": "video/mp4", - "mp4a": "audio/mp4", - "mp4s": "application/mp4", - "mp4v": "video/mp4", - "mpa": "video/mpeg", - "mpc": "application/vnd.mophun.certificate", - "mpd": "application/dash+xml", - "mpdd": "application/dashdelta", - "mpe": "video/mpeg", - "mpeg": "video/mpeg", - "mpega": "audio/mpeg", - "mpf": "text/vnd.ms-mediapackage", - "mpg": "video/mpeg", - "mpg4": "video/mp4", - "mpga": "audio/mpeg", - "mpkg": "application/vnd.apple.installer+xml", - "mpm": "application/vnd.blueice.multipass", + "flo": "application/vnd.micrografx.flo", + "igx": "application/vnd.micrografx.igx", + "mif": "application/vnd.mif", + "daf": "application/vnd.mobius.daf", + "dis": "application/vnd.mobius.dis", + "mbk": "application/vnd.mobius.mbk", + "mqy": "application/vnd.mobius.mqy", + "msl": "application/vnd.mobius.msl", + "plc": "application/vnd.mobius.plc", + "txf": "application/vnd.mobius.txf", "mpn": "application/vnd.mophun.application", + "mpc": "application/vnd.mophun.certificate", + "xul": "application/vnd.mozilla.xul+xml", + "cil": "application/vnd.ms-artgalry", + "cab": "application/vnd.ms-cab-compressed", + "xls": "application/vnd.ms-excel", + "xlm": "application/vnd.ms-excel", + "xla": "application/vnd.ms-excel", + "xlc": "application/vnd.ms-excel", + "xlt": "application/vnd.ms-excel", + "xlw": "application/vnd.ms-excel", + "xlam": "application/vnd.ms-excel.addin.macroenabled.12", + "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12", + "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12", + "xltm": "application/vnd.ms-excel.template.macroenabled.12", + "eot": "application/vnd.ms-fontobject", + "chm": "application/vnd.ms-htmlhelp", + "ims": "application/vnd.ms-ims", + "lrm": "application/vnd.ms-lrm", + "thmx": "application/vnd.ms-officetheme", + "cat": "application/vnd.ms-pki.seccat", + "stl": "application/vnd.ms-pki.stl", + "ppt": "application/vnd.ms-powerpoint", + "pps": "application/vnd.ms-powerpoint", + "pot": "application/vnd.ms-powerpoint", + "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12", + "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12", + "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12", + "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12", + "potm": "application/vnd.ms-powerpoint.template.macroenabled.12", "mpp": "application/vnd.ms-project", "mpt": "application/vnd.ms-project", - "mpv": "application/x-project", - "mpv2": "video/mpeg", - "mpx": "application/x-project", - "mpy": "application/vnd.ibm.minipay", - "mqy": "application/vnd.mobius.mqy", - "mrc": "application/marc", - "mrcx": "application/marcxml+xml", - "ms": "application/x-troff-ms", - "msa": "application/vnd.msa-disk-image", - "mscml": "application/mediaservercontrol+xml", - "msd": "application/vnd.fdsn.mseed", - "mseed": "application/vnd.fdsn.mseed", + "docm": "application/vnd.ms-word.document.macroenabled.12", + "dotm": "application/vnd.ms-word.template.macroenabled.12", + "wps": "application/vnd.ms-works", + "wks": "application/vnd.ms-works", + "wcm": "application/vnd.ms-works", + "wdb": "application/vnd.ms-works", + "wpl": "application/vnd.ms-wpl", + "xps": "application/vnd.ms-xpsdocument", "mseq": "application/vnd.mseq", - "msf": "application/vnd.epson.msf", - "msg": "application/vnd.ms-outlook", - "msh": "model/mesh", - "msi": "application/x-msi", - "msl": "application/vnd.mobius.msl", - "msm": "model/vnd.gdl", - "msty": "application/vnd.muvee.style", - "mtm": "audio/x-mod", - "mts": "model/vnd.mts", - "multitrack": "audio/vnd.presonus.multitrack", "mus": "application/vnd.musician", - "musd": "application/mmt-usd+xml", - "musicxml": "application/vnd.recordare.musicxml+xml", - "mv": "video/x-sgi-movie", - "mvb": "application/x-msmediaview", - "mvt": "application/vnd.mapbox-vector-tile", - "mwc": "application/vnd.dpgraph", - "mwf": "application/vnd.mfer", - "mxf": "application/mxf", - "mxi": "application/vnd.vd-study", - "mxl": "application/vnd.recordare.musicxml", - "mxmf": "audio/mobile-xmf", - "mxml": "application/xv+xml", - "mxs": "application/vnd.triscape.mxs", - "mxu": "video/vnd.mpegurl", - "my": "audio/make", - "mzz": "application/x-vnd.audioexplosion.mzz", - "n-gage": "application/vnd.nokia.n-gage.symbian.install", - "n3": "text/n3", - "nap": "image/naplps", - "naplps": "image/naplps", - "nb": "application/mathematica", - "nbp": "application/vnd.wolfram.player", - "nc": "application/x-netcdf", - "ncm": "application/vnd.nokia.configuration-message", - "ncx": "application/x-dtbncx+xml", - "ndc": "application/vnd.osa.netdeploy", - "ndjson": "application/json", - "ndl": "application/vnd.lotus-notes", - "nds": "application/vnd.nintendo.nitro.rom", - "nef": "image/x-nikon-nef", - "nfo": "text/x-nfo", - "ngdat": "application/vnd.nokia.n-gage.data", - "ngdoc": "text/ngdoc", - "nif": "image/x-niff", - "niff": "image/x-niff", + "msty": "application/vnd.muvee.style", + "taglet": "application/vnd.mynfc", + "nlu": "application/vnd.neurolanguage.nlu", "nim": "text/nim", "nimble": "text/nimble", "nimf": "text/nim", "nims": "text/nim", + "ntf": "application/vnd.nitf", "nitf": "application/vnd.nitf", - "nix": "application/x-mix-transfer", - "nlu": "application/vnd.neurolanguage.nlu", - "nml": "application/vnd.enliven", "nnd": "application/vnd.noblenet-directory", "nns": "application/vnd.noblenet-sealer", "nnw": "application/vnd.noblenet-web", - "notebook": "application/vnd.smart.notebook", - "npx": "image/vnd.net-fpx", - "nq": "application/n-quads", - "ns2": "application/vnd.lotus-notes", - "ns3": "application/vnd.lotus-notes", - "ns4": "application/vnd.lotus-notes", - "nsc": "application/x-conference", - "nsf": "application/vnd.lotus-notes", - "nsg": "application/vnd.lotus-notes", - "nsh": "application/vnd.lotus-notes", - "nt": "application/n-triples", - "ntf": "application/vnd.lotus-notes", - "numbers": "application/vnd.apple.numbers", - "nvd": "application/x-navidoc", - "nwc": "application/x-nwc", - "nws": "message/rfc822", - "nzb": "application/x-nzb", - "o": "application/x-object", - "o4a": "application/vnd.oma.drm.dcf", - "o4v": "application/vnd.oma.drm.dcf", - "oa2": "application/vnd.fujitsu.oasys2", - "oa3": "application/vnd.fujitsu.oasys3", - "oas": "application/vnd.fujitsu.oasys", - "obd": "application/x-msbinder", - "obg": "application/vnd.openblox.game-binary", - "obgx": "application/vnd.openblox.game+xml", - "obj": "application/x-tgif", - "oda": "application/oda", - "odb": "application/vnd.oasis.opendocument.database", + "ngdat": "application/vnd.nokia.n-gage.data", + "n-gage": "application/vnd.nokia.n-gage.symbian.install", + "rpst": "application/vnd.nokia.radio-preset", + "rpss": "application/vnd.nokia.radio-presets", + "edm": "application/vnd.novadigm.edm", + "edx": "application/vnd.novadigm.edx", + "ext": "application/vnd.novadigm.ext", "odc": "application/vnd.oasis.opendocument.chart", - "odd": "application/tei+xml", + "otc": "application/vnd.oasis.opendocument.chart-template", + "odb": "application/vnd.oasis.opendocument.database", "odf": "application/vnd.oasis.opendocument.formula", "odft": "application/vnd.oasis.opendocument.formula-template", "odg": "application/vnd.oasis.opendocument.graphics", - "odi": "application/vnd.oasis.opendocument.image", - "odm": "application/vnd.oasis.opendocument.text-master", - "odp": "application/vnd.oasis.opendocument.presentation", - "ods": "application/vnd.oasis.opendocument.spreadsheet", - "odt": "application/vnd.oasis.opendocument.text", - "odx": "application/odx", - "oeb": "application/vnd.openeye.oeb", - "oga": "audio/ogg", - "ogex": "model/vnd.opengex", - "ogg": "audio/ogg", - "ogv": "video/ogg", - "ogx": "application/ogg", - "old": "application/x-trash", - "omc": "application/x-omc", - "omcd": "application/x-omcdatamaker", - "omcr": "application/x-omcregerator", - "omdoc": "application/omdoc+xml", - "omg": "audio/atrac3", - "onepkg": "application/onenote", - "onetmp": "application/onenote", - "onetoc": "application/onenote", - "onetoc2": "application/onenote", - "opf": "application/oebps-package+xml", - "opml": "text/x-opml", - "oprc": "application/vnd.palm", - "opus": "audio/ogg", - "or2": "application/vnd.lotus-organizer", - "or3": "application/vnd.lotus-organizer", - "orf": "image/x-olympus-orf", - "org": "text/x-org", - "orq": "application/ocsp-request", - "ors": "application/ocsp-response", - "osf": "application/vnd.yamaha.openscoreformat", - "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml", - "osm": "application/vnd.openstreetmap.data+xml", - "otc": "application/vnd.oasis.opendocument.chart-template", - "otf": "font/otf", "otg": "application/vnd.oasis.opendocument.graphics-template", - "oth": "application/vnd.oasis.opendocument.text-web", + "odi": "application/vnd.oasis.opendocument.image", "oti": "application/vnd.oasis.opendocument.image-template", + "odp": "application/vnd.oasis.opendocument.presentation", "otp": "application/vnd.oasis.opendocument.presentation-template", + "ods": "application/vnd.oasis.opendocument.spreadsheet", "ots": "application/vnd.oasis.opendocument.spreadsheet-template", + "odt": "application/vnd.oasis.opendocument.text", + "odm": "application/vnd.oasis.opendocument.text-master", "ott": "application/vnd.oasis.opendocument.text-template", - "ova": "application/x-virtualbox-ova", - "ovf": "application/x-virtualbox-ovf", - "owx": "application/owl+xml", - "oxlicg": "application/vnd.oxli.countgraph", - "oxps": "application/oxps", + "oth": "application/vnd.oasis.opendocument.text-web", + "xo": "application/vnd.olpc-sugar", + "dd2": "application/vnd.oma.dd2+xml", "oxt": "application/vnd.openofficeorg.extension", - "oza": "application/x-oz-application", - "p": "text/x-pascal", - "p10": "application/pkcs10", - "p12": "application/pkcs12", - "p2p": "application/vnd.wfa.p2p", - "p7a": "application/x-pkcs7-signature", - "p7b": "application/x-pkcs7-certificates", - "p7c": "application/pkcs7-mime", - "p7m": "application/pkcs7-mime", - "p7r": "application/x-pkcs7-certreqresp", - "p7s": "application/pkcs7-signature", - "p8": "application/pkcs8", - "pac": "application/x-ns-proxy-autoconfig", - "pack": "application/x-java-pack200", - "package": "application/vnd.autopackage", - "pages": "application/vnd.apple.pages", - "par": "text/plain-bas", - "part": "application/pro_eng", - "pas": "text/pascal", - "pat": "image/x-coreldrawpattern", - "patch": "text/x-diff", - "paw": "application/vnd.pawaafile", - "pbd": "application/vnd.powerbuilder6", - "pbm": "image/x-portable-bitmap", - "pcap": "application/vnd.tcpdump.pcap", - "pcf": "application/x-font-pcf", - "pcl": "application/vnd.hp-pcl", - "pclxl": "application/vnd.hp-pclxl", - "pct": "image/x-pict", - "pcurl": "application/vnd.curl.pcurl", - "pcx": "image/x-pcx", - "pdb": "application/vnd.palm", - "pde": "text/x-processing", - "pdf": "application/pdf", - "pdx": "application/pdx", - "pem": "text/pem", - "pfa": "application/x-font-type1", - "pfb": "application/x-font-type1", - "pfm": "application/x-font-type1", - "pfr": "application/font-tdpfr", - "pfunk": "audio/make", - "pfx": "application/pkcs12", - "pgb": "image/vnd.globalgraphics.pgb", - "pgm": "image/x-portable-graymap", - "pgn": "application/x-chess-pgn", - "pgp": "application/pgp-encrypted", - "php": "application/x-httpd-php", - "php3": "application/x-httpd-php3", - "php3p": "application/x-httpd-php3-preprocessed", - "php4": "application/x-httpd-php4", - "php5": "application/x-httpd-php5", - "phps": "application/x-httpd-php-source", - "pht": "application/x-httpd-php", - "phtml": "application/x-httpd-php", - "pic": "image/pict", - "pict": "image/pict", - "pil": "application/vnd.piaccess.application-license", - "pk": "application/x-tex-pk", - "pkd": "application/vnd.hbci", - "pkg": "application/vnd.apple.installer+xml", - "pki": "application/pkixcmp", - "pkipath": "application/pkix-pkipath", - "pko": "application/ynd.ms-pkipko", - "pkpass": "application/vnd.apple.pkpass", - "pl": "application/x-perl", - "plantuml": "text/plantuml", - "plb": "application/vnd.3gpp.pic-bw-large", - "plc": "application/vnd.mobius.plc", - "plf": "application/vnd.pocketlearn", - "plj": "audio/vnd.everad.plj", - "plp": "application/vnd.panoply", - "pls": "application/pls+xml", - "plx": "application/x-pixclscript", - "ply": "model/stanford", - "pm": "text/plain", - "pm4": "application/x-pagemaker", - "pm5": "application/x-pagemaker", - "pma": "application/x-perfmon", - "pmc": "application/x-perfmon", - "pml": "application/vnd.ctc-posml", - "pmr": "application/x-perfmon", - "pmw": "application/x-perfmon", - "png": "image/png", - "pnm": "image/x-portable-anymap", - "po": "text/pofile", - "pod": "text/x-pod", - "portpkg": "application/vnd.macports.portpkg", - "pot": "application/vnd.ms-powerpoint", - "potm": "application/vnd.ms-powerpoint.template.macroenabled.12", - "potx": "application/vnd.openxmlformats-officedocument.presentationml.template", - "pov": "model/x-pov", - "pp": "text/puppet", - "ppa": "application/vnd.ms-powerpoint", - "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12", - "ppd": "application/vnd.cups-ppd", - "ppkg": "application/vnd.xmpie.ppkg", - "ppm": "image/x-portable-pixmap", - "pps": "application/vnd.ms-powerpoint", - "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12", - "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow", - "ppt": "application/vnd.ms-powerpoint", - "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12", "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", - "ppz": "application/mspowerpoint", + "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide", + "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow", + "potx": "application/vnd.openxmlformats-officedocument.presentationml.template", + "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template", + "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template", + "mgp": "application/vnd.osgeo.mapguide.package", + "dp": "application/vnd.osgi.dp", + "esa": "application/vnd.osgi.subsystem", + "pdb": "application/vnd.palm", "pqa": "application/vnd.palm", - "prc": "application/vnd.palm", - "pre": "application/vnd.lotus-freelance", - "preminet": "application/vnd.preminet", - "prf": "application/pics-rules", - "proto": "text/proto", - "provn": "text/provenance-notation", - "provx": "application/provenance+xml", - "prt": "application/pro_eng", - "prz": "application/vnd.lotus-freelance", - "ps": "application/postscript", - "psb": "application/vnd.3gpp.pic-bw-small", - "psd": "image/vnd.adobe.photoshop", - "pseg3820": "application/vnd.ibm.modcap", - "psf": "application/x-font-linux-psf", - "psid": "audio/prs.sid", - "pskcxml": "application/pskc+xml", - "pti": "image/prs.pti", - "ptid": "application/vnd.pvi.ptid1", - "pub": "application/x-mspublisher", - "purs": "text/purescript", - "pvb": "application/vnd.3gpp.pic-bw-var", - "pvu": "paleovu/x-pv", - "pwn": "application/vnd.3m.post-it-notes", - "pwz": "application/vnd.ms-powerpoint", - "pxd": "text/cython", - "pxi": "text/cython", - "py": "text/x-script.phyton", - "pya": "audio/vnd.ms-playready.media.pya", - "pyc": "application/x-python-code", - "pyi": "text/pyi", - "pyo": "application/x-python-code", - "pyv": "video/vnd.ms-playready.media.pyv", - "pyx": "text/cython", - "qam": "application/vnd.epson.quickanime", - "qbo": "application/vnd.intu.qbo", - "qca": "application/vnd.ericsson.quickcall", - "qcall": "application/vnd.ericsson.quickcall", - "qcp": "audio/qcelp", - "qd3": "x-world/x-3dmf", - "qd3d": "x-world/x-3dmf", - "qfx": "application/vnd.intu.qfx", - "qgs": "application/x-qgis", - "qif": "image/x-quicktime", + "oprc": "application/vnd.palm", + "paw": "application/vnd.pawaafile", + "str": "application/vnd.pg.format", + "ei6": "application/vnd.pg.osasli", + "efif": "application/vnd.picsel", + "wg": "application/vnd.pmi.widget", + "plf": "application/vnd.pocketlearn", + "pbd": "application/vnd.powerbuilder6", + "box": "application/vnd.previewsystems.box", + "mgz": "application/vnd.proteus.magazine", "qps": "application/vnd.publishare-delta-tree", - "qt": "video/quicktime", - "qtc": "video/x-qtc", - "qti": "image/x-quicktime", - "qtif": "image/x-quicktime", - "qtl": "application/x-quicktimeplayer", - "quiz": "application/vnd.quobject-quoxdocument", - "quox": "application/vnd.quobject-quoxdocument", - "qvd": "application/vnd.theqvd", + "ptid": "application/vnd.pvi.ptid1", + "qxd": "application/vnd.quark.quarkxpress", + "qxt": "application/vnd.quark.quarkxpress", "qwd": "application/vnd.quark.quarkxpress", "qwt": "application/vnd.quark.quarkxpress", - "qxb": "application/vnd.quark.quarkxpress", - "qxd": "application/vnd.quark.quarkxpress", "qxl": "application/vnd.quark.quarkxpress", - "qxt": "application/vnd.quark.quarkxpress", - "r": "text/r", - "ra": "audio/x-realaudio", - "ram": "audio/x-pn-realaudio", - "raml": "application/raml+yaml", - "rapd": "application/route-apd+xml", - "rar": "application/x-rar-compressed", - "ras": "image/x-cmu-raster", - "rast": "image/cmu-raster", - "rb": "application/x-ruby", - "rcprofile": "application/vnd.ipunplugged.rcprofile", - "rct": "application/prs.nprend", - "rd": "chemical/x-mdl-rdfile", - "rda": "text/r", - "rdata": "text/r", - "rds": "text/r", - "rdf": "application/rdf+xml", - "rdf-crypt": "application/prs.rdf-xml-crypt", - "rdz": "application/vnd.data-vision.rdz", - "relo": "application/p2p-overlay+xml", - "rep": "application/vnd.businessobjects", - "request": "application/vnd.nervana", - "res": "application/x-dtbresource+xml", - "rexx": "text/x-script.rexx", - "rf": "image/vnd.rn-realflash", - "rfcxml": "application/rfc+xml", - "rgb": "image/x-rgb", - "rgbe": "image/vnd.radiance", - "rhtml": "application/x-httpd-eruby", - "rif": "application/reginfo+xml", - "rip": "audio/vnd.rip", - "ris": "application/x-research-info-systems", - "rl": "application/resource-lists+xml", - "rlc": "image/vnd.fujixerox.edmics-rlc", - "rld": "application/resource-lists-diff+xml", - "rlib": "text/rust", + "qxb": "application/vnd.quark.quarkxpress", + "bed": "application/vnd.realvnc.bed", + "mxl": "application/vnd.recordare.musicxml", + "musicxml": "application/vnd.recordare.musicxml+xml", + "cryptonote": "application/vnd.rig.cryptonote", + "cod": "application/vnd.rim.cod", "rm": "application/vnd.rn-realmedia", - "rmi": "audio/mid", - "rmm": "audio/x-pn-realaudio", - "rmp": "audio/x-pn-realaudio-plugin", - "rms": "application/vnd.jcp.javame.midlet-rms", "rmvb": "application/vnd.rn-realmedia-vbr", - "rnc": "application/relax-ng-compact-syntax", - "rnd": "application/prs.nprend", - "rng": "text/xml", - "rnx": "application/vnd.rn-realplayer", - "roa": "application/rpki-roa", - "roff": "text/troff", - "ros": "chemical/x-rosdal", - "rp": "image/vnd.rn-realpix", - "rp9": "application/vnd.cloanto.rp9", - "rpm": "application/x-redhat-package-manager", - "rpss": "application/vnd.nokia.radio-presets", - "rpst": "application/vnd.nokia.radio-preset", - "rq": "application/sparql-query", - "rs": "application/rls-services+xml", - "rsd": "application/rsd+xml", - "rsheet": "application/urc-ressheet+xml", - "rsm": "model/vnd.gdl", - "rss": "application/rss+xml", - "rst": "text/prs.fallenstein.rst", - "rt": "text/richtext", - "rtf": "text/rtf", - "rtx": "text/richtext", - "run": "application/x-makeself", - "rusd": "application/route-usd+xml", - "rv": "video/vnd.rn-realvideo", - "rxn": "chemical/x-mdl-rxnfile", - "s": "text/x-asm", - "s11": "video/vnd.sealed.mpeg1", - "s14": "video/vnd.sealed.mpeg4", - "s1a": "application/vnd.sealedmedia.softseal.pdf", - "s1e": "application/vnd.sealed.xls", - "s1g": "image/vnd.sealedmedia.softseal.gif", - "s1h": "application/vnd.sealedmedia.softseal.html", - "s1j": "image/vnd.sealedmedia.softseal.jpg", - "s1m": "audio/vnd.sealedmedia.softseal.mpeg", - "s1n": "image/vnd.sealed.png", - "s1p": "application/vnd.sealed.ppt", - "s1q": "video/vnd.sealedmedia.softseal.mov", - "s1w": "application/vnd.sealed.doc", - "s3df": "application/vnd.sealed.3df", - "s3m": "audio/s3m", - "sac": "application/tamp-sequence-adjust-confirm", - "saf": "application/vnd.yamaha.smaf-audio", - "sam": "application/vnd.lotus-wordpro", - "sandboxed": "text/html-sandboxed", - "sass": "text/x-sass", - "saveme": "application/octet-stream", - "sbk": "application/x-tbook", - "sbml": "application/sbml+xml", - "sc": "application/vnd.ibm.secure-container", - "scala": "text/x-scala", - "scd": "application/x-msschedule", - "sce": "application/vnd.etsi.asic-e+zip", - "scim": "application/scim+json", - "scld": "application/vnd.doremir.scorecloud-binary-document", - "scm": "application/vnd.lotus-screencam", - "scq": "application/scvp-cv-request", - "scr": "application/x-silverlight", - "scs": "application/scvp-cv-response", - "scsf": "application/vnd.sealed.csf", - "scss": "text/x-scss", - "sct": "text/scriptlet", - "scurl": "text/vnd.curl.scurl", - "sd": "chemical/x-mdl-sdfile", - "sd2": "audio/x-sd2", - "sda": "application/vnd.stardivision.draw", - "sdc": "application/vnd.stardivision.calc", - "sdd": "application/vnd.stardivision.impress", - "sdf": "application/vnd.kinar", - "sdkd": "application/vnd.solent.sdkm+xml", - "sdkm": "application/vnd.solent.sdkm+xml", - "sdml": "text/plain", - "sdo": "application/vnd.sealed.doc", - "sdoc": "application/vnd.sealed.doc", - "sdp": "application/sdp", - "sdr": "application/sounder", - "sdw": "application/vnd.stardivision.writer", - "sea": "application/x-sea", + "link66": "application/vnd.route66.link66+xml", + "st": "application/vnd.sailingtracker.track", "see": "application/vnd.seemail", - "seed": "application/vnd.fdsn.seed", - "sem": "application/vnd.sealed.eml", "sema": "application/vnd.sema", "semd": "application/vnd.semd", "semf": "application/vnd.semf", - "seml": "application/vnd.sealed.eml", - "ser": "application/java-serialized-object", - "set": "application/set", - "setpay": "application/set-payment-initiation", - "setreg": "application/set-registration-initiation", - "sfc": "application/vnd.nintendo.snes.rom", - "sfd": "application/vnd.font-fontforge-sfd", - "sfd-hdstx": "application/vnd.hydrostatix.sof-data", + "ifm": "application/vnd.shana.informed.formdata", + "itp": "application/vnd.shana.informed.formtemplate", + "iif": "application/vnd.shana.informed.interchange", + "ipk": "application/vnd.shana.informed.package", + "twd": "application/vnd.simtech-mindmapper", + "twds": "application/vnd.simtech-mindmapper", + "mmf": "application/vnd.smaf", + "teacher": "application/vnd.smart.teacher", + "sdkm": "application/vnd.solent.sdkm+xml", + "sdkd": "application/vnd.solent.sdkm+xml", + "dxp": "application/vnd.spotfire.dxp", "sfs": "application/vnd.spotfire.sfs", - "sfv": "text/x-sfv", - "sgf": "application/x-go-sgf", - "sgi": "image/sgi", - "sgif": "image/vnd.sealedmedia.softseal.gif", - "sgl": "application/vnd.stardivision.writer-global", - "sgm": "text/sgml", - "sgml": "text/sgml", - "sh": "application/x-sh", - "shar": "application/x-shar", - "shex": "text/shex", - "shf": "application/shf+xml", - "shp": "application/x-qgis", - "shx": "application/x-qgis", - "si": "text/vnd.wap.si", - "sic": "application/vnd.wap.sic", - "sid": "image/x-mrsid-image", - "sieve": "application/sieve", - "sig": "application/pgp-signature", - "sik": "application/x-trash", - "sil": "audio/silk", - "silo": "model/mesh", - "sis": "application/vnd.symbian.install", - "sisx": "x-epoc/x-sisx-app", - "sit": "application/x-stuffit", - "sitx": "application/x-stuffitx", - "siv": "application/sieve", - "sjp": "image/vnd.sealedmedia.softseal.jpg", - "sjpg": "image/vnd.sealedmedia.softseal.jpg", - "skd": "application/vnd.koan", - "skm": "application/vnd.koan", - "skp": "application/vnd.koan", - "skt": "application/vnd.koan", - "sl": "text/vnd.wap.sl", - "sla": "application/vnd.scribus", - "slaz": "application/vnd.scribus", - "slc": "application/vnd.wap.slc", - "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12", - "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide", - "sls": "application/route-s-tsid+xml", - "slt": "application/vnd.epson.salt", - "sm": "application/vnd.stepmania.stepchart", - "smc": "application/vnd.nintendo.snes.rom", + "sdc": "application/vnd.stardivision.calc", + "sda": "application/vnd.stardivision.draw", + "sdd": "application/vnd.stardivision.impress", "smf": "application/vnd.stardivision.math", - "smh": "application/vnd.sealed.mht", - "smht": "application/vnd.sealed.mht", - "smi": "application/smil+xml", - "smil": "application/smil+xml", - "smk": "video/vnd.radgamettools.smacker", - "sml": "application/smil+xml", - "smo": "video/vnd.sealedmedia.softseal.mov", - "smov": "video/vnd.sealedmedia.softseal.mov", - "smp": "audio/vnd.sealedmedia.softseal.mpeg", - "smp3": "audio/vnd.sealedmedia.softseal.mpeg", - "smpg": "video/vnd.sealed.mpeg1", - "sms": "application/vnd.3gpp2.sms", - "smv": "video/x-smv", + "sdw": "application/vnd.stardivision.writer", + "vor": "application/vnd.stardivision.writer", + "sgl": "application/vnd.stardivision.writer-global", "smzip": "application/vnd.stepmania.package", - "snd": "audio/basic", - "snf": "application/x-font-snf", - "so": "application/octet-stream", - "soa": "text/dns", - "soc": "application/sgml-open-catalog", - "sol": "application/solids", - "spc": "text/x-speech", - "spd": "application/vnd.sealedmedia.softseal.pdf", - "spdf": "application/vnd.sealedmedia.softseal.pdf", - "spec": "text/spec", - "spf": "application/vnd.yamaha.smaf-phrase", - "spl": "application/x-futuresplash", - "spn": "image/vnd.sealed.png", - "spng": "image/vnd.sealed.png", - "spo": "text/vnd.in3d.spot", - "spot": "text/vnd.in3d.spot", - "spp": "application/scvp-vp-response", - "sppt": "application/vnd.sealed.ppt", - "spq": "application/scvp-vp-request", - "spr": "application/x-sprite", - "sprite": "application/x-sprite", - "spx": "audio/ogg", - "sql": "application/x-sql", - "sr": "application/vnd.sigrok.session", - "src": "application/x-wais-source", - "srt": "application/x-subrip", - "sru": "application/sru+xml", - "srx": "application/sparql-results+xml", - "ssdl": "application/ssdl+xml", - "sse": "application/vnd.kodak-descriptor", - "ssf": "application/vnd.epson.ssf", - "ssi": "text/x-server-parsed-html", - "ssm": "application/streamingmedia", - "ssml": "application/ssml+xml", - "sst": "application/vnd.ms-pki.certstore", - "ssw": "video/vnd.sealed.swf", - "sswf": "video/vnd.sealed.swf", - "st": "application/vnd.sailingtracker.track", + "sm": "application/vnd.stepmania.stepchart", + "sxc": "application/vnd.sun.xml.calc", "stc": "application/vnd.sun.xml.calc.template", + "sxd": "application/vnd.sun.xml.draw", "std": "application/vnd.sun.xml.draw.template", - "step": "application/step", - "stf": "application/vnd.wt.stf", + "sxi": "application/vnd.sun.xml.impress", "sti": "application/vnd.sun.xml.impress.template", - "stif": "application/vnd.sealed.tiff", - "stk": "application/hyperstudio", - "stl": "application/vnd.ms-pki.stl", - "stm": "audio/x-stm", - "stml": "application/vnd.sealedmedia.softseal.html", - "stp": "application/step", - "str": "application/vnd.pg.format", - "study-inter": "application/vnd.vd-study", + "sxm": "application/vnd.sun.xml.math", + "sxw": "application/vnd.sun.xml.writer", + "sxg": "application/vnd.sun.xml.writer.global", "stw": "application/vnd.sun.xml.writer.template", - "sty": "text/x-tex", - "styl": "text/stylus", - "sub": "text/vnd.dvb.subtitle", "sus": "application/vnd.sus-calendar", "susp": "application/vnd.sus-calendar", - "sv4cpio": "application/x-sv4cpio", - "sv4crc": "application/x-sv4crc", - "svc": "application/vnd.dvb.service", "svd": "application/vnd.svd", - "svf": "image/x-dwg", - "svg": "image/svg+xml", - "svgz": "image/svg+xml", - "sw": "chemical/x-swissprot", - "swa": "application/x-director", - "swf": "application/x-shockwave-flash", - "swfl": "application/x-shockwave-flash", - "swi": "application/vnd.aristanetworks.swi", - "swift": "text/swift", - "swiftdeps": "text/swiftdeps", - "sxc": "application/vnd.sun.xml.calc", - "sxd": "application/vnd.sun.xml.draw", - "sxg": "application/vnd.sun.xml.writer.global", - "sxi": "application/vnd.sun.xml.impress", - "sxl": "application/vnd.sealed.xls", - "sxls": "application/vnd.sealed.xls", - "sxm": "application/vnd.sun.xml.math", - "sxw": "application/vnd.sun.xml.writer", - "t": "text/troff", - "t3": "application/x-t3vm-image", - "t38": "image/t38", - "tac": "text/twisted", - "tag": "text/prs.lines.tag", - "taglet": "application/vnd.mynfc", - "talk": "text/x-speech", - "tam": "application/vnd.onepager", - "tamp": "application/vnd.onepagertamp", - "tamx": "application/vnd.onepagertamx", + "sis": "application/vnd.symbian.install", + "sisx": "application/vnd.symbian.install", + "xsm": "application/vnd.syncml+xml", + "bdm": "application/vnd.syncml.dm+wbxml", + "xdm": "application/vnd.syncml.dm+xml", "tao": "application/vnd.tao.intent-module-archive", - "tap": "image/vnd.tencent.tap", - "tar": "application/x-tar", - "tat": "application/vnd.onepagertat", - "tatp": "application/vnd.onepagertatp", - "tatx": "application/vnd.onepagertatx", - "tau": "application/tamp-apex-update", - "taz": "application/x-gtar", - "tbk": "application/toolbook", - "tcap": "application/vnd.3gpp2.tcap", - "tcl": "application/x-tcl", - "tcsh": "text/x-script.tcsh", - "tcu": "application/tamp-community-update", - "td": "application/urc-targetdesc+xml", - "teacher": "application/vnd.smart.teacher", - "tei": "application/tei+xml", - "teicorpus": "application/tei+xml", - "ter": "application/tamp-error", - "tex": "application/x-tex", - "texi": "application/x-texinfo", - "texinfo": "application/x-texinfo", - "text": "text/plain", - "tf": "text/terraform", - "tfi": "application/thraud+xml", - "tfm": "application/x-tex-tfm", - "tfx": "image/tiff-fx", - "tga": "image/x-tga", - "tgf": "chemical/x-mdl-tgf", - "tgz": "application/gzip", - "thmx": "application/vnd.ms-officetheme", - "thrift": "text/thrift", - "tif": "image/tiff", - "tiff": "image/tiff", - "tk": "text/x-tcl", - "tlclient": "application/vnd.cendio.thinlinc.clientconf", - "tm": "text/texmacs", + "pcap": "application/vnd.tcpdump.pcap", + "cap": "application/vnd.tcpdump.pcap", + "dmp": "application/vnd.tcpdump.pcap", "tmo": "application/vnd.tmobile-livetv", - "tnef": "application/vnd.ms-tnef", - "tnf": "application/vnd.ms-tnef", - "toml": "text/toml", - "torrent": "application/x-bittorrent", - "tpl": "application/vnd.groove-tool-template", "tpt": "application/vnd.trid.tpt", - "tr": "text/troff", + "mxs": "application/vnd.triscape.mxs", "tra": "application/vnd.trueapp", - "tree": "application/vnd.rainstor.data", - "trig": "application/trig", - "trm": "application/x-msterminal", - "ts": "video/mp2t", - "tsa": "application/tamp-sequence-adjust", - "tscn": "text/godot", - "tsd": "application/timestamped-data", - "tsi": "audio/tsp-audio", - "tsp": "audio/tsplayer", - "tsq": "application/timestamp-query", - "tsr": "application/timestamp-reply", - "tst": "application/vnd.etsi.timestamp-token", - "tsv": "text/tab-separated-values", - "tsx": "text/tsx", - "ttc": "font/collection", - "ttf": "font/ttf", - "ttl": "text/turtle", - "ttml": "application/ttml+xml", - "tuc": "application/tamp-update-confirm", - "tur": "application/tamp-update", - "turbot": "image/florian", - "twd": "application/vnd.simtech-mindmapper", - "twds": "application/vnd.simtech-mindmapper", - "txd": "application/vnd.genomatix.tuxedo", - "txf": "application/vnd.mobius.txf", - "txt": "text/plain", - "u32": "application/x-authorware-bin", - "u8dsn": "message/global-delivery-status", - "u8hdr": "message/global-headers", - "u8mdn": "message/global-disposition-notification", - "u8msg": "message/global", - "udeb": "application/vnd.debian.binary-package", "ufd": "application/vnd.ufdl", "ufdl": "application/vnd.ufdl", - "uil": "text/x-uil", - "uis": "application/urc-uisocketdesc+xml", - "uls": "text/iuls", - "ult": "audio/x-mod", - "ulx": "application/x-glulx", + "utz": "application/vnd.uiq.theme", "umj": "application/vnd.umajin", - "uni": "audio/x-mod", - "unis": "text/uri-list", "unityweb": "application/vnd.unity", - "unv": "application/i-deas", - "uo": "application/vnd.uoml+xml", "uoml": "application/vnd.uoml+xml", - "upa": "application/vnd.hbci", - "uri": "text/uri-list", - "uric": "text/vnd.si.uricatalogue", - "urim": "application/vnd.uri-map", - "urimap": "application/vnd.uri-map", - "uris": "text/uri-list", - "urls": "text/uri-list", - "ustar": "application/x-ustar", - "utz": "application/vnd.uiq.theme", - "uu": "text/x-uuencode", - "uue": "text/x-uuencode", - "uva": "audio/vnd.dece.audio", - "uvd": "application/vnd.dece.data", - "uvf": "application/vnd.dece.data", - "uvg": "image/vnd.dece.graphic", - "uvh": "video/vnd.dece.hd", - "uvi": "image/vnd.dece.graphic", - "uvm": "video/vnd.dece.mobile", - "uvp": "video/vnd.dece.pd", - "uvs": "video/vnd.dece.sd", - "uvt": "application/vnd.dece.ttml+xml", - "uvu": "video/vnd.dece.mp4", - "uvv": "video/vnd.dece.video", - "uvva": "audio/vnd.dece.audio", - "uvvd": "application/vnd.dece.data", - "uvvf": "application/vnd.dece.data", - "uvvg": "image/vnd.dece.graphic", - "uvvh": "video/vnd.dece.hd", - "uvvi": "image/vnd.dece.graphic", - "uvvm": "video/vnd.dece.mobile", - "uvvp": "video/vnd.dece.pd", - "uvvs": "video/vnd.dece.sd", - "uvvt": "application/vnd.dece.ttml+xml", - "uvvu": "video/vnd.dece.mp4", - "uvvv": "video/vnd.dece.video", - "uvvx": "application/vnd.dece.unspecified", - "uvvz": "application/vnd.dece.zip", - "uvx": "application/vnd.dece.unspecified", - "uvz": "application/vnd.dece.zip", - "val": "chemical/x-ncbi-asn1-binary", - "vbk": "audio/vnd.nortel.vbk", - "vbox": "application/x-virtualbox-vbox", - "vbox-extpack": "application/x-virtualbox-vbox-extpack", - "vcard": "text/vcard", - "vcd": "application/x-cdlink", - "vcf": "text/x-vcard", - "vcg": "application/vnd.groove-vcard", - "vcs": "text/x-vcalendar", "vcx": "application/vnd.vcx", - "vda": "application/vda", - "vdi": "application/x-virtualbox-vdi", - "vdo": "video/vdo", - "vdx": "text/vdx", - "vew": "application/vnd.lotus-approach", - "vfr": "application/vnd.tml", - "vhd": "application/x-virtualbox-vhd", - "viaframe": "application/vnd.tml", - "vim": "text/vim", - "vis": "application/vnd.visionary", - "viv": "video/vnd.vivo", - "vivo": "video/vivo", - "vmd": "application/vocaltec-media-desc", - "vmdk": "application/x-virtualbox-vmdk", - "vmf": "application/vocaltec-media-file", - "vms": "chemical/x-vamas-iso14976", - "vmt": "application/vnd.valve.source.material", - "vob": "video/x-ms-vob", - "voc": "audio/voc", - "vor": "application/vnd.stardivision.writer", - "vos": "video/vosaic", - "vox": "audio/voxware", - "vpm": "multipart/voice-message", - "vqe": "audio/x-twinvq-plugin", - "vqf": "audio/x-twinvq", - "vql": "audio/x-twinvq-plugin", - "vrm": "x-world/x-vrml", - "vrml": "model/vrml", - "vrt": "x-world/x-vrt", - "vsc": "application/vnd.vidsoft.vidconference", "vsd": "application/vnd.visio", - "vsf": "application/vnd.vsf", - "vss": "application/vnd.visio", "vst": "application/vnd.visio", + "vss": "application/vnd.visio", "vsw": "application/vnd.visio", - "vtf": "image/vnd.valve.source.texture", - "vtt": "text/vtt", - "vtu": "model/vnd.vtu", - "vue": "text/vue", - "vwx": "application/vnd.vectorworks", - "vxml": "application/voicexml+xml", - "w3d": "application/x-director", - "w60": "application/wordperfect6.0", - "w61": "application/wordperfect6.1", - "w6w": "application/msword", - "wad": "application/x-doom", - "wadl": "application/vnd.sun.wadl+xml", - "war": "binary/zip", - "wasm": "application/wasm", - "wav": "audio/wave", - "wax": "audio/x-ms-wax", - "wb1": "application/x-qpro", - "wbmp": "image/vnd.wap.wbmp", - "wbs": "application/vnd.criticaltools.wbs+xml", + "vis": "application/vnd.visionary", + "vsf": "application/vnd.vsf", "wbxml": "application/vnd.wap.wbxml", - "wcm": "application/vnd.ms-works", - "wdb": "application/vnd.ms-works", - "wdp": "image/vnd.ms-photo", - "web": "application/vnd.xara", - "weba": "audio/webm", - "webapp": "application/x-web-app-manifest+json", - "webm": "video/webm", - "webmanifest": "application/manifest+json", - "webp": "image/webp", - "wg": "application/vnd.pmi.widget", - "wgt": "application/widget", - "whl": "binary/wheel", - "wif": "application/watcherinfo+xml", - "win": "model/vnd.gdl", - "wiz": "application/msword", - "wk": "application/x-123", - "wk1": "application/vnd.lotus-1-2-3", - "wk3": "application/vnd.lotus-1-2-3", - "wk4": "application/vnd.lotus-1-2-3", - "wks": "application/vnd.ms-works", - "wkt": "text/wkt", - "wlnk": "application/link-format", - "wm": "video/x-ms-wm", - "wma": "audio/x-ms-wma", - "wmc": "application/vnd.wmc", - "wmd": "application/x-ms-wmd", - "wmf": "image/wmf", - "wml": "text/vnd.wap.wml", "wmlc": "application/vnd.wap.wmlc", - "wmls": "text/vnd.wap.wmlscript", "wmlsc": "application/vnd.wap.wmlscriptc", - "wmv": "video/x-ms-wmv", - "wmx": "video/x-ms-wmx", - "wmz": "application/x-ms-wmz", - "woff": "font/woff", - "woff2": "font/woff2", - "word": "application/msword", - "wp": "application/wordperfect", - "wp5": "application/wordperfect", - "wp6": "application/wordperfect", + "wtb": "application/vnd.webturbo", + "nbp": "application/vnd.wolfram.player", "wpd": "application/vnd.wordperfect", - "wpl": "application/vnd.ms-wpl", - "wps": "application/vnd.ms-works", - "wq1": "application/x-lotus", "wqd": "application/vnd.wqd", - "wri": "application/x-mswrite", - "wrl": "model/vrml", - "wrz": "model/vrml", - "wsc": "message/vnd.wfa.wsc", + "stf": "application/vnd.wt.stf", + "xar": "application/vnd.xara", + "xfdl": "application/vnd.xfdl", + "hvd": "application/vnd.yamaha.hv-dic", + "hvs": "application/vnd.yamaha.hv-script", + "hvp": "application/vnd.yamaha.hv-voice", + "osf": "application/vnd.yamaha.openscoreformat", + "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml", + "saf": "application/vnd.yamaha.smaf-audio", + "spf": "application/vnd.yamaha.smaf-phrase", + "cmp": "application/vnd.yellowriver-custom-menu", + "zir": "application/vnd.zul", + "zirz": "application/vnd.zul", + "zaz": "application/vnd.zzazz.deck+xml", + "vxml": "application/voicexml+xml", + "wasm": "application/wasm", + "wgt": "application/widget", + "hlp": "application/winhlp", "wsdl": "application/wsdl+xml", - "wsgi": "text/wsgi", "wspolicy": "application/wspolicy+xml", - "wsrc": "application/x-wais-source", - "wtb": "application/vnd.webturbo", - "wtk": "application/x-wintalk", - "wv": "application/vnd.wv.csp+wbxml", - "wvx": "video/x-ms-wvx", - "wz": "application/x-wingz", - "x-png": "image/png", + "7z": "application/x-7z-compressed", + "abw": "application/x-abiword", + "ace": "application/x-ace-compressed", + "dmg": "application/x-apple-diskimage", + "aab": "application/x-authorware-bin", "x32": "application/x-authorware-bin", - "x3d": "application/vnd.hzn-3d-crossword", - "x3db": "model/x3d+xml", - "x3dbz": "model/x3d+binary", - "x3dv": "model/x3d-vrml", - "x3dvz": "model/x3d-vrml", - "x3dz": "model/x3d+xml", - "x_b": "model/vnd.parasolid.transmit.binary", - "x_t": "model/vnd.parasolid.transmit.text", - "xaf": "x-world/x-vrml", - "xaml": "application/xaml+xml", - "xap": "application/x-silverlight-app", - "xar": "application/vnd.xara", - "xav": "application/xcap-att+xml", + "u32": "application/x-authorware-bin", + "vox": "application/x-authorware-bin", + "aam": "application/x-authorware-map", + "aas": "application/x-authorware-seg", + "bcpio": "application/x-bcpio", + "torrent": "application/x-bittorrent", + "blb": "application/x-blorb", + "blorb": "application/x-blorb", + "bz": "application/x-bzip", + "bz2": "application/x-bzip2", + "boz": "application/x-bzip2", + "cbr": "application/x-cbr", + "cba": "application/x-cbr", + "cbt": "application/x-cbr", + "cbz": "application/x-cbr", + "cb7": "application/x-cbr", + "vcd": "application/x-cdlink", + "cfs": "application/x-cfs-compressed", + "chat": "application/x-chat", + "pgn": "application/x-chess-pgn", + "nsc": "application/x-conference", + "cpio": "application/x-cpio", + "csh": "application/x-csh", + "deb": "application/x-debian-package", + "udeb": "application/x-debian-package", + "dgc": "application/x-dgc-compressed", + "dir": "application/x-director", + "dcr": "application/x-director", + "dxr": "application/x-director", + "cst": "application/x-director", + "cct": "application/x-director", + "cxt": "application/x-director", + "w3d": "application/x-director", + "fgd": "application/x-director", + "swa": "application/x-director", + "wad": "application/x-doom", + "ncx": "application/x-dtbncx+xml", + "dtb": "application/x-dtbook+xml", + "res": "application/x-dtbresource+xml", + "dvi": "application/x-dvi", + "evy": "application/x-envoy", + "eva": "application/x-eva", + "bdf": "application/x-font-bdf", + "gsf": "application/x-font-ghostscript", + "psf": "application/x-font-linux-psf", + "pcf": "application/x-font-pcf", + "snf": "application/x-font-snf", + "pfa": "application/x-font-type1", + "pfb": "application/x-font-type1", + "pfm": "application/x-font-type1", + "afm": "application/x-font-type1", + "arc": "application/x-freearc", + "spl": "application/x-futuresplash", + "gca": "application/x-gca-compressed", + "ulx": "application/x-glulx", + "gnumeric": "application/x-gnumeric", + "gramps": "application/x-gramps-xml", + "gtar": "application/x-gtar", + "hdf": "application/x-hdf", + "install": "application/x-install-instructions", + "iso": "application/x-iso9660-image", + "jnlp": "application/x-java-jnlp-file", + "latex": "application/x-latex", + "lzh": "application/x-lzh-compressed", + "lha": "application/x-lzh-compressed", + "mie": "application/x-mie", + "prc": "application/x-mobipocket-ebook", + "mobi": "application/x-mobipocket-ebook", + "application": "application/x-ms-application", + "lnk": "application/x-ms-shortcut", + "wmd": "application/x-ms-wmd", + "wmz": "application/x-ms-wmz", "xbap": "application/x-ms-xbap", - "xbd": "application/vnd.fujixerox.docuworks.binder", - "xbm": "image/x-xbitmap", - "xca": "application/xcap-caps+xml", - "xcf": "application/x-xcf", - "xcs": "application/calendar+xml", - "xct": "application/vnd.fujixerox.docuworks.container", - "xdd": "application/bacnet-xdd+zip", - "xdf": "application/xcap-diff+xml", - "xdm": "application/vnd.syncml.dm+xml", - "xdp": "application/vnd.adobe.xdp+xml", - "xdr": "video/x-amt-demorun", - "xdssc": "application/dssc+xml", - "xdw": "application/vnd.fujixerox.docuworks", - "xel": "application/xcap-el+xml", - "xenc": "application/xenc+xml", - "xer": "application/patch-ops-error+xml", - "xfd": "application/vnd.xfdl", - "xfdf": "application/vnd.adobe.xfdf", - "xfdl": "application/vnd.xfdl", - "xgz": "xgl/drawing", - "xht": "application/xhtml+xml", - "xhtm": "application/xhtml+xml", - "xhtml": "application/xhtml+xml", - "xhvml": "application/xv+xml", - "xif": "image/vnd.xiff", - "xl": "application/excel", - "xla": "application/vnd.ms-excel", - "xlam": "application/vnd.ms-excel.addin.macroenabled.12", - "xlb": "application/vndms-excel", - "xlc": "application/vnd.ms-excel", + "mdb": "application/x-msaccess", + "obd": "application/x-msbinder", + "crd": "application/x-mscardfile", + "clp": "application/x-msclip", + "exe": "application/x-msdownload", + "dll": "application/x-msdownload", + "com": "application/x-msdownload", + "bat": "application/x-msdownload", + "msi": "application/x-msdownload", + "mvb": "application/x-msmediaview", + "m13": "application/x-msmediaview", + "m14": "application/x-msmediaview", + "wmf": "application/x-msmetafile", + "wmz": "application/x-msmetafile", + "emf": "application/x-msmetafile", + "emz": "application/x-msmetafile", + "mny": "application/x-msmoney", + "pub": "application/x-mspublisher", + "scd": "application/x-msschedule", + "trm": "application/x-msterminal", + "wri": "application/x-mswrite", + "nc": "application/x-netcdf", + "cdf": "application/x-netcdf", + "nzb": "application/x-nzb", + "p12": "application/x-pkcs12", + "pfx": "application/x-pkcs12", + "p7b": "application/x-pkcs7-certificates", + "spc": "application/x-pkcs7-certificates", + "p7r": "application/x-pkcs7-certreqresp", + "rar": "application/x-rar-compressed", + "ris": "application/x-research-info-systems", + "sh": "application/x-sh", + "shar": "application/x-shar", + "swf": "application/x-shockwave-flash", + "xap": "application/x-silverlight-app", + "sql": "application/x-sql", + "sit": "application/x-stuffit", + "sitx": "application/x-stuffitx", + "srt": "application/x-subrip", + "sv4cpio": "application/x-sv4cpio", + "sv4crc": "application/x-sv4crc", + "t3": "application/x-t3vm-image", + "gam": "application/x-tads", + "tar": "application/x-tar", + "tcl": "application/x-tcl", + "tex": "application/x-tex", + "tfm": "application/x-tex-tfm", + "texinfo": "application/x-texinfo", + "texi": "application/x-texinfo", + "obj": "application/x-tgif", + "ustar": "application/x-ustar", + "src": "application/x-wais-source", + "der": "application/x-x509-ca-cert", + "crt": "application/x-x509-ca-cert", + "fig": "application/x-xfig", "xlf": "application/x-xliff+xml", - "xlim": "application/vnd.xmpie.xlim", - "xlm": "application/vnd.ms-excel", - "xls": "application/vnd.ms-excel", - "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12", - "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12", - "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "xlt": "application/vnd.ms-excel", - "xltm": "application/vnd.ms-excel.template.macroenabled.12", - "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template", - "xlw": "application/vnd.ms-excel", - "xm": "audio/xm", - "xml": "text/xml", - "xmls": "application/dskpp+xml", - "xmt_bin": "model/vnd.parasolid.transmit.binary", - "xmt_txt": "model/vnd.parasolid.transmit.text", - "xmz": "xgl/movie", - "xns": "application/xcap-ns+xml", - "xo": "application/vnd.olpc-sugar", - "xof": "x-world/x-vrml", - "xop": "application/xop+xml", - "xpdl": "application/xml", "xpi": "application/x-xpinstall", - "xpix": "application/x-vnd.ls-xpix", - "xpl": "application/xproc+xml", - "xpm": "image/x-xpixmap", - "xpr": "application/vnd.is-xpr", - "xps": "application/vnd.ms-xpsdocument", - "xpw": "application/vnd.intercon.formnet", - "xpx": "application/vnd.intercon.formnet", - "xq": "text/xquery", - "xql": "text/xquery", - "xqm": "text/xquery", - "xqu": "text/xquery", - "xquery": "text/xquery", - "xqy": "text/xquery", - "xsd": "text/xml", - "xsf": "application/prs.xsf+xml", - "xsl": "application/xslt+xml", - "xslt": "application/xslt+xml", - "xsm": "application/vnd.syncml+xml", - "xspf": "application/xspf+xml", - "xsr": "video/x-amt-showrun", - "xtel": "chemical/x-xtel", - "xul": "application/vnd.mozilla.xul+xml", - "xvm": "application/xv+xml", - "xvml": "application/xv+xml", - "xwd": "image/x-xwindowdump", - "xyz": "chemical/x-xyz", - "xyze": "image/vnd.radiance", "xz": "application/x-xz", - "yaml": "text/yaml", - "yang": "application/yang", - "yin": "application/yin+xml", - "yme": "application/vnd.yaoweme", - "yml": "text/yaml", - "ymp": "text/x-suse-ymp", "z1": "application/x-zmachine", "z2": "application/x-zmachine", "z3": "application/x-zmachine", @@ -1888,19 +744,293 @@ const mimes* = { "z6": "application/x-zmachine", "z7": "application/x-zmachine", "z8": "application/x-zmachine", - "zaz": "application/vnd.zzazz.deck+xml", - "zfc": "application/vnd.filmit.zfc", - "zfo": "application/vnd.software602.filler.form-xml-zip", - "zig": "text/zig", + "xaml": "application/xaml+xml", + "xdf": "application/xcap-diff+xml", + "xenc": "application/xenc+xml", + "xhtml": "application/xhtml+xml", + "xht": "application/xhtml+xml", + "xml": "application/xml", + "xsl": "application/xml", + "dtd": "application/xml-dtd", + "xop": "application/xop+xml", + "xpl": "application/xproc+xml", + "xslt": "application/xslt+xml", + "xspf": "application/xspf+xml", + "mxml": "application/xv+xml", + "xhvml": "application/xv+xml", + "xvml": "application/xv+xml", + "xvm": "application/xv+xml", + "yang": "application/yang", + "yin": "application/yin+xml", "zip": "application/zip", - "zir": "application/vnd.zul", - "zirz": "application/vnd.zul", - "zmm": "application/vnd.handheld-entertainment+xml", - "zmt": "chemical/x-mopac-input", - "zone": "text/dns", - "zoo": "application/octet-stream", - "zsh": "text/x-script.zsh", - "~": "application/x-trash" + "adp": "audio/adpcm", + "au": "audio/basic", + "snd": "audio/basic", + "mid": "audio/midi", + "midi": "audio/midi", + "kar": "audio/midi", + "rmi": "audio/midi", + "m4a": "audio/mp4", + "mp4a": "audio/mp4", + "mpga": "audio/mpeg", + "mp2": "audio/mpeg", + "mp2a": "audio/mpeg", + "mp3": "audio/mpeg", + "m2a": "audio/mpeg", + "m3a": "audio/mpeg", + "oga": "audio/ogg", + "ogg": "audio/ogg", + "spx": "audio/ogg", + "opus": "audio/ogg", + "s3m": "audio/s3m", + "sil": "audio/silk", + "uva": "audio/vnd.dece.audio", + "uvva": "audio/vnd.dece.audio", + "eol": "audio/vnd.digital-winds", + "dra": "audio/vnd.dra", + "dts": "audio/vnd.dts", + "dtshd": "audio/vnd.dts.hd", + "lvp": "audio/vnd.lucent.voice", + "pya": "audio/vnd.ms-playready.media.pya", + "ecelp4800": "audio/vnd.nuera.ecelp4800", + "ecelp7470": "audio/vnd.nuera.ecelp7470", + "ecelp9600": "audio/vnd.nuera.ecelp9600", + "rip": "audio/vnd.rip", + "weba": "audio/webm", + "aac": "audio/x-aac", + "aif": "audio/x-aiff", + "aiff": "audio/x-aiff", + "aifc": "audio/x-aiff", + "caf": "audio/x-caf", + "flac": "audio/x-flac", + "mka": "audio/x-matroska", + "m3u": "audio/x-mpegurl", + "wax": "audio/x-ms-wax", + "wma": "audio/x-ms-wma", + "ram": "audio/x-pn-realaudio", + "ra": "audio/x-pn-realaudio", + "rmp": "audio/x-pn-realaudio-plugin", + "wav": "audio/x-wav", + "xm": "audio/xm", + "cdx": "chemical/x-cdx", + "cif": "chemical/x-cif", + "cmdf": "chemical/x-cmdf", + "cml": "chemical/x-cml", + "csml": "chemical/x-csml", + "xyz": "chemical/x-xyz", + "ttc": "font/collection", + "otf": "font/otf", + "ttf": "font/ttf", + "woff": "font/woff", + "woff2": "font/woff2", + "bmp": "image/bmp", + "cgm": "image/cgm", + "g3": "image/g3fax", + "gif": "image/gif", + "ief": "image/ief", + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "jpe": "image/jpeg", + "ktx": "image/ktx", + "png": "image/png", + "btif": "image/prs.btif", + "sgi": "image/sgi", + "svg": "image/svg+xml", + "svgz": "image/svg+xml", + "tiff": "image/tiff", + "tif": "image/tiff", + "psd": "image/vnd.adobe.photoshop", + "uvi": "image/vnd.dece.graphic", + "uvvi": "image/vnd.dece.graphic", + "uvg": "image/vnd.dece.graphic", + "uvvg": "image/vnd.dece.graphic", + "djvu": "image/vnd.djvu", + "djv": "image/vnd.djvu", + "sub": "image/vnd.dvb.subtitle", + "dwg": "image/vnd.dwg", + "dxf": "image/vnd.dxf", + "fbs": "image/vnd.fastbidsheet", + "fpx": "image/vnd.fpx", + "fst": "image/vnd.fst", + "mmr": "image/vnd.fujixerox.edmics-mmr", + "rlc": "image/vnd.fujixerox.edmics-rlc", + "mdi": "image/vnd.ms-modi", + "wdp": "image/vnd.ms-photo", + "npx": "image/vnd.net-fpx", + "wbmp": "image/vnd.wap.wbmp", + "xif": "image/vnd.xiff", + "webp": "image/webp", + "3ds": "image/x-3ds", + "ras": "image/x-cmu-raster", + "cmx": "image/x-cmx", + "fh": "image/x-freehand", + "fhc": "image/x-freehand", + "fh4": "image/x-freehand", + "fh5": "image/x-freehand", + "fh7": "image/x-freehand", + "ico": "image/x-icon", + "sid": "image/x-mrsid-image", + "pcx": "image/x-pcx", + "pic": "image/x-pict", + "pct": "image/x-pict", + "pnm": "image/x-portable-anymap", + "pbm": "image/x-portable-bitmap", + "pgm": "image/x-portable-graymap", + "ppm": "image/x-portable-pixmap", + "rgb": "image/x-rgb", + "tga": "image/x-tga", + "xbm": "image/x-xbitmap", + "xpm": "image/x-xpixmap", + "xwd": "image/x-xwindowdump", + "eml": "message/rfc822", + "mime": "message/rfc822", + "igs": "model/iges", + "iges": "model/iges", + "msh": "model/mesh", + "mesh": "model/mesh", + "silo": "model/mesh", + "dae": "model/vnd.collada+xml", + "dwf": "model/vnd.dwf", + "gdl": "model/vnd.gdl", + "gtw": "model/vnd.gtw", + "mts": "model/vnd.mts", + "vtu": "model/vnd.vtu", + "wrl": "model/vrml", + "vrml": "model/vrml", + "x3db": "model/x3d+binary", + "x3dbz": "model/x3d+binary", + "x3dv": "model/x3d+vrml", + "x3dvz": "model/x3d+vrml", + "x3d": "model/x3d+xml", + "x3dz": "model/x3d+xml", + "appcache": "text/cache-manifest", + "ics": "text/calendar", + "ifb": "text/calendar", + "css": "text/css", + "csv": "text/csv", + "html": "text/html", + "htm": "text/html", + "js": "text/javascript", + "mjs": "text/javascript", + "n3": "text/n3", + "txt": "text/plain", + "text": "text/plain", + "conf": "text/plain", + "def": "text/plain", + "list": "text/plain", + "log": "text/plain", + "in": "text/plain", + "dsc": "text/prs.lines.tag", + "rtx": "text/richtext", + "sgml": "text/sgml", + "sgm": "text/sgml", + "tsv": "text/tab-separated-values", + "t": "text/troff", + "tr": "text/troff", + "roff": "text/troff", + "man": "text/troff", + "me": "text/troff", + "ms": "text/troff", + "ttl": "text/turtle", + "uri": "text/uri-list", + "uris": "text/uri-list", + "urls": "text/uri-list", + "vcard": "text/vcard", + "curl": "text/vnd.curl", + "dcurl": "text/vnd.curl.dcurl", + "mcurl": "text/vnd.curl.mcurl", + "scurl": "text/vnd.curl.scurl", + "sub": "text/vnd.dvb.subtitle", + "fly": "text/vnd.fly", + "flx": "text/vnd.fmi.flexstor", + "gv": "text/vnd.graphviz", + "3dml": "text/vnd.in3d.3dml", + "spot": "text/vnd.in3d.spot", + "jad": "text/vnd.sun.j2me.app-descriptor", + "wml": "text/vnd.wap.wml", + "wmls": "text/vnd.wap.wmlscript", + "s": "text/x-asm", + "asm": "text/x-asm", + "c": "text/x-c", + "cc": "text/x-c", + "cxx": "text/x-c", + "cpp": "text/x-c", + "h": "text/x-c", + "hh": "text/x-c", + "dic": "text/x-c", + "f": "text/x-fortran", + "for": "text/x-fortran", + "f77": "text/x-fortran", + "f90": "text/x-fortran", + "java": "text/x-java-source", + "nfo": "text/x-nfo", + "opml": "text/x-opml", + "p": "text/x-pascal", + "pas": "text/x-pascal", + "etx": "text/x-setext", + "sfv": "text/x-sfv", + "uu": "text/x-uuencode", + "vcs": "text/x-vcalendar", + "vcf": "text/x-vcard", + "3gp": "video/3gpp", + "3g2": "video/3gpp2", + "h261": "video/h261", + "h263": "video/h263", + "h264": "video/h264", + "jpgv": "video/jpeg", + "jpm": "video/jpm", + "jpgm": "video/jpm", + "mj2": "video/mj2", + "mjp2": "video/mj2", + "mp4": "video/mp4", + "mp4v": "video/mp4", + "mpg4": "video/mp4", + "mpeg": "video/mpeg", + "mpg": "video/mpeg", + "mpe": "video/mpeg", + "m1v": "video/mpeg", + "m2v": "video/mpeg", + "ogv": "video/ogg", + "qt": "video/quicktime", + "mov": "video/quicktime", + "uvh": "video/vnd.dece.hd", + "uvvh": "video/vnd.dece.hd", + "uvm": "video/vnd.dece.mobile", + "uvvm": "video/vnd.dece.mobile", + "uvp": "video/vnd.dece.pd", + "uvvp": "video/vnd.dece.pd", + "uvs": "video/vnd.dece.sd", + "uvvs": "video/vnd.dece.sd", + "uvv": "video/vnd.dece.video", + "uvvv": "video/vnd.dece.video", + "dvb": "video/vnd.dvb.file", + "fvt": "video/vnd.fvt", + "mxu": "video/vnd.mpegurl", + "m4u": "video/vnd.mpegurl", + "pyv": "video/vnd.ms-playready.media.pyv", + "uvu": "video/vnd.uvvu.mp4", + "uvvu": "video/vnd.uvvu.mp4", + "viv": "video/vnd.vivo", + "webm": "video/webm", + "f4v": "video/x-f4v", + "fli": "video/x-fli", + "flv": "video/x-flv", + "m4v": "video/x-m4v", + "mkv": "video/x-matroska", + "mk3d": "video/x-matroska", + "mks": "video/x-matroska", + "mng": "video/x-mng", + "asf": "video/x-ms-asf", + "asx": "video/x-ms-asf", + "vob": "video/x-ms-vob", + "wm": "video/x-ms-wm", + "wmv": "video/x-ms-wmv", + "wmx": "video/x-ms-wmx", + "wvx": "video/x-ms-wvx", + "avi": "video/x-msvideo", + "movie": "video/x-sgi-movie", + "smv": "video/x-smv", + "ice": "x-conference/x-cooltalk", } diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 6d6b3097a..656c98a20 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -67,7 +67,7 @@ type ## some procedures, such as getaddrinfo) AF_UNIX = 1, ## for local socket (using a file). Unsupported on Windows. AF_INET = 2, ## for network protocol IPv4 or - AF_INET6 = when defined(macosx): 30 else: 23 ## for network protocol IPv6. + AF_INET6 = when defined(macosx): 30 elif defined(windows): 23 else: 10 ## for network protocol IPv6. SockType* = enum ## second argument to `socket` proc SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets @@ -97,6 +97,8 @@ type length*: int addrList*: seq[string] +const IPPROTO_NONE* = IPPROTO_IP ## Use this if your socket type requires a protocol value of zero (e.g. Unix sockets). + when useWinVersion: let osInvalidSocket* = winlean.INVALID_SOCKET diff --git a/lib/pure/net.nim b/lib/pure/net.nim index d77ab5db1..24c94b651 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -97,7 +97,7 @@ import std/nativesockets import std/[os, strutils, times, sets, options, monotimes] import std/ssl_config export nativesockets.Port, nativesockets.`$`, nativesockets.`==` -export Domain, SockType, Protocol +export Domain, SockType, Protocol, IPPROTO_NONE const useWinVersion = defined(windows) or defined(nimdoc) const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or @@ -1321,7 +1321,7 @@ when defined(nimdoc) or (defined(posix) and not useNimNetLite): when not defined(nimdoc): var socketAddr = makeUnixAddr(path) if socket.fd.connect(cast[ptr SockAddr](addr socketAddr), - (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32: + (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) != 0'i32: raiseOSError(osLastError()) proc bindUnix*(socket: Socket, path: string) = @@ -1330,7 +1330,7 @@ when defined(nimdoc) or (defined(posix) and not useNimNetLite): when not defined(nimdoc): var socketAddr = makeUnixAddr(path) if socket.fd.bindAddr(cast[ptr SockAddr](addr socketAddr), - (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32: + (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) != 0'i32: raiseOSError(osLastError()) when defineSsl: @@ -2040,8 +2040,10 @@ proc dial*(address: string, port: Port, if success: result = newSocket(lastFd, domain, sockType, protocol, buffered) elif lastError != 0.OSErrorCode: + lastFd.close() raiseOSError(lastError) else: + lastFd.close() raise newException(IOError, "Couldn't resolve address: " & address) proc connect*(socket: Socket, address: string, diff --git a/lib/pure/options.nim b/lib/pure/options.nim index b7a6a6212..b34ff72c0 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -117,9 +117,10 @@ proc option*[T](val: sink T): Option[T] {.inline.} = assert option[Foo](nil).isNone assert option(42).isSome - result.val = val - when T isnot SomePointer: - result.has = true + when T is SomePointer: + result = Option[T](val: val) + else: + result = Option[T](has: true, val: val) proc some*[T](val: sink T): Option[T] {.inline.} = ## Returns an `Option` that has the value `val`. @@ -136,10 +137,9 @@ proc some*[T](val: sink T): Option[T] {.inline.} = when T is SomePointer: assert not val.isNil - result.val = val + result = Option[T](val: val) else: - result.has = true - result.val = val + result = Option[T](has: true, val: val) proc none*(T: typedesc): Option[T] {.inline.} = ## Returns an `Option` for this type that has no value. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 1fd08e763..78ebb1c88 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -424,13 +424,13 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", when defined(windows): var bufsize = MAX_PATH.int32 var unused: WideCString = nil - var res = newWideCString("", bufsize) + var res = newWideCString(bufsize) while true: var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused) if L == 0'i32: raiseOSError(osLastError(), filename) elif L > bufsize: - res = newWideCString("", L) + res = newWideCString(L) bufsize = L else: result = res$L @@ -453,7 +453,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", c_free(cast[pointer](r)) proc getCurrentCompilerExe*(): string {.compileTime.} = discard - ## This is `getAppFilename()`_ at compile time. + ## Returns the path of the currently running Nim compiler or nimble executable. ## ## Can be used to retrieve the currently executing ## Nim compiler from a Nim or nimscript program, or the nimble binary @@ -692,7 +692,10 @@ proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdT proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTarget.} = ## Sleeps `milsecs` milliseconds. + ## A negative `milsecs` causes sleep to return immediately. when defined(windows): + if milsecs < 0: + return # fixes #23732 winlean.sleep(int32(milsecs)) else: var a, b: Timespec @@ -754,14 +757,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = ## 'rawInfo' is either a 'BY_HANDLE_FILE_INFORMATION' structure on Windows, ## or a 'Stat' structure on posix when defined(windows): - template merge(a, b): untyped = - int64( + template merge[T](a, b): untyped = + cast[T]( (uint64(cast[uint32](a))) or (uint64(cast[uint32](b)) shl 32) ) formalInfo.id.device = rawInfo.dwVolumeSerialNumber - formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh) - formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh) + formalInfo.id.file = merge[FileId](rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh) + formalInfo.size = merge[BiggestInt](rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh) formalInfo.linkCount = rawInfo.nNumberOfLinks formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime)) formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime)) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 9284e823a..c304ecca6 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -212,7 +212,7 @@ proc processID*(p: Process): int {.rtl, extern: "nosp$1".} = return p.id proc waitForExit*(p: Process, timeout: int = -1): int {.rtl, - extern: "nosp$1", raises: [OSError, ValueError], tags: [].} + extern: "nosp$1", raises: [OSError, ValueError], tags: [TimeEffect].} ## Waits for the process to finish and returns `p`'s error code. ## ## .. warning:: Be careful when using `waitForExit` for processes created without @@ -457,7 +457,7 @@ proc execProcesses*(cmds: openArray[string], if afterRunEvent != nil: afterRunEvent(i, p) close(p) -iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raises: [OSError, IOError, ValueError], tags: [ReadIOEffect].} = +iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raises: [OSError, IOError, ValueError], tags: [ReadIOEffect, TimeEffect].} = ## Convenience iterator for working with `startProcess` to read data from a ## background process. ## @@ -487,7 +487,7 @@ iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raise discard waitForExit(p) proc readLines*(p: Process): (seq[string], int) {.since: (1, 3), - raises: [OSError, IOError, ValueError], tags: [ReadIOEffect].} = + raises: [OSError, IOError, ValueError], tags: [ReadIOEffect, TimeEffect].} = ## Convenience function for working with `startProcess` to read data from a ## background process. ## @@ -1123,14 +1123,13 @@ elif not defined(useNimRtl): var error: cint let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error)) if sizeRead == sizeof(error): - raiseOSError(osLastError(), + raiseOSError(OSErrorCode(error), "Could not find command: '" & $data.sysCommand & "'. OS error: " & $strerror(error)) return pid {.push stacktrace: off, profiler: off.} - proc startProcessFail(data: ptr StartProcessData) = - var error: cint = errno + proc startProcessFail(data: ptr StartProcessData, error: cint = errno) = discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error)) exitnow(1) @@ -1167,7 +1166,11 @@ elif not defined(useNimRtl): if (poUsePath in data.options): when defined(uClibc) or defined(linux) or defined(haiku): # uClibc environment (OpenWrt included) doesn't have the full execvpe - let exe = findExe(data.sysCommand) + var exe: string + try: + exe = findExe(data.sysCommand) + except OSError as e: + startProcessFail(data, e.errorCode) discard execve(exe.cstring, data.sysArgs, data.sysEnv) else: # MacOSX doesn't have execvpe, so we need workaround. @@ -1353,114 +1356,63 @@ elif not defined(useNimRtl): result = exitStatusLikeShell(p.exitStatus) else: - import std/times - - const - hasThreadSupport = compileOption("threads") and not defined(nimscript) + import std/times except getTime + import std/monotimes proc waitForExit(p: Process, timeout: int = -1): int = - template adjustTimeout(t, s, e: Timespec) = - var diff: int - var b: Timespec - b.tv_sec = e.tv_sec - b.tv_nsec = e.tv_nsec - e.tv_sec = e.tv_sec - s.tv_sec - if e.tv_nsec >= s.tv_nsec: - e.tv_nsec -= s.tv_nsec - else: - if e.tv_sec == posix.Time(0): - raise newException(ValueError, "System time was modified") - else: - diff = s.tv_nsec - e.tv_nsec - e.tv_nsec = 1_000_000_000 - diff - t.tv_sec = t.tv_sec - e.tv_sec - if t.tv_nsec >= e.tv_nsec: - t.tv_nsec -= e.tv_nsec - else: - t.tv_sec = t.tv_sec - posix.Time(1) - diff = e.tv_nsec - t.tv_nsec - t.tv_nsec = 1_000_000_000 - diff - s.tv_sec = b.tv_sec - s.tv_nsec = b.tv_nsec - if p.exitFlag: return exitStatusLikeShell(p.exitStatus) - if timeout == -1: - var status: cint = 1 + if timeout < 0: + # Backwards compatibility with previous verison to + # handle cases where timeout == -1, but extend + # to handle cases where timeout < 0 + var status: cint if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) p.exitFlag = true p.exitStatus = status else: - var nmask, omask: Sigset - var sinfo: SigInfo - var stspec, enspec, tmspec: Timespec - - discard sigemptyset(nmask) - discard sigemptyset(omask) - discard sigaddset(nmask, SIGCHLD) - - when hasThreadSupport: - if pthread_sigmask(SIG_BLOCK, nmask, omask) == -1: - raiseOSError(osLastError()) - else: - if sigprocmask(SIG_BLOCK, nmask, omask) == -1: - raiseOSError(osLastError()) - - if timeout >= 1000: - tmspec.tv_sec = posix.Time(timeout div 1_000) - tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000 - else: - tmspec.tv_sec = posix.Time(0) - tmspec.tv_nsec = (timeout * 1_000_000) - - try: - if clock_gettime(CLOCK_REALTIME, stspec) == -1: - raiseOSError(osLastError()) - while true: - let res = sigtimedwait(nmask, sinfo, tmspec) - if res == SIGCHLD: - if sinfo.si_pid == p.id: - var status: cint = 1 - if waitpid(p.id, status, 0) < 0: - raiseOSError(osLastError()) - p.exitFlag = true - p.exitStatus = status - break - else: - # we have SIGCHLD, but not for process we are waiting, - # so we need to adjust timeout value and continue - if clock_gettime(CLOCK_REALTIME, enspec) == -1: - raiseOSError(osLastError()) - adjustTimeout(tmspec, stspec, enspec) - elif res < 0: - let err = osLastError() - if err.cint == EINTR: - # we have received another signal, so we need to - # adjust timeout and continue - if clock_gettime(CLOCK_REALTIME, enspec) == -1: - raiseOSError(osLastError()) - adjustTimeout(tmspec, stspec, enspec) - elif err.cint == EAGAIN: - # timeout expired, so we trying to kill process - if posix.kill(p.id, SIGKILL) == -1: - raiseOSError(osLastError()) - var status: cint = 1 - if waitpid(p.id, status, 0) < 0: - raiseOSError(osLastError()) - p.exitFlag = true - p.exitStatus = status - break - else: - raiseOSError(err) - finally: - when hasThreadSupport: - if pthread_sigmask(SIG_UNBLOCK, nmask, omask) == -1: - raiseOSError(osLastError()) + # Max 50ms delay + const maxWait = initDuration(milliseconds = 50) + let wait = initDuration(milliseconds = timeout) + let deadline = getMonoTime() + wait + # starting 50μs delay + var delay = initDuration(microseconds = 50) + + while true: + var status: cint + let pid = waitpid(p.id, status, WNOHANG) + if p.id == pid : + p.exitFlag = true + p.exitStatus = status + break + elif pid.int == -1: + raiseOsError(osLastError()) else: - if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1: - raiseOSError(osLastError()) + # Continue waiting if needed + if getMonoTime() >= deadline: + # Previous version of `waitForExit` + # foricibly killed the process. + # We keep this so we don't break programs + # that depend on this behavior + if posix.kill(p.id, SIGKILL) < 0: + raiseOSError(osLastError()) + else: + const max = 1_000_000_000 + let + newWait = getMonoTime() + delay + ticks = newWait.ticks() + ns = ticks mod max + secs = ticks div max + var + waitSpec: TimeSpec + unused: Timespec + waitSpec.tv_sec = posix.Time(secs) + waitSpec.tv_nsec = clong ns + discard posix.clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, waitSpec, unused) + let remaining = deadline - getMonoTime() + delay = min([delay * 2, remaining, maxWait]) result = exitStatusLikeShell(p.exitStatus) diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index 24c903b58..03f151b66 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -176,6 +176,7 @@ include "system/inclrtl" +import std/strutils import std/os type @@ -249,9 +250,20 @@ proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {}, result.cmds[i] = cmdline[i] else: when declared(paramCount): - result.cmds = newSeq[string](paramCount()) - for i in countup(1, paramCount()): - result.cmds[i-1] = paramStr(i) + when defined(nimscript): + var ctr = 0 + var firstNimsFound = false + for i in countup(0, paramCount()): + if firstNimsFound: + result.cmds[ctr] = paramStr(i) + inc ctr, 1 + if paramStr(i).endsWith(".nims") and not firstNimsFound: + firstNimsFound = true + result.cmds = newSeq[string](paramCount()-i) + else: + result.cmds = newSeq[string](paramCount()) + for i in countup(1, paramCount()): + result.cmds[i-1] = paramStr(i) else: # we cannot provide this for NimRtl creation on Posix, because we can't # access the command line arguments then! diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index ce642fcf7..2ca255fa0 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -460,6 +460,8 @@ proc parseBiggestInt*(s: openArray[char], number: var BiggestInt): int {. var res: BiggestInt doAssert parseBiggestInt("9223372036854775807", res) == 19 doAssert res == 9223372036854775807 + doAssert parseBiggestInt("-2024_05_09", res) == 11 + doAssert res == -20240509 var res = BiggestInt(0) # use 'res' for exception safety (don't write to 'number' in case of an # overflow exception): @@ -474,10 +476,8 @@ proc parseInt*(s: openArray[char], number: var int): int {. ## `ValueError` is raised if the parsed integer is out of the valid range. runnableExamples: var res: int - doAssert parseInt("2019", res, 0) == 4 - doAssert res == 2019 - doAssert parseInt("2019", res, 2) == 2 - doAssert res == 19 + doAssert parseInt("-2024_05_02", res) == 11 + doAssert res == -20240502 var res = BiggestInt(0) result = parseBiggestInt(s, res) when sizeof(int) <= 4: @@ -992,6 +992,10 @@ proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.noSid var res: BiggestInt doAssert parseBiggestInt("9223372036854775807", res, 0) == 19 doAssert res == 9223372036854775807 + doAssert parseBiggestInt("-2024_05_09", res) == 11 + doAssert res == -20240509 + doAssert parseBiggestInt("-2024_05_02", res, 7) == 4 + doAssert res == 502 parseBiggestInt(s.toOpenArray(start, s.high), number) proc parseInt*(s: string, number: var int, start = 0): int {.noSideEffect, raises: [ValueError].} = @@ -1000,10 +1004,10 @@ proc parseInt*(s: string, number: var int, start = 0): int {.noSideEffect, raise ## `ValueError` is raised if the parsed integer is out of the valid range. runnableExamples: var res: int - doAssert parseInt("2019", res, 0) == 4 - doAssert res == 2019 - doAssert parseInt("2019", res, 2) == 2 - doAssert res == 19 + doAssert parseInt("-2024_05_02", res) == 11 + doAssert res == -20240502 + doAssert parseInt("-2024_05_02", res, 7) == 4 + doAssert res == 502 parseInt(s.toOpenArray(start, s.high), number) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index d0c449bdd..2969fd6d7 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -314,21 +314,21 @@ func capture*(a: Peg = Peg(kind: pkEmpty)): Peg {.rtl, extern: "npegsCapture".} func backref*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. `reverse` specifies wether indexing starts from the end of the + ## from 1. `reverse` specifies whether indexing starts from the end of the ## capture list. result = Peg(kind: pkBackRef, index: (if reverse: -index else: index - 1)) func backrefIgnoreCase*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. `reverse` specifies wether indexing starts from the end of the + ## from 1. `reverse` specifies whether indexing starts from the end of the ## capture list. Ignores case for matching. result = Peg(kind: pkBackRefIgnoreCase, index: (if reverse: -index else: index - 1)) func backrefIgnoreStyle*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. `reverse` specifies wether indexing starts from the end of the + ## from 1. `reverse` specifies whether indexing starts from the end of the ## capture list. Ignores style for matching. result = Peg(kind: pkBackRefIgnoreStyle, index: (if reverse: -index else: index - 1)) @@ -1047,7 +1047,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) = `enter hdPostf`(s, pegNode, start) else: discard - let hdPostf = ident(substr(strVal(pegKind), 2)) + let hdPostf = ident(substr($pegKind, 2)) getAst(mkDoEnter(hdPostf, s, pegNode, start)) macro leave(pegKind, s, pegNode, start, length: untyped): untyped = @@ -1058,7 +1058,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) = `leave hdPostf`(s, pegNode, start, length) else: discard - let hdPostf = ident(substr(strVal(pegKind), 2)) + let hdPostf = ident(substr($pegKind, 2)) getAst(mkDoLeave(hdPostf, s, pegNode, start, length)) matchOrParse(parseIt) @@ -1198,7 +1198,7 @@ template `=~`*(s: string, pattern: Peg): bool = ## ``` bind MaxSubpatterns when not declaredInScope(matches): - var matches {.inject.}: array[0..MaxSubpatterns-1, string] + var matches {.inject.} = default(array[0..MaxSubpatterns-1, string]) match(s, pattern, matches) # ------------------------- more string handling ------------------------------ diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 749944376..3ec77d37e 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -79,15 +79,28 @@ when defined(nimPreviewSlimSystem): include system/inclrtl {.push debugger: off.} +template whenHasBigInt64(yes64, no64): untyped = + when defined(js): + when compiles(compileOption("jsbigint64")): + when compileOption("jsbigint64"): + yes64 + else: + no64 + else: + no64 + else: + yes64 -when defined(js): - type Ui = uint32 - const randMax = 4_294_967_295u32 -else: +whenHasBigInt64: type Ui = uint64 const randMax = 18_446_744_073_709_551_615u64 +do: + type Ui = uint32 + + const randMax = 4_294_967_295u32 + type Rand* = object ## State of a random number generator. @@ -105,17 +118,17 @@ type ## generator are **not** thread-safe! a0, a1: Ui -when defined(js): - var state = Rand( - a0: 0x69B4C98Cu32, - a1: 0xFED1DD30u32) # global for backwards compatibility -else: +whenHasBigInt64: const DefaultRandSeed = Rand( a0: 0x69B4C98CB8530805u64, a1: 0xFED1DD3004688D67CAu64) # racy for multi-threading but good enough for now: var state = DefaultRandSeed # global for backwards compatibility +do: + var state = Rand( + a0: 0x69B4C98Cu32, + a1: 0xFED1DD30u32) # global for backwards compatibility func isValid(r: Rand): bool {.inline.} = ## Check whether state of `r` is valid. @@ -208,10 +221,10 @@ proc skipRandomNumbers*(s: var Rand) = doAssert vals == [501737, 497901, 500683, 500157] - when defined(js): - const helper = [0xbeac0467u32, 0xd86b048bu32] - else: + whenHasBigInt64: const helper = [0xbeac0467eba5facbu64, 0xd86b048b86aa9922u64] + do: + const helper = [0xbeac0467u32, 0xd86b048bu32] var s0 = Ui 0 s1 = Ui 0 @@ -294,7 +307,13 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = let x = next(r) when defined(js): - result = (float(x) / float(high(uint32))) * max + when compiles(compileOption("jsbigint64")): + when compileOption("jsbigint64"): + result = (float(x) / float(high(uint64))) * max + else: + result = (float(x) / float(high(uint32))) * max + else: + result = (float(x) / float(high(uint32))) * max else: let u = (0x3FFu64 shl 52u64) or (x shr 12u64) result = (cast[float](u) - 1.0) * max diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index ab05bcc25..5f806bd70 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -40,16 +40,16 @@ func reduce*[T: SomeInteger](x: var Rational[T]) = reduce(r) doAssert r.num == 1 doAssert r.den == 2 - + if x.den == 0: + raise newException(DivByZeroDefect, "division by zero") let common = gcd(x.num, x.den) if x.den > 0: x.num = x.num div common x.den = x.den div common - elif x.den < 0: - x.num = -x.num div common - x.den = -x.den div common - else: - raise newException(DivByZeroDefect, "division by zero") + when T isnot SomeUnsignedInt: + if x.den < 0: + x.num = -x.num div common + x.den = -x.den div common func initRational*[T: SomeInteger](num, den: T): Rational[T] = ## Creates a new rational number with numerator `num` and denominator `den`. @@ -318,3 +318,23 @@ func hash*[T](x: Rational[T]): Hash = h = h !& hash(copy.num) h = h !& hash(copy.den) result = !$h + +func `^`*[T: SomeInteger](x: Rational[T], y: T): Rational[T] = + ## Computes `x` to the power of `y`. + ## + ## The exponent `y` must be an integer. Negative exponents are supported + ## but floating point exponents are not. + runnableExamples: + doAssert (-3 // 5) ^ 0 == (1 // 1) + doAssert (-3 // 5) ^ 1 == (-3 // 5) + doAssert (-3 // 5) ^ 2 == (9 // 25) + doAssert (-3 // 5) ^ -2 == (25 // 9) + + if y >= 0: + result.num = x.num ^ y + result.den = x.den ^ y + else: + result.num = x.den ^ -y + result.den = x.num ^ -y + # Note that all powers of reduced rationals are already reduced, + # so we don't need to call reduce() here diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 8ae7fb3c1..56f49d7b1 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -15,6 +15,11 @@ ## Other modules may provide other implementations for this standard ## stream interface. ## +## .. warning:: Due to the use of `pointer`, the `readData`, `peekData` and +## `writeData` interfaces are not available on the compile-time VM, and must +## be cast from a `ptr string` on the JS backend. However, `readDataStr` is +## available generally in place of `readData`. +## ## Basic usage ## =========== ## @@ -938,10 +943,14 @@ proc peekFloat64*(s: Stream): float64 = proc readStrPrivate(s: Stream, length: int, str: var string) = if length > len(str): setLen(str, length) - when defined(js): - let L = readData(s, addr(str), length) + var L: int + when nimvm: + L = readDataStr(s, str, 0..length-1) else: - let L = readData(s, cstring(str), length) + when defined(js): + L = readData(s, addr(str), length) + else: + L = readData(s, cstring(str), length) if L != len(str): setLen(str, L) proc readStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} = diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 170046413..7d093ebb3 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -481,50 +481,48 @@ proc parseStandardFormatSpecifier*(s: string; start = 0; raise newException(ValueError, "invalid format string, cannot parse: " & s[i..^1]) +proc toRadix(typ: char): int = + case typ + of 'x', 'X': 16 + of 'd', '\0': 10 + of 'o': 8 + of 'b': 2 + else: + raise newException(ValueError, + "invalid type in format string for number, expected one " & + " of 'x', 'X', 'b', 'd', 'o' but got: " & typ) + proc formatValue*[T: SomeInteger](result: var string; value: T; - specifier: string) = + specifier: static string) = ## Standard format implementation for `SomeInteger`. It makes little ## sense to call this directly, but it is required to exist ## by the `&` macro. - if specifier.len == 0: + when specifier.len == 0: result.add $value - return - let spec = parseStandardFormatSpecifier(specifier) - var radix = 10 - case spec.typ - of 'x', 'X': radix = 16 - of 'd', '\0': discard - of 'b': radix = 2 - of 'o': radix = 8 else: - raise newException(ValueError, - "invalid type in format string for number, expected one " & - " of 'x', 'X', 'b', 'd', 'o' but got: " & spec.typ) - result.add formatInt(value, radix, spec) + const + spec = parseStandardFormatSpecifier(specifier) + radix = toRadix(spec.typ) -proc formatValue*(result: var string; value: SomeFloat; specifier: string) = - ## Standard format implementation for `SomeFloat`. It makes little + result.add formatInt(value, radix, spec) + +proc formatValue*[T: SomeInteger](result: var string; value: T; + specifier: string) = + ## Standard format implementation for `SomeInteger`. It makes little ## sense to call this directly, but it is required to exist ## by the `&` macro. if specifier.len == 0: result.add $value - return - let spec = parseStandardFormatSpecifier(specifier) - - var fmode = ffDefault - case spec.typ - of 'e', 'E': - fmode = ffScientific - of 'f', 'F': - fmode = ffDecimal - of 'g', 'G': - fmode = ffDefault - of '\0': discard else: - raise newException(ValueError, - "invalid type in format string for number, expected one " & - " of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & spec.typ) + let + spec = parseStandardFormatSpecifier(specifier) + radix = toRadix(spec.typ) + + result.add formatInt(value, radix, spec) +proc formatFloat( + result: var string, value: SomeFloat, fmode: FloatFormatMode, + spec: StandardFormatSpecifier) = var f = formatBiggestFloat(value, fmode, spec.precision) var sign = false if value >= 0.0: @@ -559,23 +557,83 @@ proc formatValue*(result: var string; value: SomeFloat; specifier: string) = else: result.add res +proc toFloatFormatMode(typ: char): FloatFormatMode = + case typ + of 'e', 'E': ffScientific + of 'f', 'F': ffDecimal + of 'g', 'G': ffDefault + of '\0': ffDefault + else: + raise newException(ValueError, + "invalid type in format string for number, expected one " & + " of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & typ) + +proc formatValue*(result: var string; value: SomeFloat; specifier: static string) = + ## Standard format implementation for `SomeFloat`. It makes little + ## sense to call this directly, but it is required to exist + ## by the `&` macro. + when specifier.len == 0: + result.add $value + else: + const + spec = parseStandardFormatSpecifier(specifier) + fmode = toFloatFormatMode(spec.typ) + + formatFloat(result, value, fmode, spec) + +proc formatValue*(result: var string; value: SomeFloat; specifier: string) = + ## Standard format implementation for `SomeFloat`. It makes little + ## sense to call this directly, but it is required to exist + ## by the `&` macro. + if specifier.len == 0: + result.add $value + else: + let + spec = parseStandardFormatSpecifier(specifier) + fmode = toFloatFormatMode(spec.typ) + + formatFloat(result, value, fmode, spec) + +proc formatValue*(result: var string; value: string; specifier: static string) = + ## Standard format implementation for `string`. It makes little + ## sense to call this directly, but it is required to exist + ## by the `&` macro. + const spec = parseStandardFormatSpecifier(specifier) + var value = + when spec.typ in {'s', '\0'}: value + else: static: + raise newException(ValueError, + "invalid type in format string for string, expected 's', but got " & + spec.typ) + when spec.precision != -1: + if spec.precision < runeLen(value): + const precision = cast[Natural](spec.precision) + setLen(value, Natural(runeOffset(value, precision))) + + result.add alignString(value, spec.minimumWidth, spec.align, spec.fill) + proc formatValue*(result: var string; value: string; specifier: string) = ## Standard format implementation for `string`. It makes little ## sense to call this directly, but it is required to exist ## by the `&` macro. let spec = parseStandardFormatSpecifier(specifier) - var value = value - case spec.typ - of 's', '\0': discard - else: - raise newException(ValueError, - "invalid type in format string for string, expected 's', but got " & - spec.typ) + var value = + if spec.typ in {'s', '\0'}: value + else: + raise newException(ValueError, + "invalid type in format string for string, expected 's', but got " & + spec.typ) if spec.precision != -1: if spec.precision < runeLen(value): - setLen(value, runeOffset(value, spec.precision)) + let precision = cast[Natural](spec.precision) + setLen(value, Natural(runeOffset(value, precision))) + result.add alignString(value, spec.minimumWidth, spec.align, spec.fill) +proc formatValue[T: not SomeInteger](result: var string; value: T; specifier: static string) = + mixin `$` + formatValue(result, $value, specifier) + proc formatValue[T: not SomeInteger](result: var string; value: T; specifier: string) = mixin `$` formatValue(result, $value, specifier) @@ -673,7 +731,6 @@ proc strformatImpl(f: string; openChar, closeChar: char, inc i, 2 else: raiseAssert "invalid format string: '$1' instead of '$1$1'" % $closeChar - inc i else: strlit.add f[i] inc i diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim index c8cd839be..a3e539e7e 100644 --- a/lib/pure/strmisc.nim +++ b/lib/pure/strmisc.nim @@ -27,20 +27,17 @@ func expandTabs*(s: string, tabSize: int = 8): string = doAssert expandTabs("a\tb\n\txy\t", 3) == "a b\n xy " result = newStringOfCap(s.len + s.len shr 2) - var pos = 0 template addSpaces(n) = - for j in 0 ..< n: + for _ in 1..n: result.add(' ') - pos += 1 + pos += n - for i in 0 ..< len(s): - let c = s[i] + var pos = 0 + let denominator = if tabSize > 0: tabSize else: 1 + for c in s: if c == '\t': - let - denominator = if tabSize > 0: tabSize else: 1 - numSpaces = tabSize - pos mod denominator - + let numSpaces = tabSize - pos mod denominator addSpaces(numSpaces) else: result.add(c) diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index cf3f3116b..16ef9e642 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -94,7 +94,7 @@ which we then use in our scanf pattern to help us in the matching process: ... ``` -It also possible to pass arguments to a user definable matcher: +It is also possible to pass arguments to a user definable matcher: ```nim proc ndigits(input: string; intVal: var int; start: int; n: int): int = diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 479acc075..81be7db17 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -334,9 +334,9 @@ func normalize*(s: string): string {.rtl, extern: "nsuNormalize".} = func cmpIgnoreCase*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreCase".} = ## Compares two strings in a case insensitive manner. Returns: ## - ## | 0 if a == b - ## | < 0 if a < b - ## | > 0 if a > b + ## | `0` if a == b + ## | `< 0` if a < b + ## | `> 0` if a > b runnableExamples: doAssert cmpIgnoreCase("FooBar", "foobar") == 0 doAssert cmpIgnoreCase("bar", "Foo") < 0 @@ -354,9 +354,9 @@ func cmpIgnoreStyle*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreStyle".} = ## ## Returns: ## - ## | 0 if a == b - ## | < 0 if a < b - ## | > 0 if a > b + ## | `0` if a == b + ## | `< 0` if a < b + ## | `> 0` if a > b runnableExamples: doAssert cmpIgnoreStyle("foo_bar", "FooBar") == 0 doAssert cmpIgnoreStyle("foo_bar_5", "FooBar4") > 0 @@ -565,7 +565,7 @@ iterator rsplit*(s: string, sep: char, maxsplit: int = -1): string = ## Splits the string `s` into substrings from the right using a ## string separator. Works exactly the same as `split iterator - ## <#split.i,string,char,int>`_ except in reverse order. + ## <#split.i,string,char,int>`_ except in **reverse** order. ## ## ```nim ## for piece in "foo:bar".rsplit(':'): @@ -592,7 +592,7 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): string = ## Splits the string `s` into substrings from the right using a ## string separator. Works exactly the same as `split iterator - ## <#split.i,string,char,int>`_ except in reverse order. + ## <#split.i,string,char,int>`_ except in **reverse** order. ## ## ```nim ## for piece in "foo bar".rsplit(WhiteSpace): @@ -622,7 +622,7 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1, keepSeparators: bool = false): string = ## Splits the string `s` into substrings from the right using a ## string separator. Works exactly the same as `split iterator - ## <#split.i,string,string,int>`_ except in reverse order. + ## <#split.i,string,string,int>`_ except in **reverse** order. ## ## ```nim ## for piece in "foothebar".rsplit("the"): @@ -805,7 +805,7 @@ func split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl, func rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl, extern: "nsuRSplitChar".} = ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a func - ## that returns a sequence of substrings. + ## that returns a sequence of substrings in original order. ## ## A possible common use case for `rsplit` is path manipulation, ## particularly on systems that don't use a common delimiter. @@ -835,7 +835,7 @@ func rsplit*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.rtl, extern: "nsuRSplitCharSet".} = ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a - ## func that returns a sequence of substrings. + ## func that returns a sequence of substrings in original order. ## ## A possible common use case for `rsplit` is path manipulation, ## particularly on systems that don't use a common delimiter. @@ -867,7 +867,7 @@ func rsplit*(s: string, seps: set[char] = Whitespace, func rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl, extern: "nsuRSplitString".} = ## The same as the `rsplit iterator <#rsplit.i,string,string,int,bool>`_, but is a func - ## that returns a sequence of substrings. + ## that returns a sequence of substrings in original order. ## ## A possible common use case for `rsplit` is path manipulation, ## particularly on systems that don't use a common delimiter. @@ -1307,7 +1307,7 @@ func parseEnum*[T: enum](s: string): T = ## type contains multiple fields with the same string value. ## ## Raises `ValueError` for an invalid value in `s`. The comparison is - ## done in a style insensitive way. + ## done in a style insensitive way (first letter is still case-sensitive). runnableExamples: type MyEnum = enum @@ -1327,7 +1327,7 @@ func parseEnum*[T: enum](s: string, default: T): T = ## type contains multiple fields with the same string value. ## ## Uses `default` for an invalid value in `s`. The comparison is done in a - ## style insensitive way. + ## style insensitive way (first letter is still case-sensitive). runnableExamples: type MyEnum = enum @@ -2281,18 +2281,31 @@ func replaceWord*(s, sub: string, by = ""): string {.rtl, add result, substr(s, i) func multiReplace*(s: string, replacements: varargs[(string, string)]): string = - ## Same as replace, but specialized for doing multiple replacements in a single - ## pass through the input string. + ## Same as `replace<#replace,string,string,string>`_, but specialized for + ## doing multiple replacements in a single pass through the input string. ## - ## `multiReplace` performs all replacements in a single pass, this means it - ## can be used to swap the occurrences of "a" and "b", for instance. + ## `multiReplace` scans the input string from left to right and replaces the + ## matching substrings in the same order as passed in the argument list. + ## + ## The implications of the order of scanning the string and matching the + ## replacements: + ## - In case of multiple matches at a given position, the earliest + ## replacement is applied. + ## - Overlaps are not handled. After performing a replacement, the scan + ## continues from the character after the matched substring. If the + ## resulting string then contains a possible match starting in a newly + ## placed substring, the additional replacement is not performed. ## ## If the resulting string is not longer than the original input string, ## only a single memory allocation is required. ## - ## Replacements are done left to right in the string. If at a given position - ## multiple replacements match, earlier replacements are preferred over - ## later replacements in the argument list. + runnableExamples: + # Swapping occurrences of 'a' and 'b': + doAssert multireplace("abba", [("a", "b"), ("b", "a")]) == "baab" + + # The second replacement ("ab") is matched and performed first, the scan then + # continues from 'c', so the "bc" replacement is never matched and thus skipped. + doAssert multireplace("abc", [("bc", "x"), ("ab", "_b")]) == "_bc" result = newStringOfCap(s.len) var i = 0 var fastChk: set[char] = {} @@ -2441,8 +2454,8 @@ func validIdentifier*(s: string): bool {.rtl, extern: "nsuValidIdentifier".} = # floating point formatting: when not defined(js): - func c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>", - importc: "sprintf", varargs.} + func c_snprintf(buf: cstring, n: csize_t, frmt: cstring): cint {.header: "<stdio.h>", + importc: "snprintf", varargs.} type FloatFormatMode* = enum @@ -2475,7 +2488,7 @@ func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, when defined(js): var precision = precision if precision == -1: - # use the same default precision as c_sprintf + # use the same default precision as c_snprintf precision = 6 var res: cstring case format @@ -2506,11 +2519,11 @@ func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, frmtstr[3] = '*' frmtstr[4] = floatFormatToChar[format] frmtstr[5] = '\0' - L = c_sprintf(cast[cstring](addr buf), cast[cstring](addr frmtstr), precision, f) + L = c_snprintf(cast[cstring](addr buf), csize_t(2501), cast[cstring](addr frmtstr), precision, f) else: frmtstr[1] = floatFormatToChar[format] frmtstr[2] = '\0' - L = c_sprintf(cast[cstring](addr buf), cast[cstring](addr frmtstr), f) + L = c_snprintf(cast[cstring](addr buf), csize_t(2501), cast[cstring](addr frmtstr), f) result = newString(L) for i in 0 ..< L: # Depending on the locale either dot or comma is produced, diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index cfa04a837..90ba20c13 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -203,7 +203,7 @@ proc freshIdentNodes(ast: NimNode): NimNode = # see also https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458 proc inspect(node: NimNode): NimNode = case node.kind: - of nnkIdent, nnkSym: + of nnkIdent, nnkSym, nnkOpenSymChoice, nnkClosedSymChoice, nnkOpenSym: result = ident($node) of nnkEmpty, nnkLiterals: result = node @@ -345,9 +345,10 @@ proc collectImpl(init, body: NimNode): NimNode {.since: (1, 1).} = let res = genSym(nskVar, "collectResult") var bracketExpr: NimNode if init != nil: - expectKind init, {nnkCall, nnkIdent, nnkSym} + expectKind init, {nnkCall, nnkIdent, nnkSym, nnkClosedSymChoice, nnkOpenSymChoice, nnkOpenSym} bracketExpr = newTree(nnkBracketExpr, - if init.kind == nnkCall: freshIdentNodes(init[0]) else: freshIdentNodes(init)) + if init.kind in {nnkCall, nnkClosedSymChoice, nnkOpenSymChoice, nnkOpenSym}: + freshIdentNodes(init[0]) else: freshIdentNodes(init)) else: bracketExpr = newTree(nnkBracketExpr) let (resBody, keyType, valueType) = trans(body, res, bracketExpr) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index a8e24313f..e59153455 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -131,9 +131,10 @@ =========== ================================================================================= ============================================== Other strings can be inserted by putting them in `''`. For example - `hh'->'mm` will give `01->56`. The following characters can be - inserted without quoting them: `:` `-` `(` `)` `/` `[` `]` - `,`. A literal `'` can be specified with `''`. + `hh'->'mm` will give `01->56`. In addition to spaces, + the following characters can be inserted without quoting them: + `:` `-` `,` `.` `(` `)` `/` `[` `]`. + A literal `'` can be specified with `''`. However you don't need to necessarily separate format patterns, as an unambiguous format string like `yyyyMMddhhmmss` is also valid (although @@ -1498,11 +1499,11 @@ proc `-=`*(a: var DateTime, b: Duration) = a = a - b proc getDateStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current local date as a string of the format `YYYY-MM-DD`. + ## Gets the current local date as a string of the format `YYYY-MM-dd`. runnableExamples: echo getDateStr(now() - 1.months) assertDateTimeInitialized dt - result = newStringOfCap(10) # len("YYYY-MM-DD") == 10 + result = newStringOfCap(10) # len("YYYY-MM-dd") == 10 result.addInt dt.year result.add '-' result.add intToStr(dt.monthZero, 2) @@ -1631,7 +1632,7 @@ const "Sunday"], ) - FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ','} + FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ',', '.'} proc `$`*(f: TimeFormat): string = ## Returns the format string that was used to construct `f`. diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 7c58d1ddc..78af84fdd 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -130,6 +130,40 @@ template pointerBase*[T](_: typedesc[ptr T | ref T]): typedesc = assert (var s = "abc"; s[0].addr).typeof.pointerBase is char T +proc rangeBase*(T: typedesc[range]): typedesc {.magic: "TypeTrait".} = + ## Returns the base type for range types, or the type itself otherwise. + ## + ## **See also:** + ## * `rangeBase template <#rangeBase.t,T>`_ + runnableExamples: + type MyRange = range[0..5] + type MyEnum = enum a, b, c + type MyEnumRange = range[b..c] + doAssert rangeBase(MyRange) is int + doAssert rangeBase(MyEnumRange) is MyEnum + doAssert rangeBase(range['a'..'z']) is char + +template rangeBase*[T: range](a: T): untyped = + ## Overload of `rangeBase <#rangeBase,typedesc,static[bool]>`_ for values. + runnableExamples: + type MyRange = range[0..5] + type MyEnum = enum a, b, c + type MyEnumRange = range[b..c] + let x = MyRange(3) + doAssert rangeBase(x) is int + doAssert $typeof(rangeBase(x)) == "int" + doAssert rangeBase(x) == 3 + let y: set[MyEnumRange] = {c} + for e in y: + doAssert rangeBase(e) is MyEnum + doAssert $typeof(rangeBase(e)) == "MyEnum" + doAssert rangeBase(e) == c + let z: seq[range['a'..'z']] = @['c'] + doAssert rangeBase(z[0]) is char + doAssert $typeof(rangeBase(z[0])) == "char" + doAssert rangeBase(z[0]) == 'c' + rangeBase(typeof(T))(a) + proc distinctBase*(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} = ## Returns the base type for distinct types, or the type itself otherwise. ## If `recursive` is false, only the immediate distinct base will be returned. diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index c8d18831a..8cbe117bb 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -464,7 +464,7 @@ proc `==`*(a, b: Rune): bool = include "includes/unicode_ranges" -proc binarySearch(c: RuneImpl, tab: openArray[int], len, stride: int): int = +proc binarySearch(c: RuneImpl, tab: openArray[int32], len, stride: int): int = var n = len var t = 0 while n > 1: @@ -836,9 +836,9 @@ proc toRunes*(s: openArray[char]): seq[Rune] = proc cmpRunesIgnoreCase*(a, b: openArray[char]): int {.rtl, extern: "nuc$1".} = ## Compares two UTF-8 strings and ignores the case. Returns: ## - ## | 0 if a == b - ## | < 0 if a < b - ## | > 0 if a > b + ## | `0` if a == b + ## | `< 0` if a < b + ## | `> 0` if a > b var i = 0 var j = 0 var ar, br: Rune @@ -846,7 +846,12 @@ proc cmpRunesIgnoreCase*(a, b: openArray[char]): int {.rtl, extern: "nuc$1".} = # slow path: fastRuneAt(a, i, ar) fastRuneAt(b, j, br) - result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br)) + when sizeof(int) < 4: + const lo = low(int).int32 + const hi = high(int).int32 + result = clamp(RuneImpl(toLower(ar)) - RuneImpl(toLower(br)), lo, hi).int + else: + result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br)) if result != 0: return result = a.len - b.len @@ -1370,9 +1375,9 @@ proc toRunes*(s: string): seq[Rune] {.inline.} = proc cmpRunesIgnoreCase*(a, b: string): int {.inline.} = ## Compares two UTF-8 strings and ignores the case. Returns: ## - ## | 0 if a == b - ## | < 0 if a < b - ## | > 0 if a > b + ## | `0` if a == b + ## | `< 0` if a < b + ## | `> 0` if a > b cmpRunesIgnoreCase(a.toOa(), b.toOa()) proc reversed*(s: string): string {.inline.} = diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 2f3437ac7..cfb762258 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -773,7 +773,8 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped = except errorTypes: discard except: - checkpoint(lineInfoLit & ": Expect Failed, unexpected exception was thrown.") + let err = getCurrentException() + checkpoint(lineInfoLit & ": Expect Failed, " & $err.name & " was thrown.") fail() {.pop.} diff --git a/lib/pure/volatile.nim b/lib/pure/volatile.nim index f30d899df..a38247c7d 100644 --- a/lib/pure/volatile.nim +++ b/lib/pure/volatile.nim @@ -10,20 +10,18 @@ ## This module contains code for generating volatile loads and stores, ## which are useful in embedded and systems programming. -template volatileLoad*[T](src: ptr T): T = +proc volatileLoad*[T](src: ptr T): T {.inline, noinit.} = ## Generates a volatile load of the value stored in the container `src`. ## Note that this only effects code generation on `C` like backends. when nimvm: - src[] + result = src[] else: when defined(js): - src[] + result = src[] else: - var res: T - {.emit: [res, " = (*(", typeof(src[]), " volatile*)", src, ");"].} - res + {.emit: [result, " = (*(", typeof(src[]), " volatile*)", src, ");"].} -template volatileStore*[T](dest: ptr T, val: T) = +proc volatileStore*[T](dest: ptr T, val: T) {.inline.} = ## Generates a volatile store into the container `dest` of the value ## `val`. Note that this only effects code generation on `C` like ## backends. diff --git a/lib/std/cmdline.nim b/lib/std/cmdline.nim index a57fb76a4..0ba4619e5 100644 --- a/lib/std/cmdline.nim +++ b/lib/std/cmdline.nim @@ -181,7 +181,7 @@ when defined(nimdoc): ## Similarly to `argv`:idx: in C, ## it is possible to call `paramStr(0)` but this will return OS specific ## contents (usually the name of the invoked executable). You should avoid - ## this and call `getAppFilename()`_ instead. + ## this and call `getAppFilename() <os.html#getAppFilename>`_ instead. ## ## **Availability**: When generating a dynamic library (see `--app:lib`) on ## Posix this proc is not defined. @@ -192,7 +192,7 @@ when defined(nimdoc): ## * `parseCmdLine proc`_ ## * `paramCount proc`_ ## * `commandLineParams proc`_ - ## * `getAppFilename proc`_ + ## * `getAppFilename proc <os.html#getAppFilename>`_ ## ## **Examples:** ## @@ -282,7 +282,7 @@ when declared(paramCount) or defined(nimdoc): ## Convenience proc which returns the command line parameters. ## ## This returns **only** the parameters. If you want to get the application - ## executable filename, call `getAppFilename()`_. + ## executable filename, call `getAppFilename() <os.html#getAppFilename>`_. ## ## **Availability**: On Posix there is no portable way to get the command ## line from a DLL and thus the proc isn't defined in this environment. You @@ -294,7 +294,7 @@ when declared(paramCount) or defined(nimdoc): ## * `parseCmdLine proc`_ ## * `paramCount proc`_ ## * `paramStr proc`_ - ## * `getAppFilename proc`_ + ## * `getAppFilename proc <os.html#getAppFilename>`_ ## ## **Examples:** ## diff --git a/lib/std/editdistance.nim b/lib/std/editdistance.nim index 6a25ca4b9..40c0017ae 100644 --- a/lib/std/editdistance.nim +++ b/lib/std/editdistance.nim @@ -18,7 +18,7 @@ proc editDistance*(a, b: string): int {.noSideEffect.} = ## This uses the `Levenshtein`:idx: distance algorithm with only a linear ## memory overhead. runnableExamples: static: doAssert editdistance("Kitten", "Bitten") == 1 - if len(a) > len(b): + if runeLen(a) > runeLen(b): # make `b` the longer string return editDistance(b, a) # strip common prefix diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index bcfb2d5d7..9c338817d 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -174,6 +174,9 @@ template symbolRank*[T: enum](a: T): int = when T is Ordinal: ord(a) - T.low.ord.static else: symbolRankImpl(a) +proc rangeBase(T: typedesc): typedesc {.magic: "TypeTrait".} + # skip one level of range; return the base type of a range type + func symbolName*[T: enum](a: T): string = ## Returns the symbol name of an enum. ## @@ -192,5 +195,8 @@ func symbolName*[T: enum](a: T): string = c1 = 4 c2 = 20 assert c1.symbolName == "c1" - const names = enumNames(T) + when T is range: + const names = enumNames(rangeBase T) + else: + const names = enumNames(T) names[a.symbolRank] diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim index 52e9653df..f26368f42 100644 --- a/lib/std/exitprocs.nim +++ b/lib/std/exitprocs.nim @@ -29,13 +29,13 @@ initLock(gFunsLock) when defined(js): proc addAtExit(quitProc: proc() {.noconv.}) = when defined(nodejs): - asm """ + {.emit: """ process.on('exit', `quitProc`); - """ + """.} elif defined(js): - asm """ + {.emit: """ window.onbeforeunload = `quitProc`; - """ + """.} else: proc addAtExit(quitProc: proc() {.noconv.}) {. importc: "atexit", header: "<stdlib.h>".} @@ -72,16 +72,16 @@ proc addExitProc*(cl: proc() {.noconv.}) = when not defined(nimscript) and (not defined(js) or defined(nodejs)): proc getProgramResult*(): int = when defined(js) and defined(nodejs): - asm """ + {.emit: """ `result` = process.exitCode; -""" +""".} else: result = programResult proc setProgramResult*(a: int) = when defined(js) and defined(nodejs): - asm """ + {.emit: """ process.exitCode = `a`; -""" +""".} else: programResult = a diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim index 872549c3b..9258245f6 100644 --- a/lib/std/formatfloat.nim +++ b/lib/std/formatfloat.nim @@ -35,8 +35,8 @@ proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int result = float32ToChars(buf, value, forceTrailingDotZero=true).int buf[result] = '\0' -proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>", - importc: "sprintf", varargs, noSideEffect.} +proc c_snprintf(buf: cstring, n: csize_t, frmt: cstring): cint {.header: "<stdio.h>", + importc: "snprintf", varargs, noSideEffect.} proc writeToBuffer(buf: var array[65, char]; value: cstring) = var i = 0 @@ -49,7 +49,7 @@ proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): ## ## returns the amount of bytes written to `buf` not counting the ## terminating '\0' character. - var n = c_sprintf(cast[cstring](addr buf), "%.16g", value).int + var n = c_snprintf(cast[cstring](addr buf), 65, "%.16g", value).int var hasDot = false for i in 0..n-1: if buf[i] == ',': @@ -104,19 +104,19 @@ when defined(js): proc nimFloatToString(a: float): cstring = ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 # print `-0.0` properly - asm """ + {.emit: """ function nimOnlyDigitsOrMinus(n) { return n.toString().match(/^-?\d+$/); } if (Number.isSafeInteger(`a`)) - `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" + `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"; else { - `result` = `a`+"" + `result` = `a`+""; if(nimOnlyDigitsOrMinus(`result`)){ - `result` = `a`+".0" + `result` = `a`+".0"; } } - """ + """.} proc addFloat*(result: var string; x: float | float32) {.inline.} = ## Converts float to its string representation and appends it to `result`. diff --git a/lib/std/paths.nim b/lib/std/paths.nim index b488d2fea..664dedd31 100644 --- a/lib/std/paths.nim +++ b/lib/std/paths.nim @@ -9,7 +9,7 @@ export osseps import std/envvars import std/private/osappdirs -import std/pathnorm +import std/[pathnorm, hashes, sugar, strutils] from std/private/ospaths2 import joinPath, splitPath, ReadDirEffect, WriteDirEffect, @@ -25,6 +25,16 @@ export ReadDirEffect, WriteDirEffect type Path* = distinct string +func hash*(x: Path): Hash = + let x = x.string.dup(normalizePath) + if FileSystemCaseSensitive: + result = x.hash + else: + result = x.toLowerAscii.hash + +template `$`*(x: Path): string = + string(x) + func `==`*(x, y: Path): bool {.inline.} = ## Compares two paths. ## diff --git a/lib/std/private/gitutils.nim b/lib/std/private/gitutils.nim index db323bee1..6dc9c8f3b 100644 --- a/lib/std/private/gitutils.nim +++ b/lib/std/private/gitutils.nim @@ -4,7 +4,7 @@ internal API for now, API subject to change # xxx move other git utilities here; candidate for stdlib. -import std/[os, osproc, strutils, tempfiles] +import std/[os, paths, osproc, strutils, tempfiles] when defined(nimPreviewSlimSystem): import std/[assertions, syncio] @@ -32,15 +32,8 @@ template retryCall*(maxRetry = 3, backoffDuration = 1.0, call: untyped): bool = result proc isGitRepo*(dir: string): bool = - ## This command is used to get the relative path to the root of the repository. - ## Using this, we can verify whether a folder is a git repository by checking - ## whether the command success and if the output is empty. - let (output, status) = execCmdEx("git rev-parse --show-cdup", workingDir = dir) - # On Windows there will be a trailing newline on success, remove it. - # The value of a successful call typically won't have a whitespace (it's - # usually a series of ../), so we know that it's safe to unconditionally - # remove trailing whitespaces from the result. - result = status == 0 and output.strip() == "" + ## Avoid calling git since it depends on /bin/sh existing and fails in Nix. + return fileExists(dir/".git/HEAD") proc diffFiles*(path1, path2: string): tuple[output: string, same: bool] = ## Returns a human readable diff of files `path1`, `path2`, the exact form of diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim index fd1f395f3..5f79eab27 100644 --- a/lib/std/private/jsutils.nim +++ b/lib/std/private/jsutils.nim @@ -37,13 +37,13 @@ when defined(js): let a = array[2, float64].default assert jsConstructorName(a) == "Float64Array" assert jsConstructorName(a.toJs) == "Float64Array" - asm """`result` = `a`.constructor.name""" + {.emit: """`result` = `a`.constructor.name;""".} proc hasJsBigInt*(): bool = - asm """`result` = typeof BigInt != 'undefined'""" + {.emit: """`result` = typeof BigInt != 'undefined';""".} proc hasBigUint64Array*(): bool = - asm """`result` = typeof BigUint64Array != 'undefined'""" + {.emit: """`result` = typeof BigUint64Array != 'undefined';""".} proc getProtoName*[T](a: T): cstring {.importjs: "Object.prototype.toString.call(#)".} = runnableExamples: diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim index b89a59c8d..a44cad7d9 100644 --- a/lib/std/private/osdirs.nim +++ b/lib/std/private/osdirs.nim @@ -446,13 +446,17 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", else: discard existsOrCreateDir(p) -proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", +proc copyDir*(source, dest: string, skipSpecial = false) {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = ## Copies a directory from `source` to `dest`. ## ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks ## are skipped. ## + ## If `skipSpecial` is true, then (besides all directories) only *regular* + ## files (**without** special "file" objects like FIFOs, device files, + ## etc) will be copied on Unix. + ## ## If this fails, `OSError` is raised. ## ## On the Windows platform this proc will copy the attributes from @@ -472,16 +476,17 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", ## * `createDir proc`_ ## * `moveDir proc`_ createDir(dest) - for kind, path in walkDir(source): + for kind, path in walkDir(source, skipSpecial = skipSpecial): var noSource = splitPath(path).tail if kind == pcDir: - copyDir(path, dest / noSource) + copyDir(path, dest / noSource, skipSpecial = skipSpecial) else: copyFile(path, dest / noSource, {cfSymlinkAsIs}) proc copyDirWithPermissions*(source, dest: string, - ignorePermissionErrors = true) + ignorePermissionErrors = true, + skipSpecial = false) {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = ## Copies a directory from `source` to `dest` preserving file permissions. @@ -489,6 +494,10 @@ proc copyDirWithPermissions*(source, dest: string, ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks ## are skipped. ## + ## If `skipSpecial` is true, then (besides all directories) only *regular* + ## files (**without** special "file" objects like FIFOs, device files, + ## etc) will be copied on Unix. + ## ## If this fails, `OSError` is raised. This is a wrapper proc around ## `copyDir`_ and `copyFileWithPermissions`_ procs ## on non-Windows platforms. @@ -518,10 +527,10 @@ proc copyDirWithPermissions*(source, dest: string, except: if not ignorePermissionErrors: raise - for kind, path in walkDir(source): + for kind, path in walkDir(source, skipSpecial = skipSpecial): var noSource = splitPath(path).tail if kind == pcDir: - copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors) + copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors, skipSpecial = skipSpecial) else: copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors, {cfSymlinkAsIs}) diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index a1d7079c5..37d8eabca 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -240,7 +240,7 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 1 else: # generic version of copyFile which works for any platform: var d, s: File - if not open(s, source):raiseOSError(osLastError(), source) + if not open(s, source): raiseOSError(osLastError(), source) if not open(d, dest, fmWrite): close(s) raiseOSError(osLastError(), dest) diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim index 37fae3ccd..bc69ff725 100644 --- a/lib/std/private/ospaths2.nim +++ b/lib/std/private/ospaths2.nim @@ -254,10 +254,10 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raise result = path[0] != ':' elif defined(RISCOS): result = path[0] == '$' - elif defined(posix) or defined(js): - # `or defined(js)` wouldn't be needed pending https://github.com/nim-lang/Nim/issues/13469 - # This works around the problem for posix, but Windows is still broken with nim js -d:nodejs + elif defined(posix): result = path[0] == '/' + elif defined(nodejs): + {.emit: [result," = require(\"path\").isAbsolute(",path.cstring,");"].} else: raiseAssert "unreachable" # if ever hits here, adapt as needed @@ -763,9 +763,9 @@ proc cmpPaths*(pathA, pathB: string): int {. ## On a case-sensitive filesystem this is done ## case-sensitively otherwise case-insensitively. Returns: ## - ## | 0 if pathA == pathB - ## | < 0 if pathA < pathB - ## | > 0 if pathA > pathB + ## | `0` if pathA == pathB + ## | `< 0` if pathA < pathB + ## | `> 0` if pathA > pathB runnableExamples: when defined(macosx): assert cmpPaths("foo", "Foo") == 0 @@ -862,13 +862,13 @@ when not defined(nimscript): raiseAssert "use -d:nodejs to have `getCurrentDir` defined" elif defined(windows): var bufsize = MAX_PATH.int32 - var res = newWideCString("", bufsize) + var res = newWideCString(bufsize) while true: var L = getCurrentDirectoryW(bufsize, res) if L == 0'i32: raiseOSError(osLastError()) elif L > bufsize: - res = newWideCString("", L) + res = newWideCString(L) bufsize = L else: result = res$L diff --git a/lib/std/private/ossymlinks.nim b/lib/std/private/ossymlinks.nim index c0774b573..c1760c42e 100644 --- a/lib/std/private/ossymlinks.nim +++ b/lib/std/private/ossymlinks.nim @@ -66,11 +66,13 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = when defined(windows) or defined(nintendoswitch): result = symlinkPath else: - result = newString(maxSymlinkLen) - var len = readlink(symlinkPath, result.cstring, maxSymlinkLen) - if len < 0: - raiseOSError(osLastError(), symlinkPath) - if len > maxSymlinkLen: - result = newString(len+1) - len = readlink(symlinkPath, result.cstring, len) - setLen(result, len) + var bufLen = 1024 + while true: + result = newString(bufLen) + let len = readlink(symlinkPath.cstring, result.cstring, bufLen) + if len < 0: + raiseOSError(osLastError(), symlinkPath) + if len < bufLen: + result.setLen(len) + break + bufLen = bufLen shl 1 diff --git a/lib/std/private/syslocks.nim b/lib/std/private/syslocks.nim index ca8897dc2..e19ec2c04 100644 --- a/lib/std/private/syslocks.nim +++ b/lib/std/private/syslocks.nim @@ -16,7 +16,7 @@ when defined(windows): Handle = int SysLock* {.importc: "CRITICAL_SECTION", - header: "<windows.h>", final, pure.} = object # CRITICAL_SECTION in WinApi + header: "<windows.h>", final, pure, byref.} = object # CRITICAL_SECTION in WinApi DebugInfo: pointer LockCount: int32 RecursionCount: int32 @@ -24,7 +24,7 @@ when defined(windows): LockSemaphore: int SpinCount: int - SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>".} = object + SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>", byref.} = object thePtr {.importc: "Ptr".} : Handle proc initSysLock*(L: var SysLock) {.importc: "InitializeCriticalSection", @@ -46,7 +46,7 @@ when defined(windows): header: "<windows.h>".} ## Releases the lock `L`. - proc deinitSys*(L: var SysLock) {.importc: "DeleteCriticalSection", + proc deinitSys*(L: SysLock) {.importc: "DeleteCriticalSection", header: "<windows.h>".} proc initializeConditionVariable( @@ -68,7 +68,7 @@ when defined(windows): proc initSysCond*(cond: var SysCond) {.inline.} = initializeConditionVariable(cond) - proc deinitSysCond*(cond: var SysCond) {.inline.} = + proc deinitSysCond*(cond: SysCond) {.inline.} = discard proc waitSysCond*(cond: var SysCond, lock: var SysLock) = discard sleepConditionVariableCS(cond, lock, -1'i32) @@ -83,13 +83,13 @@ elif defined(genode): header: Header.} = object proc initSysLock*(L: var SysLock) = discard - proc deinitSys*(L: var SysLock) = discard + proc deinitSys*(L: SysLock) = discard proc acquireSys*(L: var SysLock) {.noSideEffect, importcpp.} proc tryAcquireSys*(L: var SysLock): bool {.noSideEffect, importcpp.} proc releaseSys*(L: var SysLock) {.noSideEffect, importcpp.} proc initSysCond*(L: var SysCond) = discard - proc deinitSysCond*(L: var SysCond) = discard + proc deinitSysCond*(L: SysCond) = discard proc waitSysCond*(cond: var SysCond, lock: var SysLock) {. noSideEffect, importcpp.} proc signalSysCond*(cond: var SysCond) {. @@ -101,7 +101,7 @@ else: type SysLockObj {.importc: "pthread_mutex_t", pure, final, header: """#include <sys/types.h> - #include <pthread.h>""".} = object + #include <pthread.h>""", byref.} = object when defined(linux) and defined(amd64): abi: array[40 div sizeof(clong), clong] @@ -113,7 +113,7 @@ else: SysCondObj {.importc: "pthread_cond_t", pure, final, header: """#include <sys/types.h> - #include <pthread.h>""".} = object + #include <pthread.h>""", byref.} = object when defined(linux) and defined(amd64): abi: array[48 div sizeof(clonglong), clonglong] @@ -127,7 +127,7 @@ else: proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {. importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.} - proc deinitSysAux(L: var SysLockObj) {.noSideEffect, + proc deinitSysAux(L: SysLockObj) {.noSideEffect, importc: "pthread_mutex_destroy", header: "<pthread.h>".} proc acquireSysAux(L: var SysLockObj) {.noSideEffect, @@ -156,7 +156,7 @@ else: L = cast[SysLock](c_malloc(csize_t(sizeof(SysLockObj)))) initSysLockAux(L[], attr) - proc deinitSys*(L: var SysLock) = + proc deinitSys*(L: SysLock) = deinitSysAux(L[]) c_free(L) @@ -173,7 +173,7 @@ else: template initSysLock*(L: var SysLock, attr: ptr SysLockAttr = nil) = initSysLockAux(L, attr) - template deinitSys*(L: var SysLock) = + template deinitSys*(L: SysLock) = deinitSysAux(L) template acquireSys*(L: var SysLock) = acquireSysAux(L) @@ -193,7 +193,7 @@ else: # locks proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {. importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.} - proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect, + proc deinitSysCondAux(cond: SysCondObj) {.noSideEffect, importc: "pthread_cond_destroy", header: "<pthread.h>".} proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj): cint {. @@ -208,7 +208,7 @@ else: cond = cast[SysCond](c_malloc(csize_t(sizeof(SysCondObj)))) initSysCondAux(cond[], cond_attr) - proc deinitSysCond*(cond: var SysCond) = + proc deinitSysCond*(cond: SysCond) = deinitSysCondAux(cond[]) c_free(cond) @@ -221,7 +221,7 @@ else: else: template initSysCond*(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = initSysCondAux(cond, cond_attr) - template deinitSysCond*(cond: var SysCond) = + template deinitSysCond*(cond: SysCond) = deinitSysCondAux(cond) template waitSysCond*(cond: var SysCond, lock: var SysLock) = diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index b664c3b60..c34a025af 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -320,7 +320,7 @@ elif defined(windows): const BufSize = 4000 -proc close*(f: File) {.tags: [], gcsafe.} = +proc close*(f: File) {.tags: [], gcsafe, sideEffect.} = ## Closes the file. if not f.isNil: discard c_fclose(f) @@ -419,7 +419,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], if f.isatty: const numberOfCharsToRead = 2048 var numberOfCharsRead = 0'i32 - var buffer = newWideCString("", numberOfCharsToRead) + var buffer = newWideCString(numberOfCharsToRead) if readConsole(getOsFileHandle(f), addr(buffer[0]), numberOfCharsToRead, addr(numberOfCharsRead), nil) == 0: var error = getLastError() @@ -648,6 +648,9 @@ const "" else: "" + RawFormatOpen: array[FileMode, cstring] = [ + # used for open by FileHandle, which calls `fdopen` + cstring("rb"), "wb", "w+b", "r+b", "ab"] FormatOpen: array[FileMode, cstring] = [ cstring("rb" & NoInheritFlag), "wb" & NoInheritFlag, "w+b" & NoInheritFlag, "r+b" & NoInheritFlag, "ab" & NoInheritFlag @@ -749,7 +752,7 @@ proc open*(f: var File, filehandle: FileHandle, filehandle) else: filehandle if not setInheritable(oshandle, false): return false - f = c_fdopen(filehandle, FormatOpen[mode]) + f = c_fdopen(filehandle, RawFormatOpen[mode]) result = f != nil proc open*(filename: string, @@ -763,7 +766,7 @@ proc open*(filename: string, if not open(result, filename, mode, bufSize): raise newException(IOError, "cannot open: " & filename) -proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.} = +proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign, sideEffect.} = ## Sets the position of the file pointer that is used for read/write ## operations. The file's first byte has the index zero. if c_fseek(f, pos, cint(relativeTo)) != 0: @@ -871,7 +874,7 @@ proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} = var f: File = nil if open(f, filename, fmWrite): try: - f.writeBuffer(unsafeAddr content[0], content.len) + discard f.writeBuffer(unsafeAddr content[0], content.len) finally: close(f) else: diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index 9eb7c97c4..7e59747f5 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -68,7 +68,8 @@ type proc `=copy`*(x: var Task, y: Task) {.error.} -when defined(nimAllowNonVarDestructor): +const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) +when defined(nimAllowNonVarDestructor) and arcLike: proc `=destroy`*(t: Task) {.inline, gcsafe.} = ## Frees the resources allocated for a `Task`. if t.args != nil: @@ -109,6 +110,19 @@ template addAllNode(assignParam: NimNode, procParam: NimNode) = tempAssignList.add newLetStmt(tempNode, newDotExpr(objTemp, formalParams[i][0])) scratchRecList.add newIdentDefs(newIdentNode(formalParams[i][0].strVal), assignParam) +proc analyseRootSym(s: NimNode): NimNode = + result = s + while true: + case result.kind + of nnkBracketExpr, nnkDerefExpr, nnkHiddenDeref, + nnkAddr, nnkHiddenAddr, + nnkObjDownConv, nnkObjUpConv: + result = result[0] + of nnkDotExpr, nnkCheckedFieldExpr, nnkHiddenStdConv, nnkHiddenSubConv: + result = result[1] + else: + break + macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkCallStrLit}): Task = ## Converts the call and its arguments to `Task`. runnableExamples: @@ -120,11 +134,14 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC let retType = getTypeInst(e) let returnsVoid = retType.typeKind == ntyVoid + let rootSym = analyseRootSym(e[0]) + expectKind rootSym, nnkSym + when compileOption("threads"): - if not isGcSafe(e[0]): + if not isGcSafe(rootSym): error("'toTask' takes a GC safe call expression", e) - if hasClosure(e[0]): + if hasClosure(rootSym): error("closure call is not allowed", e) if e.len > 1: @@ -208,7 +225,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC let funcCall = newCall(e[0], callNode) functionStmtList.add tempAssignList - let funcName = genSym(nskProc, e[0].strVal) + let funcName = genSym(nskProc, rootSym.strVal) let destroyName = genSym(nskProc, "destroyScratch") let objTemp2 = genSym(ident = "obj") let tempNode = quote("@") do: @@ -240,7 +257,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC Task(callback: `funcName`, args: `scratchIdent`, destroy: `destroyName`) else: let funcCall = newCall(e[0]) - let funcName = genSym(nskProc, e[0].strVal) + let funcName = genSym(nskProc, rootSym.strVal) if returnsVoid: result = quote do: diff --git a/lib/std/time_t.nim b/lib/std/time_t.nim index 5fa95fff3..de051b135 100644 --- a/lib/std/time_t.nim +++ b/lib/std/time_t.nim @@ -14,7 +14,7 @@ when defined(nimdoc): ## Wrapper for `time_t`. On posix, this is an alias to `posix.Time`. elif defined(windows): when defined(i386) and defined(gcc): - type Time* {.importc: "time_t", header: "<time.h>".} = distinct int32 + type Time* {.importc: "time_t", header: "<time.h>".} = distinct clong else: # newest version of Visual C++ defines time_t to be of 64 bits type Time* {.importc: "time_t", header: "<time.h>".} = distinct int64 diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 501a0d0fa..7b0b81968 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -7,33 +7,73 @@ # distribution, for details about the copyright. # -## Thread support for Nim. -## -## Examples -## ======== -## -## ```Nim -## import std/locks -## -## var -## thr: array[0..4, Thread[tuple[a,b: int]]] -## L: Lock -## -## proc threadFunc(interval: tuple[a,b: int]) {.thread.} = -## for i in interval.a..interval.b: -## acquire(L) # lock stdout -## echo i -## release(L) -## -## initLock(L) -## -## for i in 0..high(thr): -## createThread(thr[i], threadFunc, (i*10, i*10+5)) -## joinThreads(thr) -## -## deinitLock(L) -## ``` - +##[ +Thread support for Nim. Threads allow multiple functions to execute concurrently. + +In Nim, threads are a low-level construct and using a library like `malebolgia`, `taskpools` or `weave` is recommended. + +When creating a thread, you can pass arguments to it. As Nim's garbage collector does not use atomic references, sharing +`ref` and other variables managed by the garbage collector between threads is not supported. +Use global variables to do so, or pointers. + +Memory allocated using [`sharedAlloc`](./system.html#allocShared.t%2CNatural) can be used and shared between threads. + +To communicate between threads, consider using [channels](./system.html#Channel) + +Examples +======== + +```Nim +import std/locks + +var + thr: array[0..4, Thread[tuple[a,b: int]]] + L: Lock + +proc threadFunc(interval: tuple[a,b: int]) {.thread.} = + for i in interval.a..interval.b: + acquire(L) # lock stdout + echo i + release(L) + +initLock(L) + +for i in 0..high(thr): + createThread(thr[i], threadFunc, (i*10, i*10+5)) +joinThreads(thr) + +deinitLock(L) +``` + +When using a memory management strategy that supports shared heaps like `arc` or `boehm`, +you can pass pointer to threads and share memory between them, but the memory must outlive the thread. +The default memory management strategy, `orc`, supports this. +The example below is **not valid** for memory management strategies that use local heaps like `refc`! + +```Nim +import locks + +var l: Lock + +proc threadFunc(obj: ptr seq[int]) {.thread.} = + withLock l: + for i in 0..<100: + obj[].add(obj[].len * obj[].len) + +proc threadHandler() = + var thr: array[0..4, Thread[ptr seq[int]]] + var s = newSeq[int]() + + for i in 0..high(thr): + createThread(thr[i], threadFunc, s.addr) + joinThreads(thr) + echo s + +initLock(l) +threadHandler() +deinitLock(l) +``` +]## import std/private/[threadtypes] diff --git a/lib/std/varints.nim b/lib/std/varints.nim index 0d18b9069..32fe2fffb 100644 --- a/lib/std/varints.nim +++ b/lib/std/varints.nim @@ -82,29 +82,29 @@ proc writeVu64*(z: var openArray[byte], x: uint64): int = z[3] = cast[uint8](y) return 4 z[0] = 251 - varintWrite32(toOpenArray(z, 1, z.high-1), y) + varintWrite32(toOpenArray(z, 1, 4), y) return 5 if w <= 255: z[0] = 252 z[1] = cast[uint8](w) - varintWrite32(toOpenArray(z, 2, z.high-2), y) + varintWrite32(toOpenArray(z, 2, 5), y) return 6 if w <= 65535: z[0] = 253 z[1] = cast[uint8](w shr 8) z[2] = cast[uint8](w) - varintWrite32(toOpenArray(z, 3, z.high-3), y) + varintWrite32(toOpenArray(z, 3, 6), y) return 7 if w <= 16777215: z[0] = 254 z[1] = cast[uint8](w shr 16) z[2] = cast[uint8](w shr 8) z[3] = cast[uint8](w) - varintWrite32(toOpenArray(z, 4, z.high-4), y) + varintWrite32(toOpenArray(z, 4, 7), y) return 8 z[0] = 255 - varintWrite32(toOpenArray(z, 1, z.high-1), w) - varintWrite32(toOpenArray(z, 5, z.high-5), y) + varintWrite32(toOpenArray(z, 1, 4), w) + varintWrite32(toOpenArray(z, 5, 8), y) return 9 proc sar(a, b: int64): int64 = diff --git a/lib/std/widestrs.nim b/lib/std/widestrs.nim index 0bf50be45..2ddf80d14 100644 --- a/lib/std/widestrs.nim +++ b/lib/std/widestrs.nim @@ -25,7 +25,8 @@ when not (defined(cpu16) or defined(cpu8)): bytes: int data: WideCString - when defined(nimAllowNonVarDestructor): + const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) + when defined(nimAllowNonVarDestructor) and arcLike: proc `=destroy`(a: WideCStringObj) = if a.data != nil: when compileOption("threads"): @@ -154,6 +155,7 @@ when not (defined(cpu16) or defined(cpu8)): createWide(result, size * 2 + 2) proc newWideCString*(source: cstring, L: int): WideCStringObj = + ## Warning:: `source` needs to be preallocated with the length `L` createWide(result, L * 2 + 2) var d = 0 for ch in runes(source, L): diff --git a/lib/system.nim b/lib/system.nim index 8c17afaa0..2f9cdc5f9 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -125,9 +125,6 @@ proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = const ThisIsSystem = true -proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.} - ## Leaked implementation detail. Do not use. - proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {. magic: "NewFinalize", noSideEffect.} ## Creates a new object of type `T` and returns a safe (traced) @@ -161,9 +158,11 @@ when defined(nimHasEnsureMove): ## Ensures that `x` is moved to the new location, otherwise it gives ## an error at the compile time. runnableExamples: - var x = "Hello" - let y = ensureMove(x) - doAssert y == "Hello" + proc foo = + var x = "Hello" + let y = ensureMove(x) + doAssert y == "Hello" + foo() discard "implemented in injectdestructors" type @@ -376,7 +375,7 @@ else: discard when defined(nimAllowNonVarDestructor) and arcLikeMem: - proc `=destroy`*(x: string) {.inline, magic: "Destroy".} = + proc `=destroy`*(x: string) {.inline, magic: "Destroy", enforceNoRaises.} = discard proc `=destroy`*[T](x: seq[T]) {.inline, magic: "Destroy".} = @@ -1038,7 +1037,7 @@ const ## Possible values: ## `"i386"`, `"alpha"`, `"powerpc"`, `"powerpc64"`, `"powerpc64el"`, ## `"sparc"`, `"amd64"`, `"mips"`, `"mipsel"`, `"arm"`, `"arm64"`, - ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`, '"loongarch64"'. + ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`, `"loongarch64"`. seqShallowFlag = low(int) strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \ @@ -1703,8 +1702,24 @@ when not defined(nimscript): when not declared(sysFatal): include "system/fatal" +type + PFrame* = ptr TFrame ## Represents a runtime frame of the call stack; + ## part of the debugger API. + # keep in sync with nimbase.h `struct TFrame_` + TFrame* {.importc, nodecl, final.} = object ## The frame itself. + prev*: PFrame ## Previous frame; used for chaining the call stack. + procname*: cstring ## Name of the proc that is currently executing. + line*: int ## Line number of the proc that is currently executing. + filename*: cstring ## Filename of the proc that is currently executing. + len*: int16 ## Length of the inspectable slots. + calldepth*: int16 ## Used for max call depth checking. + when NimStackTraceMsgs: + frameMsgLen*: int ## end position in frameMsgBuf for this frame. when defined(nimV2): + var + framePtr {.threadvar.}: PFrame + include system/arc template newException*(exceptn: typedesc, message: string; @@ -1849,20 +1864,6 @@ when notJSnotNims: ## writes an error message and terminates the program, except when ## using `--os:any` -type - PFrame* = ptr TFrame ## Represents a runtime frame of the call stack; - ## part of the debugger API. - # keep in sync with nimbase.h `struct TFrame_` - TFrame* {.importc, nodecl, final.} = object ## The frame itself. - prev*: PFrame ## Previous frame; used for chaining the call stack. - procname*: cstring ## Name of the proc that is currently executing. - line*: int ## Line number of the proc that is currently executing. - filename*: cstring ## Filename of the proc that is currently executing. - len*: int16 ## Length of the inspectable slots. - calldepth*: int16 ## Used for max call depth checking. - when NimStackTraceMsgs: - frameMsgLen*: int ## end position in frameMsgBuf for this frame. - when defined(js) or defined(nimdoc): proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = ## Appends `y` to `x` in place. @@ -1871,14 +1872,14 @@ when defined(js) or defined(nimdoc): tmp.add(cstring("ab")) tmp.add(cstring("cd")) doAssert tmp == "abcd" - asm """ + {.emit: """ if (`x` === null) { `x` = []; } var off = `x`.length; `x`.length += `y`.length; for (var i = 0; i < `y`.length; ++i) { `x`[off+i] = `y`.charCodeAt(i); } - """ + """.} proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".} = ## Appends `y` to `x` in place. ## Only implemented for JS backend. @@ -2084,7 +2085,8 @@ when notJSnotNims: proc cmpMem(a, b: pointer, size: Natural): int = nimCmpMem(a, b, size).int -when not defined(js): +when not defined(js) or defined(nimscript): + # nimscript can be defined if config file for js compilation proc cmp(x, y: string): int = when nimvm: if x < y: result = -1 @@ -2101,12 +2103,14 @@ when not defined(js): proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] = ## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be ## of length `len`. + if a == nil: return @[] newSeq(result, len) for i in 0..len-1: result[i] = $a[i] proc cstringArrayToSeq*(a: cstringArray): seq[string] = ## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be ## terminated by `nil`. + if a == nil: return @[] var L = 0 while a[L] != nil: inc(L) result = cstringArrayToSeq(a, L) @@ -2131,7 +2135,7 @@ when not defined(js) and declared(alloc0) and declared(dealloc): inc(i) dealloc(a) -when notJSnotNims: +when notJSnotNims and not gotoBasedExceptions: type PSafePoint = ptr TSafePoint TSafePoint {.compilerproc, final.} = object @@ -2347,8 +2351,14 @@ when notJSnotNims: `result` = `x`.ClE_0; """.} - proc finished*[T: iterator {.closure.}](x: T): bool {.noSideEffect, inline, magic: "Finished".} = - ## It can be used to determine if a first class iterator has finished. +proc finished*[T: iterator {.closure.}](x: T): bool {.noSideEffect, inline, magic: "Finished".} = + ## It can be used to determine if a first class iterator has finished. + when defined(js): + # TODO: mangle `:state` + {.emit: """ + `result` = (`x`.ClE_0).HEX3Astate < 0; + """.} + else: {.emit: """ `result` = ((NI*) `x`.ClE_0)[1] < 0; """.} @@ -2356,7 +2366,8 @@ when notJSnotNims: from std/private/digitsutils import addInt export addInt -when defined(js): +when defined(js) and not defined(nimscript): + # nimscript can be defined if config file for js compilation include "system/jssys" include "system/reprjs" @@ -2520,6 +2531,8 @@ when hasAlloc or defined(nimscript): ## var a = "abc" ## a.insert("zz", 0) # a <- "zzabc" ## ``` + if item.len == 0: # prevents self-assignment + return var xl = x.len setLen(x, xl+item.len) var j = xl-1 @@ -2785,6 +2798,18 @@ when not defined(js): proc toOpenArray*[T](x: seq[T]; first, last: int): openArray[T] {. magic: "Slice".} + ## Allows passing the slice of `x` from the element at `first` to the element + ## at `last` to `openArray[T]` parameters without copying it. + ## + ## Example: + ## ```nim + ## proc test(x: openArray[int]) = + ## doAssert x == [1, 2, 3] + ## + ## let s = @[0, 1, 2, 3, 4] + ## s.toOpenArray(1, 3).test + ## ``` + proc toOpenArray*[T](x: openArray[T]; first, last: int): openArray[T] {. magic: "Slice".} proc toOpenArray*[I, T](x: array[I, T]; first, last: I): openArray[T] {. @@ -2926,4 +2951,5 @@ proc arrayWith*[T](y: T, size: static int): array[size, T] {.raises: [].} = when nimvm: result[i] = y else: - result[i] = `=dup`(y) + # TODO: fixme it should be `=dup` + result[i] = y diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index edb094f33..3de6d8713 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -20,6 +20,37 @@ template track(op, address, size) = # We manage *chunks* of memory. Each chunk is a multiple of the page size. # Each chunk starts at an address that is divisible by the page size. +# Small chunks may be divided into smaller cells of reusable pointers to reduce the number of page allocations. + +# An allocation of a small pointer looks approximately like this +#[ + + alloc -> rawAlloc -> No free chunk available > Request a new page from tslf -> result = chunk.data -------------+ + | | + v | + Free chunk available | + | | + v v + Fetch shared cells -> No free cells available -> Advance acc -> result = chunk.data + chunk.acc -------> return + (may not add new cells) ^ + | | + v | + Free cells available -> result = chunk.freeList -> Advance chunk.freeList -----------------------------------+ +]# +# so it is split into 3 paths, where the last path is preferred to prevent unnecessary allocations. +# +# +# A deallocation of a small pointer then looks like this +#[ + dealloc -> rawDealloc -> chunk.owner == addr(a) --------------> This thread owns the chunk ------> The current chunk is active -> Chunk is completely unused -----> Chunk references no foreign cells + | | (Add cell into the current chunk) | Return the current chunk back to tlsf + | | | | + v v v v + A different thread owns this chunk. The current chunk is not active. chunk.free was < size Chunk references foreign cells, noop + Add the cell to a.sharedFreeLists Add the cell into the active chunk Activate the chunk (end) + (end) (end) (end) +]# +# So "true" deallocation is delayed for as long as possible in favor of reusing cells. const nimMinHeapPages {.intdefine.} = 128 # 0.5 MB @@ -71,6 +102,8 @@ const type FreeCell {.final, pure.} = object + # A free cell is a pointer that has been freed, meaning it became available for reuse. + # It may become foreign if it is lent to a chunk that did not create it, doing so reduces the amount of needed pages. next: ptr FreeCell # next free cell in chunk (overlaid with refcount) when not defined(gcDestructors): zeroField: int # 0 means cell is not used (overlaid with typ field) @@ -90,11 +123,18 @@ type SmallChunk = object of BaseChunk next, prev: PSmallChunk # chunks of the same size - freeList: ptr FreeCell - free: int # how many bytes remain - acc: int # accumulator for small object allocation - when defined(gcDestructors): - sharedFreeList: ptr FreeCell # make no attempt at avoiding false sharing for now for this object field + freeList: ptr FreeCell # Singly linked list of cells. They may be from foreign chunks or from the current chunk. + # Should be `nil` when the chunk isn't active in `a.freeSmallChunks`. + free: int32 # Bytes this chunk is able to provide using both the accumulator and free cells. + # When a cell is considered foreign, its source chunk's free field is NOT adjusted until it + # reaches dealloc while the source chunk is active. + # Instead, the receiving chunk gains the capacity and thus reserves space in the foreign chunk. + acc: uint32 # Offset from data, used when there are no free cells available but the chunk is considered free. + foreignCells: int # When a free cell is given to a chunk that is not its origin, + # both the cell and the source chunk are considered foreign. + # Receiving a foreign cell can happen both when deallocating from another thread or when + # the active chunk in `a.freeSmallChunks` is not the current chunk. + # Freeing a chunk while `foreignCells > 0` leaks memory as all references to it become lost. data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory BigChunk = object of BaseChunk # not necessarily > PageSize! @@ -109,7 +149,12 @@ type MemRegion = object when not defined(gcDestructors): minLargeObj, maxLargeObj: int - freeSmallChunks: array[0..max(1,SmallChunkSize div MemAlign-1), PSmallChunk] + freeSmallChunks: array[0..max(1, SmallChunkSize div MemAlign-1), PSmallChunk] + # List of available chunks per size class. Only one is expected to be active per class. + when defined(gcDestructors): + sharedFreeLists: array[0..max(1, SmallChunkSize div MemAlign-1), ptr FreeCell] + # When a thread frees a pointer it did not create, it must not adjust the counters. + # Instead, the cell is placed here and deferred until the next allocation. flBitmap: uint32 slBitmap: array[RealFli, uint32] matrix: array[RealFli, array[MaxSli, PBigChunk]] @@ -334,7 +379,7 @@ when not defined(gcDestructors): n.link[0] = a.freeAvlNodes a.freeAvlNodes = n -proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) = +proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int): ptr HeapLinks = var it = addr(a.heapLinks) while it != nil and it.len >= it.chunks.len: it = it.next if it == nil: @@ -343,10 +388,12 @@ proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) = a.heapLinks.next = n n.chunks[0] = (p, size) n.len = 1 + result = n else: let L = it.len it.chunks[L] = (p, size) inc it.len + result = it when not defined(gcDestructors): include "system/avltree" @@ -431,7 +478,7 @@ iterator allObjects(m: var MemRegion): pointer {.inline.} = let size = c.size var a = cast[int](addr(c.data)) - let limit = a + c.acc + let limit = a + c.acc.int while a <% limit: yield cast[pointer](a) a = a +% size @@ -490,10 +537,10 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = incCurrMem(a, size) inc(a.freeMem, size) - a.addHeapLink(result, size) + let heapLink = a.addHeapLink(result, size) when defined(debugHeapLinks): cprintf("owner: %p; result: %p; next pointer %p; size: %ld\n", addr(a), - result, result.heapLink, result.size) + result, heapLink, size) when defined(memtracker): trackLocation(addr result.size, sizeof(int)) @@ -775,41 +822,42 @@ when defined(gcDestructors): sysAssert c.next == nil, "c.next pointer must be nil" atomicPrepend a.sharedFreeListBigChunks, c - proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell) {.inline.} = - atomicPrepend c.sharedFreeList, f + proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell; size: int) {.inline.} = + atomicPrepend c.owner.sharedFreeLists[size], f + + const MaxSteps = 20 proc compensateCounters(a: var MemRegion; c: PSmallChunk; size: int) = # rawDealloc did NOT do the usual: # `inc(c.free, size); dec(a.occ, size)` because it wasn't the owner of these # memory locations. We have to compensate here for these for the entire list. - # Well, not for the entire list, but for `max` elements of the list because - # we split the list in order to achieve bounded response times. var it = c.freeList - var x = 0 - var maxIters = 20 # make it time-bounded + var total = 0 while it != nil: - if maxIters == 0: - let rest = it.next.loada - if rest != nil: - it.next.storea nil - addToSharedFreeList(c, rest) - break - inc x, size - it = it.next.loada - dec maxIters - inc(c.free, x) - dec(a.occ, x) + inc total, size + let chunk = cast[PSmallChunk](pageAddr(it)) + if c != chunk: + # The cell is foreign, potentially even from a foreign thread. + # It must block the current chunk from being freed, as doing so would leak memory. + inc c.foreignCells + it = it.next + # By not adjusting the foreign chunk we reserve space in it to prevent deallocation + inc(c.free, total) + dec(a.occ, total) proc freeDeferredObjects(a: var MemRegion; root: PBigChunk) = var it = root - var maxIters = 20 # make it time-bounded + var maxIters = MaxSteps # make it time-bounded while true: + let rest = it.next.loada + it.next.storea nil + deallocBigChunk(a, cast[PBigChunk](it)) if maxIters == 0: - let rest = it.next.loada - it.next.storea nil - addToSharedFreeListBigChunks(a, rest) + if rest != nil: + addToSharedFreeListBigChunks(a, rest) + sysAssert a.sharedFreeListBigChunks != nil, "re-enqueing failed" break - it = it.next.loada + it = rest dec maxIters if it == nil: break @@ -824,56 +872,85 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size) if size <= SmallChunkSize-smallChunkOverhead(): + template fetchSharedCells(tc: PSmallChunk) = + # Consumes cells from (potentially) foreign threads from `a.sharedFreeLists[s]` + when defined(gcDestructors): + if tc.freeList == nil: + when hasThreadSupport: + # Steal the entire list from `sharedFreeList`: + tc.freeList = atomicExchangeN(addr a.sharedFreeLists[s], nil, ATOMIC_RELAXED) + else: + tc.freeList = a.sharedFreeLists[s] + a.sharedFreeLists[s] = nil + # if `tc.freeList` isn't nil, `tc` will gain capacity. + # We must calculate how much it gained and how many foreign cells are included. + compensateCounters(a, tc, size) + # allocate a small block: for small chunks, we use only its next pointer let s = size div MemAlign var c = a.freeSmallChunks[s] if c == nil: + # There is no free chunk of the requested size available, we need a new one. c = getSmallChunk(a) + # init all fields in case memory didn't get zeroed c.freeList = nil + c.foreignCells = 0 sysAssert c.size == PageSize, "rawAlloc 3" c.size = size - c.acc = size - when defined(gcDestructors): - c.sharedFreeList = nil - c.free = SmallChunkSize - smallChunkOverhead() - size + c.acc = size.uint32 + c.free = SmallChunkSize - smallChunkOverhead() - size.int32 sysAssert c.owner == addr(a), "rawAlloc: No owner set!" c.next = nil c.prev = nil - listAdd(a.freeSmallChunks[s], c) + # Shared cells are fetched here in case `c.size * 2 >= SmallChunkSize - smallChunkOverhead()`. + # For those single cell chunks, we would otherwise have to allocate a new one almost every time. + fetchSharedCells(c) + if c.free >= size: + # Because removals from `a.freeSmallChunks[s]` only happen in the other alloc branch and during dealloc, + # we must not add it to the list if it cannot be used the next time a pointer of `size` bytes is needed. + listAdd(a.freeSmallChunks[s], c) result = addr(c.data) sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 4") else: + # There is a free chunk of the requested size available, use it. sysAssert(allocInv(a), "rawAlloc: begin c != nil") sysAssert c.next != c, "rawAlloc 5" #if c.size != size: # c_fprintf(stdout, "csize: %lld; size %lld\n", c.size, size) sysAssert c.size == size, "rawAlloc 6" - when defined(gcDestructors): - if c.freeList == nil: - when hasThreadSupport: - c.freeList = atomicExchangeN(addr c.sharedFreeList, nil, ATOMIC_RELAXED) - else: - c.freeList = c.sharedFreeList - c.sharedFreeList = nil - compensateCounters(a, c, size) if c.freeList == nil: - sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize, + sysAssert(c.acc.int + smallChunkOverhead() + size <= SmallChunkSize, "rawAlloc 7") - result = cast[pointer](cast[int](addr(c.data)) +% c.acc) + result = cast[pointer](cast[int](addr(c.data)) +% c.acc.int) inc(c.acc, size) else: + # There are free cells available, prefer them over the accumulator result = c.freeList when not defined(gcDestructors): sysAssert(c.freeList.zeroField == 0, "rawAlloc 8") c.freeList = c.freeList.next + if cast[PSmallChunk](pageAddr(result)) != c: + # This cell isn't a blocker for the current chunk's deallocation anymore + dec(c.foreignCells) + else: + sysAssert(c == cast[PSmallChunk](pageAddr(result)), "rawAlloc: Bad cell") + # Even if the cell we return is foreign, the local chunk's capacity decreases. + # The capacity was previously reserved in the source chunk (when it first got allocated), + # then added into the current chunk during dealloc, + # so the source chunk will not be freed or leak memory because of this. dec(c.free, size) sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 9") sysAssert(allocInv(a), "rawAlloc: end c != nil") - sysAssert(allocInv(a), "rawAlloc: before c.free < size") - if c.free < size: - sysAssert(allocInv(a), "rawAlloc: before listRemove test") - listRemove(a.freeSmallChunks[s], c) - sysAssert(allocInv(a), "rawAlloc: end listRemove test") + # We fetch deferred cells *after* advancing `c.freeList`/`acc` to adjust `c.free`. + # If after the adjustment it turns out there's free cells available, + # the chunk stays in `a.freeSmallChunks[s]` and the need for a new chunk is delayed. + fetchSharedCells(c) + sysAssert(allocInv(a), "rawAlloc: before c.free < size") + if c.free < size: + # Even after fetching shared cells the chunk has no usable memory left. It is no longer the active chunk + sysAssert(allocInv(a), "rawAlloc: before listRemove test") + listRemove(a.freeSmallChunks[s], c) + sysAssert(allocInv(a), "rawAlloc: end listRemove test") sysAssert(((cast[int](result) and PageMask) - smallChunkOverhead()) %% size == 0, "rawAlloc 21") sysAssert(allocInv(a), "rawAlloc: end small size") @@ -905,7 +982,7 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = trackSize(c.size) sysAssert(isAccessible(a, result), "rawAlloc 14") sysAssert(allocInv(a), "rawAlloc: end") - when logAlloc: cprintf("var pointer_%p = alloc(%ld)\n", result, requestedSize) + when logAlloc: cprintf("var pointer_%p = alloc(%ld) # %p\n", result, requestedSize, addr a) proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer = result = rawAlloc(a, requestedSize) @@ -921,7 +998,7 @@ proc rawDealloc(a: var MemRegion, p: pointer) = if isSmallChunk(c): # `p` is within a small chunk: var c = cast[PSmallChunk](c) - var s = c.size + let s = c.size # ^ We might access thread foreign storage here. # The other thread cannot possibly free this block as it's still alive. var f = cast[ptr FreeCell](p) @@ -936,31 +1013,48 @@ proc rawDealloc(a: var MemRegion, p: pointer) = #echo("setting to nil: ", $cast[int](addr(f.zeroField))) sysAssert(f.zeroField != 0, "rawDealloc 1") f.zeroField = 0 - f.next = c.freeList - c.freeList = f when overwriteFree: # set to 0xff to check for usage after free bugs: nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32, s -% sizeof(FreeCell)) - # check if it is not in the freeSmallChunks[s] list: - if c.free < s: - # add it to the freeSmallChunks[s] array: - listAdd(a.freeSmallChunks[s div MemAlign], c) - inc(c.free, s) + let activeChunk = a.freeSmallChunks[s div MemAlign] + if activeChunk != nil and c != activeChunk: + # This pointer is not part of the active chunk, lend it out + # and do not adjust the current chunk (same logic as compensateCounters.) + # Put the cell into the active chunk, + # may prevent a queue of available chunks from forming in a.freeSmallChunks[s div MemAlign]. + # This queue would otherwise waste memory in the form of free cells until we return to those chunks. + f.next = activeChunk.freeList + activeChunk.freeList = f # lend the cell + inc(activeChunk.free, s) # By not adjusting the current chunk's capacity it is prevented from being freed + inc(activeChunk.foreignCells) # The cell is now considered foreign from the perspective of the active chunk else: - inc(c.free, s) - if c.free == SmallChunkSize-smallChunkOverhead(): - listRemove(a.freeSmallChunks[s div MemAlign], c) - c.size = SmallChunkSize - freeBigChunk(a, cast[PBigChunk](c)) + f.next = c.freeList + c.freeList = f + if c.free < s: + # The chunk could not have been active as it didn't have enough space to give + listAdd(a.freeSmallChunks[s div MemAlign], c) + inc(c.free, s) + else: + inc(c.free, s) + # Free only if the entire chunk is unused and there are no borrowed cells. + # If the chunk were to be freed while it references foreign cells, + # the foreign chunks will leak memory and can never be freed. + if c.free == SmallChunkSize-smallChunkOverhead() and c.foreignCells == 0: + listRemove(a.freeSmallChunks[s div MemAlign], c) + c.size = SmallChunkSize + freeBigChunk(a, cast[PBigChunk](c)) else: + when logAlloc: cprintf("dealloc(pointer_%p) # SMALL FROM %p CALLER %p\n", p, c.owner, addr(a)) + when defined(gcDestructors): - addToSharedFreeList(c, f) + addToSharedFreeList(c, f, s div MemAlign) sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %% s == 0, "rawDealloc 2") else: # set to 0xff to check for usage after free bugs: when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead()) + when logAlloc: cprintf("dealloc(pointer_%p) # BIG %p\n", p, c.owner) when defined(gcDestructors): if c.owner == addr(a): deallocBigChunk(a, cast[PBigChunk](c)) @@ -968,8 +1062,9 @@ proc rawDealloc(a: var MemRegion, p: pointer) = addToSharedFreeListBigChunks(c.owner[], cast[PBigChunk](c)) else: deallocBigChunk(a, cast[PBigChunk](c)) + sysAssert(allocInv(a), "rawDealloc: end") - when logAlloc: cprintf("dealloc(pointer_%p)\n", p) + #when logAlloc: cprintf("dealloc(pointer_%p)\n", p) when not defined(gcDestructors): proc isAllocatedPtr(a: MemRegion, p: pointer): bool = @@ -980,7 +1075,7 @@ when not defined(gcDestructors): var c = cast[PSmallChunk](c) var offset = (cast[int](p) and (PageSize-1)) -% smallChunkOverhead() - result = (c.acc >% offset) and (offset %% c.size == 0) and + result = (c.acc.int >% offset) and (offset %% c.size == 0) and (cast[ptr FreeCell](p).zeroField >% 1) else: var c = cast[PBigChunk](c) @@ -998,7 +1093,7 @@ when not defined(gcDestructors): var c = cast[PSmallChunk](c) var offset = (cast[int](p) and (PageSize-1)) -% smallChunkOverhead() - if c.acc >% offset: + if c.acc.int >% offset: sysAssert(cast[int](addr(c.data)) +% offset == cast[int](p), "offset is not what you think it is") var d = cast[ptr FreeCell](cast[int](addr(c.data)) +% @@ -1091,7 +1186,7 @@ proc deallocOsPages(a: var MemRegion) = let (p, size) = it.chunks[i] when defined(debugHeapLinks): cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a), - it, it.size, next) + it, size, next) sysAssert size >= PageSize, "origSize too small" osDeallocPages(p, size) it = next diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 1c8a79fd8..3098e17d6 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -187,6 +187,9 @@ proc c_sprintf*(buf, frmt: cstring): cint {. importc: "sprintf", header: "<stdio.h>", varargs, noSideEffect.} # we use it only in a way that cannot lead to security issues +proc c_snprintf*(buf: cstring, n: csize_t, frmt: cstring): cint {. + importc: "snprintf", header: "<stdio.h>", varargs, noSideEffect.} + when defined(zephyr) and not defined(zephyrUseLibcMalloc): proc c_malloc*(size: csize_t): pointer {. importc: "k_malloc", header: "<kernel.h>".} diff --git a/lib/system/arc.nim b/lib/system/arc.nim index b788ac664..d001fcaa5 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -26,6 +26,9 @@ else: rcMask = 0b111 rcShift = 3 # shift by rcShift to get the reference counter +const + orcLeakDetector = defined(nimOrcLeakDetector) + type RefHeader = object rc: int # the object header is now a single RC field. @@ -36,9 +39,21 @@ type # in O(1) without doubly linked lists when defined(nimArcDebug) or defined(nimArcIds): refId: int + when defined(gcOrc) and orcLeakDetector: + filename: cstring + line: int Cell = ptr RefHeader +template setFrameInfo(c: Cell) = + when orcLeakDetector: + if framePtr != nil and framePtr.prev != nil: + c.filename = framePtr.prev.filename + c.line = framePtr.prev.line + else: + c.filename = nil + c.line = 0 + template head(p: pointer): Cell = cast[Cell](cast[int](p) -% sizeof(RefHeader)) @@ -87,6 +102,7 @@ proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} = cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).count) when traceCollector: cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result) + setFrameInfo head(result) proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} = # Same as 'newNewObj' but do not initialize the memory to zero. @@ -109,6 +125,7 @@ proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} = when traceCollector: cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result) + setFrameInfo head(result) proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} = decrement head(p) @@ -204,7 +221,7 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} = when defined(gcAtomicArc) and hasThreadSupport: # `atomicDec` returns the new value - if atomicDec(cell.rc, rcIncrement) == -1: + if atomicDec(cell.rc, rcIncrement) == -rcIncrement: result = true when traceCollector: cprintf("[ABOUT TO DESTROY] %p\n", cell) diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim index fa7ca784d..e229a0f4b 100644 --- a/lib/system/arithmetics.nim +++ b/lib/system/arithmetics.nim @@ -1,4 +1,4 @@ -proc succ*[T: Ordinal](x: T, y: int = 1): T {.magic: "Succ", noSideEffect.} = +proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} = ## Returns the `y`-th successor (default: 1) of the value `x`. ## ## If such a value does not exist, `OverflowDefect` is raised @@ -7,7 +7,7 @@ proc succ*[T: Ordinal](x: T, y: int = 1): T {.magic: "Succ", noSideEffect.} = assert succ(5) == 6 assert succ(5, 3) == 8 -proc pred*[T: Ordinal](x: T, y: int = 1): T {.magic: "Pred", noSideEffect.} = +proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} = ## Returns the `y`-th predecessor (default: 1) of the value `x`. ## ## If such a value does not exist, `OverflowDefect` is raised @@ -16,7 +16,7 @@ proc pred*[T: Ordinal](x: T, y: int = 1): T {.magic: "Pred", noSideEffect.} = assert pred(5) == 4 assert pred(5, 3) == 2 -proc inc*[T: Ordinal](x: var T, y: int = 1) {.magic: "Inc", noSideEffect.} = +proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} = ## Increments the ordinal `x` by `y`. ## ## If such a value does not exist, `OverflowDefect` is raised or a compile @@ -28,7 +28,7 @@ proc inc*[T: Ordinal](x: var T, y: int = 1) {.magic: "Inc", noSideEffect.} = inc(i, 3) assert i == 6 -proc dec*[T: Ordinal](x: var T, y: int = 1) {.magic: "Dec", noSideEffect.} = +proc dec*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Dec", noSideEffect.} = ## Decrements the ordinal `x` by `y`. ## ## If such a value does not exist, `OverflowDefect` is raised or a compile diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index 9759c3c99..a8d78bb93 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -324,7 +324,7 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} = return true else: var sameObject = false - asm """`sameObject` = `x` === `y`""" + {.emit: """`sameObject` = `x` === `y`;""".} if sameObject: return true if x.len != y.len: diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim index 7cf80eb17..cdb976ed5 100644 --- a/lib/system/compilation.nim +++ b/lib/system/compilation.nim @@ -6,7 +6,7 @@ const ## ``` # see also std/private/since - NimMinor* {.intdefine.}: int = 1 + NimMinor* {.intdefine.}: int = 2 ## is the minor number of Nim's version. ## Odd for devel, even for releases. @@ -144,16 +144,17 @@ template currentSourcePath*: string = instantiationInfo(-1, true).filename ## Returns the full file-system path of the current source. ## ## To get the directory containing the current source, use it with - ## `os.parentDir() <os.html#parentDir%2Cstring>`_ as `currentSourcePath.parentDir()`. + ## `ospaths2.parentDir() <ospaths2.html#parentDir%2Cstring>`_ as + ## `currentSourcePath.parentDir()`. ## ## The path returned by this template is set at compile time. ## ## See the docstring of `macros.getProjectPath() <macros.html#getProjectPath>`_ - ## for an example to see the distinction between the `currentSourcePath` - ## and `getProjectPath`. + ## for an example to see the distinction between the `currentSourcePath()` + ## and `getProjectPath()`. ## ## See also: - ## * `getCurrentDir proc <os.html#getCurrentDir>`_ + ## * `ospaths2.getCurrentDir() proc <ospaths2.html#getCurrentDir>`_ proc slurp*(filename: string): string {.magic: "Slurp".} ## This is an alias for `staticRead <#staticRead,string>`_. diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim index e0b053c7b..ea6776f58 100644 --- a/lib/system/embedded.nim +++ b/lib/system/embedded.nim @@ -19,8 +19,9 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} = discard proc popFrame {.compilerRtl, inl.} = discard proc setFrame(s: PFrame) {.compilerRtl, inl.} = discard -proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = discard -proc popSafePoint {.compilerRtl, inl.} = discard +when not gotoBasedExceptions: + proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = discard + proc popSafePoint {.compilerRtl, inl.} = discard proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = discard proc popCurrentException {.compilerRtl, inl.} = discard diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 6b40ca391..dae5c4a4a 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -73,26 +73,45 @@ type when NimStackTraceMsgs: var frameMsgBuf* {.threadvar.}: string + +when not defined(nimV2): + var + framePtr {.threadvar.}: PFrame + var - framePtr {.threadvar.}: PFrame - excHandler {.threadvar.}: PSafePoint - # list of exception handlers - # a global variable for the root of all try blocks currException {.threadvar.}: ref Exception - gcFramePtr {.threadvar.}: GcFrame -type - FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame, - excHandler: PSafePoint, currException: ref Exception] +when not gotoBasedExceptions: + var + excHandler {.threadvar.}: PSafePoint + # list of exception handlers + # a global variable for the root of all try blocks + gcFramePtr {.threadvar.}: GcFrame + +when gotoBasedExceptions: + type + FrameState = tuple[framePtr: PFrame, + currException: ref Exception] +else: + type + FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame, + excHandler: PSafePoint, currException: ref Exception] proc getFrameState*(): FrameState {.compilerRtl, inl.} = - return (gcFramePtr, framePtr, excHandler, currException) + when gotoBasedExceptions: + return (framePtr, currException) + else: + return (gcFramePtr, framePtr, excHandler, currException) proc setFrameState*(state: FrameState) {.compilerRtl, inl.} = - gcFramePtr = state.gcFramePtr - framePtr = state.framePtr - excHandler = state.excHandler - currException = state.currException + when gotoBasedExceptions: + framePtr = state.framePtr + currException = state.currException + else: + gcFramePtr = state.gcFramePtr + framePtr = state.framePtr + excHandler = state.excHandler + currException = state.currException proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr @@ -114,20 +133,21 @@ when false: proc setFrame*(s: PFrame) {.compilerRtl, inl.} = framePtr = s -proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr -proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev -proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s -proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} = - s.prev = gcFramePtr - zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer)) - gcFramePtr = s +when not gotoBasedExceptions: + proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr + proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev + proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s + proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} = + s.prev = gcFramePtr + zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer)) + gcFramePtr = s -proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = - s.prev = excHandler - excHandler = s + proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = + s.prev = excHandler + excHandler = s -proc popSafePoint {.compilerRtl, inl.} = - excHandler = excHandler.prev + proc popSafePoint {.compilerRtl, inl.} = + excHandler = excHandler.prev proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} = e.up = currException @@ -407,15 +427,16 @@ proc reportUnhandledError(e: ref Exception) {.nodestroy, gcsafe.} = when hostOS != "any": reportUnhandledErrorAux(e) -proc nimLeaveFinally() {.compilerRtl.} = - when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions: - {.emit: "throw;".} - else: - if excHandler != nil: - c_longjmp(excHandler.context, 1) +when not gotoBasedExceptions: + proc nimLeaveFinally() {.compilerRtl.} = + when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions: + {.emit: "throw;".} else: - reportUnhandledError(currException) - rawQuit(1) + if excHandler != nil: + c_longjmp(excHandler.context, 1) + else: + reportUnhandledError(currException) + rawQuit(1) when gotoBasedExceptions: var nimInErrorMode {.threadvar.}: bool diff --git a/lib/system/hti.nim b/lib/system/hti.nim index 9acaae88b..a26aff982 100644 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -59,7 +59,7 @@ type tyOwned, tyUnused1, tyUnused2, tyVarargsHidden, tyUncheckedArray, - tyProxyHidden, + tyErrorHidden, tyBuiltInTypeClassHidden, tyUserTypeClassHidden, tyUserTypeClassInstHidden, diff --git a/lib/system/indices.nim b/lib/system/indices.nim index fb6151a74..f2bad2528 100644 --- a/lib/system/indices.nim +++ b/lib/system/indices.nim @@ -110,13 +110,13 @@ proc `[]`*[Idx, T; U, V: Ordinal](a: array[Idx, T], x: HSlice[U, V]): seq[T] {.s ## var a = [1, 2, 3, 4] ## assert a[0..2] == @[1, 2, 3] ## ``` + ## + ## See also: + ## * `toOpenArray(array[I, T];I,I) <#toOpenArray,array[I,T],I,I>`_ let xa = a ^^ x.a let L = (a ^^ x.b) - xa + 1 - # Workaround bug #22852: - result = newSeq[T](if L < 0: 0 else: L) + result = newSeq[T](L) for i in 0..<L: result[i] = a[Idx(i + xa)] - # Workaround bug #22852 - discard Natural(L) proc `[]=`*[Idx, T; U, V: Ordinal](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) {.systemRaisesDefect.} = ## Slice assignment for arrays. @@ -139,6 +139,9 @@ proc `[]`*[T; U, V: Ordinal](s: openArray[T], x: HSlice[U, V]): seq[T] {.systemR ## var s = @[1, 2, 3, 4] ## assert s[0..2] == @[1, 2, 3] ## ``` + ## + ## See also: + ## * `toOpenArray(openArray[T];int,int) <#toOpenArray,openArray[T],int,int>`_ let a = s ^^ x.a let L = (s ^^ x.b) - a + 1 newSeq(result, L) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 1c8ea9d88..5599240fd 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -49,7 +49,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} = result[0] = x proc isNimException(): bool {.asmNoStackFrame.} = - asm "return `lastJSError` && `lastJSError`.m_type;" + {.emit: "return `lastJSError` && `lastJSError`.m_type;".} proc getCurrentException*(): ref Exception {.compilerRtl, benign.} = if isNimException(): result = cast[ref Exception](lastJSError) @@ -72,6 +72,10 @@ proc getCurrentExceptionMsg*(): string = proc setCurrentException*(exc: ref Exception) = lastJSError = cast[PJSError](exc) +proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} = + ## Used to set up exception handling for closure iterators + setCurrentException(e) + proc auxWriteStackTrace(f: PCallFrame): string = type TempFrame = tuple[procname: cstring, line: int, filename: cstring] @@ -148,7 +152,7 @@ proc raiseException(e: ref Exception, ename: cstring) {. unhandledException(e) when NimStackTrace: e.trace = rawWriteStackTrace() - asm "throw `e`;" + {.emit: "throw `e`;".} proc reraiseException() {.compilerproc, asmNoStackFrame.} = if lastJSError == nil: @@ -158,7 +162,7 @@ proc reraiseException() {.compilerproc, asmNoStackFrame.} = if isNimException(): unhandledException(cast[ref Exception](lastJSError)) - asm "throw lastJSError;" + {.emit: "throw lastJSError;".} proc raiseOverflow {.exportc: "raiseOverflow", noreturn, compilerproc.} = raise newException(OverflowDefect, "over- or underflow") @@ -176,7 +180,7 @@ proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noreturn.} = raise newException(FieldDefect, formatFieldDefect(f, discVal)) proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ var result = {}; for (var i = 0; i < arguments.length; ++i) { var x = arguments[i]; @@ -189,7 +193,7 @@ proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = } } return result; - """ + """.} proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} = {.emit: """ @@ -277,64 +281,64 @@ proc toJSStr(s: string): cstring {.compilerproc.} = result = join(res) proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ var result = new Array(`len`); for (var i = 0; i < `len`; i++) {result[i] = 0;} return result; - """ + """.} proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} = # argument type is a fake - asm """ + {.emit: """ var result = 0; for (var elem in `a`) { ++result; } return result; - """ + """.} proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} = - asm """ + {.emit: """ for (var elem in `a`) { if (!`b`[elem]) return false; } for (var elem in `b`) { if (!`a`[elem]) return false; } return true; - """ + """.} proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} = - asm """ + {.emit: """ for (var elem in `a`) { if (!`b`[elem]) return false; } return true; - """ + """.} proc SetLt(a, b: int): bool {.compilerproc.} = result = SetLe(a, b) and not SetEq(a, b) proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} = - asm """ + {.emit: """ var result = {}; for (var elem in `a`) { if (`b`[elem]) { result[elem] = true; } } return result; - """ + """.} proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} = - asm """ + {.emit: """ var result = {}; for (var elem in `a`) { result[elem] = true; } for (var elem in `b`) { result[elem] = true; } return result; - """ + """.} proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} = - asm """ + {.emit: """ var result = {}; for (var elem in `a`) { if (!`b`[elem]) { result[elem] = true; } } return result; - """ + """.} proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ if (`a` == `b`) return 0; if (!`a`) return -1; if (!`b`) return 1; @@ -343,7 +347,7 @@ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerproc.} = if (result != 0) return result; } return `a`.length - `b`.length; - """ + """.} proc cmp(x, y: string): int = when nimvm: @@ -354,7 +358,7 @@ proc cmp(x, y: string): int = result = cmpStrings(x, y) proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ if (`a` == `b`) return true; if (`a` === null && `b`.length == 0) return true; if (`b` === null && `a`.length == 0) return true; @@ -364,29 +368,29 @@ proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerproc.} = for (var i = 0; i < alen; ++i) if (`a`[i] != `b`[i]) return false; return true; - """ + """.} when defined(kwin): proc rawEcho {.compilerproc, asmNoStackFrame.} = - asm """ + {.emit: """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { buf += `toJSStr`(arguments[i]); } print(buf); - """ + """.} elif not defined(nimOldEcho): proc ewriteln(x: cstring) = log(x) proc rawEcho {.compilerproc, asmNoStackFrame.} = - asm """ + {.emit: """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { buf += `toJSStr`(arguments[i]); } console.log(buf); - """ + """.} else: proc ewriteln(x: cstring) = @@ -414,84 +418,84 @@ else: # Arithmetic: proc checkOverflowInt(a: int) {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ if (`a` > 2147483647 || `a` < -2147483648) `raiseOverflow`(); - """ + """.} proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ var result = `a` + `b`; `checkOverflowInt`(result); return result; - """ + """.} proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ var result = `a` - `b`; `checkOverflowInt`(result); return result; - """ + """.} proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ var result = `a` * `b`; `checkOverflowInt`(result); return result; - """ + """.} proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); return Math.trunc(`a` / `b`); - """ + """.} proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); return Math.trunc(`a` % `b`); - """ + """.} proc checkOverflowInt64(a: int64) {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ if (`a` > 9223372036854775807n || `a` < -9223372036854775808n) `raiseOverflow`(); - """ + """.} proc addInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ var result = `a` + `b`; `checkOverflowInt64`(result); return result; - """ + """.} proc subInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ var result = `a` - `b`; `checkOverflowInt64`(result); return result; - """ + """.} proc mulInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ var result = `a` * `b`; `checkOverflowInt64`(result); return result; - """ + """.} proc divInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ if (`b` == 0n) `raiseDivByZero`(); if (`b` == -1n && `a` == 9223372036854775807n) `raiseOverflow`(); return `a` / `b`; - """ + """.} proc modInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} = - asm """ + {.emit: """ if (`b` == 0n) `raiseDivByZero`(); if (`b` == -1n && `a` == 9223372036854775807n) `raiseOverflow`(); return `a` % `b`; - """ + """.} proc negInt(a: int): int {.compilerproc.} = result = a*(-1) @@ -508,7 +512,7 @@ proc absInt64(a: int64): int64 {.compilerproc.} = proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b -proc chckNilDisp(p: pointer) {.compilerproc.} = +proc chckNilDisp(p: JSRef) {.compilerproc.} = if p == nil: sysFatal(NilAccessDefect, "cannot dispatch; dispatcher is nil") @@ -526,22 +530,22 @@ proc nimCopyAux(dest, src: JSRef, n: ptr TNimNode) {.compilerproc.} = case n.kind of nkNone: sysAssert(false, "nimCopyAux") of nkSlot: - asm """ + {.emit: """ `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); - """ + """.} of nkList: - asm """ + {.emit: """ for (var i = 0; i < `n`.sons.length; i++) { nimCopyAux(`dest`, `src`, `n`.sons[i]); } - """ + """.} of nkCase: - asm """ + {.emit: """ `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); for (var i = 0; i < `n`.sons.length; ++i) { nimCopyAux(`dest`, `src`, `n`.sons[i][1]); } - """ + """.} proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = case ti.kind @@ -549,9 +553,9 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = if not isFatPointer(ti): result = src else: - asm "`result` = [`src`[0], `src`[1]];" + {.emit: "`result` = [`src`[0], `src`[1]];".} of tySet: - asm """ + {.emit: """ if (`dest` === null || `dest` === undefined) { `dest` = {}; } @@ -560,18 +564,18 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = } for (var key in `src`) { `dest`[key] = `src`[key]; } `result` = `dest`; - """ + """.} of tyTuple, tyObject: if ti.base != nil: result = nimCopy(dest, src, ti.base) elif ti.kind == tyObject: - asm "`result` = (`dest` === null || `dest` === undefined) ? {m_type: `ti`} : `dest`;" + {.emit: "`result` = (`dest` === null || `dest` === undefined) ? {m_type: `ti`} : `dest`;".} else: - asm "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;" + {.emit: "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;".} nimCopyAux(result, src, ti.node) of tyArrayConstr, tyArray: # In order to prevent a type change (TypedArray -> Array) and to have better copying performance, # arrays constructors are considered separately - asm """ + {.emit: """ if(ArrayBuffer.isView(`src`)) { if(`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { `dest` = new `src`.constructor(`src`); @@ -593,9 +597,9 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = } } } - """ + """.} of tySequence, tyOpenArray: - asm """ + {.emit: """ if (`src` === null) { `result` = null; } @@ -608,55 +612,24 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base); } } - """ + """.} of tyString: - asm """ + {.emit: """ if (`src` !== null) { `result` = `src`.slice(0); } - """ + """.} else: result = src -proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} = - asm "`result` = null;" - case ti.kind - of tyPtr, tyRef, tyVar, tyNil: - if isFatPointer(ti): - asm """ - `result` = [null, 0]; - """ - of tySet: - asm """ - `result` = {}; - """ - of tyTuple, tyObject: - if ti.kind == tyObject: - asm "`result` = {m_type: `ti`};" - else: - asm "`result` = {};" - of tySequence, tyOpenArray, tyString: - asm """ - `result` = []; - """ - of tyArrayConstr, tyArray: - asm """ - `result` = new Array(`x`.length); - for (var i = 0; i < `x`.length; ++i) { - `result`[i] = genericReset(`x`[i], `ti`.base); - } - """ - else: - discard - proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {. asmNoStackFrame, compilerproc.} = # types are fake - asm """ + {.emit: """ var result = new Array(`len`); for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`); return result; - """ + """.} proc chckIndx(i, a, b: int): int {.compilerproc.} = if i >= a and i <= b: return i @@ -685,7 +658,7 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = return true proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} = - asm "`x`.push(`c`);" + {.emit: "`x`.push(`c`);".} {.pop.} @@ -712,9 +685,9 @@ proc parseFloatNative(a: openarray[char]): float = let cstr = cstring str - asm """ + {.emit: """ `result` = Number(`cstr`); - """ + """.} proc nimParseBiggestFloat(s: openarray[char], number: var BiggestFloat): int {.compilerproc.} = var sign: bool @@ -781,3 +754,15 @@ if (!Math.trunc) { }; } """.} + +proc cmpClosures(a, b: JSRef): bool {.compilerproc, asmNoStackFrame.} = + # Both `a` and `b` need to be a closure + {.emit: """ + if (`a` !== null && `a`.ClP_0 !== undefined && + `b` !== null && `b`.ClP_0 !== undefined) { + return `a`.ClP_0 == `b`.ClP_0 && `a`.ClE_0 == `b`.ClE_0; + } else { + return `a` == `b`; + } + """ + .} diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index bdf6145a1..cf81f6d86 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -253,11 +253,12 @@ proc cpDir*(`from`, to: string) {.raises: [OSError].} = proc exec*(command: string) {. raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} = ## Executes an external process. If the external process terminates with - ## a non-zero exit code, an OSError exception is raised. + ## a non-zero exit code, an OSError exception is raised. The command is + ## executed relative to the current source path. ## - ## **Note:** If you need a version of `exec` that returns the exit code - ## and text output of the command, you can use `system.gorgeEx - ## <system.html#gorgeEx,string,string,string>`_. + ## .. note:: If you need a version of `exec` that returns the exit code + ## and text output of the command, you can use `system.gorgeEx + ## <system.html#gorgeEx,string,string,string>`_. log "exec: " & command: if rawExec(command) != 0: raise newException(OSError, "FAILED: " & command) @@ -267,11 +268,17 @@ proc exec*(command: string, input: string, cache = "") {. raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} = ## Executes an external process. If the external process terminates with ## a non-zero exit code, an OSError exception is raised. + ## + ## .. warning:: This version of `exec` is executed relative to the nimscript + ## module path, which affects how the command resolves relative paths. Thus + ## it is generally better to use `gorgeEx` directly when you need more + ## control over the execution environment or when working with commands + ## that deal with relative paths. log "exec: " & command: let (output, exitCode) = gorgeEx(command, input, cache) + echo output if exitCode != 0: raise newException(OSError, "FAILED: " & command) - echo output proc selfExec*(command: string) {. raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} = diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 335d49d0f..c02a24989 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -81,10 +81,14 @@ proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} = include threadids -when logOrc: +when logOrc or orcLeakDetector: proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) = - cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld; thread: %ld\n", - msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color, getThreadId()) + when orcLeakDetector: + cfprintf(cstderr, "%s %s file: %s:%ld; color: %ld; thread: %ld\n", + msg, desc.name, s.filename, s.line, s.color, getThreadId()) + else: + cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld; thread: %ld\n", + msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color, getThreadId()) proc free(s: Cell; desc: PNimTypeV2) {.inline.} = when traceCollector: @@ -142,7 +146,7 @@ proc unregisterCycle(s: Cell) = let idx = s.rootIdx-1 when false: if idx >= roots.len or idx < 0: - cprintf("[Bug!] %ld\n", idx) + cprintf("[Bug!] %ld %ld\n", idx, roots.len) rawQuit 1 roots.d[idx] = roots.d[roots.len-1] roots.d[idx][0].rootIdx = idx+1 @@ -299,6 +303,14 @@ proc collectColor(s: Cell; desc: PNimTypeV2; col: int; j: var GcEnv) = t.setColor(colBlack) trace(t, desc, j) +const + defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128 + +when defined(nimStressOrc): + const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8 +else: + var rootsThreshold {.threadvar.}: int + proc collectCyclesBacon(j: var GcEnv; lowMark: int) = # pretty direct translation from # https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf @@ -337,20 +349,28 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) = s.rootIdx = 0 collectColor(s, roots.d[i][1], colToCollect, j) + # Bug #22927: `free` calls destructors which can append to `roots`. + # We protect against this here by setting `roots.len` to 0 and also + # setting the threshold so high that no cycle collection can be triggered + # until we are out of this critical section: + when not defined(nimStressOrc): + let oldThreshold = rootsThreshold + rootsThreshold = high(int) + roots.len = 0 + for i in 0 ..< j.toFree.len: + when orcLeakDetector: + writeCell("CYCLIC OBJECT FREED", j.toFree.d[i][0], j.toFree.d[i][1]) free(j.toFree.d[i][0], j.toFree.d[i][1]) + when not defined(nimStressOrc): + rootsThreshold = oldThreshold + inc j.freed, j.toFree.len deinit j.toFree - #roots.len = 0 -const - defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128 - -when defined(nimStressOrc): - const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8 -else: - var rootsThreshold {.threadvar.}: int +when defined(nimOrcStats): + var freedCyclicObjects {.threadvar.}: int proc partialCollect(lowMark: int) = when false: @@ -365,6 +385,8 @@ proc partialCollect(lowMark: int) = roots.len - lowMark) roots.len = lowMark deinit j.traceStack + when defined(nimOrcStats): + inc freedCyclicObjects, j.freed proc collectCycles() = ## Collect cycles. @@ -385,7 +407,8 @@ proc collectCycles() = collectCyclesBacon(j, 0) deinit j.traceStack - deinit roots + if roots.len == 0: + deinit roots when not defined(nimStressOrc): # compute the threshold based on the previous history @@ -405,6 +428,16 @@ proc collectCycles() = when logOrc: cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld rcSum: %ld edges: %ld\n", j.freed, rootsThreshold, j.touched, getOccupiedMem(), j.rcSum, j.edges) + when defined(nimOrcStats): + inc freedCyclicObjects, j.freed + +when defined(nimOrcStats): + type + OrcStats* = object ## Statistics of the cycle collector subsystem. + freedCyclicObjects*: int ## Number of freed cyclic objects. + proc GC_orcStats*(): OrcStats = + ## Returns the statistics of the cycle collector subsystem. + result = OrcStats(freedCyclicObjects: freedCyclicObjects) proc registerCycle(s: Cell; desc: PNimTypeV2) = s.rootIdx = roots.len+1 diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 6b6f7e340..13118e40b 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -17,7 +17,7 @@ proc reprFloat(x: float): string {.compilerproc.} = return $x proc reprPointer(x: pointer): string {.compilerproc.} = result = newString(60) - let n = c_sprintf(cast[cstring](addr result[0]), "%p", x) + let n = c_snprintf(cast[cstring](addr result[0]), csize_t(60), "%p", x) setLen(result, n) proc reprStrAux(result: var string, s: cstring; len: int) = diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index f81c615e9..d2aef536c 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -41,7 +41,7 @@ proc repr*(x: char): string {.noSideEffect, raises: [].} = ## ```Nim ## assert repr('c') == "'c'" ## ``` - result.add '\'' + result = "'" # Elides string creations if not needed if x in {'\\', '\0'..'\31', '\127'..'\255'}: result.add '\\' @@ -54,7 +54,7 @@ proc repr*(x: char): string {.noSideEffect, raises: [].} = proc repr*(x: string | cstring): string {.noSideEffect, raises: [].} = ## repr for a string argument. Returns `x` ## converted to a quoted and escaped string. - result.add '\"' + result = "\"" for i in 0..<x.len: if x[i] in {'"', '\\', '\0'..'\31', '\127'..'\255'}: result.add '\\' @@ -99,7 +99,7 @@ proc repr*(p: proc | iterator {.closure.}): string = ## repr of a proc as its address repr(cast[ptr pointer](unsafeAddr p)[]) -template repr*[T: distinct|range](x: T): string = +template repr*[T: distinct|(range and not enum)](x: T): string = when T is range: # add a branch to handle range repr(rangeBase(typeof(x))(x)) elif T is distinct: diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 30e84ebee..761d66aec 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -29,7 +29,7 @@ proc reprBool(x: bool): string {.compilerRtl.} = proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = var tmp: bool let item = typ.node.sons[e] - {.emit: "`tmp` = `item` !== undefined".} + {.emit: "`tmp` = `item` !== undefined;".} if tmp: result = makeNimstrLit(item.name) else: @@ -136,7 +136,7 @@ proc reprArray(a: pointer, typ: PNimType, add(result, "]") proc isPointedToNil(p: pointer): bool = - {. emit: "if (`p` === null) {`result` = true};\n" .} + {. emit: "if (`p` === null) {`result` = true;}\n" .} proc reprRef(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure) = diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 8b3e9ee88..572e77408 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -90,6 +90,12 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin q.cap = newCap result = q +proc zeroNewElements(len: int; q: pointer; addlen, elemSize, elemAlign: int) {. + noSideEffect, tags: [], raises: [], compilerRtl.} = + {.noSideEffect.}: + let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign) + zeroMem(q +! headerSize +! len * elemSize, addlen * elemSize) + proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. noSideEffect, tags: [], raises: [], compilerRtl.} = {.noSideEffect.}: diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index dbee10777..404b4f78d 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -166,7 +166,7 @@ proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} = s.len = newLen proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = - if a.p == b.p: return + if a.p == b.p and a.len == b.len: return if isLiteral(b): # we can shallow copy literals: frees(a) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index e4d6479ec..3621c4960 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -220,7 +220,10 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} = proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = let n = max(newLen, 0) if s == nil: - result = mnewString(n) + if n == 0: + return s + else: + result = mnewString(n) elif n <= s.space: result = s else: @@ -301,7 +304,10 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. compilerRtl.} = sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq" if s == nil: - result = cast[PGenericSeq](newSeq(typ, newLen)) + if newLen == 0: + result = s + else: + result = cast[PGenericSeq](newSeq(typ, newLen)) else: let elemSize = typ.base.size let elemAlign = typ.base.align diff --git a/nimdoc/extlinks/project/expected/_._/util.html b/nimdoc/extlinks/project/expected/_._/util.html index 35f333211..32dab9216 100644 --- a/nimdoc/extlinks/project/expected/_._/util.html +++ b/nimdoc/extlinks/project/expected/_._/util.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>nimdoc/extlinks/util</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -97,8 +101,5 @@ </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/extlinks/project/expected/doc/manual.html b/nimdoc/extlinks/project/expected/doc/manual.html index 7b964f4ac..2946f803a 100644 --- a/nimdoc/extlinks/project/expected/doc/manual.html +++ b/nimdoc/extlinks/project/expected/doc/manual.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Nothing User Manual</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -37,8 +41,5 @@ </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/extlinks/project/expected/main.html b/nimdoc/extlinks/project/expected/main.html index 1a58ea2ac..1e7c9c126 100644 --- a/nimdoc/extlinks/project/expected/main.html +++ b/nimdoc/extlinks/project/expected/main.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>nimdoc/extlinks/project/main</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -95,8 +99,7 @@ <h1><a class="toc-backref" href="#7">Types</a></h1> <dl class="item"> <div id="A"> - <dt><pre><a href="main.html#A"><span class="Identifier">A</span></a> <span class="Other">=</span> <span class="Keyword">object</span> - </pre></dt> + <dt><pre><a href="main.html#A"><span class="Identifier">A</span></a> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt> <dd> @@ -135,8 +138,5 @@ </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/extlinks/project/expected/sub/submodule.html b/nimdoc/extlinks/project/expected/sub/submodule.html index 60887ae37..408ce2060 100644 --- a/nimdoc/extlinks/project/expected/sub/submodule.html +++ b/nimdoc/extlinks/project/expected/sub/submodule.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>nimdoc/extlinks/project/sub/submodule</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -123,8 +127,5 @@ </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/extlinks/project/expected/theindex.html b/nimdoc/extlinks/project/expected/theindex.html index 9d2f6723c..cf250edd1 100644 --- a/nimdoc/extlinks/project/expected/theindex.html +++ b/nimdoc/extlinks/project/expected/theindex.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Index</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -51,8 +55,5 @@ </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index af46771f3..a26704133 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Not a Nim Manual</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -266,8 +270,5 @@ stmt = IND{>} stmt ^+ IND{=} DED # list of statements </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/test_doctype/expected/test_doctype.html b/nimdoc/test_doctype/expected/test_doctype.html index 694ed9b00..2cbf6ec0f 100644 --- a/nimdoc/test_doctype/expected/test_doctype.html +++ b/nimdoc/test_doctype/expected/test_doctype.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>nimdoc/test_doctype/test_doctype</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -58,8 +62,7 @@ <div id="tocRoot"></div> <p class="module-desc"><p>Check</p> -<p><pre class="listing"> -<span class="Identifier">text</span></pre></p> +<p><pre class="listing"><span class="Identifier">text</span></pre></p> <h1><a class="toc-backref" id="check" href="#check">Check</a></h1> <h1><a class="toc-backref" id="text" href="#text">text</a></h1></p> @@ -75,8 +78,5 @@ </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 40df1943a..4370f0df8 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>nimdoc/test_out_index_dot_html/foo</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -97,8 +101,5 @@ </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html index ea3486d4b..ca7c2d7af 100644 --- a/nimdoc/test_out_index_dot_html/expected/theindex.html +++ b/nimdoc/test_out_index_dot_html/expected/theindex.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Index</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -35,8 +39,5 @@ </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index a9e4ac9c6..0c399e4c1 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -623,8 +623,8 @@ pre { table.line-nums-table { border-radius: 4px; - border: 1px solid #cccccc; - background-color: ghostwhite; + border: 1px solid var(--border); + background-color: var(--secondary-background); border-collapse: separate; margin-top: 15px; margin-bottom: 25px; } @@ -660,6 +660,9 @@ table { border-collapse: collapse; border-color: var(--third-background); border-spacing: 0; +} + +table:not(.line-nums-table) { font-size: 0.9em; } @@ -678,7 +681,7 @@ table th.docinfo-name { text-align: right; } -table tr:hover { +table:not(.line-nums-table) tr:hover { background-color: var(--third-background); } diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index ba9512d5a..6decf79a3 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>subdir/subdir_b/utils</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -253,8 +257,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href= <h1><a class="toc-backref" href="#7">Types</a></h1> <dl class="item"> <div id="G"> - <dt><pre><a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span> <span class="Other">=</span> <span class="Keyword">object</span> - </pre></dt> + <dt><pre><a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt> <dd> @@ -607,8 +610,5 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href= </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index db49102f8..43a72d99d 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>testproject</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -47,7 +51,10 @@ </select> </div> <ul class="simple simple-toc" id="toc-list"> - <li> + <li><a class="reference" id="basic-usage_toc" href="#basic-usage">Basic usage</a></li> +<ul class="simple"><li><a class="reference" id="basic-usage-encoding-data_toc" href="#basic-usage-encoding-data">Encoding data</a></li> +<li><a class="reference" id="basic-usage-decoding-data_toc" href="#basic-usage-decoding-data">Decoding data</a></li> +</ul><li> <a class="reference reference-toplevel" href="#6" id="56">Imports</a> </li> <li> @@ -59,8 +66,7 @@ <li><a class="reference" href="#AnotherObject" title="AnotherObject = object case x*: bool of true: - y*: proc (x: string) - + y*: proc (x: string) of false:">AnotherObject</a></li> <li><a class="reference" href="#B" title="B {.inject.} = enum bB">B</a></li> @@ -344,7 +350,11 @@ <div id="tocRoot"></div> - <p class="module-desc">This is the top level module. + <p class="module-desc"> +<h1><a class="toc-backref" id="basic-usage" href="#basic-usage">Basic usage</a></h1> +<h2><a class="toc-backref" id="basic-usage-encoding-data" href="#basic-usage-encoding-data">Encoding data</a></h2><p>Apart from strings you can also encode lists of integers or characters:</p> + +<h2><a class="toc-backref" id="basic-usage-decoding-data" href="#basic-usage-decoding-data">Decoding data</a></h2>This is the top level module. <p><strong class="examples_text">Example:</strong></p> <pre class="listing"><span class="Keyword">import</span> <span class="Identifier">testproject</span> <span class="Keyword">import</span> <span class="Identifier">subdir</span> <span class="Operator">/</span> <span class="Identifier">subdir_b</span> <span class="Operator">/</span> <span class="Identifier">utils</span> @@ -380,11 +390,8 @@ <dt><pre><a href="testproject.html#AnotherObject"><span class="Identifier">AnotherObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span> <span class="Keyword">case</span> <span class="Identifier">x</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">bool</span> <span class="Keyword">of</span> <span class="Identifier">true</span><span class="Other">:</span> - <span class="Identifier">y</span><span class="Operator">*</span><span class="Other">:</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> - - <span class="Keyword">of</span> <span class="Identifier">false</span><span class="Other">:</span> - - </pre></dt> + <span class="Identifier">y</span><span class="Operator">*</span><span class="Other">:</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span> + <span class="Keyword">of</span> <span class="Identifier">false</span><span class="Other">:</span></pre></dt> <dd> @@ -423,8 +430,7 @@ <div id="MyObject"> <dt><pre><a href="testproject.html#MyObject"><span class="Identifier">MyObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span> <span class="Identifier">someString</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## This is a string</span> - <span class="Identifier">annotated</span><span class="Operator">*</span> {.<span class="Identifier">somePragma</span>.}<span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## This is an annotated string</span> - </pre></dt> + <span class="Identifier">annotated</span><span class="Operator">*</span> {.<span class="Identifier">somePragma</span>.}<span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## This is an annotated string</span></pre></dt> <dd> @@ -444,8 +450,7 @@ </div> <div id="T19396"> <dt><pre><a href="testproject.html#T19396"><span class="Identifier">T19396</span></a> <span class="Other">=</span> <span class="Keyword">object</span> - <span class="Identifier">a</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span> - </pre></dt> + <span class="Identifier">a</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span></pre></dt> <dd> @@ -1251,8 +1256,5 @@ bar </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index ac9bbb2ce..81d65a05a 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -1,71 +1,74 @@ nimTitle testproject testproject.html module testproject 0 -nim someVariable testproject.html#someVariable var someVariable 13 -nim C_A testproject.html#C_A const C_A 26 -nim C_B testproject.html#C_B const C_B 27 -nim C_C testproject.html#C_C const C_C 28 -nim C_D testproject.html#C_D const C_D 29 -nim bar testproject.html#bar,T,T proc bar[T](a, b: T): T 31 -nim baz testproject.html#baz,T,T proc baz[T](a, b: T): T 34 -nim buzz testproject.html#buzz,T,T proc buzz[T](a, b: T): T 38 -nim FooBuzz testproject.html#FooBuzz type FooBuzz 43 -nim bar testproject.html#bar proc bar(f: FooBuzz) 47 -nim aVariable testproject.html#aVariable var aVariable 52 +nim someVariable testproject.html#someVariable var someVariable 25 +nim C_A testproject.html#C_A const C_A 38 +nim C_B testproject.html#C_B const C_B 39 +nim C_C testproject.html#C_C const C_C 40 +nim C_D testproject.html#C_D const C_D 41 +nim bar testproject.html#bar,T,T proc bar[T](a, b: T): T 43 +nim baz testproject.html#baz,T,T proc baz[T](a, b: T): T 46 +nim buzz testproject.html#buzz,T,T proc buzz[T](a, b: T): T 50 +nim FooBuzz testproject.html#FooBuzz type FooBuzz 55 +nim bar testproject.html#bar proc bar(f: FooBuzz) 59 +nim aVariable testproject.html#aVariable var aVariable 64 nim A testproject.html#A enum A 92 nim B testproject.html#B enum B 97 -nim someFunc testproject.html#someFunc proc someFunc() 56 +nim someFunc testproject.html#someFunc proc someFunc() 68 nim fromUtils1 testproject.html#fromUtils1.i iterator fromUtils1(): int 112 nim fromUtils2 testproject.html#fromUtils2.t template fromUtils2() 119 -nim fromUtils3 testproject.html#fromUtils3 proc fromUtils3() 57 -nim isValid testproject.html#isValid,T proc isValid[T](x: T): bool 59 -nim enumValueA2 testproject.html#enumValueA2 Foo.enumValueA2 66 -nim Foo testproject.html#Foo enum Foo 66 -nim z1 testproject.html#z1 proc z1(): Foo 69 -nim z2 testproject.html#z2 proc z2() 73 -nim z3 testproject.html#z3 proc z3() 78 -nim z4 testproject.html#z4 proc z4() 81 -nim z5 testproject.html#z5 proc z5(): int 87 -nim z6 testproject.html#z6 proc z6(): int 91 -nim z6t testproject.html#z6t.t template z6t(): int 95 -nim z7 testproject.html#z7 proc z7(): int 99 -nim z8 testproject.html#z8 proc z8(): int 103 -nim z9 testproject.html#z9 proc z9() 111 -nim z10 testproject.html#z10 proc z10() 114 -nim z11 testproject.html#z11 proc z11() 119 -nim z12 testproject.html#z12 proc z12(): int 124 -nim z13 testproject.html#z13 proc z13() 129 -nim baz testproject.html#baz proc baz() 134 -nim z17 testproject.html#z17 proc z17() 144 -nim p1 testproject.html#p1 proc p1() 156 -nim addfBug14485 testproject.html#addfBug14485 proc addfBug14485() 177 -nim c_printf testproject.html#c_printf,cstring proc c_printf(frmt: cstring): cint 193 -nim c_nonexistent testproject.html#c_nonexistent,cstring proc c_nonexistent(frmt: cstring): cint 197 -nim low testproject.html#low,T proc low[T: Ordinal | enum | range](x: T): T 200 -nim low2 testproject.html#low2,T proc low2[T: Ordinal | enum | range](x: T): T 210 -nim tripleStrLitTest testproject.html#tripleStrLitTest proc tripleStrLitTest() 223 -nim method1 testproject.html#method1.e,Moo method method1(self: Moo) 264 -nim method2 testproject.html#method2.e,Moo method method2(self: Moo): int 266 -nim method3 testproject.html#method3.e,Moo method method3(self: Moo): int 269 -nim iter1 testproject.html#iter1.i,int iterator iter1(n: int): int 274 -nim iter2 testproject.html#iter2.i,int iterator iter2(n: int): int 278 -nim bar testproject.html#bar.m macro bar(): untyped 285 -nim z16 testproject.html#z16.m macro z16() 288 -nim z18 testproject.html#z18.m macro z18(): int 297 -nim foo testproject.html#foo.t,SomeType,SomeType template foo(a, b: SomeType) 302 -nim myfn testproject.html#myfn.t template myfn() 307 -nim z14 testproject.html#z14.t template z14() 328 -nim z15 testproject.html#z15.t template z15() 333 -nim asyncFun1 testproject.html#asyncFun1 proc asyncFun1(): Future[int] 358 -nim asyncFun2 testproject.html#asyncFun2 proc asyncFun2(): owned(Future[void]) 361 -nim asyncFun3 testproject.html#asyncFun3 proc asyncFun3(): owned(Future[void]) 362 -nim testNimDocTrailingExample testproject.html#testNimDocTrailingExample.t template testNimDocTrailingExample() 371 -nim Circle testproject.html#Circle Shapes.Circle 380 -nim Triangle testproject.html#Triangle Shapes.Triangle 380 -nim Rectangle testproject.html#Rectangle Shapes.Rectangle 380 -nim Shapes testproject.html#Shapes enum Shapes 380 -nim anything testproject.html#anything proc anything() 387 -nim T19396 testproject.html#T19396 object T19396 392 -nim somePragma testproject.html#somePragma.t template somePragma() 396 -nim MyObject testproject.html#MyObject object MyObject 400 -nim AnotherObject testproject.html#AnotherObject object AnotherObject 405 -nimgrp bar testproject.html#bar-procs-all proc 31 -nimgrp baz testproject.html#baz-procs-all proc 34 +nim fromUtils3 testproject.html#fromUtils3 proc fromUtils3() 69 +nim isValid testproject.html#isValid,T proc isValid[T](x: T): bool 71 +nim enumValueA2 testproject.html#enumValueA2 Foo.enumValueA2 78 +nim Foo testproject.html#Foo enum Foo 78 +nim z1 testproject.html#z1 proc z1(): Foo 81 +nim z2 testproject.html#z2 proc z2() 85 +nim z3 testproject.html#z3 proc z3() 90 +nim z4 testproject.html#z4 proc z4() 93 +nim z5 testproject.html#z5 proc z5(): int 99 +nim z6 testproject.html#z6 proc z6(): int 103 +nim z6t testproject.html#z6t.t template z6t(): int 107 +nim z7 testproject.html#z7 proc z7(): int 111 +nim z8 testproject.html#z8 proc z8(): int 115 +nim z9 testproject.html#z9 proc z9() 123 +nim z10 testproject.html#z10 proc z10() 126 +nim z11 testproject.html#z11 proc z11() 131 +nim z12 testproject.html#z12 proc z12(): int 136 +nim z13 testproject.html#z13 proc z13() 141 +nim baz testproject.html#baz proc baz() 146 +nim z17 testproject.html#z17 proc z17() 156 +nim p1 testproject.html#p1 proc p1() 168 +nim addfBug14485 testproject.html#addfBug14485 proc addfBug14485() 189 +nim c_printf testproject.html#c_printf,cstring proc c_printf(frmt: cstring): cint 205 +nim c_nonexistent testproject.html#c_nonexistent,cstring proc c_nonexistent(frmt: cstring): cint 209 +nim low testproject.html#low,T proc low[T: Ordinal | enum | range](x: T): T 212 +nim low2 testproject.html#low2,T proc low2[T: Ordinal | enum | range](x: T): T 222 +nim tripleStrLitTest testproject.html#tripleStrLitTest proc tripleStrLitTest() 235 +nim method1 testproject.html#method1.e,Moo method method1(self: Moo) 276 +nim method2 testproject.html#method2.e,Moo method method2(self: Moo): int 278 +nim method3 testproject.html#method3.e,Moo method method3(self: Moo): int 281 +nim iter1 testproject.html#iter1.i,int iterator iter1(n: int): int 286 +nim iter2 testproject.html#iter2.i,int iterator iter2(n: int): int 290 +nim bar testproject.html#bar.m macro bar(): untyped 297 +nim z16 testproject.html#z16.m macro z16() 300 +nim z18 testproject.html#z18.m macro z18(): int 309 +nim foo testproject.html#foo.t,SomeType,SomeType template foo(a, b: SomeType) 314 +nim myfn testproject.html#myfn.t template myfn() 319 +nim z14 testproject.html#z14.t template z14() 340 +nim z15 testproject.html#z15.t template z15() 345 +nim asyncFun1 testproject.html#asyncFun1 proc asyncFun1(): Future[int] 370 +nim asyncFun2 testproject.html#asyncFun2 proc asyncFun2(): owned(Future[void]) 373 +nim asyncFun3 testproject.html#asyncFun3 proc asyncFun3(): owned(Future[void]) 374 +nim testNimDocTrailingExample testproject.html#testNimDocTrailingExample.t template testNimDocTrailingExample() 383 +nim Circle testproject.html#Circle Shapes.Circle 392 +nim Triangle testproject.html#Triangle Shapes.Triangle 392 +nim Rectangle testproject.html#Rectangle Shapes.Rectangle 392 +nim Shapes testproject.html#Shapes enum Shapes 392 +nim anything testproject.html#anything proc anything() 399 +nim T19396 testproject.html#T19396 object T19396 404 +nim somePragma testproject.html#somePragma.t template somePragma() 408 +nim MyObject testproject.html#MyObject object MyObject 412 +nim AnotherObject testproject.html#AnotherObject object AnotherObject 417 +nimgrp bar testproject.html#bar-procs-all proc 43 +nimgrp baz testproject.html#baz-procs-all proc 46 +heading Basic usage testproject.html#basic-usage Basic usage 0 +heading Encoding data testproject.html#basic-usage-encoding-data Encoding data 0 +heading Decoding data testproject.html#basic-usage-decoding-data Decoding data 0 diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 70916f7e0..71a487e0c 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- This file is generated by Nim. --> -<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Index</title> +<!-- Google fonts --> +<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> +<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> + <!-- Favicon --> <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/> <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII="> @@ -423,8 +427,5 @@ </div> </div> - <!-- Google fonts --> - <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/> - <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> </body> </html> diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index d2d3fef3f..383c4c827 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -1,3 +1,15 @@ +## Basic usage +## =========== +## +## Encoding data +## ------------- +## +## Apart from strings you can also encode lists of integers or characters: + +## Decoding data +## ------------- +## + import subdir / subdir_b / utils diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim index e3b4c9a3a..e5abf0d2d 100644 --- a/nimpretty/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -12,7 +12,7 @@ when not defined(nimpretty): {.error: "This needs to be compiled with --define:nimPretty".} -import ../compiler / [idents, msgs, syntaxes, options, pathutils, layouter] +import ../compiler / [idents, llstream, ast, msgs, syntaxes, options, pathutils, layouter] import parseopt, strutils, os, sequtils @@ -48,6 +48,42 @@ type indWidth*: Natural maxLineLen*: Positive +proc goodEnough(a, b: PNode): bool = + if a.kind == b.kind and a.safeLen == b.safeLen: + case a.kind + of nkNone, nkEmpty, nkNilLit: result = true + of nkIdent: result = a.ident.id == b.ident.id + of nkSym: result = a.sym == b.sym + of nkType: result = true + of nkCharLit, nkIntLit..nkInt64Lit, nkUIntLit..nkUInt64Lit: + result = a.intVal == b.intVal + of nkFloatLit..nkFloat128Lit: + result = a.floatVal == b.floatVal + of nkStrLit, nkRStrLit, nkTripleStrLit: + result = a.strVal == b.strVal + else: + for i in 0 ..< a.len: + if not goodEnough(a[i], b[i]): return false + return true + elif a.kind == nkStmtList and a.len == 1: + result = goodEnough(a[0], b) + elif b.kind == nkStmtList and b.len == 1: + result = goodEnough(a, b[0]) + else: + result = false + +proc finalCheck(content: string; origAst: PNode): bool {.nimcall.} = + var conf = newConfigRef() + let oldErrors = conf.errorCounter + var parser: Parser + parser.em.indWidth = 2 + let fileIdx = fileInfoIdx(conf, AbsoluteFile "nimpretty_bug.nim") + + openParser(parser, fileIdx, llStreamOpen(content), newIdentCache(), conf) + let newAst = parseAll(parser) + closeParser(parser) + result = conf.errorCounter == oldErrors # and goodEnough(newAst, origAst) + proc prettyPrint*(infile, outfile: string, opt: PrettyOptions) = var conf = newConfigRef() let fileIdx = fileInfoIdx(conf, AbsoluteFile infile) @@ -58,8 +94,10 @@ proc prettyPrint*(infile, outfile: string, opt: PrettyOptions) = parser.em.indWidth = opt.indWidth if setupParser(parser, fileIdx, newIdentCache(), conf): parser.em.maxLineLen = opt.maxLineLen - discard parseAll(parser) + let fullAst = parseAll(parser) closeParser(parser) + when defined(nimpretty): + closeEmitter(parser.em, fullAst, finalCheck) proc main = var outfile, outdir: string @@ -78,7 +116,13 @@ proc main = for kind, key, val in getopt(): case kind of cmdArgument: - infiles.add(key.addFileExt(".nim")) + if dirExists(key): + for file in walkDirRec(key, skipSpecial = true): + if file.endsWith(".nim") or file.endsWith(".nimble"): + infiles.add(file) + else: + infiles.add(key.addFileExt(".nim")) + of cmdLongOption, cmdShortOption: case normalize(key) of "help", "h": writeHelp() diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 03a6e32e1..04bae08c1 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -8,6 +8,10 @@ # import compiler/renderer +import compiler/types +import compiler/trees +import compiler/wordrecg +import compiler/sempass2 import strformat import algorithm import tables @@ -34,7 +38,7 @@ import compiler / [options, commands, modules, passes, passaux, msgs, sigmatch, ast, idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper, - pathutils, condsyms, syntaxes] + pathutils, condsyms, syntaxes, suggestsymdb] when defined(nimPreviewSlimSystem): import std/typedthreads @@ -68,11 +72,14 @@ Options: --info:X information --info:nimVer return the Nim compiler version that nimsuggest uses internally --info:protocolVer return the newest protocol version that is supported + --info:capabilities return the capabilities supported by nimsuggest --refresh perform automatic refreshes to keep the analysis precise --maxresults:N limit the number of suggestions to N --tester implies --stdin and outputs a line '""" & DummyEof & """' for the tester --find attempts to find the project file of the current project + --exceptionInlayHints:on|off + globally turn exception inlay hints on|off The server then listens to the connection and takes line-based commands. @@ -91,7 +98,7 @@ type var gPort = 6000.Port - gAddress = "" + gAddress = "127.0.0.1" gMode: Mode gEmitEof: bool # whether we write '!EOF!' dummy lines gLogging = defined(logging) @@ -124,6 +131,12 @@ const "type 'quit' to quit\n" & "type 'debug' to toggle debug mode on/off\n" & "type 'terse' to toggle terse mode on/off" + #List of currently supported capabilities. So lang servers/ides can iterate over and check for what's enabled + Capabilities = [ + "con", #current NimSuggest supports the `con` commmand + "exceptionInlayHints", + "unknownFile", #current NimSuggest can handle unknown files + ] proc parseQuoted(cmd: string; outp: var string; start: int): int = var i = start @@ -218,6 +231,14 @@ proc clearInstCache(graph: ModuleGraph, projectFileIdx: FileIndex) = for id in procIdsToDelete: graph.procInstCache.del id + for tbl in mitems(graph.attachedOps): + var attachedOpsToDelete = newSeq[ItemId]() + for id in tbl.keys: + if id.module == projectFileIdx.int and sfOverridden in resolveAttachedOp(graph, tbl[id]).flags: + attachedOpsToDelete.add id + for id in attachedOpsToDelete: + tbl.del id + proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, tag: string, graph: ModuleGraph) = let conf = graph.config @@ -689,8 +710,16 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = of "nimver": stdout.writeLine(system.NimVersion) quit 0 + of "capabilities": + stdout.writeLine(Capabilities.toSeq.mapIt($it).join(" ")) + quit 0 else: processSwitch(pass, p, conf) + of "exceptioninlayhints": + case p.val.normalize + of "", "on": incl(conf.globalOptions, optIdeExceptionInlayHints) + of "off": excl(conf.globalOptions, optIdeExceptionInlayHints) + else: processSwitch(pass, p, conf) of "tester": gMode = mstdin gEmitEof = true @@ -738,20 +767,16 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = if gMode != mstdin: conf.writelnHook = proc (msg: string) = discard - # Find Nim's prefix dir. - let binaryPath = findExe("nim") - if binaryPath == "": - raise newException(IOError, - "Cannot find Nim standard library: Nim compiler not in PATH") - conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir() - if not dirExists(conf.prefixDir / RelativeDir"lib"): - conf.prefixDir = AbsoluteDir"" - + conf.prefixDir = conf.getPrefixDir() #msgs.writelnHook = proc (line: string) = log(line) myLog("START " & conf.projectFull.string) var graph = newModuleGraph(cache, conf) if self.loadConfigsAndProcessCmdLine(cache, conf, graph): + + if conf.selectedGC == gcUnselected and + conf.backend != backendJs: + initOrcDefines(conf) mainCommand(graph) # v3 start @@ -794,25 +819,57 @@ func deduplicateSymInfoPair[SymInfoPair](xs: seq[SymInfoPair]): seq[SymInfoPair] result.add(itm) result.reverse() +func deduplicateSymInfoPair(xs: SuggestFileSymbolDatabase): SuggestFileSymbolDatabase = + # xs contains duplicate items and we want to filter them by range because the + # sym may not match. This can happen when xs contains the same definition but + # with different signature because suggestSym might be called multiple times + # for the same symbol (e. g. including/excluding the pragma) + result = SuggestFileSymbolDatabase( + lineInfo: newSeqOfCap[TinyLineInfo](xs.lineInfo.len), + sym: newSeqOfCap[PSym](xs.sym.len), + isDecl: newPackedBoolArray(), + caughtExceptions: newSeqOfCap[seq[PType]](xs.caughtExceptions.len), + caughtExceptionsSet: newPackedBoolArray(), + fileIndex: xs.fileIndex, + trackCaughtExceptions: xs.trackCaughtExceptions, + isSorted: false + ) + var i = xs.lineInfo.high + while i >= 0: + let itm = xs.lineInfo[i] + var found = false + for res in result.lineInfo: + if res.exactEquals(itm): + found = true + break + if not found: + result.add(xs.getSymInfoPair(i)) + dec i + result.reverse() + proc findSymData(graph: ModuleGraph, trackPos: TLineInfo): ref SymInfoPair = - for s in graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair: - if isTracked(s.info, trackPos, s.sym.name.s.len): + let db = graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair + doAssert(db.fileIndex == trackPos.fileIndex) + for i in db.lineInfo.low..db.lineInfo.high: + if isTracked(db.lineInfo[i], TinyLineInfo(line: trackPos.line, col: trackPos.col), db.sym[i].name.s.len): + var res = db.getSymInfoPair(i) new(result) - result[] = s + result[] = res break -func isInRange*(current, startPos, endPos: TLineInfo, tokenLen: int): bool = - result = current.fileIndex == startPos.fileIndex and +func isInRange*(current, startPos, endPos: TinyLineInfo, tokenLen: int): bool = + result = (current.line > startPos.line or (current.line == startPos.line and current.col>=startPos.col)) and (current.line < endPos.line or (current.line == endPos.line and current.col <= endPos.col)) proc findSymDataInRange(graph: ModuleGraph, startPos, endPos: TLineInfo): seq[SymInfoPair] = result = newSeq[SymInfoPair]() - for s in graph.fileSymbols(startPos.fileIndex).deduplicateSymInfoPair: - if isInRange(s.info, startPos, endPos, s.sym.name.s.len): - result.add(s) + let db = graph.fileSymbols(startPos.fileIndex).deduplicateSymInfoPair + for i in db.lineInfo.low..db.lineInfo.high: + if isInRange(db.lineInfo[i], TinyLineInfo(line: startPos.line, col: startPos.col), TinyLineInfo(line: endPos.line, col: endPos.col), db.sym[i].name.s.len): + result.add(db.getSymInfoPair(i)) proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int): ref SymInfoPair = @@ -831,7 +888,7 @@ proc findSymDataInRange(graph: ModuleGraph, file: AbsoluteFile; startLine, start proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) = let sha = $sha1.secureHashFile(file) - if graph.config.m.fileInfos[originalFileIdx.int32].hash != sha or graph.config.ideCmd == ideSug: + if graph.config.m.fileInfos[originalFileIdx.int32].hash != sha or graph.config.ideCmd in {ideSug, ideCon}: myLog fmt "{file} changed compared to last compilation" graph.markDirty originalFileIdx graph.markClientsDirty originalFileIdx @@ -851,7 +908,7 @@ proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, endLine = endLine, endCol = endCol) suggestResult(graph.config, suggest) -proc suggestInlayHintResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, +proc suggestInlayHintResultType(graph: ModuleGraph, sym: PSym, info: TLineInfo, defaultSection = ideNone, endLine: uint16 = 0, endCol = 0) = let section = if defaultSection != ideNone: defaultSection @@ -862,12 +919,61 @@ proc suggestInlayHintResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, var suggestDef = symToSuggest(graph, sym, isLocal=false, section, info, 100, PrefixMatch.None, false, 0, true, endLine = endLine, endCol = endCol) - suggestDef.inlayHintInfo = suggestToSuggestInlayHint(suggestDef) + suggestDef.inlayHintInfo = suggestToSuggestInlayTypeHint(suggestDef) suggestDef.section = ideInlayHints if sym.kind == skForVar: suggestDef.inlayHintInfo.allowInsert = false suggestResult(graph.config, suggestDef) +proc suggestInlayHintResultException(graph: ModuleGraph, sym: PSym, info: TLineInfo, + defaultSection = ideNone, caughtExceptions: seq[PType], caughtExceptionsSet: bool, endLine: uint16 = 0, endCol = 0) = + if not caughtExceptionsSet: + return + + if sym.kind == skParam and sfEffectsDelayed in sym.flags: + return + + var raisesList: seq[PType] = @[getEbase(graph, info)] + + let t = sym.typ + if not isNil(t) and not isNil(t.n) and t.n.len > 0 and t.n[0].len > exceptionEffects: + let effects = t.n[0] + if effects.kind == nkEffectList and effects.len == effectListLen: + let effs = effects[exceptionEffects] + if not isNil(effs): + raisesList = @[] + for eff in items(effs): + if not isNil(eff): + raisesList.add(eff.typ) + + var propagatedExceptionList: seq[PType] = @[] + for re in raisesList: + var exceptionIsPropagated = true + for ce in caughtExceptions: + if isNil(ce) or safeInheritanceDiff(re, ce) <= 0: + exceptionIsPropagated = false + break + if exceptionIsPropagated: + propagatedExceptionList.add(re) + + if propagatedExceptionList.len == 0: + return + + let section = if defaultSection != ideNone: + defaultSection + elif sym.info.exactEquals(info): + ideDef + else: + ideUse + var suggestDef = symToSuggest(graph, sym, isLocal=false, section, + info, 100, PrefixMatch.None, false, 0, true, + endLine = endLine, endCol = endCol) + suggestDef.inlayHintInfo = suggestToSuggestInlayExceptionHintLeft(suggestDef, propagatedExceptionList) + suggestDef.section = ideInlayHints + suggestResult(graph.config, suggestDef) + suggestDef.inlayHintInfo = suggestToSuggestInlayExceptionHintRight(suggestDef, propagatedExceptionList) + suggestResult(graph.config, suggestDef) + const # kinds for ideOutline and ideGlobalSymbols searchableSymKinds = {skField, skEnumField, skIterator, skMethod, skFunc, skProc, skConverter, skTemplate} @@ -885,15 +991,18 @@ proc findDef(n: PNode, line: uint16, col: int16): PNode = let res = findDef(n[i], line, col) if res != nil: return res -proc findByTLineInfo(trackPos: TLineInfo, infoPairs: seq[SymInfoPair]): +proc findByTLineInfo(trackPos: TLineInfo, infoPairs: SuggestFileSymbolDatabase): ref SymInfoPair = - for s in infoPairs: - if s.info.exactEquals trackPos: - new(result) - result[] = s - break + result = nil + if infoPairs.fileIndex == trackPos.fileIndex: + for i in infoPairs.lineInfo.low..infoPairs.lineInfo.high: + let s = infoPairs.getSymInfoPair(i) + if s.info.exactEquals trackPos: + new(result) + result[] = s + break -proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: seq[SymInfoPair]): bool = +proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: SuggestFileSymbolDatabase): bool = proc checkSymbol(sym: PSym, info: TLineInfo): bool = result = (sym.owner.kind in {skModule, skType} or sym.kind in {skProc, skMethod, skIterator, skTemplate, skType}) @@ -907,7 +1016,7 @@ proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: se graph.suggestResult(sym, sym.info, ideOutline, endInfo.line, endInfo.col) return true -proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: seq[SymInfoPair]): bool = +proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: SuggestFileSymbolDatabase): bool = for child in n: if child.kind in {nkIdent, nkSym}: if graph.outlineNode(child, endInfo, infoPairs): @@ -916,7 +1025,7 @@ proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPair if graph.handleIdentOrSym(child, endInfo, infoPairs): return true -proc iterateOutlineNodes(graph: ModuleGraph, n: PNode, infoPairs: seq[SymInfoPair]) = +proc iterateOutlineNodes(graph: ModuleGraph, n: PNode, infoPairs: SuggestFileSymbolDatabase) = var matched = true if n.kind == nkIdent: let symData = findByTLineInfo(n.info, infoPairs) @@ -961,10 +1070,6 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, var fileIndex: FileIndex if not (cmd in {ideRecompile, ideGlobalSymbols}): - if not fileInfoKnown(conf, file): - myLog fmt "{file} is unknown, returning no results" - return - fileIndex = fileInfoIdx(conf, file) msgs.setDirtyFile( conf, @@ -984,16 +1089,15 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, graph.unmarkAllDirty() # these commands require partially compiled project - elif cmd in {ideSug, ideOutline, ideHighlight, ideDef, ideChkFile, ideType, ideDeclaration, ideExpand} and - (graph.needsCompilation(fileIndex) or cmd == ideSug): + elif cmd in {ideSug, ideCon, ideOutline, ideHighlight, ideDef, ideChkFile, ideType, ideDeclaration, ideExpand} and + (graph.needsCompilation(fileIndex) or cmd in {ideSug, ideCon}): # for ideSug use v2 implementation - if cmd == ideSug: + if cmd in {ideSug, ideCon}: conf.m.trackPos = newLineInfo(fileIndex, line, col) conf.m.trackPosAttached = false else: conf.m.trackPos = default(TLineInfo) - - graph.recompilePartially(fileIndex) + graph.recompilePartially(fileIndex) case cmd of ideDef: @@ -1021,7 +1125,11 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, of ideHighlight: let sym = graph.findSymData(file, line, col) if not sym.isNil: - let usages = graph.fileSymbols(fileIndex).filterIt(it.sym == sym.sym) + let fs = graph.fileSymbols(fileIndex) + var usages: seq[SymInfoPair] = @[] + for i in fs.lineInfo.low..fs.lineInfo.high: + if fs.sym[i] == sym.sym: + usages.add(fs.getSymInfoPair(i)) myLog fmt "Found {usages.len} usages in {file.string}" for s in usages: graph.suggestResult(s.sym, s.info) @@ -1029,10 +1137,13 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, graph.recompileFullProject() of ideChanged: graph.markDirtyIfNeeded(file.string, fileIndex) - of ideSug: - # ideSug performs partial build of the file, thus mark it dirty for the + of ideSug, ideCon: + # ideSug/ideCon performs partial build of the file, thus mark it dirty for the # future calls. graph.markDirtyIfNeeded(file.string, fileIndex) + graph.recompilePartially(fileIndex) + let m = graph.getModule fileIndex + incl m.flags, sfDirty of ideOutline: let n = parseFile(fileIndex, graph.cache, graph.config) graph.iterateOutlineNodes(n, graph.fileSymbols(fileIndex).deduplicateSymInfoPair) @@ -1087,9 +1198,10 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, # find first mention of the symbol in the file containing the definition. # It is either the definition or the declaration. var first: SymInfoPair - for symbol in graph.fileSymbols(s.sym.info.fileIndex).deduplicateSymInfoPair: - if s.sym.symbolEqual(symbol.sym): - first = symbol + let db = graph.fileSymbols(s.sym.info.fileIndex).deduplicateSymInfoPair + for i in db.lineInfo.low..db.lineInfo.high: + if s.sym.symbolEqual(db.sym[i]): + first = db.getSymInfoPair(i) break if s.info.exactEquals(first.info): @@ -1140,10 +1252,31 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, i += parseInt(tag, endLine, i) i += skipWhile(tag, seps, i) i += parseInt(tag, endCol, i) + i += skipWhile(tag, seps, i) + var typeHints = true + var exceptionHints = false + while i <= tag.high: + var token: string + i += parseUntil(tag, token, seps, i) + i += skipWhile(tag, seps, i) + case token: + of "+typeHints": + typeHints = true + of "-typeHints": + typeHints = false + of "+exceptionHints": + exceptionHints = true + of "-exceptionHints": + exceptionHints = false + else: + myLog fmt "Discarding unknown inlay hint parameter {token}" + let s = graph.findSymDataInRange(file, line, col, endLine, endCol) for q in s: - if q.sym.kind in {skLet, skVar, skForVar, skConst} and q.isDecl and not q.sym.hasUserSpecifiedType: - graph.suggestInlayHintResult(q.sym, q.info, ideInlayHints) + if typeHints and q.sym.kind in {skLet, skVar, skForVar, skConst} and q.isDecl and not q.sym.hasUserSpecifiedType: + graph.suggestInlayHintResultType(q.sym, q.info, ideInlayHints) + if exceptionHints and q.sym.kind in {skProc, skFunc, skMethod, skVar, skLet, skParam} and not q.isDecl: + graph.suggestInlayHintResultException(q.sym, q.info, ideInlayHints, caughtExceptions = q.caughtExceptions, caughtExceptionsSet = q.caughtExceptionsSet) else: myLog fmt "Discarding {cmd}" @@ -1212,13 +1345,7 @@ else: conf.writelnHook = proc (msg: string) = discard # Find Nim's prefix dir. if nimPath == "": - let binaryPath = findExe("nim") - if binaryPath == "": - raise newException(IOError, - "Cannot find Nim standard library: Nim compiler not in PATH") - conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir() - if not dirExists(conf.prefixDir / RelativeDir"lib"): - conf.prefixDir = AbsoluteDir"" + conf.prefixDir = conf.getPrefixDir() else: conf.prefixDir = AbsoluteDir nimPath diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim index 060335959..9b9488348 100644 --- a/nimsuggest/tester.nim +++ b/nimsuggest/tester.nim @@ -279,7 +279,13 @@ proc runEpcTest(filename: string): int = os.sleep(50) inc i let a = outp.readAll().strip() - let port = parseInt(a) + var port: int + try: + port = parseInt(a) + except ValueError: + echo "Error parsing port number: " & a + echo outp.readAll() + quit 1 socket.connect("localhost", Port(port)) for req, resp in items(s.script): diff --git a/nimsuggest/tests/tarrowcrash.nim b/nimsuggest/tests/tarrowcrash.nim new file mode 100644 index 000000000..a303e88f5 --- /dev/null +++ b/nimsuggest/tests/tarrowcrash.nim @@ -0,0 +1,20 @@ +# issue #24179 + +import sugar + +type + Parser[T] = object + +proc eatWhile[T](p: Parser[T], predicate: T -> bool): seq[T] = + return @[] + +proc skipWs(p: Parser[char]) = + discard p.eatWhile((c: char) => c == 'a') +#[!]# + +discard """ +$nimsuggest --tester $file +>chk $1 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tarrowcrash.nim [Processing]";;0 +chk;;skUnknown;;;;Hint;;$file;;11;;5;;"\'skipWs\' is declared but not used [XDeclaredButNotUsed]";;0 +""" diff --git a/nimsuggest/tests/tchk2.nim b/nimsuggest/tests/tchk2.nim new file mode 100644 index 000000000..f5404368d --- /dev/null +++ b/nimsuggest/tests/tchk2.nim @@ -0,0 +1,35 @@ +# bug #22794 +type O = object + +proc `=destroy`(x: O) = discard +proc `=trace`(x: var O; env: pointer) = discard +proc `=copy`(a: var O; b: O) = discard +proc `=dup`(a: O): O {.nodestroy.} = a +proc `=sink`(a: var O; b: O) = discard + + +# bug #23316 +type SomeSturct = object + +proc `=destroy`(x: SomeSturct) = + echo "SomeSturct destroyed" + +# bug #23867 +type ObjStr = object + s: string + +let ostr = ObjStr() # <-- nimsuggest crashes +discard ostr + +type ObjSeq = object + s: seq[int] + +let oseq = ObjSeq() # <-- nimsuggest crashes +discard oseq + +#[!]# +discard """ +$nimsuggest --tester $file +>chk $1 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk2.nim [Processing]";;0 +""" diff --git a/nimsuggest/tests/tconcept1.nim b/nimsuggest/tests/tconcept1.nim new file mode 100644 index 000000000..d81cd8120 --- /dev/null +++ b/nimsuggest/tests/tconcept1.nim @@ -0,0 +1,12 @@ +SomeNumber = concept + +#[!]# +discard """ +$nimsuggest --tester $file +>chk $1 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tconcept1.nim [Processing]";;0 +chk;;skUnknown;;;;Error;;$file;;1;;13;;"the \'concept\' keyword is only valid in \'type\' sections";;0 +chk;;skUnknown;;;;Error;;$file;;1;;13;;"invalid indentation";;0 +chk;;skUnknown;;;;Error;;$file;;1;;13;;"expression expected, but found \'keyword concept\'";;0 +chk;;skUnknown;;;;Error;;$file;;1;;0;;"\'SomeNumber\' cannot be assigned to";;0 +""" diff --git a/nimsuggest/tests/tconcept2.nim b/nimsuggest/tests/tconcept2.nim new file mode 100644 index 000000000..7f7d147f5 --- /dev/null +++ b/nimsuggest/tests/tconcept2.nim @@ -0,0 +1,15 @@ + SomeNumber = concept a, type T + a.int is int + int.to(T) is type(a) + +#[!]# +discard """ +$nimsuggest --tester $file +>chk $1 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tconcept2.nim [Processing]";;0 +chk;;skUnknown;;;;Error;;$file;;1;;2;;"invalid indentation";;0 +chk;;skUnknown;;;;Error;;$file;;1;;15;;"the \'concept\' keyword is only valid in \'type\' sections";;0 +chk;;skUnknown;;;;Error;;$file;;1;;15;;"invalid indentation";;0 +chk;;skUnknown;;;;Error;;$file;;1;;15;;"expression expected, but found \'keyword concept\'";;0 +chk;;skUnknown;;;;Error;;$file;;1;;2;;"\'SomeNumber\' cannot be assigned to";;0 +""" diff --git a/nimsuggest/tests/tdef1.nim b/nimsuggest/tests/tdef1.nim index 46d7c0b5d..49265bbc1 100644 --- a/nimsuggest/tests/tdef1.nim +++ b/nimsuggest/tests/tdef1.nim @@ -1,11 +1,11 @@ discard """ $nimsuggest --tester $file >def $1 -def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe.};;$file;;11;;5;;"Return hello";;100 +def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;11;;5;;"Return hello";;100 >def $2 -def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe.};;$file;;11;;5;;"Return hello";;100 +def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;11;;5;;"Return hello";;100 >def $2 -def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe.};;$file;;11;;5;;"Return hello";;100 +def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;11;;5;;"Return hello";;100 """ proc hel#[!]#lo(): string = diff --git a/nimsuggest/tests/tdot4.nim b/nimsuggest/tests/tdot4.nim index 8681d045a..f2c6c765f 100644 --- a/nimsuggest/tests/tdot4.nim +++ b/nimsuggest/tests/tdot4.nim @@ -15,7 +15,7 @@ discard """ $nimsuggest --tester --maxresults:2 $file >sug $1 sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;6;;5;;"";;100;;None -sug;;skFunc;;mstrutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe.};;*fixtures/mstrutils.nim;;9;;5;;"this is a test version of strutils.replace, it simply returns `by`";;100;;None +sug;;skFunc;;mstrutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, raises: <inferred> [].};;*fixtures/mstrutils.nim;;9;;5;;"this is a test version of strutils.replace, it simply returns `by`";;100;;None """ # TODO - determine appropriate behaviour for further suggest output and test it diff --git a/nimsuggest/tests/tfatal1.nim b/nimsuggest/tests/tfatal1.nim new file mode 100644 index 000000000..19778f22e --- /dev/null +++ b/nimsuggest/tests/tfatal1.nim @@ -0,0 +1,15 @@ +{.warning: "I'm a warning!".} +{.error: "I'm an error!".} +{.fatal: "I'm a fatal error!".} +{.error: "I'm an error after fatal error!".} + +#[!]# +discard """ +$nimsuggest --tester $file +>chk $1 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tfatal1.nim [Processing]";;0 +chk;;skUnknown;;;;Warning;;$file;;1;;9;;"I\'m a warning! [User]";;0 +chk;;skUnknown;;;;Error;;$file;;2;;7;;"I\'m an error!";;0 +chk;;skUnknown;;;;Error;;$file;;3;;7;;"fatal error: I\'m a fatal error!";;0 +chk;;skUnknown;;;;Error;;$file;;4;;7;;"I\'m an error after fatal error!";;0 +""" diff --git a/nimsuggest/tests/tgeneric_highlight.nim b/nimsuggest/tests/tgeneric_highlight.nim index f351ab705..c7291d08b 100644 --- a/nimsuggest/tests/tgeneric_highlight.nim +++ b/nimsuggest/tests/tgeneric_highlight.nim @@ -7,12 +7,7 @@ $nimsuggest --tester $file >highlight $1 highlight;;skType;;1;;7;;3 highlight;;skProc;;1;;0;;6 -highlight;;skProc;;1;;0;;6 -highlight;;skProc;;1;;0;;6 highlight;;skType;;2;;14;;3 highlight;;skProc;;2;;7;;6 -highlight;;skProc;;2;;7;;6 -highlight;;skProc;;2;;7;;6 -highlight;;skTemplate;;3;;0;;8 highlight;;skType;;3;;9;;3 """ diff --git a/nimsuggest/tests/tic.nim b/nimsuggest/tests/tic.nim new file mode 100644 index 000000000..26e644f83 --- /dev/null +++ b/nimsuggest/tests/tic.nim @@ -0,0 +1,20 @@ +import std/[appdirs, assertions, cmdline, compilesettings, decls, + dirs, editdistance, effecttraits, enumerate, enumutils, envvars, + exitprocs, files, formatfloat, genasts, importutils, + isolation, jsonutils, logic, monotimes, objectdollar, + oserrors, outparams, packedsets, paths, private, setutils, sha1, + socketstreams, stackframes, staticos, strbasics, symlinks, syncio, + sysatomics, sysrand, tasks, tempfiles, time_t, typedthreads, varints, + vmutils, widestrs, with, wordwrap, wrapnils] + +proc test(a: string, b:string) = discard +proc test(a: int) = discard + +test(#[!]# + +discard """ +$nimsuggest --v3 --ic:off --tester $file +>con $1 +con;;skProc;;tic.test;;proc (a: string, b: string);;$file;;10;;5;;"";;100 +con;;skProc;;tic.test;;proc (a: int);;$file;;11;;5;;"";;100 +""" \ No newline at end of file diff --git a/nimsuggest/tests/timport_highlight.nim b/nimsuggest/tests/timport_highlight.nim new file mode 100644 index 000000000..043f87d98 --- /dev/null +++ b/nimsuggest/tests/timport_highlight.nim @@ -0,0 +1,12 @@ +import std/paths +import json as J +import std/[os,streams]#[!]# + +discard """ +$nimsuggest --tester $file +>highlight $1 +highlight;;skModule;;1;;11;;5 +highlight;;skModule;;2;;7;;4 +highlight;;skModule;;3;;12;;2 +highlight;;skModule;;3;;15;;7 +""" diff --git a/nimsuggest/tests/tinclude.nim b/nimsuggest/tests/tinclude.nim index 66726f907..f5cbabf05 100644 --- a/nimsuggest/tests/tinclude.nim +++ b/nimsuggest/tests/tinclude.nim @@ -11,7 +11,7 @@ go() discard """ $nimsuggest --tester $file >def $path/tinclude.nim:7:14 -def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe.};;*fixtures/minclude_include.nim;;3;;5;;"";;100 +def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe, raises: <inferred> [].};;*fixtures/minclude_include.nim;;3;;5;;"";;100 >def $path/fixtures/minclude_include.nim:3:71 def;;skType;;minclude_types.Greet;;Greet;;*fixtures/minclude_types.nim;;4;;2;;"";;100 >def $path/fixtures/minclude_include.nim:3:71 diff --git a/nimsuggest/tests/tsug_pragmas.nim b/nimsuggest/tests/tsug_pragmas.nim index 03a9cba4c..ce9c4e8f8 100644 --- a/nimsuggest/tests/tsug_pragmas.nim +++ b/nimsuggest/tests/tsug_pragmas.nim @@ -18,23 +18,23 @@ $nimsuggest --tester $file >sug $1 sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix -sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe.};;$file;;3;;6;;"";;50;;Prefix +sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix >sug $2 sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix -sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe.};;$file;;3;;6;;"";;50;;Prefix +sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix >sug $3 sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix -sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe.};;$file;;3;;6;;"";;50;;Prefix +sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix >sug $4 sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix -sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe.};;$file;;3;;6;;"";;50;;Prefix +sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix >sug $5 sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix -sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe.};;$file;;3;;6;;"";;50;;Prefix +sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix """ diff --git a/nimsuggest/tests/tsug_template.nim b/nimsuggest/tests/tsug_template.nim index a2558c81c..da494d279 100644 --- a/nimsuggest/tests/tsug_template.nim +++ b/nimsuggest/tests/tsug_template.nim @@ -6,7 +6,7 @@ tmp#[!]# discard """ $nimsuggest --tester $file >sug $1 -sug;;skMacro;;tsug_template.tmpb;;macro (){.noSideEffect, gcsafe.};;$file;;2;;6;;"";;100;;Prefix +sug;;skMacro;;tsug_template.tmpb;;macro (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;2;;6;;"";;100;;Prefix sug;;skConverter;;tsug_template.tmpc;;converter ();;$file;;3;;10;;"";;100;;Prefix sug;;skTemplate;;tsug_template.tmpa;;template ();;$file;;1;;9;;"";;100;;Prefix """ diff --git a/nimsuggest/tests/tuse.nim b/nimsuggest/tests/tuse.nim index deaf81ef2..7c1d1ad0c 100644 --- a/nimsuggest/tests/tuse.nim +++ b/nimsuggest/tests/tuse.nim @@ -14,9 +14,9 @@ proc #[!]#someProc*() = discard """ $nimsuggest --tester $file >use $1 -def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe.};;$file;;9;;5;;"";;100 -use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe.};;$file;;12;;0;;"";;100 +def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;9;;5;;"";;100 +use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;12;;0;;"";;100 >use $2 -def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe.};;$file;;9;;5;;"";;100 -use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe.};;$file;;12;;0;;"";;100 +def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;9;;5;;"";;100 +use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;12;;0;;"";;100 """ diff --git a/nimsuggest/tests/tv3.nim b/nimsuggest/tests/tv3.nim index fd736a1d8..80e51e364 100644 --- a/nimsuggest/tests/tv3.nim +++ b/nimsuggest/tests/tv3.nim @@ -19,7 +19,7 @@ def skField tv3.Foo.bar string $file 5 4 "" 100 >sug $1 sug skField bar string $file 5 4 "" 100 Prefix >globalSymbols test -def skProc tv3.test proc (f: Foo){.gcsafe.} $file 7 5 "" 100 +def skProc tv3.test proc (f: Foo){.gcsafe, raises: <inferred> [].} $file 7 5 "" 100 >globalSymbols Foo def skType tv3.Foo Foo $file 4 2 "" 100 >def $2 diff --git a/nimsuggest/tests/tv3_con.nim b/nimsuggest/tests/tv3_con.nim new file mode 100644 index 000000000..4714c366b --- /dev/null +++ b/nimsuggest/tests/tv3_con.nim @@ -0,0 +1,13 @@ +# tests v3 + +proc test(a: string, b:string) = discard +proc test(a: int) = discard + +test(#[!]# + +discard """ +$nimsuggest --v3 --tester $file +>con $1 +con;;skProc;;tv3_con.test;;proc (a: string, b: string);;$file;;3;;5;;"";;100 +con;;skProc;;tv3_con.test;;proc (a: int);;$file;;4;;5;;"";;100 +""" diff --git a/nimsuggest/tests/tv3_forward_definition.nim b/nimsuggest/tests/tv3_forward_definition.nim index 392e019ff..7a16ea331 100644 --- a/nimsuggest/tests/tv3_forward_definition.nim +++ b/nimsuggest/tests/tv3_forward_definition.nim @@ -7,17 +7,17 @@ let a = de#[!]#mo() discard """ $nimsuggest --v3 --tester $file >use $1 -use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 1 5 "" 100 -def skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 3 5 "" 100 -use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 5 8 "" 100 +use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 1 5 "" 100 +def skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 3 5 "" 100 +use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 5 8 "" 100 >use $2 -use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 1 5 "" 100 -def skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 3 5 "" 100 -use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 5 8 "" 100 +use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 1 5 "" 100 +def skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 3 5 "" 100 +use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 5 8 "" 100 >declaration $1 -declaration skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 3 5 "" 100 +declaration skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 3 5 "" 100 >declaration $2 -declaration skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 1 5 "" 100 +declaration skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 1 5 "" 100 >declaration $3 -declaration skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 1 5 "" 100 +declaration skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 1 5 "" 100 """ diff --git a/nimsuggest/tests/tv3_globalSymbols.nim b/nimsuggest/tests/tv3_globalSymbols.nim index f965a07aa..c3bb9933b 100644 --- a/nimsuggest/tests/tv3_globalSymbols.nim +++ b/nimsuggest/tests/tv3_globalSymbols.nim @@ -7,8 +7,8 @@ proc BBtokenA(): int = 5 discard """ $nimsuggest --v3 --tester $file >globalSymbols token -def skProc tv3_globalSymbols.token proc (): int{.noSideEffect, gcsafe.} $file 4 5 "" 100 -def skProc tv3_globalSymbols.tokenA proc (): int{.noSideEffect, gcsafe.} $file 3 5 "" 100 -def skProc tv3_globalSymbols.Btoken proc (): int{.noSideEffect, gcsafe.} $file 2 5 "" 100 -def skProc tv3_globalSymbols.BBtokenA proc (): int{.noSideEffect, gcsafe.} $file 5 5 "" 100 +def skProc tv3_globalSymbols.token proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 4 5 "" 100 +def skProc tv3_globalSymbols.tokenA proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 3 5 "" 100 +def skProc tv3_globalSymbols.Btoken proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 2 5 "" 100 +def skProc tv3_globalSymbols.BBtokenA proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 5 5 "" 100 """ diff --git a/nimsuggest/tests/tv3_outline.nim b/nimsuggest/tests/tv3_outline.nim index 6370948d9..518620c87 100644 --- a/nimsuggest/tests/tv3_outline.nim +++ b/nimsuggest/tests/tv3_outline.nim @@ -33,13 +33,13 @@ outline skType tv3_outline.FooEnum FooEnum $file 6 2 "" 100 6 31 outline skEnumField tv3_outline.FooEnum.value1 FooEnum $file 6 17 "" 100 6 23 outline skEnumField tv3_outline.FooEnum.value2 FooEnum $file 6 25 "" 100 6 31 outline skType tv3_outline.FooPrivate FooPrivate $file 7 2 "" 100 8 22 -outline skMacro tv3_outline.m macro (arg: untyped): untyped{.noSideEffect, gcsafe.} $file 10 6 "" 100 10 40 +outline skMacro tv3_outline.m macro (arg: untyped): untyped{.noSideEffect, gcsafe, raises: <inferred> [].} $file 10 6 "" 100 10 40 outline skTemplate tv3_outline.t template (arg: untyped): untyped $file 11 9 "" 100 11 43 -outline skProc tv3_outline.p proc (){.noSideEffect, gcsafe.} $file 12 5 "" 100 12 24 -outline skConverter tv3_outline.c converter (s: string): int{.noSideEffect, gcsafe.} $file 14 10 "" 100 14 37 -outline skFunc tv3_outline.f proc (){.noSideEffect, gcsafe.} $file 16 5 "" 100 16 24 +outline skProc tv3_outline.p proc (){.noSideEffect, gcsafe, raises: <inferred> [].} $file 12 5 "" 100 12 24 +outline skConverter tv3_outline.c converter (s: string): int{.noSideEffect, gcsafe, raises: <inferred> [].} $file 14 10 "" 100 14 37 +outline skFunc tv3_outline.f proc (){.noSideEffect, gcsafe, raises: <inferred> [].} $file 16 5 "" 100 16 24 outline skConst tv3_outline.con int literal(2) $file 20 6 "" 100 20 13 -outline skProc tv3_outline.outer proc (){.noSideEffect, gcsafe.} $file 22 5 "" 100 23 24 -outline skProc tv3_outline.outer.inner proc (){.noSideEffect, gcsafe.} $file 23 7 "" 100 23 24 -outline skProc tv3_outline.procWithLocal proc (){.noSideEffect, gcsafe.} $file 25 5 "" 100 26 16 +outline skProc tv3_outline.outer proc (){.noSideEffect, gcsafe, raises: <inferred> [].} $file 22 5 "" 100 23 24 +outline skProc tv3_outline.outer.inner proc (){.noSideEffect, gcsafe, raises: <inferred> [].} $file 23 7 "" 100 23 24 +outline skProc tv3_outline.procWithLocal proc (){.noSideEffect, gcsafe, raises: <inferred> [].} $file 25 5 "" 100 26 16 """ diff --git a/nimsuggest/tests/twithin_macro.nim b/nimsuggest/tests/twithin_macro.nim index d79dae1be..98d58381f 100644 --- a/nimsuggest/tests/twithin_macro.nim +++ b/nimsuggest/tests/twithin_macro.nim @@ -45,7 +45,7 @@ $nimsuggest --tester --maxresults:5 $file >sug $1 sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;None sug;;skField;;name;;string;;$file;;5;;6;;"";;100;;None -sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int;;$file;;8;;9;;"";;100;;None -sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string;;$file;;7;;9;;"";;100;;None +sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int{.raises: <inferred> [].};;$file;;8;;9;;"";;100;;None +sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string{.raises: <inferred> [].};;$file;;7;;9;;"";;100;;None sug;;skMethod;;twithin_macro.vocalize;;proc (self: Rabbit): string;;$file;;23;;9;;"";;100;;None """ diff --git a/nimsuggest/tests/twithin_macro_prefix.nim b/nimsuggest/tests/twithin_macro_prefix.nim index dd3810818..e89c8b942 100644 --- a/nimsuggest/tests/twithin_macro_prefix.nim +++ b/nimsuggest/tests/twithin_macro_prefix.nim @@ -44,5 +44,5 @@ discard """ $nimsuggest --tester $file >sug $1 sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;Prefix -sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int;;$file;;8;;9;;"";;100;;Prefix +sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int{.raises: <inferred> [].};;$file;;8;;9;;"";;100;;Prefix """ diff --git a/readme.md b/readme.md index cbe902c52..da54a8d0d 100644 --- a/readme.md +++ b/readme.md @@ -121,7 +121,6 @@ This project exists thanks to all the people who contribute. ## Contributing [](#backers) [](#sponsors) -[![Setup a bounty via Bountysource][badge-nim-bountysource]][nim-bountysource] [![Donate Bitcoins][badge-nim-bitcoin]][nim-bitcoin] [](https://www.codetriage.com/nim-lang/nim) @@ -165,7 +164,6 @@ You can also help with the development of Nim by making donations. Donations can made using: * [Open Collective](https://opencollective.com/nim) -* [Bountysource][nim-bountysource] * [Bitcoin][nim-bitcoin] If you have any questions feel free to submit a question on the @@ -204,7 +202,7 @@ Nim. You are explicitly permitted to develop commercial applications using Nim. Please read the [copying.txt](copying.txt) file for more details. -Copyright © 2006-2023 Andreas Rumpf, all rights reserved. +Copyright © 2006-2024 Andreas Rumpf, all rights reserved. [nim-site]: https://nim-lang.org [nim-forum]: https://forum.nim-lang.org @@ -219,7 +217,6 @@ Copyright © 2006-2023 Andreas Rumpf, all rights reserved. [nim-matrix]: https://matrix.to/#/#nim-lang:matrix.org [nim-matrix-space]: https://matrix.to/#/#nim:envs.net [nim-telegram]: https://t.me/nim_lang -[nim-bountysource]: https://www.bountysource.com/teams/nim [nim-bitcoin]: https://blockchain.info/address/1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ [nimble-repo]: https://github.com/nim-lang/nimble [nimsuggest-repo]: https://github.com/nim-lang/nimsuggest @@ -231,7 +228,6 @@ Copyright © 2006-2023 Andreas Rumpf, all rights reserved. [badge-nim-forum-gethelp]: https://img.shields.io/badge/Forum-get%20help-4eb899.svg?style=flat-square [badge-nim-twitter]: https://img.shields.io/twitter/follow/nim_lang.svg?style=social [badge-nim-stackoverflow]: https://img.shields.io/badge/stackoverflow-nim_tag-yellow.svg?style=flat-square -[badge-nim-bountysource]: https://img.shields.io/bountysource/team/nim/activity.svg?style=flat-square [badge-nim-bitcoin]: https://img.shields.io/badge/bitcoin-1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ-D69134.svg?style=flat-square [pull-request-instructions]: https://help.github.com/articles/using-pull-requests/ [nim-wiki]: https://github.com/nim-lang/Nim/wiki diff --git a/testament/important_packages.nim b/testament/important_packages.nim index c2b2476e7..efec04b3c 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -58,15 +58,19 @@ pkg "cligen", "nim c --path:. -r cligen.nim" pkg "combparser", "nimble test --mm:orc" pkg "compactdict" pkg "comprehension", "nimble test", "https://github.com/alehander92/comprehension" +pkg "constantine", "nimble make_lib" pkg "cowstrings" pkg "criterion", allowFailure = true # needs testing binary pkg "datamancer" pkg "dashing", "nim c tests/functional.nim" pkg "delaunay" +pkg "dnsclient", allowFailure = true # super fragile pkg "docopt" +pkg "dotenv" # when defined(linux): pkg "drchaos" pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg "elvis" +pkg "faststreams" pkg "fidget" pkg "fragments", "nim c -r fragments/dsl.nim", allowFailure = true # pending https://github.com/nim-lang/packages/issues/2115 pkg "fusion" @@ -79,10 +83,13 @@ pkg "gnuplot", "nim c gnuplot.nim" # pending https://github.com/nim-lang/Nim/issues/16509 pkg "hts", "nim c -o:htss src/hts.nim" pkg "httpauth" +pkg "httputils" pkg "illwill", "nimble examples" pkg "inim" pkg "itertools", "nim doc src/itertools.nim" pkg "iterutils" +pkg "json_rpc" +pkg "json_serialization" pkg "jstin" pkg "karax", "nim c -r tests/tester.nim" pkg "kdtree", "nimble test -d:nimLegacyRandomInitRand", "https://github.com/jblindsay/kdtree" @@ -92,12 +99,12 @@ pkg "macroutils" pkg "manu" pkg "markdown" pkg "measuremancer", "nimble testDeps; nimble -y test" -# when unchained is version 0.3.7 or higher, use `nimble testDeps;` pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" pkg "nake", "nim c nakefile.nim" -pkg "neo", "nim c -d:blas=openblas --mm:refc tests/all.nim", url = "https://github.com/nim-lang/neo" -pkg "nesm", "nimble tests", "https://github.com/nim-lang/NESM", useHead = true +pkg "neo", "nim c -d:blas=openblas --mm:refc tests/all.nim" +pkg "nesm", "nimble tests", "https://github.com/nim-lang/NESM", useHead = true, allowFailure = true + # inactive, tests not adapted to #23096 pkg "netty" pkg "nico", allowFailure = true pkg "nicy", "nim c -r src/nicy.nim" @@ -123,6 +130,7 @@ pkg "nimwc", "nim c nimwc.nim" pkg "nimx", "nim c test/main.nim", allowFailure = true pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" pkg "norm", "testament r tests/common/tmodel.nim" +pkg "normalize" pkg "npeg", "nimble testarc" pkg "numericalnim", "nimble nimCI" pkg "optionsutils" @@ -133,9 +141,10 @@ pkg "pixie" pkg "plotly", "nim c examples/all.nim" pkg "pnm" pkg "polypbren" +pkg "presto" pkg "prologue", "nimble tcompile" -pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim" -pkg "pylib" +# remove fork after https://github.com/PMunch/combparser/pull/7 is merged: +pkg "protobuf", "nimble install -y https://github.com/metagn/combparser@#HEAD; nim c -o:protobuff -r src/protobuf.nim" pkg "rbtree" pkg "react", "nimble example" pkg "regex", "nim c src/regex" @@ -144,6 +153,7 @@ pkg "RollingHash", "nim c -r tests/test_cyclichash.nim" pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim" pkg "sdl1", "nim c -r src/sdl.nim" pkg "sdl2_nim", "nim c -r sdl2/sdl.nim" +pkg "serialization" pkg "sigv4", "nim c --mm:arc -r sigv4.nim", "https://github.com/disruptek/sigv4" pkg "sim" pkg "smtp", "nimble compileExample" @@ -162,18 +172,22 @@ pkg "templates" pkg "tensordsl", "nim c -r --mm:refc tests/tests.nim", "https://krux02@bitbucket.org/krux02/tensordslnim.git" pkg "terminaltables", "nim c src/terminaltables.nim" pkg "termstyle", "nim c -r termstyle.nim" +pkg "testutils" pkg "timeit" pkg "timezones" pkg "tiny_sqlite" pkg "unicodedb", "nim c -d:release -r tests/tests.nim" pkg "unicodeplus", "nim c -d:release -r tests/tests.nim" pkg "union", "nim c -r tests/treadme.nim", url = "https://github.com/alaviss/union" +pkg "unittest2" pkg "unpack" -pkg "weave", "nimble test_gc_arc", useHead = true +pkg "weave", "nimble install -y cligen@#HEAD; nimble test_gc_arc", useHead = true +pkg "websock" pkg "websocket", "nim c websocket.nim" -pkg "winim", "nim c winim.nim" +# pkg "winim", allowFailure = true pkg "with" pkg "ws", allowFailure = true pkg "yaml" pkg "zero_functional", "nim c -r test.nim" pkg "zippy" +pkg "zxcvbn" diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index 66afd62af..a490b17c8 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -66,6 +66,11 @@ template enableRemoteNetworking*: bool = ## a `nim` invocation (possibly via additional intermediate processes). getEnv("NIM_TESTAMENT_REMOTE_NETWORKING") == "1" +template disableSSLTesting*: bool = + ## TODO: workaround for GitHub Action gcc 14 matrix; remove this + ## matrix and the flag after Azure agent supports ubuntu 24.04 + getEnv("NIM_TESTAMENT_DISABLE_SSL") == "1" + template whenRuntimeJs*(bodyIf, bodyElse) = ##[ Behaves as `when defined(js) and not nimvm` (which isn't legal yet). diff --git a/testament/specs.nim b/testament/specs.nim index 5107c9ed7..c3040c1d8 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -513,7 +513,7 @@ proc parseSpec*(filename: string): TSpec = try: msg % ["/", $DirSep, "file", result.filename] except ValueError: - result.parseErrors.addLine "invalid variable interpolation (see 'https://nim-lang.github.io/Nim/testament.html#writing-unitests-output-message-variable-interpolation')" + result.parseErrors.addLine "invalid variable interpolation (see 'https://nim-lang.github.io/Nim/testament.html#writing-unit-tests-output-message-variable-interpolation')" msg result.nimout = result.nimout.varSub result.msg = result.msg.varSub diff --git a/testament/testament.nim b/testament/testament.nim index 5f443ffba..1e892e636 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -10,9 +10,12 @@ ## This program verifies Nim against the testcases. import - strutils, pegs, os, osproc, streams, json, std/exitprocs, - backend, parseopt, specs, htmlgen, browsers, terminal, - algorithm, times, azure, intsets, macros + std/[strutils, pegs, os, osproc, streams, json, + parseopt, browsers, terminal, exitprocs, + algorithm, times, intsets, macros] + +import backend, specs, azure, htmlgen + from std/sugar import dup import compiler/nodejs import lib/stdtest/testutils @@ -193,7 +196,6 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, foundSuccessMsg = true elif not running(p): break - close(p) result.msg = "" result.file = "" result.output = "" @@ -201,8 +203,9 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, result.column = 0 result.err = reNimcCrash - let exitCode = p.peekExitCode - case exitCode + result.exitCode = p.peekExitCode + close p + case result.exitCode of 0: if foundErrorMsg: result.debugInfo.add " compiler exit code was 0 but some Error's were found." @@ -214,7 +217,7 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, if foundSuccessMsg: result.debugInfo.add " compiler exit code was 1 but no `isSuccess` was true." else: - result.debugInfo.add " expected compiler exit code 0 or 1, got $1." % $exitCode + result.debugInfo.add " expected compiler exit code 0 or 1, got $1." % $result.exitCode if err =~ pegLineError: result.file = extractFilename(matches[0]) @@ -521,7 +524,29 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, r.addResult(test, target, extraOptions, expected.output, bufB, reOutputsDiffer) compilerOutputTests(test, target, extraOptions, given, expected, r) of actionReject: + # Make sure its the compiler rejecting and not the system (e.g. segfault) cmpMsgs(r, expected, given, test, target, extraOptions) + if given.exitCode != QuitFailure: + r.addResult(test, target, extraOptions, "exitcode: " & $QuitFailure, + "exitcode: " & $given.exitCode & "\n\nOutput:\n" & + given.nimout, reExitcodesDiffer) + + + +proc changeTarget(extraOptions: string; defaultTarget: TTarget): TTarget = + result = defaultTarget + var p = parseopt.initOptParser(extraOptions) + + while true: + parseopt.next(p) + case p.kind + of cmdEnd: break + of cmdLongOption, cmdShortOption: + if p.key == "b" or p.key == "backend": + result = parseEnum[TTarget](p.val.normalize) + # chooses the last one + else: + discard proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: string) = for target in expected.targets: @@ -535,6 +560,7 @@ proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: s else: let nimcache = nimcacheDir(test.name, test.options, target) var testClone = test + let target = changeTarget(extraOptions, target) testSpecHelper(r, testClone, expected, target, extraOptions, nimcache) proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) = diff --git a/tests/alloc/tmembug.nim b/tests/alloc/tmembug.nim new file mode 100644 index 000000000..63b51ec5d --- /dev/null +++ b/tests/alloc/tmembug.nim @@ -0,0 +1,54 @@ +discard """ + joinable: false +""" + +import std / [atomics, strutils, sequtils] + +type + BackendMessage* = object + field*: seq[int] + +var + chan1: Channel[BackendMessage] + chan2: Channel[BackendMessage] + +chan1.open() +chan2.open() + +proc routeMessage*(msg: BackendMessage) = + discard chan2.trySend(msg) + +var + recv: Thread[void] + stopToken: Atomic[bool] + +proc recvMsg() = + while not stopToken.load(moRelaxed): + let resp = chan1.tryRecv() + if resp.dataAvailable: + routeMessage(resp.msg) + echo "child consumes ", formatSize getOccupiedMem() + +createThread[void](recv, recvMsg) + +const MESSAGE_COUNT = 100 + +proc main() = + let msg: BackendMessage = BackendMessage(field: (0..500).toSeq()) + for j in 0..0: #100: + echo "New iteration" + + for _ in 1..MESSAGE_COUNT: + chan1.send(msg) + echo "After sending" + + var counter = 0 + while counter < MESSAGE_COUNT: + let resp = recv(chan2) + counter.inc + echo "After receiving ", formatSize getOccupiedMem() + + stopToken.store true, moRelaxed + joinThreads(recv) + +main() diff --git a/tests/alloc/tmembug2.nim b/tests/alloc/tmembug2.nim new file mode 100644 index 000000000..01bce6f14 --- /dev/null +++ b/tests/alloc/tmembug2.nim @@ -0,0 +1,58 @@ +discard """ + disabled: "true" +""" + +import std / [atomics, strutils, sequtils, isolation] + +import threading / channels + +type + BackendMessage* = object + field*: seq[int] + +const MESSAGE_COUNT = 100 + +var + chan1 = newChan[BackendMessage](MESSAGE_COUNT*2) + chan2 = newChan[BackendMessage](MESSAGE_COUNT*2) + +#chan1.open() +#chan2.open() + +proc routeMessage*(msg: BackendMessage) = + var m = isolate(msg) + discard chan2.trySend(m) + +var + thr: Thread[void] + stopToken: Atomic[bool] + +proc recvMsg() = + while not stopToken.load(moRelaxed): + var resp: BackendMessage + if chan1.tryRecv(resp): + #if resp.dataAvailable: + routeMessage(resp) + echo "child consumes ", formatSize getOccupiedMem() + +createThread[void](thr, recvMsg) + +proc main() = + let msg: BackendMessage = BackendMessage(field: (0..5).toSeq()) + for j in 0..100: + echo "New iteration" + + for _ in 1..MESSAGE_COUNT: + chan1.send(msg) + echo "After sending" + + var counter = 0 + while counter < MESSAGE_COUNT: + let resp = recv(chan2) + counter.inc + echo "After receiving ", formatSize getOccupiedMem() + + stopToken.store true, moRelaxed + joinThreads(thr) + +main() diff --git a/tests/arc/t22218.nim b/tests/arc/t22218.nim new file mode 100644 index 000000000..7837ed1d0 --- /dev/null +++ b/tests/arc/t22218.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim c --mm:arc $file" + errormsg: "'=copy' is not available for type <Obj>; requires a copy because it's not the last read of 'chan[]'; routine: test" +""" + +# bug #22218 +type Obj[T] = object + v: T + +proc `=copy`[T]( + dest: var Obj[T], + source: Obj[T] + ) {.error: "A channel cannot be copied".} + +from system/ansi_c import c_calloc + +proc test() = + var v: bool = true + var chan = cast[ptr Obj[int]](c_calloc(1, csize_t sizeof(Obj[int]))) + var copy = chan[] + + echo chan.v + echo v + +test() \ No newline at end of file diff --git a/tests/arc/t23247.nim b/tests/arc/t23247.nim new file mode 100644 index 000000000..0fadc50cd --- /dev/null +++ b/tests/arc/t23247.nim @@ -0,0 +1,52 @@ +discard """ + matrix: ";-d:useMalloc" +""" + +# bug #23247 +import std/hashes + +func baseAddr[T](x: openArray[T]): ptr T = + # Return the address of the zero:th element of x or `nil` if x is empty + if x.len == 0: nil else: cast[ptr T](x) + +func makeUncheckedArray[T](p: ptr T): ptr UncheckedArray[T] = + cast[ptr UncheckedArray[T]](p) + +type + LabelKey = object + data: seq[string] + refs: ptr UncheckedArray[string] + refslen: int + + Gauge = ref object + metrics: seq[seq[seq[string]]] + +template values(key: LabelKey): openArray[string] = + if key.refslen > 0: + key.refs.toOpenArray(0, key.refslen - 1) + else: + key.data + +proc hash(key: LabelKey): Hash = + hash(key.values) + +proc view(T: type LabelKey, values: openArray[string]): T = + # TODO some day, we might get view types - until then.. + LabelKey(refs: baseAddr(values).makeUncheckedArray(), refslen: values.len()) + +template withValue2(k: untyped) = + discard hash(k) + +proc setGauge( + collector: Gauge, + labelValues: openArray[string], +) = + let v = LabelKey.view(labelValues) + withValue2(v) + collector.metrics.add @[@labelValues, @labelValues] + discard @labelValues + +var nim_gc_mem_bytes = Gauge() +let threadID = $getThreadId() +setGauge(nim_gc_mem_bytes, @[threadID]) +setGauge(nim_gc_mem_bytes, @[threadID]) \ No newline at end of file diff --git a/tests/arc/tarc_macro.nim b/tests/arc/tarc_macro.nim index ea7d279fd..33ade1da4 100644 --- a/tests/arc/tarc_macro.nim +++ b/tests/arc/tarc_macro.nim @@ -43,4 +43,15 @@ macro bar2() = doAssert &%&%y == 1 # unary operator => need to escape doAssert y &% y == 2 # binary operator => no need to escape doAssert y == 3 -bar2() \ No newline at end of file +bar2() + +block: + macro foo(a: openArray[string] = []): string = + echo a # Segfault doesn't happen if this is removed + newLit "" + + proc bar(a: static[openArray[string]] = []) = + const tmp = foo(a) + + # bug #22909 + doAssert not compiles(bar()) diff --git a/tests/arc/tarc_orc.nim b/tests/arc/tarc_orc.nim index 594950d71..f2c7de2fc 100644 --- a/tests/arc/tarc_orc.nim +++ b/tests/arc/tarc_orc.nim @@ -136,4 +136,51 @@ proc main2 = doAssert a.len == 2 doAssert b.len == 0 -main2() \ No newline at end of file +main2() + +block: + type + TestObj = object of RootObj + name: string + + TestSubObj = object of TestObj + objname: string + + proc `=destroy`(x: TestObj) = + `=destroy`(x.name) + + proc `=destroy`(x: TestSubObj) = + `=destroy`(x.objname) + `=destroy`(TestObj(x)) + + proc testCase() = + let t1 {.used.} = TestSubObj(objname: "tso1", name: "to1") + + proc main() = + testCase() + + main() + +block: # bug #23858 + type Object = object + a: int + b: ref int + var x = 0 + proc fn(): auto {.cdecl.} = + inc x + return Object() + discard fn() + doAssert x == 1 + +block: # bug #24147 + type + O = object of RootObj + val: string + OO = object of O + + proc `=copy`(dest: var O, src: O) = + dest.val = src.val + + let oo = OO(val: "hello world") + var ooCopy : OO + `=copy`(ooCopy, oo) diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 3b60fcd02..b4476ef4f 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -1,5 +1,6 @@ discard """ output: ''' +Destructor for TestTestObj =destroy called 123xyzabc destroyed: false @@ -31,14 +32,39 @@ true copying 123 42 +@["", "d", ""] ok destroying variable: 20 destroying variable: 10 closed ''' - cmd: "nim c --gc:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file" + cmd: "nim c --mm:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file" """ +block: # bug #23627 + type + TestObj = object of RootObj + + Test2 = object of RootObj + foo: TestObj + + TestTestObj = object of RootObj + shit: TestObj + + proc `=destroy`(x: TestTestObj) = + echo "Destructor for TestTestObj" + let test = Test2(foo: TestObj()) + + proc testCaseT() = + let tt1 {.used.} = TestTestObj(shit: TestObj()) + + + proc main() = + testCaseT() + + main() + + # bug #9401 type @@ -46,13 +72,12 @@ type len: int data: ptr UncheckedArray[float] -proc `=destroy`*(m: var MyObj) = +proc `=destroy`*(m: MyObj) = echo "=destroy called" if m.data != nil: deallocShared(m.data) - m.data = nil type MyObjDistinct = distinct MyObj @@ -104,7 +129,7 @@ bbb("123") type Variable = ref object value: int -proc `=destroy`(self: var typeof(Variable()[])) = +proc `=destroy`(self: typeof(Variable()[])) = echo "destroying variable: ",self.value proc newVariable(value: int): Variable = @@ -158,7 +183,7 @@ type B = ref object of A x: int -proc `=destroy`(x: var AObj) = +proc `=destroy`(x: AObj) = close(x.io) echo "closed" @@ -688,3 +713,124 @@ block: # bug #22259 f(wrapper) main() + +block: + block: # bug #22923 + block: + let + a: int = 100 + b: int32 = 200'i32 + + let + x = arrayWith(a, 8) # compiles + y = arrayWith(b, 8) # internal error + z = arrayWith(14, 8) # integer literal also results in a crash + + doAssert x == [100, 100, 100, 100, 100, 100, 100, 100] + doAssert $y == "[200, 200, 200, 200, 200, 200, 200, 200]" + doAssert z == [14, 14, 14, 14, 14, 14, 14, 14] + + block: + let a: string = "nim" + doAssert arrayWith(a, 3) == ["nim", "nim", "nim"] + + let b: char = 'c' + doAssert arrayWith(b, 3) == ['c', 'c', 'c'] + + let c: uint = 300'u + doAssert $arrayWith(c, 3) == "[300, 300, 300]" + +block: # bug #23505 + type + K = object + C = object + value: ptr K + + proc init(T: type C): C = + let tmp = new K + C(value: addr tmp[]) + + discard init(C) + +block: # bug #23524 + type MyType = object + a: int + + proc `=destroy`(typ: MyType) = discard + + var t1 = MyType(a: 100) + var t2 = t1 # Should be a copy? + + proc main() = + t2 = t1 + doAssert t1.a == 100 + doAssert t2.a == 100 + + main() + +block: # bug #23907 + type + Thingy = object + value: int + + ExecProc[C] = proc(value: sink C): int {.nimcall.} + + proc `=copy`(a: var Thingy, b: Thingy) {.error.} + + var thingyDestroyCount = 0 + + proc `=destroy`(thingy: Thingy) = + assert(thingyDestroyCount <= 0) + thingyDestroyCount += 1 + + proc store(value: sink Thingy): int = + result = value.value + + let callback: ExecProc[Thingy] = store + + doAssert callback(Thingy(value: 123)) == 123 + +import std/strutils + +block: # bug #23974 + func g(e: seq[string]): lent seq[string] = result = e + proc k(f: string): seq[string] = f.split("/") + proc n() = + const r = "/d/" + let t = + if true: + k(r).g() + else: + k("/" & r).g() + echo t + + n() + +block: # bug #23973 + func g(e: seq[string]): lent seq[string] = result = e + proc k(f: string): seq[string] = f.split("/") + proc n() = + const r = "/test/empty" # or "/test/empty/1" + let a = k(r).g() + let t = + if true: + k(r).g() + else: + k("/" & r).g() # or raiseAssert "" + doAssert t == a + + n() + +block: # bug #24141 + func reverse(s: var openArray[char]) = + s[0] = 'f' + + func rev(s: var string) = + s.reverse + + proc main = + var abc = "abc" + abc.rev + doAssert abc == "fbc" + + main() diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim index be1d722ed..3499f5c1e 100644 --- a/tests/arc/tcaseobj.nim +++ b/tests/arc/tcaseobj.nim @@ -338,3 +338,29 @@ block: doAssert ff.s == 12 mainSync() + +import std/sequtils + +# bug #23690 +type + SomeObj* = object of RootObj + + Item* = object + case kind*: 0..1 + of 0: + a*: int + b*: SomeObj + of 1: + c*: string + + ItemExt* = object + a*: Item + b*: string + +proc do1(x: int): seq[(string, Item)] = + result = @[("zero", Item(kind: 1, c: "first"))] + +proc do2(x: int, e: ItemExt): seq[(string, ItemExt)] = + do1(x).map(proc(v: (string, Item)): auto = (v[0], ItemExt(a: v[1], b: e.b))) + +doAssert $do2(0, ItemExt(a: Item(kind: 1, c: "second"), b: "third")) == """@[("zero", (a: (kind: 1, c: "first"), b: "third"))]""" diff --git a/tests/arc/titeration_doesnt_copy.nim b/tests/arc/titeration_doesnt_copy.nim index e1cdb6166..e510a6eff 100644 --- a/tests/arc/titeration_doesnt_copy.nim +++ b/tests/arc/titeration_doesnt_copy.nim @@ -54,3 +54,14 @@ proc toBinString*(data: openArray[uint8], col: int): string = doAssert @[0b0000_1111'u8, 0b1010_1010].toBinString(8) == "0000111110101010" doAssert @[0b1000_0000'u8, 0b0000_0000].toBinString(1) == "10" + +block: # bug #23982 + iterator `..`(a, b: ptr int16): ptr int16 = discard + var a: seq[int16] #; let p = a[0].addr + var b: seq[ptr int16] + + try: + for x in a[0].addr .. b[1]: # `p .. b[1]` works + discard + except IndexDefect: + discard diff --git a/tests/arc/topenarray.nim b/tests/arc/topenarray.nim index 67c512e4f..ba91666ba 100644 --- a/tests/arc/topenarray.nim +++ b/tests/arc/topenarray.nim @@ -68,3 +68,19 @@ block: doAssert foo(noBugConst) == expected let noBugSeq = @["0", "c", "a"] doAssert foo(noBugSeq) == expected + +block: # bug #20865 + var p: pointer + var x: array[0, int] + # echo toOpenArray(x, 0, 1)[0] # Raises IndexDefect + doAssertRaises(IndexDefect): + echo toOpenArray(cast[ptr array[0, int]](p)[], 0, 1)[0] # Does not raise IndexDefect + +block: # bug #20987 + var v: array[1, byte] + + var p = cast[ptr array[0, byte]](addr v) + + doAssertRaises(IndexDefect): + echo toOpenArray(p[], 1, 2) + diff --git a/tests/arc/tstringliteral.nim b/tests/arc/tstringliteral.nim new file mode 100644 index 000000000..c5fac22d8 --- /dev/null +++ b/tests/arc/tstringliteral.nim @@ -0,0 +1,17 @@ +discard """ + matrix: "--mm:arc; --mm:orc" +""" + +block: # issue #24080 + var a = (s: "a") + var b = "a" + a.s.setLen 0 + b = a.s + doAssert b == "" + +block: # issue #24080, longer string + var a = (s: "abc") + var b = "abc" + a.s.setLen 2 + b = a.s + doAssert b == "ab" diff --git a/tests/array/tlargeindex.nim b/tests/array/tlargeindex.nim new file mode 100644 index 000000000..61bcbd61d --- /dev/null +++ b/tests/array/tlargeindex.nim @@ -0,0 +1,18 @@ +discard """ + cmd: "nim check --hints:off $file" +""" + +# issue #17163 +var e: array[int32, byte] #[tt.Error + ^ index type 'int32' for array is too large]# +var f: array[uint32, byte] #[tt.Error + ^ index type 'uint32' for array is too large]# +var g: array[int64, byte] #[tt.Error + ^ index type 'int64' for array is too large]# +var h: array[uint64, byte] #[tt.Error + ^ index type 'uint64' for array is too large]# + +# crash in issue #23204 +proc y[N](): array[N, int] = default(array[N, int]) #[tt.Error + ^ index type 'int' for array is too large]# +discard y[int]() diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim index 77d03f8f6..f53767408 100644 --- a/tests/async/tioselectors.nim +++ b/tests/async/tioselectors.nim @@ -58,7 +58,11 @@ when not defined(windows): registerHandle(selector, client_socket, {Event.Write}, 0) freeAddrInfo(aiList) - discard selector.select(100) + + # make sure both sockets are selected + var nevs = 0 + while nevs < 2: + nevs += selector.select(100).len var sockAddress: SockAddr var addrLen = sizeof(sockAddress).Socklen @@ -427,6 +431,20 @@ when not defined(windows): doAssert(res[0].fd == dirfd and {Event.Vnode, Event.VnodeDelete} <= res[0].events) + proc pipe_test(): bool = + # closing the read end of a pipe will result in it automatically + # being removed from the kqueue; make sure no exception is raised + var s = newSelector[int]() + var fds: array[2, cint] + discard pipe(fds) + s.registerHandle(fds[1], {Write}, 0) + discard close(fds[0]) + let res = s.select(-1) + doAssert(res.len == 1) + s.unregister(fds[1]) + discard close(fds[1]) + return true + when hasThreadSupport: var counter = 0 @@ -468,6 +486,7 @@ when not defined(windows): when defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): processTest("File notification test...", vnode_test()) + processTest("Pipe test...", pipe_test()) echo("All tests passed!") else: import nativesockets, winlean, os, osproc diff --git a/tests/c/temit.nim b/tests/c/temit.nim index ee7455d4c..1943c94ea 100644 --- a/tests/c/temit.nim +++ b/tests/c/temit.nim @@ -4,6 +4,7 @@ discard """ # Test the new ``emit`` pragma: {.emit: """ +#include <stdio.h> static int cvariable = 420; """.} diff --git a/tests/casestmt/tcase_issues.nim b/tests/casestmt/tcase_issues.nim new file mode 100644 index 000000000..20a79df2c --- /dev/null +++ b/tests/casestmt/tcase_issues.nim @@ -0,0 +1,7 @@ +discard """ + targets: "c js" +""" + +block: # bug #24031 + case 0 + else: discard \ No newline at end of file diff --git a/tests/casestmt/tcasestmt.nim b/tests/casestmt/tcasestmt.nim index 3a4907494..66de4183d 100644 --- a/tests/casestmt/tcasestmt.nim +++ b/tests/casestmt/tcasestmt.nim @@ -41,6 +41,11 @@ block t8333: case 0 of 'a': echo 0 else: echo 1 +block: # issue #11422 + var c: int = 5 + case c + of 'a' .. 'c': discard + else: discard block emptyset_when: @@ -298,3 +303,17 @@ proc main(a: uint64) = static: main(10) main(10) + +block: + # Just needs to compile + proc bar(): int {.discardable.} = discard + + proc foo() {.noreturn.} = discard + + case "*" + of "*": + bar() + else: + # Make sure this noreturn doesn't + # cause the discardable to not discard + foo() diff --git a/tests/casestmt/trangeexhaustiveness.nim b/tests/casestmt/trangeexhaustiveness.nim new file mode 100644 index 000000000..2b7f3558e --- /dev/null +++ b/tests/casestmt/trangeexhaustiveness.nim @@ -0,0 +1,7 @@ +block: # issue #22661 + template foo(a: typed) = + a + + foo: + case false + of false..true: discard diff --git a/tests/ccgbugs/t10964.nim b/tests/ccgbugs/t10964.nim index a331b16cd..c19db6997 100644 --- a/tests/ccgbugs/t10964.nim +++ b/tests/ccgbugs/t10964.nim @@ -3,4 +3,5 @@ func test*(input: var openArray[int32], start: int = 0, fin: int = input.len - 1 var someSeq = @[1'i32] -test(someSeq) \ No newline at end of file +test(someSeq) +# bug with gcc 14 \ No newline at end of file diff --git a/tests/ccgbugs/t13062.nim b/tests/ccgbugs/t13062.nim index a8d2c1fec..cfda1da7c 100644 --- a/tests/ccgbugs/t13062.nim +++ b/tests/ccgbugs/t13062.nim @@ -24,7 +24,10 @@ type fulfilled: Atomic[bool] var x: Pledge -when defined(gcRefc): +when defined(cpp): + # TODO: fixme + discard "it doesn't work for refc/orc because of contrived `Atomic` in cpp" +elif defined(gcRefc): doAssert x.repr == "[p = nil]" -elif not defined(cpp): # fixme # bug #20081 +else: # fixme # bug #20081 doAssert x.repr == "Pledge(p: nil)" diff --git a/tests/ccgbugs/t20141.nim b/tests/ccgbugs/t20141.nim index 499cd21aa..60e130690 100644 --- a/tests/ccgbugs/t20141.nim +++ b/tests/ccgbugs/t20141.nim @@ -16,7 +16,7 @@ template n[T, U](x: U): T = proc k() = var res: A - m(n[B](res)) + m(n[B, A](res)) proc w(mounter: U) = discard @@ -24,4 +24,4 @@ proc mount(proto: U) = discard proc v() = mount k # This is required for failure -w(v) \ No newline at end of file +w(v) diff --git a/tests/ccgbugs/t22462.nim b/tests/ccgbugs/t22462.nim new file mode 100644 index 000000000..9adfbb19b --- /dev/null +++ b/tests/ccgbugs/t22462.nim @@ -0,0 +1,20 @@ +discard """ + action: "run" + output: ''' +1 +1 +1 +''' + matrix: "--mm:refc" + targets: "c cpp" +""" + +type Object = object + someComplexType: seq[int] + index: Natural + +func newObject(): Object = result.index.inc + +for i in 1..3: + let o = newObject() + echo o.index diff --git a/tests/ccgbugs/t23796.nim b/tests/ccgbugs/t23796.nim new file mode 100644 index 000000000..421ec04d8 --- /dev/null +++ b/tests/ccgbugs/t23796.nim @@ -0,0 +1,25 @@ +discard """ + targets: "c cpp" +""" + +# bug #23796 + +{.emit: """ +#ifdef __cplusplus +extern "C" { +#endif + +void fooArr(float data[3]) {} +void fooIntArr(int id, float data[3]) {} + +#ifdef __cplusplus +} +#endif +""".} + +proc fooArr(data: var array[3, cfloat]) {.importc.} +proc fooIntArr(id: cint, data: var array[3, cfloat]) {.importc, nodecl.} + +var arr = [cfloat 1, 2, 3] +fooArr(arr) +fooIntArr(1, arr) diff --git a/tests/ccgbugs/tcgbug.nim b/tests/ccgbugs/tcgbug.nim index 14ed390d4..2eddc6fdd 100644 --- a/tests/ccgbugs/tcgbug.nim +++ b/tests/ccgbugs/tcgbug.nim @@ -4,6 +4,7 @@ success M1 M2 ok ''' +matrix: "--mm:refc;--mm:orc" """ type @@ -123,3 +124,40 @@ proc bug19613 = doAssert x.bid.root.data[0] == 42 bug19613() + +proc foo = # bug #23280 + let foo = @[1,2,3,4,5,6] + doAssert toOpenArray(foo, 0, 5).len == 6 + doAssert toOpenArray(foo, 0, 5).len mod 6 == 0 # this should output 0 + doAssert toOpenArray(foo, 0, 5).max mod 6 == 0 + let L = toOpenArray(foo, 0, 5).len + doAssert L mod 6 == 0 + +foo() + +block: # bug #9940 + {.emit:"""/*TYPESECTION*/ +typedef struct { int base; } S; +""".} + + type S {.importc: "S", completeStruct.} = object + base: cint + proc init(x:ptr S) = + x.base = 1 + + type + Foo = object + a: seq[float] + b: seq[float] + c: seq[float] + d: seq[float] + s: S + + proc newT(): Foo = + var t: Foo + t.s.addr.init + doAssert t.s.base == 1 + t + + var t = newT() + doAssert t.s.base == 1 diff --git a/tests/ccgbugs/tsamename3.nim b/tests/ccgbugs/tsamename3.nim index a69391e5c..ded18e9f8 100644 --- a/tests/ccgbugs/tsamename3.nim +++ b/tests/ccgbugs/tsamename3.nim @@ -109,3 +109,12 @@ block: # make sure `hashType` doesn't recurse infinitely a, b: PFoo c: int var a: PFoo + +block: # issue #22571 + macro foo(x: typed) = + result = x + + block: # or `proc main =` + foo: + type Foo = object + doAssert $Foo() == "()" diff --git a/tests/closure/t8550.nim b/tests/closure/t8550.nim index 153246f08..a07f45cdc 100644 --- a/tests/closure/t8550.nim +++ b/tests/closure/t8550.nim @@ -1,4 +1,5 @@ discard """ + targets: "c js" output: "@[\"42\"]" """ diff --git a/tests/closure/tnested.nim b/tests/closure/tnested.nim index 31963ea86..ec5af9b13 100644 --- a/tests/closure/tnested.nim +++ b/tests/closure/tnested.nim @@ -1,4 +1,5 @@ discard """ +targets: "c js" output: ''' foo88 23 24foo 88 @@ -183,14 +184,32 @@ block tclosure2: import typetraits -proc myDiscard[T](a: T) = discard +block: + proc myDiscard[T](a: T) = discard -proc foo() = - let a = 5 - let f = (proc() = - myDiscard (proc() = echo a) - ) - echo name(typeof(f)) + proc foo() = + let a = 5 + let f = (proc() = + myDiscard (proc() = echo a) + ) + echo name(typeof(f)) -foo() + foo() + +block: + iterator foo: int {.closure.} = + yield 1 + yield 2 + yield 3 + + proc pork = + let call = foo + for i in call(): + discard i + + let call2 = foo + while not finished(call2): + discard call2() + + pork() diff --git a/tests/codegen/titaniummangle.nim b/tests/codegen/titaniummangle.nim new file mode 100644 index 000000000..4b45e59ae --- /dev/null +++ b/tests/codegen/titaniummangle.nim @@ -0,0 +1,193 @@ +discard """ + targets: "c" + matrix: "--debugger:native" + ccodecheck: "'_ZN14titaniummangle8testFuncE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE6stringN14titaniummangle3FooE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3int7varargsI6stringE'" + ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle3BooE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE8typeDescIN14titaniummangle17EnumAnotherSampleEE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI14uncheckedArrayI3intEE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3setIN14titaniummangle10EnumSampleEE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE4procI6string6stringE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3intN10Comparable10ComparableE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3int3int'" + ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle10EnumSampleE'" + ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle17EnumAnotherSampleE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3int3int'" + ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle10EnumSampleE'" + ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle17EnumAnotherSampleE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE5tupleI3int3intE7cstring'" + ccodecheck: "'_ZN14titaniummangle8testFuncE5tupleI5float5floatE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI3intE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrIN14titaniummangle3FooEE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI3ptrI3intEE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3refIN14titaniummangle3FooEE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3varIN14titaniummangle3FooEE5int325int323refIN14titaniummangle3FooEE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE3varI3intE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE9openArrayI6stringE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE5arrayI7range013intE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE9ContainerI3intE'" + ccodecheck: "'_ZN14titaniummangle8testFuncE10Container2I5int325int32E'" + ccodecheck: "'_ZN14titaniummangle8testFuncE9ContainerI10Container2I5int325int32EE'" +""" + +#When debugging this notice that if one check fails, it can be due to any of the above. + +type + Comparable = concept x, y + (x < y) is bool + + Foo = object + a: int32 + b: int32 + + FooTuple = tuple + a: int + b: int + + Container[T] = object + data: T + + Container2[T, T2] = object + data: T + data2: T2 + + Boo = distinct Foo + + Coo = Foo + + Doo = Boo | Foo + + TestProc = proc(a:string): string + +type EnumSample = enum + a, b, c + +type EnumAnotherSample = enum + a, b, c + +proc testFunc(a: set[EnumSample]) = + echo $a + +proc testFunc(a: typedesc) = + echo $a + +proc testFunc(a: ptr Foo) = + echo repr a + +proc testFunc(s: string, a: Coo) = + echo repr a + +proc testFunc(s: int, a: Comparable) = + echo repr a + +proc testFunc(a: TestProc) = + let b = "" + echo repr a("") + +proc testFunc(a: ref Foo) = + echo repr a + +proc testFunc(b: Boo) = + echo repr b + +proc testFunc(a: ptr UncheckedArray[int]) = + echo repr a + +proc testFunc(a: ptr int) = + echo repr a + +proc testFunc(a: ptr ptr int) = + echo repr a + +proc testFunc(e: FooTuple, str: cstring) = + echo e + +proc testFunc(e: (float, float)) = + echo e + +proc testFunc(e: EnumSample) = + echo e + +proc testFunc(e: var int) = + echo e + +proc testFunc(e: var Foo, a, b: int32, refFoo: ref Foo) = + echo e + +proc testFunc(xs: Container[int]) = + let a = 2 + echo xs + +proc testFunc(xs: Container2[int32, int32]) = + let a = 2 + echo xs + +proc testFunc(xs: Container[Container2[int32, int32]]) = + let a = 2 + echo xs + +proc testFunc(xs: seq[int]) = + let a = 2 + echo xs + +proc testFunc(xs: openArray[string]) = + let a = 2 + echo xs + +proc testFunc(xs: array[2, int]) = + let a = 2 + echo xs + +proc testFunc(e: EnumAnotherSample) = + echo e + +proc testFunc(a, b: int) = + echo "hola" + discard + +proc testFunc(a: int, xs: varargs[string]) = + let a = 10 + for x in xs: + echo x + +proc testFunc() = + var a = 2 + var aPtr = a.addr + var foo = Foo() + let refFoo : ref Foo = new(Foo) + let b = Foo().Boo() + let d: Doo = Foo() + testFunc("", Coo()) + testFunc(1, ) + testFunc(b) + testFunc(EnumAnotherSample) + var t = [1, 2] + let uArr = cast[ptr UncheckedArray[int]](t.addr) + testFunc(uArr) + testFunc({}) + testFunc(proc(s:string): string = "test") + testFunc(20, a.int32) + testFunc(20, 2) + testFunc(EnumSample.c) + testFunc(EnumAnotherSample.c) + testFunc((2, 1), "adios") + testFunc((22.1, 1.2)) + testFunc(a.addr) + testFunc(foo.addr) + testFunc(aPtr.addr) + testFunc(refFoo) + testFunc(foo, 2, 1, refFoo) + testFunc(a) + testFunc(@[2, 1, 2]) + testFunc(@["hola"]) + testFunc(2, "hola", "adios") + let arr: array[2, int] = [2, 1] + testFunc(arr) + testFunc(Container[int](data: 10)) + let c2 = Container2[int32, int32](data: 10, data2: 20) + testFunc(c2) + testFunc(Container[Container2[int32, int32]](data: c2)) + + +testFunc() \ No newline at end of file diff --git a/tests/compileoption/texperimental.nim b/tests/compileoption/texperimental.nim new file mode 100644 index 000000000..be637e58a --- /dev/null +++ b/tests/compileoption/texperimental.nim @@ -0,0 +1,20 @@ +static: + doAssert compileOption("experimental", "dotOperators") + doAssert compileOption("experimental", "callOperator") + doAssert compileOption("experimental", "parallel") + doAssert compileOption("experimental", "destructor") + doAssert compileOption("experimental", "notnil") + doAssert compileOption("experimental", "dynamicBindSym") + doAssert compileOption("experimental", "codeReordering") + doAssert compileOption("experimental", "compiletimeFFI") + doAssert compileOption("experimental", "vmopsDanger") + doAssert compileOption("experimental", "strictFuncs") + doAssert compileOption("experimental", "views") + doAssert compileOption("experimental", "strictNotNil") + doAssert compileOption("experimental", "strictEffects") + doAssert compileOption("experimental", "flexibleOptionalParams") + doAssert compileOption("experimental", "strictDefs") + doAssert compileOption("experimental", "strictCaseObjects") + doAssert compileOption("experimental", "inferGenericTypes") + doAssert compileOption("experimental", "openSym") + doAssert compileOption("experimental", "vtables") diff --git a/tests/compileoption/texperimental.nims b/tests/compileoption/texperimental.nims new file mode 100644 index 000000000..c2c357caa --- /dev/null +++ b/tests/compileoption/texperimental.nims @@ -0,0 +1,19 @@ +switch("experimental", "dotOperators") +switch("experimental", "callOperator") +switch("experimental", "parallel") +switch("experimental", "destructor") +switch("experimental", "notnil") +switch("experimental", "dynamicBindSym") +switch("experimental", "codeReordering") +switch("experimental", "compiletimeFFI") +switch("experimental", "vmopsDanger") +switch("experimental", "strictFuncs") +switch("experimental", "views") +switch("experimental", "strictNotNil") +switch("experimental", "strictEffects") +switch("experimental", "flexibleOptionalParams") +switch("experimental", "strictDefs") +switch("experimental", "strictCaseObjects") +switch("experimental", "inferGenericTypes") +switch("experimental", "openSym") +switch("experimental", "vtables") diff --git a/tests/concepts/t976.nim b/tests/concepts/t976.nim new file mode 100644 index 000000000..776d53827 --- /dev/null +++ b/tests/concepts/t976.nim @@ -0,0 +1,57 @@ +discard """ + output: ''' +Printable +''' +joinable: false +""" + +#[ + The converter is a proper example of a confounding variable + Moved to an isolated file +]# + +type + Obj1[T] = object + v: T +converter toObj1[T](t: T): Obj1[T] = + return Obj1[T](v: t) +block t976: + type + int1 = distinct int + int2 = distinct int + int1g = concept x + x is int1 + int2g = concept x + x is int2 + + proc take[T: int1g](value: int1) = + when T is int2: + static: error("killed in take(int1)") + + proc take[T: int2g](vale: int2) = + when T is int1: + static: error("killed in take(int2)") + + var i1: int1 = 1.int1 + var i2: int2 = 2.int2 + + take[int1](i1) + take[int2](i2) + + template reject(e) = + static: assert(not compiles(e)) + + reject take[string](i2) + reject take[int1](i2) + + # bug #6249 + type + Obj2 = ref object + PrintAble = concept x + $x is string + + proc `$`[T](nt: Obj1[T]): string = + when T is PrintAble: result = "Printable" + else: result = "Non Printable" + + echo Obj2() \ No newline at end of file diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim index 83f3085c3..c6d0267c5 100644 --- a/tests/concepts/tconcepts_issues.nim +++ b/tests/concepts/tconcepts_issues.nim @@ -1,7 +1,6 @@ discard """ output: ''' 20.0 USD -Printable true true true @@ -78,55 +77,6 @@ block t3414: let s2 = s1.find(10) - -type - Obj1[T] = object - v: T -converter toObj1[T](t: T): Obj1[T] = - return Obj1[T](v: t) -block t976: - type - int1 = distinct int - int2 = distinct int - int1g = concept x - x is int1 - int2g = concept x - x is int2 - - proc take[T: int1g](value: int1) = - when T is int2: - static: error("killed in take(int1)") - - proc take[T: int2g](vale: int2) = - when T is int1: - static: error("killed in take(int2)") - - var i1: int1 = 1.int1 - var i2: int2 = 2.int2 - - take[int1](i1) - take[int2](i2) - - template reject(e) = - static: assert(not compiles(e)) - - reject take[string](i2) - reject take[int1](i2) - - # bug #6249 - type - Obj2 = ref object - PrintAble = concept x - $x is string - - proc `$`[T](nt: Obj1[T]): string = - when T is PrintAble: result = "Printable" - else: result = "Non Printable" - - echo Obj2() - - - block t1128: type TFooContainer[T] = object @@ -560,7 +510,7 @@ proc depthOf*[V](orderType: typedesc[BreadthOrder], tree: AnyTree[V], root, goal if root == goal: return 0 var order = init[LevelNode[V]](orderType) - order.expand(tree, root, (leaf) => (1, leaf)) + order.expand(tree, root, (leaf) => (1.uint, leaf)) while order.hasNext(): let depthNode: LevelNode[V] = order.popNext() if depthNode.node == goal: @@ -580,3 +530,27 @@ block: # bug #12852 var tree = CappedStringTree(symbols: "^v><", cap: 5) doAssert BreadthOrder.depthOf(tree, "", ">>>") == 3 + +block: #bug #22723 + type + Node = concept n, type T + for i in n.children: + i is T + n.parent is T + + Nd = ref object + parent: Nd + children: seq[Nd] + + proc addChild(parent, child: Node) = + parent.children.add(child) + child.parent = parent + + proc foo = + var + a = Nd() + b = Nd() + a.addChild(b) + doAssert a.children.len == 1 + + foo() diff --git a/tests/concepts/tusertypeclasses2.nim b/tests/concepts/tusertypeclasses2.nim index c9978f6ef..6132bc2d8 100644 --- a/tests/concepts/tusertypeclasses2.nim +++ b/tests/concepts/tusertypeclasses2.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c js" +""" + block: type hasFieldX = concept z @@ -42,3 +46,18 @@ block: foo2(x) foo3(x) foo4(x) + +block: # bug #9550 + block: + type Foo = concept c + for v in c: (v is char) + + func foo(c: Foo) = (for v in c: discard) + foo @['a', 'b' ,'c'] + + block: + type Foo = concept c + for v in c: (v is char) + + func foo(c: Foo) = (for v in c: discard) + foo ['a', 'b' ,'c'] diff --git a/tests/config.nims b/tests/config.nims index 690123c4a..0b2b66d81 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -43,5 +43,7 @@ switch("define", "nimPreviewRangeDefault") switch("define", "nimPreviewNonVarDestructor") switch("warningAserror", "UnnamedBreak") -switch("legacy", "verboseTypeMismatch") +when not defined(testsConciseTypeMismatch): + switch("legacy", "verboseTypeMismatch") switch("experimental", "vtables") +switch("experimental", "openSym") diff --git a/tests/controlflow/tunreachable2.nim b/tests/controlflow/tunreachable2.nim new file mode 100644 index 000000000..a658880f0 --- /dev/null +++ b/tests/controlflow/tunreachable2.nim @@ -0,0 +1,12 @@ +discard """ + matrix: "--warningAsError:UnreachableCode" +""" + +proc test(): bool = + block okay: + if true: break okay + return false + + return true # Line 7 is here + +doAssert test() diff --git a/tests/cpp/23962.h b/tests/cpp/23962.h new file mode 100644 index 000000000..2d8147bfb --- /dev/null +++ b/tests/cpp/23962.h @@ -0,0 +1,17 @@ +#include <iostream> + +struct Foo { + + Foo(int inX): x(inX) { + std::cout << "Ctor Foo(" << x << ")\n"; + } + ~Foo() { + std::cout << "Destory Foo(" << x << ")\n"; + } + + void print() { + std::cout << "Foo.x = " << x << '\n'; + } + + int x; +}; \ No newline at end of file diff --git a/tests/cpp/fam.h b/tests/cpp/fam.h new file mode 100644 index 000000000..ad576425b --- /dev/null +++ b/tests/cpp/fam.h @@ -0,0 +1,4 @@ +struct Test{ + ~Test() { + } +}; diff --git a/tests/cpp/t23306.nim b/tests/cpp/t23306.nim new file mode 100644 index 000000000..ebb4edb8d --- /dev/null +++ b/tests/cpp/t23306.nim @@ -0,0 +1,12 @@ +discard """ +targets: "cpp" +""" + +type K = object + h: iterator(f: K): K + +iterator d(g: K): K {.closure.} = + defer: + discard + +discard K(h: d) \ No newline at end of file diff --git a/tests/cpp/t23434.nim b/tests/cpp/t23434.nim new file mode 100644 index 000000000..04a83227e --- /dev/null +++ b/tests/cpp/t23434.nim @@ -0,0 +1,17 @@ +discard """ +cmd:"nim cpp $file" +errormsg: "type mismatch: got <proc (self: SomeObject){.member, gcsafe.}>" +line: 17 +""" +type SomeObject = object + value: int + +proc printValue(self: SomeObject) {.virtual.} = + echo "The value is ", self.value + +proc callAProc(p: proc(self: SomeObject){.noconv.}) = + let someObj = SomeObject(value: 4) + echo "calling param proc" + p(someObj) + +callAProc(printValue) \ No newline at end of file diff --git a/tests/cpp/t23657.nim b/tests/cpp/t23657.nim new file mode 100644 index 000000000..63deb7fb0 --- /dev/null +++ b/tests/cpp/t23657.nim @@ -0,0 +1,54 @@ +discard """ + targets: "cpp" + cmd: "nim cpp -r $file" + output: ''' +1.0 +1.0 +''' + +""" +{.emit:"""/*TYPESECTION*/ +struct Point { + float x, y, z; + Point(float x, float y, float z): x(x), y(y), z(z) {} + Point() = default; +}; +struct Direction { + float x, y, z; + Direction(float x, float y, float z): x(x), y(y), z(z) {} + Direction() = default; +}; +struct Axis { + Point origin; + Direction direction; + Axis(Point origin, Direction direction): origin(origin), direction(direction) {} + Axis() = default; +}; + +""".} + +type + Point {.importcpp.} = object + x, y, z: float + + Direction {.importcpp.} = object + x, y, z: float + + Axis {.importcpp.} = object + origin: Point + direction: Direction + +proc makeAxis(origin: Point, direction: Direction): Axis {. constructor, importcpp:"Axis(@)".} +proc makePoint(x, y, z: float): Point {. constructor, importcpp:"Point(@)".} +proc makeDirection(x, y, z: float): Direction {. constructor, importcpp:"Direction(@)".} + +var axis1 = makeAxis(Point(x: 1.0, y: 2.0, z: 3.0), Direction(x: 4.0, y: 5.0, z: 6.0)) #Triggers the error (T1) +var axis2Ctor = makeAxis(makePoint(1.0, 2.0, 3.0), makeDirection(4.0, 5.0, 6.0)) #Do not triggers + +proc main() = #Do not triggers as Tx are inside the body + let test = makeAxis(Point(x: 1.0, y: 2.0, z: 3.0), Direction(x: 4.0, y: 5.0, z: 6.0)) + echo test.origin.x + +main() + +echo $axis1.origin.x #Make sures it's init \ No newline at end of file diff --git a/tests/cpp/t23962.nim b/tests/cpp/t23962.nim new file mode 100644 index 000000000..c79d888df --- /dev/null +++ b/tests/cpp/t23962.nim @@ -0,0 +1,32 @@ +discard """ + cmd: "nim cpp $file" + output: ''' +Ctor Foo(-1) +Destory Foo(-1) +Ctor Foo(-1) +Destory Foo(-1) +Ctor Foo(-1) +Destory Foo(-1) +Foo.x = 1 +Foo.x = 2 +Foo.x = -1 +''' +""" + +type + Foo {.importcpp, header: "23962.h".} = object + x: cint + +proc print(f: Foo) {.importcpp.} + +#also tests the right constructor is used +proc makeFoo(x: int32 = -1): Foo {.importcpp:"Foo(#)", constructor.} + +proc test = + var xs = newSeq[Foo](3) + xs[0].x = 1 + xs[1].x = 2 + for x in xs: + x.print + +test() \ No newline at end of file diff --git a/tests/cpp/tcasts.nim b/tests/cpp/tcasts.nim index d968d87db..80527efff 100644 --- a/tests/cpp/tcasts.nim +++ b/tests/cpp/tcasts.nim @@ -1,6 +1,5 @@ discard """ cmd: "nim cpp $file" - output: '''{"vas": "kas", "123": "123"}''' targets: "cpp" """ @@ -18,4 +17,4 @@ import tables var t = initTable[string, string]() discard t.hasKeyOrPut("123", "123") discard t.mgetOrPut("vas", "kas") -echo t \ No newline at end of file +doAssert t.len == 2 diff --git a/tests/cpp/tconstructor.nim b/tests/cpp/tconstructor.nim index ac73b78e6..922ee54fd 100644 --- a/tests/cpp/tconstructor.nim +++ b/tests/cpp/tconstructor.nim @@ -115,4 +115,17 @@ type Foo {.exportc.} = object proc makeFoo(): Foo {.used, constructor, nodecl.} = discard -echo $Foo() \ No newline at end of file +echo $Foo() + +type Boo = object +proc `=copy`(dest: var Boo; src: Boo) = discard + +proc makeBoo(): Boo {.constructor.} = Boo() +proc makeBoo2(): Boo = Boo() + +block: + proc main = + var b = makeBoo() + var b2 = makeBoo2() + + main() \ No newline at end of file diff --git a/tests/cpp/tfam.nim b/tests/cpp/tfam.nim new file mode 100644 index 000000000..6bd89fe24 --- /dev/null +++ b/tests/cpp/tfam.nim @@ -0,0 +1,7 @@ +discard """ + targets: "cpp" +""" +type + Test {.importcpp, header: "fam.h".} = object + +let test = newSeq[Test]() \ No newline at end of file diff --git a/tests/cpp/tmember.nim b/tests/cpp/tmember.nim index 07bd5e0ee..1a5b6fd97 100644 --- a/tests/cpp/tmember.nim +++ b/tests/cpp/tmember.nim @@ -8,6 +8,8 @@ hello foo hello boo hello boo FunctorSupport! +static +static destructing destructing ''' @@ -34,7 +36,7 @@ echo doo == Doo(test: 1) #virtual proc newCpp*[T](): ptr T {.importcpp:"new '*0()".} type - Foo = object of RootObj + Foo {.exportc.} = object of RootObj FooPtr = ptr Foo Boo = object of Foo BooPtr = ptr Boo @@ -62,3 +64,12 @@ proc invoke(f: NimFunctor, n:int) {.member:"operator ()('2 #2)" .} = {.experimental: "callOperator".} proc `()`(f: NimFunctor, n:int) {.importcpp:"#(@)" .} NimFunctor()(1) + +#static +proc staticProc(self: FooPtr) {.member: "static $1()".} = + echo "static" + +proc importedStaticProc() {.importcpp:"Foo::staticProc()".} + +foo.staticProc() +importedStaticProc() diff --git a/tests/cpp/tmember_forward_declaration.nim b/tests/cpp/tmember_forward_declaration.nim new file mode 100644 index 000000000..2f4a79daa --- /dev/null +++ b/tests/cpp/tmember_forward_declaration.nim @@ -0,0 +1,27 @@ +discard """ + targets: "cpp" + cmd: "nim cpp $file" + output: ''' +abc called +def called +abc called +''' +""" + +type Foo = object + +proc abc(this: Foo, x: int): void {.member: "$1('2 #2)".} +proc def(this: Foo, y: int): void {.virtual: "$1('2 #2)".} + +proc abc(this: Foo, x: int): void = + echo "abc called" + if x > 0: + this.def(x - 1) + +proc def(this: Foo, y: int): void = + echo "def called" + this.abc(y) + +var x = Foo() +x.abc(1) + diff --git a/tests/cpp/torc.nim b/tests/cpp/torc.nim index 64c6c0e5f..9f1a41a21 100644 --- a/tests/cpp/torc.nim +++ b/tests/cpp/torc.nim @@ -39,3 +39,37 @@ proc asVector*[T](t: T): EnumVector[T] = # N_NIMCALL(std::vector<> , asvector_106028_3197418230)(SomeEnum t0) discard asVector(SomeEnum.A) + + +block: # bug #10219 + type + Vector[T] {.importcpp: "std::vector", header: "vector".} = object + + proc initVector[T](n: csize_t): Vector[T] + {.importcpp: "std::vector<'*0>(@)", header: "vector".} + + proc unsafeIndex[T](this: var Vector[T], i: csize_t): var T + {.importcpp: "#[#]", header: "vector".} + + proc `[]`[T](this: var Vector[T], i: Natural): var T {.inline, noinit.} = + when compileOption("boundChecks"): + # this.checkIndex i + discard + result = this.unsafeIndex(csize_t(i)) + + var v1 = initVector[int](10) + doAssert v1[0] == 0 + +block: # bug #12703 bug #19588 + type + cstringConstImpl {.importc:"const char*".} = cstring + constChar = distinct cstringConstImpl + + {.emit: """ + const char* foo() { + return "hello"; + } + """.} + proc foo(): constChar {.importcpp.} # change to importcpp for C++ backend + doAssert $(foo().cstring) == "hello" + diff --git a/tests/destructor/t23748.nim b/tests/destructor/t23748.nim new file mode 100644 index 000000000..a3738733e --- /dev/null +++ b/tests/destructor/t23748.nim @@ -0,0 +1,31 @@ +discard """ + matrix: "--gc:refc; --gc:arc" + output: ''' +hello 42 +hello 42 +len = 2 +''' +""" + +# bug #23748 + +type + O = ref object + s: string + cb: seq[proc()] + +proc push1(o: O, i: int) = + let o = o + echo o.s, " ", i + o.cb.add(proc() = echo o.s, " ", i) + +proc push2(o: O, i: int) = + let o = o + echo o.s, " ", i + proc p() = echo o.s, " ", i + o.cb.add(p) + +let o = O(s: "hello", cb: @[]) +o.push1(42) +o.push2(42) +echo "len = ", o.cb.len diff --git a/tests/destructor/t23837.nim b/tests/destructor/t23837.nim new file mode 100644 index 000000000..e219dd6b5 --- /dev/null +++ b/tests/destructor/t23837.nim @@ -0,0 +1,51 @@ +discard """ + output: ''' +Deallocating OwnedString +HelloWorld +''' + matrix: "--cursorinference:on; --cursorinference:off" + target: "c" +""" + +# bug #23837 +{. + emit: [ + """ +#include <stdlib.h> +#include <string.h> +char *allocCString() { + char *result = (char *) malloc(10 + 1); + strcpy(result, "HelloWorld"); + return result; +} + +""" + ] +.} + +proc rawWrapper(): cstring {.importc: "allocCString", cdecl.} +proc free(p: pointer) {.importc: "free", cdecl.} + +# ------------------------- + +type OwnedString = distinct cstring + +proc `=destroy`(s: OwnedString) = + free(cstring s) + echo "Deallocating OwnedString" + +func `$`(s: OwnedString): string {.borrow.} + +proc leakyWrapper(): string = + let ostring = rawWrapper().OwnedString + $ostring + +# ------------------------- + +proc main() = + # destructor not called - definitely lost: 11 bytes in 1 blocks + # doesn't leak with --cursorInference:off + let s = leakyWrapper() + echo s + +main() \ No newline at end of file diff --git a/tests/destructor/tatomicptrs.nim b/tests/destructor/tatomicptrs.nim index 7bd5482b2..82870ac82 100644 --- a/tests/destructor/tatomicptrs.nim +++ b/tests/destructor/tatomicptrs.nim @@ -143,11 +143,13 @@ proc `=sink`*[T](m: var MySeq[T], m2: MySeq[T]) {.inline.} = `=destroy`(m) m.len = m2.len m.data = m2.data + m.refcount = m2.refcount proc len*[T](m: MySeq[T]): int {.inline.} = m.len proc newMySeq*[T](size: int, initial_value: T): MySeq[T] = result.len = size + result.refcount = 1 if size > 0: result.data = cast[ptr UncheckedArray[T]](allocShared(sizeof(T) * size)) diff --git a/tests/destructor/tdistinctseq.nim b/tests/destructor/tdistinctseq.nim new file mode 100644 index 000000000..5a2ac5ead --- /dev/null +++ b/tests/destructor/tdistinctseq.nim @@ -0,0 +1,8 @@ +discard """ + matrix: "-u:nimPreviewNonVarDestructor;" +""" +type DistinctSeq* = distinct seq[int] + +# `=destroy`(cast[ptr DistinctSeq](0)[]) +var x = @[].DistinctSeq +`=destroy`(x) diff --git a/tests/destructor/tgotoexc_leak.nim b/tests/destructor/tgotoexc_leak.nim new file mode 100644 index 000000000..c8a234085 --- /dev/null +++ b/tests/destructor/tgotoexc_leak.nim @@ -0,0 +1,19 @@ +discard """ + output: '''0 +true''' + cmd: "nim c --gc:arc $file" +""" + +# bug #22398 + +for i in 0 ..< 10_000: + try: + try: + raise newException(ValueError, "") + except CatchableError: + discard + raise newException(ValueError, "") # or raise getCurrentException(), just raise works ok + except ValueError: + discard +echo getOccupiedMem() +echo getCurrentException() == nil diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index a583a1704..cdc1eb1c0 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -137,28 +137,29 @@ doAssert seq3[0] == 1.0 var seq4, seq5: MySeqNonCopyable (seq4, i, seq5) = myfunc2(2, 3) -seq4 = block: - var tmp = newMySeq(4, 1.0) - tmp[0] = 3.0 - tmp +proc foo = + seq4 = block: + var tmp = newMySeq(4, 1.0) + tmp[0] = 3.0 + tmp -doAssert seq4[0] == 3.0 + doAssert seq4[0] == 3.0 -import macros -seq4 = - if i > 0: newMySeq(2, 5.0) - elif i < -100: raise newException(ValueError, "Parse Error") - else: newMySeq(2, 3.0) + seq4 = + if i > 0: newMySeq(2, 5.0) + elif i < -100: raise newException(ValueError, "Parse Error") + else: newMySeq(2, 3.0) -seq4 = - case (char) i: - of 'A', {'W'..'Z'}: newMySeq(2, 5.0) - of 'B': quit(-1) - else: - let (x1, x2, x3) = myfunc2(2, 3) - x3 + seq4 = + case (char) i: + of 'A', {'W'..'Z'}: newMySeq(2, 5.0) + of 'B': quit(-1) + else: + let (x1, x2, x3) = myfunc2(2, 3) + x3 +foo() #------------------------------------------------------------ #-- Move into array constructor diff --git a/tests/destructor/tsink.nim b/tests/destructor/tsink.nim new file mode 100644 index 000000000..e8750ad7c --- /dev/null +++ b/tests/destructor/tsink.nim @@ -0,0 +1,70 @@ +discard """ + matrix: "--mm:arc" +""" + +type AnObject = object of RootObj + value*: int + +proc mutate(shit: sink AnObject) = + shit.value = 1 + +proc foo = # bug #23359 + var bar = AnObject(value: 42) + mutate(bar) + doAssert bar.value == 42 + +foo() + +block: # bug #23902 + proc foo(a: sink string): auto = (a, a) + + proc bar(a: sink int): auto = return a + + proc foo(a: sink string) = + var x = (a, a) + +block: # bug #24175 + block: + func mutate(o: sink string): string = + o[1] = '1' + result = o + + static: + let s = "999" + let m = mutate(s) + doAssert s == "999" + doAssert m == "919" + + func foo() = + let s = "999" + let m = mutate(s) + doAssert s == "999" + doAssert m == "919" + + static: + foo() + foo() + + block: + type O = object + a: int + + func mutate(o: sink O): O = + o.a += 1 + o + + static: + let x = O(a: 1) + let y = mutate(x) + doAssert x.a == 1 + doAssert y.a == 2 + + proc foo() = + let x = O(a: 1) + let y = mutate(x) + doAssert x.a == 1 + doAssert y.a == 2 + + static: + foo() + foo() diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim index 4ff2dc9a0..48bdf67dd 100644 --- a/tests/destructor/tv2_cast.nim +++ b/tests/destructor/tv2_cast.nim @@ -10,69 +10,41 @@ destroying O1''' var data :tmpD - :tmpD_1 - :tmpD_2 -data = - :tmpD = `=dup`(cast[string]( - :tmpD_2 = encode(cast[seq[byte]]( - :tmpD_1 = newString(100) - :tmpD_1)) - :tmpD_2)) - :tmpD -`=destroy`(:tmpD_2) -`=destroy_1`(:tmpD_1) -`=destroy_1`(data) +data = cast[string](encode(cast[seq[byte]]( + :tmpD = newString(100) + :tmpD))) +`=destroy`(:tmpD) +`=destroy`(data) -- end of expandArc ------------------------ --expandArc: main1 var s data - :tmpD - :tmpD_1 s = newString(100) -data = - :tmpD = `=dup`(cast[string]( - :tmpD_1 = encode(toOpenArrayByte(s, 0, len(s) - 1)) - :tmpD_1)) - :tmpD -`=destroy`(:tmpD_1) -`=destroy_1`(data) -`=destroy_1`(s) +data = cast[string](encode(toOpenArrayByte(s, 0, len(s) - 1))) +`=destroy`(data) +`=destroy`(s) -- end of expandArc ------------------------ --expandArc: main2 var s data - :tmpD - :tmpD_1 s = newSeq(100) -data = - :tmpD = `=dup`(cast[string]( - :tmpD_1 = encode(s) - :tmpD_1)) - :tmpD -`=destroy`(:tmpD_1) -`=destroy_1`(data) -`=destroy`(s) +data = cast[string](encode(s)) +`=destroy`(data) +`=destroy_1`(s) -- end of expandArc ------------------------ --expandArc: main3 var data :tmpD - :tmpD_1 - :tmpD_2 -data = - :tmpD = `=dup`(cast[string]( - :tmpD_2 = encode do: - :tmpD_1 = newSeq(100) - :tmpD_1 - :tmpD_2)) - :tmpD -`=destroy`(:tmpD_2) -`=destroy`(:tmpD_1) +data = cast[string](encode do: + :tmpD = newSeq(100) + :tmpD) +`=destroy`(:tmpD) `=destroy_1`(data) -- end of expandArc ------------------------ ''' diff --git a/tests/discard/t23677.nim b/tests/discard/t23677.nim new file mode 100644 index 000000000..1ed7386bd --- /dev/null +++ b/tests/discard/t23677.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "expression '0' is of type 'int literal(0)' and has to be used (or discarded); start of expression here: t23677.nim(1, 1)" + line: 10 + column: 3 +""" + +# issue #23677 + +if true: + 0 +else: + raise newException(ValueError, "err") diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim index 69cb9f6a1..84b669ed8 100644 --- a/tests/discard/tdiscardable.nim +++ b/tests/discard/tdiscardable.nim @@ -5,6 +5,7 @@ tdiscardable 1 something defered something defered +hi ''' """ @@ -110,3 +111,65 @@ block: doAssertRaises(ValueError): doAssert foo() == 12 + +block: # issue #10440 + proc x(): int {.discardable.} = discard + try: + x() + finally: + echo "hi" + +import macros + +block: # issue #14665 + macro test(): untyped = + let b = @[1, 2, 3, 4] + + result = nnkStmtList.newTree() + var i = 0 + while i < b.len: + if false: + # this quote do is mandatory, removing it fixes the problem + result.add quote do: + let testtest = 5 + else: + result.add quote do: + let test = 6 + inc i + # removing this continue fixes the problem too + continue + inc i + test() + +block: # bug #23775 + proc retInt(): int {.discardable.} = + 42 + + proc retString(): string {.discardable.} = + "text" + + type + Enum = enum + A, B, C, D + + proc doStuff(msg: Enum) = + case msg: + of A: + retString() + of B: + retInt() + of C: + discard retString() + else: + let _ = retString() + + doStuff(C) + +block: + proc test(): (int, int) {.discardable.} = + discard + + if true: + test() + else: + quit() diff --git a/tests/discard/tfinallyerrmsg.nim b/tests/discard/tfinallyerrmsg.nim new file mode 100644 index 000000000..fbc8140aa --- /dev/null +++ b/tests/discard/tfinallyerrmsg.nim @@ -0,0 +1,19 @@ +discard """ + cmd: "nim check $file" +""" + +block: # issue #19672 + try: + 10 #[tt.Error + ^ expression '10' is of type 'int literal(10)' and has to be used (or discarded); start of expression here: tfinallyerrmsg.nim(5, 1)]# + finally: + echo "Finally block" + +block: # issue #13871 + template t(body: int) = + try: + body + finally: + echo "expression" + t: 2 #[tt.Error + ^ expression '2' is of type 'int literal(2)' and has to be used (or discarded)]# diff --git a/tests/distinct/tcomplexaddressableconv.nim b/tests/distinct/tcomplexaddressableconv.nim new file mode 100644 index 000000000..00e96bfeb --- /dev/null +++ b/tests/distinct/tcomplexaddressableconv.nim @@ -0,0 +1,21 @@ +# issue #22523 + +from std/typetraits import distinctBase + +type + V[p: static int] = distinct int + D[p: static int] = distinct int + T = V[1] + +proc f(y: var T) = discard + +var a: D[0] + +static: + doAssert distinctBase(T) is distinctBase(D[0]) + doAssert distinctBase(T) is int + doAssert distinctBase(D[0]) is int + doAssert T(a) is T + +f(cast[ptr T](addr a)[]) +f(T(a)) diff --git a/tests/effects/thooks.nim b/tests/effects/thooks.nim new file mode 100644 index 000000000..23cc005cd --- /dev/null +++ b/tests/effects/thooks.nim @@ -0,0 +1,16 @@ +discard """ + matrix: "--warningAsError:Effect" +""" + +import std/isolation + +# bug #23129 +type + Thing = object + x: string + +proc send(x: string) = + let wrapper = Thing(x: x) + discard isolate(wrapper) + +send("la") \ No newline at end of file diff --git a/tests/enum/tambiguousoverloads.nim b/tests/enum/tambiguousoverloads.nim index aa75eaa91..12c78c848 100644 --- a/tests/enum/tambiguousoverloads.nim +++ b/tests/enum/tambiguousoverloads.nim @@ -9,7 +9,7 @@ block: # bug #21887 EnumC = enum C doAssert typeof(EnumC(A)) is EnumC #[tt.Error - ^ ambiguous identifier 'A' -- use one of the following: + ^ ambiguous identifier: 'A' -- use one of the following: EnumA.A: EnumA EnumB.A: EnumB]# @@ -21,6 +21,6 @@ block: # issue #22598 red let a = red #[tt.Error - ^ ambiguous identifier 'red' -- use one of the following: + ^ ambiguous identifier: 'red' -- use one of the following: A.red: A B.red: B]# diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim index 8046c6589..a03019c5d 100644 --- a/tests/enum/tenum.nim +++ b/tests/enum/tenum.nim @@ -184,3 +184,84 @@ block: # bug #12589 A = int64.high() doAssert ord(A) == int64.high() + +import std/enumutils +from std/sequtils import toSeq +import std/macros + +block: # unordered enum + block: + type + unordered_enum = enum + a = 1 + b = 0 + + doAssert (ord(a), ord(b)) == (1, 0) + doAssert unordered_enum.toSeq == @[a, b] + + block: + type + unordered_enum = enum + a = 1 + b = 0 + c + + doAssert (ord(a), ord(b), ord(c)) == (1, 0, 2) + + block: + type + unordered_enum = enum + a = 100 + b + c = 50 + d + + doAssert (ord(a), ord(b), ord(c), ord(d)) == (100, 101, 50, 51) + + block: + type + unordered_enum = enum + a = 7 + b = 6 + c = 5 + d + + doAssert (ord(a), ord(b), ord(c), ord(d)) == (7, 6, 5, 8) + doAssert unordered_enum.toSeq == @[a, b, c, d] + + block: + type + unordered_enum = enum + a = 100 + b + c = 500 + d + e + f = 50 + g + h + + doAssert (ord(a), ord(b), ord(c), ord(d), ord(e), ord(f), ord(g), ord(h)) == + (100, 101, 500, 501, 502, 50, 51, 52) + + block: + type + unordered_enum = enum + A + B + C = -1 + D + E + G = -999 + + doAssert (ord(A), ord(B), ord(C), ord(D), ord(E), ord(G)) == + (0, 1, -1, 2, 3, -999) + + block: + type + SomeEnum = enum + seA = 3 + seB = 2 + seC = "foo" + + doAssert (ord(seA), ord(seB), ord(seC)) == (3, 2, 4) diff --git a/tests/enum/tenum_duplicate.nim b/tests/enum/tenum_duplicate.nim new file mode 100644 index 000000000..4bcad7f6f --- /dev/null +++ b/tests/enum/tenum_duplicate.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "duplicate value in enum 'd'" +""" + +type + unordered_enum = enum + a = 1 + b = 0 + c + d = 2 diff --git a/tests/enum/toverloadedname.nim b/tests/enum/toverloadedname.nim new file mode 100644 index 000000000..d11b0fb83 --- /dev/null +++ b/tests/enum/toverloadedname.nim @@ -0,0 +1,7 @@ +block: # issue #23998 + type + Enum {.pure.} = enum + a + Obj = object + a: Enum + proc test(a: Enum) = discard Obj(a: a) diff --git a/tests/enum/tpure_enums_conflict.nim b/tests/enum/tpure_enums_conflict.nim index 3c7528a72..3cf335440 100644 --- a/tests/enum/tpure_enums_conflict.nim +++ b/tests/enum/tpure_enums_conflict.nim @@ -1,6 +1,5 @@ discard """ - errormsg: "ambiguous identifier: 'amb'" - line: 19 + matrix: "-d:testsConciseTypeMismatch" """ # bug #8066 @@ -16,4 +15,13 @@ when true: echo valueA # MyEnum.valueA echo MyEnum.amb # OK. - echo amb # Error: Unclear whether it's MyEnum.amb or OtherEnum.amb + echo amb #[tt.Error + ^ type mismatch +Expression: echo amb + [1] amb: MyEnum | OtherEnum + +Expected one of (first mismatch at [position]): +[1] proc echo(x: varargs[typed, `$$`]) + ambiguous identifier: 'amb' -- use one of the following: + MyEnum.amb: MyEnum + OtherEnum.amb: OtherEnum]# diff --git a/tests/enum/tpure_enums_conflict_legacy.nim b/tests/enum/tpure_enums_conflict_legacy.nim new file mode 100644 index 000000000..e592925bc --- /dev/null +++ b/tests/enum/tpure_enums_conflict_legacy.nim @@ -0,0 +1,25 @@ +# bug #8066 + +when true: + type + MyEnum {.pure.} = enum + valueA, valueB, valueC, valueD, amb + + OtherEnum {.pure.} = enum + valueX, valueY, valueZ, amb + + + echo valueA # MyEnum.valueA + echo MyEnum.amb # OK. + echo amb #[tt.Error + ^ type mismatch: got <MyEnum | OtherEnum> +but expected one of: +proc echo(x: varargs[typed, `$$`]) + first type mismatch at position: 1 + required type for x: varargs[typed] + but expression 'amb' is of type: None + ambiguous identifier: 'amb' -- use one of the following: + MyEnum.amb: MyEnum + OtherEnum.amb: OtherEnum + +expression: echo amb]# diff --git a/tests/enum/ttypenameconflict.nim b/tests/enum/ttypenameconflict.nim new file mode 100644 index 000000000..b13bf00ce --- /dev/null +++ b/tests/enum/ttypenameconflict.nim @@ -0,0 +1,13 @@ +# issue #23689 + +type + MyEnum {.pure.} = enum + A, B, C, D + + B = object + field: int + +let x: MyEnum = B +doAssert $x == "B" +doAssert typeof(x) is MyEnum +doAssert x in {A, B} diff --git a/tests/errmsgs/mambparam1.nim b/tests/errmsgs/mambparam1.nim new file mode 100644 index 000000000..1a5133c3c --- /dev/null +++ b/tests/errmsgs/mambparam1.nim @@ -0,0 +1 @@ +const test* = "foo" diff --git a/tests/errmsgs/mambparam2.nim b/tests/errmsgs/mambparam2.nim new file mode 100644 index 000000000..073e3f8c8 --- /dev/null +++ b/tests/errmsgs/mambparam2.nim @@ -0,0 +1,2 @@ +import mambparam1 +export test diff --git a/tests/errmsgs/mambparam3.nim b/tests/errmsgs/mambparam3.nim new file mode 100644 index 000000000..5469244e2 --- /dev/null +++ b/tests/errmsgs/mambparam3.nim @@ -0,0 +1 @@ +const test* = "bar" diff --git a/tests/errmsgs/t10735.nim b/tests/errmsgs/t10735.nim index f480d35ac..a39cd196e 100644 --- a/tests/errmsgs/t10735.nim +++ b/tests/errmsgs/t10735.nim @@ -2,40 +2,62 @@ discard """ cmd: "nim check $file" errormsg: "illformed AST: case buf[pos]" nimout: ''' -t10735.nim(43, 5) Error: 'let' symbol requires an initialization -t10735.nim(44, 10) Error: undeclared identifier: 'pos' -t10735.nim(44, 10) Error: expression 'pos' has no type (or is ambiguous) -t10735.nim(44, 10) Error: expression 'pos' has no type (or is ambiguous) -t10735.nim(44, 9) Error: type mismatch: got <cstring, > +t10735.nim(65, 5) Error: 'let' symbol requires an initialization +t10735.nim(66, 10) Error: undeclared identifier: 'pos' +t10735.nim(66, 10) Error: expression 'pos' has no type (or is ambiguous) +t10735.nim(66, 10) Error: expression 'pos' has no type (or is ambiguous) +t10735.nim(66, 9) Error: type mismatch: got <cstring, > but expected one of: proc `[]`(s: string; i: BackwardsIndex): char - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: string + but expression 'buf' is of type: cstring proc `[]`(s: var string; i: BackwardsIndex): var char - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: var string + but expression 'buf' is of type: cstring proc `[]`[I: Ordinal; T](a: T; i: I): T first type mismatch at position: 0 proc `[]`[Idx, T; U, V: Ordinal](a: array[Idx, T]; x: HSlice[U, V]): seq[T] - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for a: array[Idx, T] + but expression 'buf' is of type: cstring proc `[]`[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for a: array[Idx, T] + but expression 'buf' is of type: cstring proc `[]`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for a: var array[Idx, T] + but expression 'buf' is of type: cstring proc `[]`[T, U: Ordinal](s: string; x: HSlice[T, U]): string - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: string + but expression 'buf' is of type: cstring proc `[]`[T; U, V: Ordinal](s: openArray[T]; x: HSlice[U, V]): seq[T] - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: openArray[T] + but expression 'buf' is of type: cstring proc `[]`[T](s: openArray[T]; i: BackwardsIndex): T - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: openArray[T] + but expression 'buf' is of type: cstring proc `[]`[T](s: var openArray[T]; i: BackwardsIndex): var T - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: var openArray[T] + but expression 'buf' is of type: cstring template `[]`(a: WideCStringObj; idx: int): Utf16Char - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for a: WideCStringObj + but expression 'buf' is of type: cstring template `[]`(s: string; i: int): char - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: string + but expression 'buf' is of type: cstring -expression: `[]`(buf, pos) -t10735.nim(44, 9) Error: expression '' has no type (or is ambiguous) -t10735.nim(46, 3) Error: illformed AST: case buf[pos] +expression: buf[pos] +t10735.nim(66, 9) Error: expression '' has no type (or is ambiguous) +t10735.nim(68, 3) Error: illformed AST: case buf[pos] ''' joinable: false """ diff --git a/tests/errmsgs/t16654.nim b/tests/errmsgs/t16654.nim index 749707c06..b2b57619b 100644 --- a/tests/errmsgs/t16654.nim +++ b/tests/errmsgs/t16654.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim check $options $file" - errormsg: "type mismatch: got <int> but expected 'float'" + errormsg: "type mismatch: got <int literal(1), proc (r: GenericParam): auto>" """ when true: # bug #16654 diff --git a/tests/errmsgs/t17460.nim b/tests/errmsgs/t17460.nim index e377bc48a..bb8e21198 100644 --- a/tests/errmsgs/t17460.nim +++ b/tests/errmsgs/t17460.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim check $options $file" - errormsg: "wrong number of variables" + errormsg: "tuple expected for tuple unpacking, but got 'array[0..2, int]'" """ iterator xclusters*[T](a: openArray[T]; s: static[int]): array[s, T] {.inline.} = @@ -16,4 +16,4 @@ proc m = for (i, j, k) in xclusters([1, 2, 3, 4, 5], 3): echo i, j, k -m() \ No newline at end of file +m() diff --git a/tests/errmsgs/t22097.nim b/tests/errmsgs/t22097.nim index b50db08a3..bb24ee8d3 100644 --- a/tests/errmsgs/t22097.nim +++ b/tests/errmsgs/t22097.nim @@ -1,9 +1,9 @@ discard """ - errormsg: "for a 'var' type a variable needs to be passed; but 'uint16(x)' is immutable" + errormsg: "type mismatch: got <uint8>" """ proc toUInt16(x: var uint16) = discard var x = uint8(1) -toUInt16 x \ No newline at end of file +toUInt16 x diff --git a/tests/errmsgs/t22284.nim b/tests/errmsgs/t22284.nim new file mode 100644 index 000000000..827155e6b --- /dev/null +++ b/tests/errmsgs/t22284.nim @@ -0,0 +1,25 @@ +discard """ + errormsg: "j(uRef, proc (config: F; sources: auto) {.raises: [].} = discard ) can raise an unlisted exception: Exception" +""" + +import std/macros + +macro h(): untyped = + result = newTree(nnkStmtList) + result.add quote do: + new int + +type F = object + +proc j[SecondarySources]( + uRef: ref SecondarySources, + u: proc (config: F, sources: ref SecondarySources)): F = + u(result, uRef) + +template programMain(body: untyped) = + proc main {.raises: [].} = body # doesn't SIGSEGV without this {.raises: [].} + main() + +programMain: + var uRef = h() + discard j(uRef, u = proc(config: F, sources: auto) {.raises: [].} = discard) \ No newline at end of file diff --git a/tests/errmsgs/t22753.nim b/tests/errmsgs/t22753.nim index af6a871f1..8a504109a 100644 --- a/tests/errmsgs/t22753.nim +++ b/tests/errmsgs/t22753.nim @@ -3,33 +3,50 @@ cmd: "nim check --hints:off $file" errormsg: "type mismatch" nimoutFull: true nimout: ''' -t22753.nim(34, 13) Error: array expects two type parameters -t22753.nim(35, 1) Error: expression 'x' has no type (or is ambiguous) -t22753.nim(35, 1) Error: expression 'x' has no type (or is ambiguous) -t22753.nim(35, 2) Error: type mismatch: got <> +t22753.nim(51, 13) Error: array expects two type parameters +t22753.nim(52, 1) Error: expression 'x' has no type (or is ambiguous) +t22753.nim(52, 1) Error: expression 'x' has no type (or is ambiguous) +t22753.nim(52, 2) Error: type mismatch: got <> but expected one of: proc `[]=`(s: var string; i: BackwardsIndex; x: char) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for i: BackwardsIndex + but expression '0' is of type: int literal(0) proc `[]=`[I: Ordinal; T, S](a: T; i: I; x: sink S) first type mismatch at position: 0 proc `[]=`[Idx, T; U, V: Ordinal](a: var array[Idx, T]; x: HSlice[U, V]; b: openArray[T]) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for x: HSlice[[]=.U, []=.V] + but expression '0' is of type: int literal(0) proc `[]=`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for i: BackwardsIndex + but expression '0' is of type: int literal(0) proc `[]=`[T, U: Ordinal](s: var string; x: HSlice[T, U]; b: string) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for x: HSlice[[]=.T, []=.U] + but expression '0' is of type: int literal(0) proc `[]=`[T; U, V: Ordinal](s: var seq[T]; x: HSlice[U, V]; b: openArray[T]) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for x: HSlice[[]=.U, []=.V] + but expression '0' is of type: int literal(0) proc `[]=`[T](s: var openArray[T]; i: BackwardsIndex; x: T) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for i: BackwardsIndex + but expression '0' is of type: int literal(0) template `[]=`(a: WideCStringObj; idx: int; val: Utf16Char) - first type mismatch at position: 0 + first type mismatch at position: 3 + required type for val: Utf16Char + but expression '9' is of type: int literal(9) template `[]=`(s: string; i: int; val: char) - first type mismatch at position: 0 + first type mismatch at position: 3 + required type for val: char + but expression '9' is of type: int literal(9) -expression: `[]=`(x, 0, 9) +expression: x[0] = 9 ''' """ + var x: array[3] # bug #22753 -x[0] = 9 \ No newline at end of file +x[0] = 9 diff --git a/tests/errmsgs/t22852.nim b/tests/errmsgs/t22852.nim new file mode 100644 index 000000000..7c352a49c --- /dev/null +++ b/tests/errmsgs/t22852.nim @@ -0,0 +1,9 @@ +discard """ + exitcode: 1 + outputsub: ''' +Error: unhandled exception: value out of range: -2 notin 0 .. 9223372036854775807 [RangeDefect] +''' +""" + +# bug #22852 +echo [0][2..^2] diff --git a/tests/errmsgs/t23060.nim b/tests/errmsgs/t23060.nim new file mode 100644 index 000000000..abb79bcc3 --- /dev/null +++ b/tests/errmsgs/t23060.nim @@ -0,0 +1,5 @@ +discard """ + errormsg: "undeclared identifier: '♔♕♖♗♘♙'" +""" + +♔♕♖♗♘♙ = 1 \ No newline at end of file diff --git a/tests/errmsgs/t23419.nim b/tests/errmsgs/t23419.nim new file mode 100644 index 000000000..59a72f081 --- /dev/null +++ b/tests/errmsgs/t23419.nim @@ -0,0 +1,5 @@ +discard """ + errormsg: "invalid type: 'void' in this context: '(array[0..-1, void],)' for var" +""" + +var a: (array[0, void], ) diff --git a/tests/errmsgs/t23435.nim b/tests/errmsgs/t23435.nim new file mode 100644 index 000000000..5e2e4c82a --- /dev/null +++ b/tests/errmsgs/t23435.nim @@ -0,0 +1,12 @@ +discard """ + outputsub: "Error: unhandled exception: value out of range: -15 notin 0 .. 9223372036854775807 [RangeDefect]" + exitcode: "1" +""" + +# bug #23435 +proc foo() = + for _ in @[1, 3, 5]: + discard "abcde"[25..<10] + break + +foo() diff --git a/tests/errmsgs/t23536.nim b/tests/errmsgs/t23536.nim new file mode 100644 index 000000000..610a85bab --- /dev/null +++ b/tests/errmsgs/t23536.nim @@ -0,0 +1,26 @@ +discard """ + matrix: "--stackTrace:on --excessiveStackTrace:off" +""" + +const expected = """ +wrong trace: +t23536.nim(22) t23536 +t23536.nim(17) foo +assertions.nim(41) failedAssertImpl +assertions.nim(36) raiseAssert +fatal.nim(53) sysFatal +""" + + +try: + proc foo = # bug #23536 + doAssert false + + for i in 0 .. 1: + + + foo() +except AssertionDefect: + let e = getCurrentException() + let trace = e.getStackTrace + doAssert "wrong trace:\n" & trace == expected diff --git a/tests/errmsgs/t5167_5.nim b/tests/errmsgs/t5167_5.nim index 6c1269bce..dea7e40b3 100644 --- a/tests/errmsgs/t5167_5.nim +++ b/tests/errmsgs/t5167_5.nim @@ -17,9 +17,9 @@ proc bar(x: proc (x: int)) = let x = t #[tt.Error ^ 't' has unspecified generic parameters]# bar t #[tt.Error - ^ 't' has unspecified generic parameters]# +^ type mismatch: got <template [*missing parameters*]()>]# let y = m #[tt.Error ^ 'm' has unspecified generic parameters]# bar m #[tt.Error - ^ 'm' has unspecified generic parameters]# +^ type mismatch: got <macro [*missing parameters*](): untyped{.noSideEffect, gcsafe.}>]# diff --git a/tests/errmsgs/t8064.nim b/tests/errmsgs/t8064.nim index 6be83fd1a..c35a3abcc 100644 --- a/tests/errmsgs/t8064.nim +++ b/tests/errmsgs/t8064.nim @@ -5,5 +5,5 @@ values discard """ # either this or "expression has no type": - errormsg: "ambiguous identifier 'values' -- use one of the following:" + errormsg: "ambiguous identifier: 'values' -- use one of the following:" """ diff --git a/tests/errmsgs/tambparam.nim b/tests/errmsgs/tambparam.nim new file mode 100644 index 000000000..5b56a3fce --- /dev/null +++ b/tests/errmsgs/tambparam.nim @@ -0,0 +1,16 @@ +discard """ + matrix: "-d:testsConciseTypeMismatch" +""" + +import mambparam2, mambparam3 + +echo test #[tt.Error +^ type mismatch +Expression: echo test + [1] test: string | string + +Expected one of (first mismatch at [position]): +[1] proc echo(x: varargs[typed, `$$`]) + ambiguous identifier: 'test' -- use one of the following: + mambparam1.test: string + mambparam3.test: string]# diff --git a/tests/errmsgs/tambparam_legacy.nim b/tests/errmsgs/tambparam_legacy.nim new file mode 100644 index 000000000..bd99a3aac --- /dev/null +++ b/tests/errmsgs/tambparam_legacy.nim @@ -0,0 +1,14 @@ +import mambparam2, mambparam3 + +echo test #[tt.Error +^ type mismatch: got <string | string> +but expected one of: +proc echo(x: varargs[typed, `$$`]) + first type mismatch at position: 1 + required type for x: varargs[typed] + but expression 'test' is of type: None + ambiguous identifier: 'test' -- use one of the following: + mambparam1.test: string + mambparam3.test: string + +expression: echo test]# diff --git a/tests/errmsgs/tconcisetypemismatch.nim b/tests/errmsgs/tconcisetypemismatch.nim index c2896604f..3093cc24e 100644 --- a/tests/errmsgs/tconcisetypemismatch.nim +++ b/tests/errmsgs/tconcisetypemismatch.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim c --hints:off --skipParentCfg $file" + cmd: "nim c --hints:off -d:testsConciseTypeMismatch $file" errormsg: "type mismatch" nimout: ''' tconcisetypemismatch.nim(23, 47) Error: type mismatch diff --git a/tests/errmsgs/tconcisetypemismatch.nims b/tests/errmsgs/tconcisetypemismatch.nims deleted file mode 100644 index e9dce8147..000000000 --- a/tests/errmsgs/tconcisetypemismatch.nims +++ /dev/null @@ -1,21 +0,0 @@ -switch("path", "$lib/../testament/lib") - # so we can `import stdtest/foo` inside tests - # Using $lib/../ instead of $nim/ so you can use a different nim to run tests - # during local testing, e.g. nim --lib:lib. - -## prevent common user config settings to interfere with testament expectations -## Indifidual tests can override this if needed to test for these options. -switch("colors", "off") - -switch("excessiveStackTrace", "off") - -when (NimMajor, NimMinor, NimPatch) >= (1,5,1): - # to make it easier to test against older nim versions, (best effort only) - switch("filenames", "legacyRelProj") - switch("spellSuggest", "0") - -# for std/unittest -switch("define", "nimUnittestOutputLevel:PRINT_FAILURES") -switch("define", "nimUnittestColor:off") - -hint("Processing", off) diff --git a/tests/errmsgs/tgenericmismatchsegfault.nim b/tests/errmsgs/tgenericmismatchsegfault.nim new file mode 100644 index 000000000..dbb783cb3 --- /dev/null +++ b/tests/errmsgs/tgenericmismatchsegfault.nim @@ -0,0 +1,13 @@ +discard """ + matrix: "-d:testsConciseTypeMismatch" +""" + +template v[T](c: SomeOrdinal): T = T(c) +discard v[int, char]('A') #[tt.Error + ^ type mismatch +Expression: v[int, char]('A') + [1] 'A': char + +Expected one of (first mismatch at [position]): +[2] template v[T](c: SomeOrdinal): T + generic parameter mismatch, expected SomeOrdinal but got 'char' of type: char]# diff --git a/tests/errmsgs/tgenericmismatchsegfault_legacy.nim b/tests/errmsgs/tgenericmismatchsegfault_legacy.nim new file mode 100644 index 000000000..1532611b9 --- /dev/null +++ b/tests/errmsgs/tgenericmismatchsegfault_legacy.nim @@ -0,0 +1,10 @@ +template v[T](c: SomeOrdinal): T = T(c) +discard v[int, char]('A') #[tt.Error + ^ type mismatch: got <char> +but expected one of: +template v[T](c: SomeOrdinal): T + first type mismatch at position: 2 in generic parameters + required type for SomeOrdinal: SomeOrdinal + but expression 'char' is of type: char + +expression: v[int, char]('A')]# diff --git a/tests/errmsgs/tinconsistentgensym.nim b/tests/errmsgs/tinconsistentgensym.nim new file mode 100644 index 000000000..8e4c85106 --- /dev/null +++ b/tests/errmsgs/tinconsistentgensym.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim check --hints:off $file" +""" + +block: + template foo = + when false: + let x = 123 + else: + template x: untyped {.inject.} = 456 + echo x #[tt.Error + ^ undeclared identifier: 'x`gensym0'; if declared in a template, this identifier may be inconsistently marked inject or gensym]# + foo() + +block: + template foo(y: static bool) = + block: + when y: + let x {.gensym.} = 123 + else: + let x {.inject.} = 456 + echo x #[tt.Error + ^ undeclared identifier: 'x']# + foo(false) + foo(true) diff --git a/tests/errmsgs/tmetaobjectfields.nim b/tests/errmsgs/tmetaobjectfields.nim new file mode 100644 index 000000000..47d3acf18 --- /dev/null +++ b/tests/errmsgs/tmetaobjectfields.nim @@ -0,0 +1,75 @@ +discard """ + cmd: "nim check --hints:off $file" + action: "reject" + nimout: ''' +tmetaobjectfields.nim(26, 5) Error: 'array' is not a concrete type +tmetaobjectfields.nim(30, 5) Error: 'seq' is not a concrete type +tmetaobjectfields.nim(34, 5) Error: 'set' is not a concrete type +tmetaobjectfields.nim(37, 3) Error: 'sink' is not a concrete type +tmetaobjectfields.nim(39, 3) Error: 'lent' is not a concrete type +tmetaobjectfields.nim(56, 16) Error: 'seq' is not a concrete type +tmetaobjectfields.nim(60, 5) Error: 'ptr' is not a concrete type +tmetaobjectfields.nim(61, 5) Error: 'ref' is not a concrete type +tmetaobjectfields.nim(62, 5) Error: 'auto' is not a concrete type +tmetaobjectfields.nim(63, 5) Error: 'UncheckedArray' is not a concrete type +tmetaobjectfields.nim(68, 5) Error: 'object' is not a concrete type +tmetaobjectfields.nim(72, 5) Error: 'Type3011:ObjectType' is not a concrete type +''' +""" + + +# bug #6982 +# bug #19546 +# bug #23531 +type + ExampleObj1 = object + arr: array + +type + ExampleObj2 = object + arr: seq + +type + ExampleObj3 = object + arr: set + +type A = object + b: sink + # a: openarray + c: lent + +type PropertyKind = enum + tInt, + tFloat, + tBool, + tString, + tArray + +type + Property = ref PropertyObj + PropertyObj = object + case kind: PropertyKind + of tInt: intValue: int + of tFloat: floatValue: float + of tBool: boolValue: bool + of tString: stringValue: string + of tArray: arrayValue: seq + +type + RegressionTest = object + a: ptr + b: ref + c: auto + d: UncheckedArray + +# bug #3011 +type + Type3011 = ref object + context: ref object + +type + Value3011 = ref object + typ: Type3011 + +proc x3011(): Value3011 = + nil diff --git a/tests/errmsgs/tsubscriptmismatch.nim b/tests/errmsgs/tsubscriptmismatch.nim new file mode 100644 index 000000000..a2b297b68 --- /dev/null +++ b/tests/errmsgs/tsubscriptmismatch.nim @@ -0,0 +1,11 @@ +discard """ + matrix: "-d:testsConciseTypeMismatch" + nimout: ''' +[1] proc `[]`[T; U, V: Ordinal](s: openArray[T]; x: HSlice[U, V]): seq[T] +''' +""" + +type Foo = object +let x = Foo() +discard x[1] #[tt.Error + ^ type mismatch]# diff --git a/tests/errmsgs/tsubscriptmismatch_legacy.nim b/tests/errmsgs/tsubscriptmismatch_legacy.nim new file mode 100644 index 000000000..3e1f1eb71 --- /dev/null +++ b/tests/errmsgs/tsubscriptmismatch_legacy.nim @@ -0,0 +1,10 @@ +discard """ + nimout: ''' + but expression 'x' is of type: Foo +''' +""" + +type Foo = object +let x = Foo() +discard x[1] #[tt.Error + ^ type mismatch: got <Foo, int literal(1)>]# diff --git a/tests/errmsgs/tundeclared_routine.nim b/tests/errmsgs/tundeclared_routine.nim index 2f1320fff..41b1d35f4 100644 --- a/tests/errmsgs/tundeclared_routine.nim +++ b/tests/errmsgs/tundeclared_routine.nim @@ -9,7 +9,7 @@ tundeclared_routine.nim(29, 28) Error: invalid pragma: myPragma tundeclared_routine.nim(36, 13) Error: undeclared field: 'bar3' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(33, 8)] found tundeclared_routine.bar3() [iterator declared in tundeclared_routine.nim(35, 12)] tundeclared_routine.nim(41, 13) Error: undeclared field: 'bar4' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(39, 8)] -tundeclared_routine.nim(44, 15) Error: attempting to call routine: 'bad5' +tundeclared_routine.nim(44, 11) Error: undeclared identifier: 'bad5' ''' """ diff --git a/tests/errmsgs/tuntypedoverload.nim b/tests/errmsgs/tuntypedoverload.nim new file mode 100644 index 000000000..1b1c2809c --- /dev/null +++ b/tests/errmsgs/tuntypedoverload.nim @@ -0,0 +1,37 @@ +discard """ + cmd: "nim check $file" +""" + +block: + template foo(x: var int, y: untyped) = discard + var a: float + foo(a, undeclared) #[tt.Error + ^ type mismatch: got <float, untyped>]# # `untyped` is arbitary + # previous error: undeclared identifier: 'undeclared' + +block: # issue #8697 + type + Fruit = enum + apple + banana + orange + macro hello(x, y: untyped) = discard + hello(apple, banana, orange) #[tt.Error + ^ type mismatch: got <Fruit, Fruit, Fruit>]# + +block: # issue #23265 + template declareFoo(fooName: untyped, value: uint16) = + const `fooName Value` {.inject.} = value + + declareFoo(FOO, 0xFFFF) + declareFoo(BAR, 0xFFFFF) #[tt.Error + ^ type mismatch: got <untyped, int literal(1048575)>]# + +block: # issue #9620 + template forLoop(index: untyped, length: int{lvalue}, body: untyped) = + for `index`{.inject.} in 0 ..< length: + body + var x = newSeq[int](10) + forLoop(i, x.len): #[tt.Error + ^ type mismatch: got <untyped, int, void>]# + x[i] = i diff --git a/tests/errmsgs/twrong_explicit_typeargs.nim b/tests/errmsgs/twrong_explicit_typeargs.nim new file mode 100644 index 000000000..5236e5f4f --- /dev/null +++ b/tests/errmsgs/twrong_explicit_typeargs.nim @@ -0,0 +1,26 @@ +discard """ + cmd: "nim c --hints:off -d:testsConciseTypeMismatch $file" + action: reject + nimout: ''' +twrong_explicit_typeargs.nim(26, 29) Error: type mismatch +Expression: newImage[string](320, 200) + [1] 320: int literal(320) + [2] 200: int literal(200) + +Expected one of (first mismatch at [position]): +[1] proc newImage[T: int32 | int64](w, h: int): ref Image[T] + generic parameter mismatch, expected int32 or int64 but got 'string' of type: string +''' +""" + +# bug #4084 +type + Image[T] = object + data: seq[T] + +proc newImage[T: int32|int64](w, h: int): ref Image[T] = + new(result) + result.data = newSeq[T](w * h) + +var correct = newImage[int32](320, 200) +var wrong = newImage[string](320, 200) diff --git a/tests/errmsgs/twrong_explicit_typeargs_legacy.nim b/tests/errmsgs/twrong_explicit_typeargs_legacy.nim new file mode 100644 index 000000000..cfa528c54 --- /dev/null +++ b/tests/errmsgs/twrong_explicit_typeargs_legacy.nim @@ -0,0 +1,25 @@ +discard """ + action: reject + nimout: ''' +twrong_explicit_typeargs_legacy.nim(25, 29) Error: type mismatch: got <int literal(320), int literal(200)> +but expected one of: +proc newImage[T: int32 | int64](w, h: int): ref Image[T] + first type mismatch at position: 1 in generic parameters + required type for T: int32 or int64 + but expression 'string' is of type: string + +expression: newImage[string](320, 200) +''' +""" + +# bug #4084 +type + Image[T] = object + data: seq[T] + +proc newImage[T: int32|int64](w, h: int): ref Image[T] = + new(result) + result.data = newSeq[T](w * h) + +var correct = newImage[int32](320, 200) +var wrong = newImage[string](320, 200) diff --git a/tests/generics/mopensymimport1.nim b/tests/generics/mopensymimport1.nim new file mode 100644 index 000000000..912db1302 --- /dev/null +++ b/tests/generics/mopensymimport1.nim @@ -0,0 +1,34 @@ +type + Result*[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + +template valueOr*[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def diff --git a/tests/generics/mopensymimport2.nim b/tests/generics/mopensymimport2.nim new file mode 100644 index 000000000..c17aafd00 --- /dev/null +++ b/tests/generics/mopensymimport2.nim @@ -0,0 +1,16 @@ +{.experimental: "openSym".} + +import mopensymimport1 + +type Xxx = enum + error + value + +proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + +proc g*(T: type): string = + let x = f().valueOr: + return $error + + "ok" diff --git a/tests/generics/t19848.nim b/tests/generics/t19848.nim new file mode 100644 index 000000000..f80f0e298 --- /dev/null +++ b/tests/generics/t19848.nim @@ -0,0 +1,16 @@ +discard """ + output: ''' +todo +''' +""" + +type + Maybe[T] = object + List[T] = object + +proc dump[M: Maybe](a: List[M]) = + echo "todo" + +var a: List[Maybe[int]] + +dump(a) diff --git a/tests/generics/t23186.nim b/tests/generics/t23186.nim new file mode 100644 index 000000000..76f38da6b --- /dev/null +++ b/tests/generics/t23186.nim @@ -0,0 +1,155 @@ +# issue #23186 + +block: # simplified + template typedTempl(x: int, body): untyped = + body + proc generic1[T]() = + discard + proc generic2[T]() = + typedTempl(1): + let x = generic1[T] + generic2[int]() + +import std/macros + +when not compiles(len((1, 2))): + import std/typetraits + + func len(x: tuple): int = + arity(type(x)) + +block: # full issue example + type FieldDescription = object + name: NimNode + func isTuple(t: NimNode): bool = + t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple") + proc collectFieldsFromRecList(result: var seq[FieldDescription], + n: NimNode, + parentCaseField: NimNode = nil, + parentCaseBranch: NimNode = nil, + isDiscriminator = false) = + case n.kind + of nnkRecList: + for entry in n: + collectFieldsFromRecList result, entry, + parentCaseField, parentCaseBranch + of nnkIdentDefs: + for i in 0 ..< n.len - 2: + var field: FieldDescription + field.name = n[i] + if field.name.kind == nnkPragmaExpr: + field.name = field.name[0] + if field.name.kind == nnkPostfix: + field.name = field.name[1] + result.add field + of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty: + discard + else: + doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr + proc collectFieldsInHierarchy(result: var seq[FieldDescription], + objectType: NimNode) = + var objectType = objectType + if objectType.kind == nnkRefTy: + objectType = objectType[0] + let recList = objectType[2] + collectFieldsFromRecList result, recList + proc recordFields(typeImpl: NimNode): seq[FieldDescription] = + let objectType = case typeImpl.kind + of nnkObjectTy: typeImpl + of nnkTypeDef: typeImpl[2] + else: + macros.error("object type expected", typeImpl) + return + collectFieldsInHierarchy(result, objectType) + proc skipPragma(n: NimNode): NimNode = + if n.kind == nnkPragmaExpr: n[0] + else: n + func declval(T: type): T = + doAssert false, + "declval should be used only in `typeof` expressions and concepts" + default(ptr T)[] + macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped = + var typeAst = getType(T)[1] + var typeImpl: NimNode + let isSymbol = not typeAst.isTuple + if not isSymbol: + typeImpl = typeAst + else: + typeImpl = getImpl(typeAst) + result = newStmtList() + var i = 0 + for field in recordFields(typeImpl): + let + fieldIdent = field.name + realFieldName = newLit($fieldIdent.skipPragma) + fieldName = realFieldName + fieldIndex = newLit(i) + let fieldNameDefs = + if isSymbol: + quote: + const fieldName {.inject, used.} = `fieldName` + const realFieldName {.inject, used.} = `realFieldName` + else: + quote: + const fieldName {.inject, used.} = $`fieldIndex` + const realFieldName {.inject, used.} = $`fieldIndex` + # we can't access .Fieldn, so our helper knows + # to parseInt this + let field = + if isSymbol: + quote do: declval(`T`).`fieldIdent` + else: + quote do: declval(`T`)[`fieldIndex`] + result.add quote do: + block: + `fieldNameDefs` + type FieldType {.inject, used.} = type(`field`) + `body` + i += 1 + template enumAllSerializedFields(T: type, body): untyped = + when T is ref|ptr: + type TT = type(default(T)[]) + enumAllSerializedFieldsImpl(TT, body) + else: + enumAllSerializedFieldsImpl(T, body) + type + MemRange = object + startAddr: ptr byte + length: int + SszNavigator[T] = object + m: MemRange + func sszMount(data: openArray[byte], T: type): SszNavigator[T] = + let startAddr = unsafeAddr data[0] + SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len)) + func sszMount(data: openArray[char], T: type): SszNavigator[T] = + let startAddr = cast[ptr byte](unsafeAddr data[0]) + SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len)) + template sszMount(data: MemRange, T: type): SszNavigator[T] = + SszNavigator[T](m: data) + func navigateToField[T]( + n: SszNavigator[T], + FieldType: type): SszNavigator[FieldType] = + default(SszNavigator[FieldType]) + type + FieldInfo = ref object + navigator: proc (m: MemRange): MemRange {. + gcsafe, noSideEffect, raises: [IOError] .} + func fieldNavigatorImpl[RecordType; FieldType; fieldName: static string]( + m: MemRange): MemRange = + var typedNavigator = sszMount(m, RecordType) + discard navigateToField(typedNavigator, FieldType) + default(MemRange) + func genTypeInfo(T: type) = + when T is object: + enumAllSerializedFields(T): + discard FieldInfo(navigator: fieldNavigatorImpl[T, FieldType, fieldName]) + type + Foo = object + bar: Bar + BarList = seq[uint64] + Bar = object + b: BarList + baz: Baz + Baz = object + i: uint64 + genTypeInfo(Foo) diff --git a/tests/generics/t23790.nim b/tests/generics/t23790.nim new file mode 100644 index 000000000..9ac0df6a1 --- /dev/null +++ b/tests/generics/t23790.nim @@ -0,0 +1,14 @@ +# bug #23790 + +discard compiles($default(seq[seq[ref int]])) +discard compiles($default(seq[seq[ref uint]])) +discard compiles($default(seq[seq[ref int8]])) +discard compiles($default(seq[seq[ref uint8]])) +discard compiles($default(seq[seq[ref int16]])) +discard compiles($default(seq[seq[ref uint16]])) +discard compiles($default(seq[seq[ref int32]])) +discard compiles($default(seq[seq[ref uint32]])) +discard compiles($default(seq[seq[ref int64]])) +discard compiles($default(seq[seq[ref uint64]])) +proc s(_: int | string) = discard +s(0) diff --git a/tests/generics/t23853.nim b/tests/generics/t23853.nim new file mode 100644 index 000000000..bc9514a53 --- /dev/null +++ b/tests/generics/t23853.nim @@ -0,0 +1,91 @@ +# issue #23853 + +block simplified: + type QuadraticExt[F] = object + coords: array[2, F] + template Name(E: type QuadraticExt): int = 123 + template getBigInt(Name: static int): untyped = int + type Foo[GT] = object + a: getBigInt(GT.Name) + var x: Foo[QuadraticExt[int]] + +import std/macros + +type + Algebra* = enum + BN254_Snarks + BLS12_381 + + Fp*[Name: static Algebra] = object + limbs*: array[4, uint64] + + QuadraticExt*[F] = object + ## Quadratic Extension field + coords*: array[2, F] + + CubicExt*[F] = object + ## Cubic Extension field + coords*: array[3, F] + + ExtensionField*[F] = QuadraticExt[F] or CubicExt[F] + + Fp2*[Name: static Algebra] = + QuadraticExt[Fp[Name]] + + Fp4*[Name: static Algebra] = + QuadraticExt[Fp2[Name]] + + Fp6*[Name: static Algebra] = + CubicExt[Fp2[Name]] + + Fp12*[Name: static Algebra] = + CubicExt[Fp4[Name]] + # QuadraticExt[Fp6[Name]] + +template Name*(E: type ExtensionField): Algebra = + E.F.Name + +const BLS12_381_Order = [uint64 0x1, 0x2, 0x3, 0x4] +const BLS12_381_Modulus = [uint64 0x5, 0x6, 0x7, 0x8] + + +{.experimental: "dynamicBindSym".} + +macro baseFieldModulus*(Name: static Algebra): untyped = + result = bindSym($Name & "_Modulus") + +macro scalarFieldModulus*(Name: static Algebra): untyped = + result = bindSym($Name & "_Order") + +type FieldKind* = enum + kBaseField + kScalarField + +template getBigInt*(Name: static Algebra, kind: static FieldKind): untyped = + # Workaround: + # in `ptr UncheckedArray[BigInt[EC.getScalarField().bits()]] + # EC.getScalarField is not accepted by the compiler + # + # and `ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits]]` gets undeclared field: 'Name' + # + # but `ptr UncheckedArray[getBigInt(EC.getName(), kScalarField)]` works fine + when kind == kBaseField: + Name.baseFieldModulus().typeof() + else: + Name.scalarFieldModulus().typeof() + +# ------------------------------------------------------------------------------ + +type BenchMultiexpContext*[GT] = object + elems: seq[GT] + exponents: seq[getBigInt(GT.Name, kScalarField)] + +proc createBenchMultiExpContext*(GT: typedesc, inputSizes: openArray[int]): BenchMultiexpContext[GT] = + discard + +# ------------------------------------------------------------------------------ + +proc main() = + let ctx = createBenchMultiExpContext(Fp12[BLS12_381], [2, 4, 8, 16]) + +main() diff --git a/tests/generics/t23854.nim b/tests/generics/t23854.nim new file mode 100644 index 000000000..f1175c8b2 --- /dev/null +++ b/tests/generics/t23854.nim @@ -0,0 +1,71 @@ +# issue #23854, not entirely fixed + +import std/bitops + +const WordBitWidth = sizeof(pointer) * 8 + +func wordsRequired*(bits: int): int {.inline.} = + const divShiftor = fastLog2(uint32(WordBitWidth)) + result = (bits + WordBitWidth - 1) shr divShiftor + +type + Algebra* = enum + BLS12_381 + + BigInt*[bits: static int] = object + limbs*: array[wordsRequired(bits), uint] + + Fr*[Name: static Algebra] = object + residue_form*: BigInt[255] + + Fp*[Name: static Algebra] = object + residue_form*: BigInt[381] + + FF*[Name: static Algebra] = Fp[Name] or Fr[Name] + +template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped = + ## Get the underlying BigInt type. + typeof(default(T).residue_form) + +type + EC_ShortW_Aff*[F] = object + ## Elliptic curve point for a curve in Short Weierstrass form + ## y² = x³ + a x + b + ## + ## over a field F + x*, y*: F + +type FieldKind* = enum + kBaseField + kScalarField + +func bits*[Name: static Algebra](T: type FF[Name]): static int = + T.getBigInt().bits + +template getScalarField*(EC: type EC_ShortW_Aff): untyped = + Fr[EC.F.Name] + +# ------------------------------------------------------------------------------ + +type + ECFFT_Descriptor*[EC] = object + ## Metadata for FFT on Elliptic Curve + order*: int + rootsOfUnity1*: ptr UncheckedArray[BigInt[EC.getScalarField().bits()]] # Error: in expression 'EC.getScalarField()': identifier expected, but found 'EC.getScalarField' + rootsOfUnity2*: ptr UncheckedArray[BigInt[getScalarField(EC).bits()]] # Compiler SIGSEGV: Illegal Storage Access + +func new*(T: type ECFFT_Descriptor): T = + discard + +# ------------------------------------------------------------------------------ + +template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits + +proc main() = + let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() + doAssert getBits(ctx.rootsOfUnity1) == 255 + doAssert getBits(ctx.rootsOfUnity2) == 255 + doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(255) + doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(255) + +main() diff --git a/tests/generics/t23855.nim b/tests/generics/t23855.nim new file mode 100644 index 000000000..da8135a98 --- /dev/null +++ b/tests/generics/t23855.nim @@ -0,0 +1,61 @@ +# issue #23855, not entirely fixed + +import std/bitops + +const WordBitWidth = sizeof(pointer) * 8 + +func wordsRequired*(bits: int): int {.inline.} = + const divShiftor = fastLog2(uint32(WordBitWidth)) + result = (bits + WordBitWidth - 1) shr divShiftor + +type + Algebra* = enum + BLS12_381 + + BigInt*[bits: static int] = object + limbs*: array[wordsRequired(bits), uint] + + Fr*[Name: static Algebra] = object + residue_form*: BigInt[255] + + Fp*[Name: static Algebra] = object + residue_form*: BigInt[381] + + FF*[Name: static Algebra] = Fp[Name] or Fr[Name] + +template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped = + ## Get the underlying BigInt type. + typeof(default(T).residue_form) + +type + EC_ShortW_Aff*[F] = object + ## Elliptic curve point for a curve in Short Weierstrass form + ## y² = x³ + a x + b + ## + ## over a field F + x*, y*: F + +func bits*[Name: static Algebra](T: type FF[Name]): static int = + T.getBigInt().bits + +# ------------------------------------------------------------------------------ + +type + ECFFT_Descriptor*[EC] = object + ## Metadata for FFT on Elliptic Curve + order*: int + rootsOfUnity*: ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits()]] # Undeclared identifier `Name` + +func new*(T: type ECFFT_Descriptor): T = + discard + +# ------------------------------------------------------------------------------ + +template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits + +proc main() = + let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() + doAssert getBits(ctx.rootsOfUnity) == 255 + doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(255) + +main() diff --git a/tests/generics/t6137.nim b/tests/generics/t6137.nim index 991f39dd1..fb7db22f8 100644 --- a/tests/generics/t6137.nim +++ b/tests/generics/t6137.nim @@ -1,6 +1,6 @@ discard """ - errormsg: "\'vectFunc\' doesn't have a concrete type, due to unspecified generic parameters." - line: 28 + errormsg: "cannot instantiate: 'T'" + line: 19 """ type diff --git a/tests/generics/taliashijack.nim b/tests/generics/taliashijack.nim new file mode 100644 index 000000000..fdebadded --- /dev/null +++ b/tests/generics/taliashijack.nim @@ -0,0 +1,8 @@ +# issue #23977 + +type Foo[T] = int + +proc foo(T: typedesc) = + var a: T + +foo(int) diff --git a/tests/generics/tbadcache.nim b/tests/generics/tbadcache.nim new file mode 100644 index 000000000..33e65be3a --- /dev/null +++ b/tests/generics/tbadcache.nim @@ -0,0 +1,26 @@ +# issue #16128 + +import std/[tables, hashes] + +type + NodeId*[L] = object + isSource: bool + index: Table[NodeId[L], seq[NodeId[L]]] + +func hash*[L](id: NodeId[L]): Hash = discard +func `==`[L](a, b: NodeId[L]): bool = discard + +proc makeIndex*[T, L](tree: T) = + var parent = NodeId[L]() + var tmp: Table[NodeId[L], seq[NodeId[L]]] + tmp[parent] = @[parent] + +proc simpleTreeDiff*[T, L](source, target: T) = + # Swapping these two lines makes error disappear + var m: Table[NodeId[L], NodeId[L]] + makeIndex[T, L](target) + +var tmp: Table[string, seq[string]] # removing this forward declaration also removes error + +proc diff(x1, x2: string): auto = + simpleTreeDiff[int, string](12, 12) diff --git a/tests/generics/tbracketinstantiation.nim b/tests/generics/tbracketinstantiation.nim new file mode 100644 index 000000000..22a86af4c --- /dev/null +++ b/tests/generics/tbracketinstantiation.nim @@ -0,0 +1,86 @@ +discard """ + nimout: ''' +type + Bob = object +type + Another = object +''' +""" + +block: # issue #22645 + type + Opt[T] = object + FutureBase = ref object of RootObj + Future[T] = ref object of FutureBase ## Typed future. + internalValue: T ## Stored value + template err[T](E: type Opt[T]): E = E() + proc works(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} = + var chronosInternalRetFuture: FutureBase + template result(): untyped {.used.} = + Future[Opt[int]](chronosInternalRetFuture).internalValue + result = err(type(result)) + proc breaks(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} = + var chronosInternalRetFuture: FutureBase + template result(): untyped {.used.} = + cast[Future[Opt[int]]](chronosInternalRetFuture).internalValue + result = err(type(result)) + +import macros + +block: # issue #16118 + macro thing(name: static[string]) = + result = newStmtList( + nnkTypeSection.newTree( + nnkTypeDef.newTree( + ident(name), + newEmptyNode(), + nnkObjectTy.newTree( + newEmptyNode(), + newEmptyNode(), + nnkRecList.newTree())))) + template foo(name: string): untyped = + thing(name) + expandMacros: + foo("Bob") + block: + expandMacros: + foo("Another") + +block: # issue #19670 + type + Past[Z] = object + OpenObject = object + + macro rewriter(prc: untyped): untyped = + prc.body.add(nnkCall.newTree( + prc.params[0] + )) + prc + + macro macroAsync(name, restype: untyped): untyped = + quote do: + proc `name`(): Past[seq[`restype`]] {.rewriter.} = discard + + macroAsync(testMacro, OpenObject) + +import asyncdispatch + +block: # issue #11838 long + type + R[P] = object + updates: seq[P] + D[T, P] = ref object + ps: seq[P] + t: T + proc newD[T, P](ps: seq[P], t: T): D[T, P] = + D[T, P](ps: ps, t: t) + proc loop[T, P](d: D[T, P]) = + var results = newSeq[Future[R[P]]](10) + let d = newD[string, int](@[1], "") + d.loop() + +block: # issue #11838 minimal + type R[T] = object + proc loop[T]() = + discard newSeq[R[R[T]]]() + loop[int]() diff --git a/tests/generics/tcalltype.nim b/tests/generics/tcalltype.nim new file mode 100644 index 000000000..cba691f77 --- /dev/null +++ b/tests/generics/tcalltype.nim @@ -0,0 +1,26 @@ +discard """ + joinable: false # breaks everything because of #23977 +""" + +# issue #23406 + +template helper(_: untyped): untyped = + int + +type # Each of them should always be `int`. + GenA[T] = helper int + GenB[T] = helper(int) + GenC[T] = helper helper(int) + +block: + template helper(_: untyped): untyped = + float + + type + A = GenA[int] + B = GenB[int] + C = GenC[int] + + assert A is int # OK. + assert B is int # Fails; it is `float`! + assert C is int # OK. diff --git a/tests/generics/tgeneric0.nim b/tests/generics/tgeneric0.nim index b5e1c4bb4..16a148f7b 100644 --- a/tests/generics/tgeneric0.nim +++ b/tests/generics/tgeneric0.nim @@ -9,7 +9,7 @@ float32 """ -import tables +import std/tables block tgeneric0: @@ -166,3 +166,31 @@ type # bug #8295 var x = AtomicContainer[int]() doAssert (ptr Block[int])(x.b) == nil + + +# bug #23233 +type + JsonObjectType*[T: string or uint64] = Table[string, JsonValueRef[T]] + + JsonValueRef*[T: string or uint64] = object + objVal*: JsonObjectType[T] + +proc scanValue[K](val: var K) = + var map: JsonObjectType[K.T] + var newVal: K + map["one"] = newVal + +block: + var a: JsonValueRef[uint64] + scanValue(a) + + var b: JsonValueRef[string] + scanValue(b) + +block: # bug #21347 + type K[T] = object + template s[T]() = discard + proc b1(n: bool | bool) = s[K[K[int]]]() + proc b2(n: bool) = s[K[K[int]]]() + b1(false) # Error: 's' has unspecified generic parameters + b2(false) # Builds, on its own diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim index b6ace4e7d..53661236e 100644 --- a/tests/generics/tgenerics_various.nim +++ b/tests/generics/tgenerics_various.nim @@ -238,3 +238,17 @@ block: # issue #8390 $y.type doAssert x(@[1.0]) == $1.0.type + + +block: # issue #9381 + var evalCount {.compileTime.} = 0 + + macro test(t: typed): untyped = + inc evalCount + t + + type GenericObj[T] = object + f: test(T) + + var x: GenericObj[int] + static: doAssert evalCount == 1 diff --git a/tests/generics/tgenericwhen.nim b/tests/generics/tgenericwhen.nim new file mode 100644 index 000000000..87672a699 --- /dev/null +++ b/tests/generics/tgenericwhen.nim @@ -0,0 +1,58 @@ +discard """ + targets: "c js" +""" + +block: # issue #24041 + type ArrayBuf[N: static int, T = byte] = object + when sizeof(int) > sizeof(uint8): + when N <= int(uint8.high): + n: uint8 + else: + when sizeof(int) > sizeof(uint16): + when N <= int(uint16.high): + n: uint16 + else: + when sizeof(int) > sizeof(uint32): + when N <= int(uint32.high): + n: uint32 + else: + n: int + else: + n: int + else: + n: int + else: + n: int + + var x: ArrayBuf[8] + doAssert x.n is uint8 + when sizeof(int) > sizeof(uint32): + var y: ArrayBuf[int(uint32.high) * 8] + doAssert y.n is int + +block: # constant condition after dynamic one + type Foo[T] = object + when T is int: + a: int + elif true: + a: string + else: + a: bool + var x: Foo[string] + doAssert x.a is string + var y: Foo[int] + doAssert y.a is int + var z: Foo[float] + doAssert z.a is string + +block: # issue #4774, but not with threads + const hasThreadSupport = not defined(js) + when hasThreadSupport: + type Channel[T] = object + value: T + type + SomeObj[T] = object + when hasThreadSupport: + channel: ptr Channel[T] + var x: SomeObj[int] + doAssert compiles(x.channel) == hasThreadSupport diff --git a/tests/generics/tgensyminst.nim b/tests/generics/tgensyminst.nim new file mode 100644 index 000000000..3f30188d8 --- /dev/null +++ b/tests/generics/tgensyminst.nim @@ -0,0 +1,29 @@ +# issue #24048 + +import macros + +proc map(fn: proc(val: int): void) = fn(1) + +# This works fine, and is the exact same function call as what's +# generated by the macro `aBug`. +map proc(val: auto): void = + let variable = 123 + +macro aBug() = + # 1. let sym = ident("variable") + let sym = genSym(nskLet, "variable") + let letStmt = newLetStmt(sym, newLit(123)) + + let lambda = newProc( + params = @[ + ident("void"), + newIdentDefs(ident("val"), ident("auto")), + # 2. newIdentDefs(ident("val"), ident("int")), + ], + body = newStmtList(letStmt), + procType = nnkLambda + ) + + result = newCall(bindSym("map"), lambda) + +aBug() diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim index fe61004e4..7220b7429 100644 --- a/tests/generics/timplicit_and_explicit.nim +++ b/tests/generics/timplicit_and_explicit.nim @@ -3,8 +3,8 @@ block: # basic test proc doStuff[T](a: SomeInteger): T = discard proc doStuff[T;Y](a: SomeInteger, b: Y): Y = discard assert typeof(doStuff[int](100)) is int - assert typeof(doStuff[int](100, 1.0)) is float - assert typeof(doStuff[int](100, "Hello")) is string + assert typeof(doStuff[int, float](100, 1.0)) is float + assert typeof(doStuff[int, string](100, "Hello")) is string proc t[T](x: T; z: int | float): seq[T] = result.add(x & $z) @@ -34,7 +34,8 @@ block: #15622 proc test1[T](a: T, b: static[string] = "") = discard test1[int64](123) proc test2[T](a: T, b: static[string] = "") = discard - test2[int64, static[string]](123) + doAssert not (compiles do: + test2[int64, static[string]](123)) block: #4688 proc convertTo[T](v: int or float): T = (T)(v) diff --git a/tests/generics/tmacroinjectedsym.nim b/tests/generics/tmacroinjectedsym.nim new file mode 100644 index 000000000..985e415f2 --- /dev/null +++ b/tests/generics/tmacroinjectedsym.nim @@ -0,0 +1,186 @@ +{.experimental: "openSym".} + +block: # issue #22605, normal call syntax + const error = "bad" + + template valueOr(self: int, def: untyped): untyped = + case false + of true: "" + of false: + template error: untyped {.used, inject.} = "good" + def + + proc g(T: type): string = + let x = valueOr 123: + return $error + + "ok" + + doAssert g(int) == "good" + + proc g2(T: type): string = + bind error # use the bad version on purpose + let x = valueOr 123: + return $error + + "ok" + + doAssert g2(int) == "bad" + +block: # issue #22605, method call syntax + const error = "bad" + + template valueOr(self: int, def: untyped): untyped = + case false + of true: "" + of false: + template error: untyped {.used, inject.} = "good" + def + + proc g(T: type): string = + let x = 123.valueOr: + return $error + + "ok" + + doAssert g(int) == "good" + + proc g2(T: type): string = + bind error # use the bad version on purpose + let x = 123.valueOr: + return $error + + "ok" + + doAssert g2(int) == "bad" + +block: # issue #22605, original complex example + type Xxx = enum + error + value + + type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + + template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + + proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + + proc g(T: type): string = + let x = f().valueOr: + return $error + + "ok" + + doAssert g(int) == "f" + + proc g2(T: type): string = + bind error # use the bad version on purpose + let x = f().valueOr: + return $error + + "ok" + + doAssert g2(int) == "error" + +block: # issue #23865 + type Xxx = enum + error + value + + type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate: bool + else: + case oResultPrivate: bool + of false: + eResultPrivate: E + of true: + discard + else: + when E is void: + case oResultPrivate: bool + of false: + discard + of true: + vResultPrivate: T + else: + case oResultPrivate: bool + of false: + eResultPrivate: E + of true: + vResultPrivate: T + + func error[T, E](self: Result[T, E]): E = + ## Fetch error of result if set, or raise Defect + case self.oResultPrivate + of true: + when T isnot void: + raiseResultDefect("Trying to access error when value is set", self.vResultPrivate) + else: + raiseResultDefect("Trying to access error when value is set") + of false: + when E isnot void: + self.eResultPrivate + + template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + proc g(T: type): string = + let x = f().valueOr: + return $error + "ok" + doAssert g(int) == "f" + +import sequtils + +block: # issue #12283 + var b = 5 + type Foo[T] = object + h, w: int + proc bar[T](foos: seq[Foo[T]]): T = + let w = foldl(foos, a + b.w, 0) + w + let foos = @[Foo[int](h: 3, w: 5), Foo[int](h: 4, w: 6)] + doAssert bar(foos) == 11 diff --git a/tests/generics/tmacroinjectedsymwarning.nim b/tests/generics/tmacroinjectedsymwarning.nim new file mode 100644 index 000000000..77119004b --- /dev/null +++ b/tests/generics/tmacroinjectedsymwarning.nim @@ -0,0 +1,59 @@ +discard """ + matrix: "--skipParentCfg --filenames:legacyRelProj" +""" + +type Xxx = enum + error + value + +type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + +template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + +proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + +proc g(T: type): string = + let x = f().valueOr: + {.push warningAsError[IgnoredSymbolInjection]: on.} + # test spurious error + discard true + let _ = f + {.pop.} + return $error #[tt.Warning + ^ a new symbol 'error' has been injected during template or generic instantiation, however 'error' [enumField declared in tmacroinjectedsymwarning.nim(6, 3)] captured at the proc declaration will be used instead; either enable --experimental:openSym to use the injected symbol, or `bind` this captured symbol explicitly [IgnoredSymbolInjection]]# + + "ok" + +discard g(int) diff --git a/tests/generics/tnestedissues.nim b/tests/generics/tnestedissues.nim new file mode 100644 index 000000000..e96a1927e --- /dev/null +++ b/tests/generics/tnestedissues.nim @@ -0,0 +1,24 @@ +block: # issue #23568 + type G[T] = object + j: T + proc s[T](u: int) = discard + proc s[T]() = discard + proc c(e: int | int): G[G[G[int]]] = s[G[G[int]]]() + discard c(0) + +import std/options + +block: # issue #23310 + type + BID = string or uint64 + Future[T] = ref object of RootObj + internalValue: T + InternalRaisesFuture[T] = ref object of Future[T] + proc newInternalRaisesFutureImpl[T](): InternalRaisesFuture[T] = + let fut = InternalRaisesFuture[T]() + template newFuture[T](): auto = + newInternalRaisesFutureImpl[T]() + proc problematic(blockId: BID): Future[Option[seq[int]]] = + let resultFuture = newFuture[Option[seq[int]]]() + return resultFuture + let x = problematic("latest") diff --git a/tests/generics/tnestedtemplate.nim b/tests/generics/tnestedtemplate.nim new file mode 100644 index 000000000..22d0a2d3c --- /dev/null +++ b/tests/generics/tnestedtemplate.nim @@ -0,0 +1,9 @@ +block: # issue #13979 + var s: seq[int] + proc filterScanline[T](input: openArray[T]) = + template currPix: untyped = input[i] + for i in 0..<input.len: + s.add currPix + let pix = [1, 2, 3] + filterScanline(pix) + doAssert s == @[1, 2, 3] diff --git a/tests/generics/topensymimport.nim b/tests/generics/topensymimport.nim new file mode 100644 index 000000000..a47496827 --- /dev/null +++ b/tests/generics/topensymimport.nim @@ -0,0 +1,5 @@ +# issue #23386 + +import mopensymimport2 + +doAssert g(int) == "f" diff --git a/tests/generics/tstatic_constrained.nim b/tests/generics/tstatic_constrained.nim index 3c9201548..d356b9d1c 100644 --- a/tests/generics/tstatic_constrained.nim +++ b/tests/generics/tstatic_constrained.nim @@ -8,7 +8,7 @@ but expected: <T: float or string, Y> tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] got: <typedesc[int], int literal(10)> but expected: <T: float or string, Y> -tstatic_constrained.nim(44, 31) Error: object constructor needs an object type [proxy] +tstatic_constrained.nim(44, 31) Error: object constructor needs an object type [error] tstatic_constrained.nim(44, 31) Error: expression '' has no type (or is ambiguous) tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] got: <typedesc[byte], uint8> @@ -16,7 +16,7 @@ but expected: <T: float or string, Y> tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] got: <typedesc[byte], uint8> but expected: <T: float or string, Y> -tstatic_constrained.nim(45, 34) Error: object constructor needs an object type [proxy] +tstatic_constrained.nim(45, 34) Error: object constructor needs an object type [error] tstatic_constrained.nim(45, 34) Error: expression '' has no type (or is ambiguous) tstatic_constrained.nim(77, 14) Error: cannot instantiate MyType [type declared in tstatic_constrained.nim(71, 5)] got: <typedesc[float], float64> @@ -76,4 +76,4 @@ block: b: MyType[string, "hello"] c: MyType[float, 10d] d: MyOtherType[MyOtherConstraint[float],MyOtherConstraint[float]()] - e: MyOtherType[MyOtherConstraint[int], MyOtherConstraint[int]()] \ No newline at end of file + e: MyOtherType[MyOtherConstraint[int], MyOtherConstraint[int]()] diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index bac334e95..f33fc8967 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -140,3 +140,378 @@ block: # issue #1771 var a: Foo[range[0..2], float] doAssert test(a) == 0.0 + +block: # issue #23730 + proc test(M: static[int]): array[1 shl M, int] = discard + doAssert len(test(3)) == 8 + doAssert len(test(5)) == 32 + +block: # issue #19819 + type + Example[N: static int] = distinct int + What[E: Example] = Example[E.N + E.N] + +block: # issue #23339 + type + A = object + B = object + template aToB(t: typedesc[A]): typedesc = B + type + Inner[I] = object + innerField: I + Outer[O] = object + outerField: Inner[O.aToB] + var x: Outer[A] + doAssert typeof(x.outerField.innerField) is B + +block: # deref syntax + type + Enqueueable = concept x + x is ptr + Foo[T: Enqueueable] = object + x: typeof(default(T)[]) + + proc p[T](f: Foo[T]) = + var bar: Foo[T] + discard + var foo: Foo[ptr int] + p(foo) + doAssert foo.x is int + foo.x = 123 + doAssert foo.x == 123 + inc foo.x + doAssert foo.x == 124 + +block: + type Generic[T] = object + field: T + macro foo(x: typed): untyped = x + macro bar[T](x: typedesc[Generic[T]]): untyped = x + type + Foo[T] = object + field: Generic[int].foo() + Foo2[T] = object + field: Generic[T].foo() + Bar[T] = object + field: Generic[int].bar() + Bar2[T] = object + field: Generic[T].bar() + var x: Foo[int] + var x2: Foo2[int] + var y: Bar[int] + var y2: Bar2[int] + +block: + macro pick(x: static int): untyped = + if x < 100: + result = bindSym"int" + else: + result = bindSym"float" + + type Foo[T: static int] = object + fixed1: pick(25) + fixed2: pick(125) + unknown: pick(T) + + var a: Foo[123] + doAssert a.fixed1 is int + doAssert a.fixed2 is float + doAssert a.unknown is float + var b: Foo[23] + doAssert b.fixed1 is int + doAssert b.fixed2 is float + doAssert b.unknown is int + +import std/sequtils + +block: # version of #23432 with `typed`, don't delay instantiation + type + Future[T] = object + InternalRaisesFuture[T, E] = object + macro Raising[T](F: typedesc[Future[T]], E: varargs[typed]): untyped = + let raises = nnkTupleConstr.newTree(E.mapIt(it)) + nnkBracketExpr.newTree( + ident "InternalRaisesFuture", + nnkDotExpr.newTree(F, ident"T"), + raises + ) + type X[E] = Future[void].Raising(E) + proc f(x: X) = discard + var v: Future[void].Raising([ValueError]) + f(v) + +block: # issue #22647 + proc c0(n: static int): int = 8 + proc c1(n: static int): int = n div 2 + proc c2(n: static int): int = n * 2 + proc c3(n: static int, n2: int): int = n * n2 + proc `**`(n: static int, n2: int): int = n * n2 + proc c4(n: int, n2: int): int = n * n2 + + type + a[N: static int] = object + f0 : array[N, int] + + b[N: static int] = object + f0 : a[c0(N)] # does not work + f1 : a[c1(N)] # does not work + f2 : a[c2(N)] # does not work + f3 : a[N * 2] # does not work + f4 : a[N] # works + f5: a[c3(N, 2)] + f6: a[N ** 2] + f7: a[2 * N] + f8: a[c4(N, 2)] + + proc p[N: static int](x : a[N]) = discard x.f0[0] + template check(x, s: untyped) = + p(x) + doAssert x is a[s] + doAssert x.N == s + doAssert typeof(x).N == s + doAssert x.f0 == default(array[s, int]) + doAssert x.f0.len == s + proc p2[N: static int](y : a[N]) {.gensym.} = + doAssert y is a[s] + doAssert y.N == s + doAssert typeof(y).N == s + doAssert y.f0 == default(array[s, int]) + doAssert y.f0.len == s + p2(x) + proc p3(z: typeof(x)) {.gensym.} = discard + p3(default(a[s])) + proc p[N: static int](x : b[N]) = + x.f0.check(8) + x.f1.check(2) + x.f2.check(8) + x.f3.check(8) + x.f4.check(4) + x.f5.check(8) + x.f6.check(8) + x.f7.check(8) + x.f8.check(8) + + var x: b[4] + x.p() + +block: # issue #1969 + type ZeroGenerator = object + proc next(g: ZeroGenerator): int = 0 + # This compiles. + type TripleOfInts = tuple + a, b, c: typeof(new(ZeroGenerator)[].next) + # This raises a compiler error before it's even instantiated. + # The `new` proc can't be resolved because `Generator` is not defined. + type TripleLike[Generator] = tuple + a, b, c: typeof(new(Generator)[].next) + +import std/atomics + +block: # issue #12720 + const CacheLineSize = 128 + type + Enqueueable = concept x, type T + x is ptr + x.next is Atomic[pointer] + MyChannel[T: Enqueueable] = object + pad: array[CacheLineSize - sizeof(default(T)[]), byte] + dummy: typeof(default(T)[]) + +block: # issue #12714 + type + Enqueueable = concept x, type T + x is ptr + x.next is Atomic[pointer] + MyChannel[T: Enqueueable] = object + dummy: type(default(T)[]) + +block: # issue #24044 + type ArrayBuf[N: static int, T = byte] = object + buf: array[N, T] + template maxLen(T: type): int = + sizeof(T) * 2 + type MyBuf[I] = ArrayBuf[maxLen(I)] + var v: MyBuf[int] + +block: # issue #15959 + proc my[T](a: T): typeof(a[0]) = discard + proc my2[T](a: T): array[sizeof(a[0]), T] = discard + proc byLent2[T](a: T): lent type(a[0]) = a[0] # Error: type mismatch: got <T, int literal(0)> + proc byLent3[T](a: T): lent typeof(a[0]) = a[0] # ditto + proc byLent4[T](a: T): lent[type(a[0])] = a[0] # Error: no generic parameters allowed for lent + var x = @[1, 2, 3] + doAssert my(x) is int + doAssert my2(x) is array[sizeof(int), seq[int]] + doAssert byLent2(x) == 1 + doAssert byLent2(x) is lent int + doAssert byLent3(x) == 1 + doAssert byLent3(x) is lent int + doAssert byLent4(x) == 1 + doAssert byLent4(x) is lent int + proc fn[U](a: U): auto = a + proc my3[T](a: T, b: typeof(fn(a))) = discard + my3(x, x) + doAssert not compiles(my3(x, x[0])) + +block: # issue #22342, type section version of #22607 + type GenAlias[isInt: static bool] = ( + when isInt: + int + else: + float + ) + doAssert GenAlias[true] is int + doAssert GenAlias[false] is float + proc foo(T: static bool): GenAlias[T] = discard + doAssert foo(true) is int + doAssert foo(false) is float + proc foo[T: static bool](v: var GenAlias[T]) = + v += 1 + var x: int + foo[true](x) + doAssert not compiles(foo[false](x)) + foo[true](x) + doAssert x == 2 + var y: float + foo[false](y) + doAssert not compiles(foo[true](y)) + foo[false](y) + doAssert y == 2 + +block: # `when`, test no constant semchecks + type Foo[T] = ( + when false: + {.error: "bad".} + elif defined(neverDefined): + {.error: "bad 2".} + else: + T + ) + var x: Foo[int] + type Bar[T] = ( + when true: + T + elif defined(js): + {.error: "bad".} + else: + {.error: "bad 2".} + ) + var y: Bar[int] + +block: # weird regression + type + Foo[T] = distinct int + Bar[T, U] = distinct int + proc foo[T, U](x: static Foo[T], y: static Bar[T, U]): Foo[T] = + # signature gives: + # Error: cannot instantiate Bar + # got: <typedesc[T], U> + # but expected: <T, U> + x + doAssert foo(Foo[int](1), Bar[int, int](2)).int == 1 + +block: # issue #24090 + type M[V] = object + template y[V](N: type M, v: V): M[V] = default(M[V]) + proc d(x: int | int, f: M[int] = M.y(0)) = discard + d(0, M.y(0)) + type Foo[T] = object + x: typeof(M.y(default(T))) + var a: Foo[int] + doAssert a.x is M[int] + var b: Foo[float] + doAssert b.x is M[float] + doAssert not (compiles do: + type Bar[T] = object + x: typeof(M()) # actually fails here immediately + var bar: Bar[int]) + doAssert not (compiles do: + type Bar[T] = object + x: typeof(default(M)) + var bar: Bar[int] + # gives "undeclared identifier x" because of #24091, + # normally it should fail in the line above + echo bar.x) + proc foo[T: M](x: T = default(T)) = discard x + foo[M[int]]() + doAssert not compiles(foo()) + +block: # above but encountered by sigmatch using replaceTypeVarsN + type Opt[T] = object + x: T + proc none[T](x: type Opt, y: typedesc[T]): Opt[T] = discard + proc foo[T](x: T, a = Opt.none(int)) = discard + foo(1, a = Opt.none(int)) + foo(1) + +block: # real version of above + type Opt[T] = object + x: T + template none(x: type Opt, T: type): Opt[T] = Opt[T]() + proc foo[T](x: T, a = Opt.none(int)) = discard + foo(1, a = Opt.none(int)) + foo(1) + +block: # issue #20880 + type + Child[n: static int] = object + data: array[n, int] + Parent[n: static int] = object + child: Child[3*n] + const n = 3 + doAssert $(typeof Parent[n*3]()) == "Parent[9]" + doAssert $(typeof Parent[1]().child) == "Child[3]" + doAssert Parent[1]().child.data.len == 3 + +{.experimental: "dynamicBindSym".} +block: # issue #16774 + type SecretWord = distinct uint64 + const WordBitWidth = 8 * sizeof(uint64) + func wordsRequired(bits: int): int {.compileTime.} = + ## Compute the number of limbs required + # from the **announced** bit length + (bits + WordBitWidth - 1) div WordBitWidth + type + Curve = enum BLS12_381 + BigInt[bits: static int] = object + limbs: array[bits.wordsRequired, SecretWord] + const BLS12_381_Modulus = default(BigInt[381]) + macro Mod(C: static Curve): untyped = + ## Get the Modulus associated to a curve + result = bindSym($C & "_Modulus") + macro getCurveBitwidth(C: static Curve): untyped = + result = nnkDotExpr.newTree( + getAST(Mod(C)), + ident"bits" + ) + type Fp[C: static Curve] = object + ## Finite Fields / Modular arithmetic + ## modulo the curve modulus + mres: BigInt[getCurveBitwidth(C)] + var x: Fp[BLS12_381] + doAssert x.mres.limbs.len == wordsRequired(getCurveBitWidth(BLS12_381)) + # minimized, as if we haven't tested it already: + macro makeIntLit(c: static int): untyped = + result = newLit(c) + type Test[T: static int] = object + myArray: array[makeIntLit(T), int] + var y: Test[2] + doAssert y.myArray.len == 2 + var z: Test[4] + doAssert z.myArray.len == 4 + +block: # issue #16175 + type + Thing[D: static uint] = object + when D == 0: + kid: char + else: + kid: Thing[D-1] + var t2 = Thing[3]() + doAssert t2.kid is Thing[2.uint] + doAssert t2.kid.kid is Thing[1.uint] + doAssert t2.kid.kid.kid is Thing[0.uint] + doAssert t2.kid.kid.kid.kid is char + var s = Thing[1]() + doAssert s.kid is Thing[0.uint] + doAssert s.kid.kid is char diff --git a/tests/generics/twrong_explicit_typeargs.nim b/tests/generics/twrong_explicit_typeargs.nim deleted file mode 100644 index e47b38e99..000000000 --- a/tests/generics/twrong_explicit_typeargs.nim +++ /dev/null @@ -1,16 +0,0 @@ -discard """ - errormsg: "cannot instantiate: 'newImage[string]'" - line: 16 -""" - -# bug #4084 -type - Image[T] = object - data: seq[T] - -proc newImage[T: int32|int64](w, h: int): ref Image[T] = - new(result) - result.data = newSeq[T](w * h) - -var correct = newImage[int32](320, 200) -var wrong = newImage[string](320, 200) diff --git a/tests/generics/twrong_generic_object.nim b/tests/generics/twrong_generic_object.nim index 442b89ea1..4951f735f 100644 --- a/tests/generics/twrong_generic_object.nim +++ b/tests/generics/twrong_generic_object.nim @@ -1,6 +1,6 @@ discard """ - errormsg: "cannot instantiate: 'GenericNodeObj[T]'; Maybe generic arguments are missing?" - line: 21 + errormsg: "'Node' is not a concrete type" + line: 11 """ # bug #2509 type diff --git a/tests/import/t23167.nim b/tests/import/t23167.nim new file mode 100644 index 000000000..0891ee2af --- /dev/null +++ b/tests/import/t23167.nim @@ -0,0 +1,5 @@ +# bug #23167 +template sharedImport() = + import std / os + +sharedImport() diff --git a/tests/init/tcompiles.nim b/tests/init/tcompiles.nim index e86cad1e2..67e17b241 100644 --- a/tests/init/tcompiles.nim +++ b/tests/init/tcompiles.nim @@ -89,3 +89,13 @@ block: catchError: echo bar() + +block: + proc foo(x: ptr int) = + discard + + proc main = + var s: int + foo(addr s) + + main() diff --git a/tests/init/treturns.nim b/tests/init/treturns.nim index 77469472a..18cebe0b1 100644 --- a/tests/init/treturns.nim +++ b/tests/init/treturns.nim @@ -91,3 +91,16 @@ block: return discard hasImportStmt() + +block: + block: + proc foo(x: var int) = + discard + + proc main = + var s: int + foo(s)#[tt.Warning + ^ use explicit initialization of 's' for clarity [Uninit]]# + + main() + diff --git a/tests/int/t1.nim b/tests/int/t1.nim index 36402da07..6e5cdc8d4 100644 --- a/tests/int/t1.nim +++ b/tests/int/t1.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c cpp" +""" + doAssert typeOf(1.int64 + 1.int) is int64 doAssert typeOf(1.uint64 + 1.uint) is uint64 doAssert int64 is SomeNumber @@ -12,3 +16,46 @@ doAssert typeOf(myInt16 + myInt) is int # of type `int` doAssert typeOf(myInt16 + 2i32) is int32 # of type `int32` doAssert int32 isnot int64 doAssert int32 isnot int + +block: # bug #23947 + template foo = + let test_u64 : uint64 = 0xFF07.uint64 + let test_u8 : uint8 = test_u64.uint8 + # Error: illegal conversion from '65287' to '[0..255]' + doAssert test_u8 == 7 + + static: foo() + foo() + +block: + # bug #22085 + const + x = uint32(uint64.high) # vm error + u = uint64.high + v = uint32(u) # vm error + + let + z = uint64.high + y = uint32(z) # runtime ok + + let + w = uint32(uint64.high) # semfold error + + doAssert x == w + doAssert v == y + + # bug #14522 + doAssert 0xFF000000_00000000.uint64 == 18374686479671623680'u64 + +block: # bug #23954 + let testRT_u8 : uint8 = 0x107.uint8 + doAssert testRT_u8 == 7 + const testCT_u8 : uint8 = 0x107.uint8 + doAssert testCT_u8 == 7 + +block: # issue #24104 + type P = distinct uint # uint, uint8, uint16, uint32, uint64 + let v = 0.P + case v + of 0.P: discard + else: discard diff --git a/tests/int/tints.nim b/tests/int/tints.nim index a7d27d736..773e8ccad 100644 --- a/tests/int/tints.nim +++ b/tests/int/tints.nim @@ -1,5 +1,5 @@ discard """ - matrix: "; --backend:js --jsbigint64:off; --backend:js --jsbigint64:on" + matrix: "; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on" output: ''' 0 0 0 0 @@ -7,6 +7,7 @@ Success''' """ # Test the different integer operations + import std/private/jsutils var testNumber = 0 @@ -141,4 +142,9 @@ block: # shl when not defined(js) or (defined(js) and compileOption("jsbigint64")): doAssert u64 shl 1 == u64 - 1 +block: # bug #23378 + var neg = -1 # prevent compile-time evaluation + let n = abs BiggestInt neg + doAssert n == 1 + echo("Success") #OUT Success diff --git a/tests/int/tunsignedinc.nim b/tests/int/tunsignedinc.nim index 9d1a4bbb4..9392f1b74 100644 --- a/tests/int/tunsignedinc.nim +++ b/tests/int/tunsignedinc.nim @@ -32,3 +32,9 @@ block t4175: const j = 0u - 1u doAssert i == j doAssert j + 1u == 0u + +block: # https://forum.nim-lang.org/t/12465#76998 + var a: int = 1 + var x: uint8 = 1 + a.inc(x) # Error: type mismatch + doAssert a == 2 diff --git a/tests/int/twrongexplicitvarconv.nim b/tests/int/twrongexplicitvarconv.nim new file mode 100644 index 000000000..79f770e8e --- /dev/null +++ b/tests/int/twrongexplicitvarconv.nim @@ -0,0 +1,16 @@ +discard """ + action: reject + nimout: ''' + but expression 'int(a)' is immutable, not 'var' +''' +""" + +proc `++`(n: var int) = + n += 1 + +var a: int32 = 15 + +++int(a) #[tt.Error +^ type mismatch: got <int>]# + +echo a diff --git a/tests/int/twrongvarconv.nim b/tests/int/twrongvarconv.nim new file mode 100644 index 000000000..db6ac2c53 --- /dev/null +++ b/tests/int/twrongvarconv.nim @@ -0,0 +1,9 @@ +proc `++`(n: var int) = + n += 1 + +var a: int32 = 15 + +++a #[tt.Error +^ type mismatch: got <int32>]# + +echo a diff --git a/tests/iter/t1550.nim b/tests/iter/t1550.nim index 8ad96f0da..c971943ee 100644 --- a/tests/iter/t1550.nim +++ b/tests/iter/t1550.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c js" +""" + type A[T] = iterator(x: T): T {.gcsafe, closure.} diff --git a/tests/iter/t21306.nim b/tests/iter/t21306.nim index 43fea9c80..4d0396294 100644 --- a/tests/iter/t21306.nim +++ b/tests/iter/t21306.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c js" +""" + # bug #21306 type FutureState {.pure.} = enum diff --git a/tests/iter/t22619.nim b/tests/iter/t22619.nim index 04e707633..6a98391f3 100644 --- a/tests/iter/t22619.nim +++ b/tests/iter/t22619.nim @@ -52,8 +52,12 @@ block: var numDestroy = 0 - proc `=destroy`(x: Value) = - inc numDestroy + when defined(gcRefc): + proc `=destroy`(x: var Value) = + inc numDestroy + else: + proc `=destroy`(x: Value) = + inc numDestroy iterator iter(s: seq[Value]): int {.closure.} = # because it is used across yields, `s2` is lifted into the iterator's diff --git a/tests/iter/t2771.nim b/tests/iter/t2771.nim index 49befb0a9..71a8a9dcd 100644 --- a/tests/iter/t2771.nim +++ b/tests/iter/t2771.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c js" +""" + template t1(i: int): int= i+1 template t2(i: int): int= diff --git a/tests/iter/tanoniter1.nim b/tests/iter/tanoniter1.nim index 9f0d0a74b..fee16497f 100644 --- a/tests/iter/tanoniter1.nim +++ b/tests/iter/tanoniter1.nim @@ -1,4 +1,5 @@ discard """ + targets: "c js" output: '''1 2 3 diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim index 85611373c..4a2639852 100644 --- a/tests/iter/tclosureiters.nim +++ b/tests/iter/tclosureiters.nim @@ -1,4 +1,5 @@ discard """ + targets: "c js" output: '''0 1 2 @@ -152,15 +153,18 @@ iterator filesIt(path: string): auto {.closure.} = yield prefix / f # bug #13815 -var love = iterator: int {.closure.} = - yield cast[type( - block: - var a = 0 - yield a - a)](0) - -for i in love(): - echo i +when not defined(js): + var love = iterator: int {.closure.} = + yield cast[type( + block: + var a = 0 + yield a + a)](0) + + for i in love(): + echo i +else: + echo 0 # bug #18474 iterator pairs(): (int, int) {.closure.} = diff --git a/tests/iter/titer.nim b/tests/iter/titer.nim index f32bec2fb..b03d43f36 100644 --- a/tests/iter/titer.nim +++ b/tests/iter/titer.nim @@ -127,3 +127,21 @@ block: # bug #21110 e() static: foo() foo() + + +# bug #15924 +iterator walk(): (int, int) {.closure.} = + yield (10,11) + +for (i,j) in walk(): + doAssert i == 10 + +proc main123() = + let x = false + iterator it(): (bool, bool) {.closure.} = # normally {.closure.} here makes #21476 work + discard x + + for (_, _) in it(): + discard + +main123() diff --git a/tests/iter/titer11.nim b/tests/iter/titer11.nim index 2b39c74f7..153b3c29a 100644 --- a/tests/iter/titer11.nim +++ b/tests/iter/titer11.nim @@ -1,4 +1,5 @@ discard """ +targets: "c js" output: ''' [ 1 diff --git a/tests/iter/titer12.nim b/tests/iter/titer12.nim index f7fc64da4..f264a0e82 100644 --- a/tests/iter/titer12.nim +++ b/tests/iter/titer12.nim @@ -1,4 +1,5 @@ discard """ +targets: "c js" output: ''' Selecting 2 1.0 diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim index adba8a8e3..c82b3902d 100644 --- a/tests/iter/titer_issues.nim +++ b/tests/iter/titer_issues.nim @@ -1,4 +1,5 @@ discard """ + target: "c js" output: ''' 0 1 @@ -392,3 +393,19 @@ iterator tryFinally() {.closure.} = var x = tryFinally x() + +block: # bug #24033 + type Query = ref object + + iterator pairs(query: Query): (int, (string, float32)) = + var output: (int, (string, float32)) = (0, ("foo", 3.14)) + for id in @[0, 1, 2]: + output[0] = id + yield output + + var collections: seq[(int, string, string)] + + for id, (str, num) in Query(): + collections.add (id, str, $num) + + doAssert collections[1] == (1, "foo", "3.14") diff --git a/tests/iter/titervaropenarray.nim b/tests/iter/titervaropenarray.nim index ad1192bd8..b2fe71ceb 100644 --- a/tests/iter/titervaropenarray.nim +++ b/tests/iter/titervaropenarray.nim @@ -1,6 +1,6 @@ discard """ output: "123" - targets: "c" + targets: "c cpp" """ # Try to break the transformation pass: iterator iterAndZero(a: var openArray[int]): int = diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim index 47ba6efa6..e51ab7f0d 100644 --- a/tests/iter/tyieldintry.nim +++ b/tests/iter/tyieldintry.nim @@ -1,5 +1,5 @@ discard """ - matrix: "; --experimental:strictdefs" + matrix: "; --experimental:strictdefs; -d:nimOptIters" targets: "c cpp" """ @@ -504,3 +504,26 @@ block: # void iterator except: discard var a = it + +if defined(nimOptIters): # Locals present in only 1 state should be on the stack + proc checkOnStack(a: pointer, shouldBeOnStack: bool) = + # Quick and dirty way to check if a points to stack + var dummy = 0 + let dummyAddr = addr dummy + let distance = abs(cast[int](dummyAddr) - cast[int](a)) + const requiredDistance = 300 + if shouldBeOnStack: + doAssert(distance <= requiredDistance, "a is not on stack, but should") + else: + doAssert(distance > requiredDistance, "a is on stack, but should not") + + iterator it(): int {.closure.} = + var a = 1 + var b = 2 + var c {.liftLocals.} = 3 + checkOnStack(addr a, true) + checkOnStack(addr b, false) + checkOnStack(addr c, false) + yield a + yield b + test(it, 1, 2) diff --git a/tests/js/t21439.nim b/tests/js/t21439.nim index 972356cd0..3caeb090a 100644 --- a/tests/js/t21439.nim +++ b/tests/js/t21439.nim @@ -1,8 +1,5 @@ -discard """ - action: "compile" -""" - proc test(a: openArray[string]): proc = + let a = @a result = proc = for i in a: discard i diff --git a/tests/js/t7109.nim b/tests/js/t7109.nim index 0d071dbbf..a1a3b718e 100644 --- a/tests/js/t7109.nim +++ b/tests/js/t7109.nim @@ -1,8 +1,8 @@ -discard """ - errormsg: "Closure iterators are not supported by JS backend!" -""" - iterator iter*(): int {.closure.} = yield 3 var x = iter +doAssert x() == 3 + +let fIt = iterator(): int = yield 70 +doAssert fIt() == 70 diff --git a/tests/js/tcodegendeclproc.nim b/tests/js/tcodegendeclproc.nim index 3acf0bc13..33064bdf1 100644 --- a/tests/js/tcodegendeclproc.nim +++ b/tests/js/tcodegendeclproc.nim @@ -3,7 +3,7 @@ discard """ -1 8 ''' - ccodecheck: "'console.log(-1); function fac_' \\d+ '(n_' \\d+ ')'" + ccodecheck: "'console.log(-1); function fac__tcodegendeclproc_u1(n_p0)'" """ proc fac(n: int): int {.codegenDecl: "console.log(-1); function $2($3)".} = return n diff --git a/tests/js/tdanger.nim b/tests/js/tdanger.nim new file mode 100644 index 000000000..9088859a8 --- /dev/null +++ b/tests/js/tdanger.nim @@ -0,0 +1,17 @@ +discard """ + matrix: ";--d:danger" +""" + +block: + proc foo() = + var name = int64(12) + var x = uint32(name) + var m = x + 12 + + var y = int32(name) + var n = y + 1 + + doAssert m == uint32(n + 11) + + + foo() diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index b54d13e43..f27ea5546 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--legacy:jsnolambdalifting;" output: ''' 3 2 @@ -180,7 +181,7 @@ block: # Test lit block: # Test bindMethod type TestObject = object a: int - onWhatever: proc(e: int): int + onWhatever: proc(e: int): int {.nimcall.} proc handleWhatever(this: TestObject, e: int): int = e + this.a block: @@ -269,5 +270,5 @@ block: # test ** block: # issue #21208 type MyEnum = enum baz var obj: JsObject - {.emit: "`obj` = {bar: {baz: 123}}".} + {.emit: "`obj` = {bar: {baz: 123}};".} discard obj.bar.baz diff --git a/tests/js/tjsffi_old.nim b/tests/js/tjsffi_old.nim index 19f30ee2c..378003f4e 100644 --- a/tests/js/tjsffi_old.nim +++ b/tests/js/tjsffi_old.nim @@ -279,8 +279,8 @@ block: block: type TestObject = object a: int - onWhatever: proc(e: int): int - proc handleWhatever(this: TestObject, e: int): int = + onWhatever: proc(e: int): int {.nimcall.} + proc handleWhatever(this: TestObject, e: int): int {.nimcall.} = e + this.a proc test(): bool = let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever)) diff --git a/tests/js/tjsnimscombined.nim b/tests/js/tjsnimscombined.nim new file mode 100644 index 000000000..4d3e6c453 --- /dev/null +++ b/tests/js/tjsnimscombined.nim @@ -0,0 +1 @@ +import std/jsffi diff --git a/tests/js/tjsnimscombined.nims b/tests/js/tjsnimscombined.nims new file mode 100644 index 000000000..01b93d3fa --- /dev/null +++ b/tests/js/tjsnimscombined.nims @@ -0,0 +1 @@ +# test the condition where both `js` and `nimscript` are defined (nimscript receives priority) diff --git a/tests/js/tos.nim b/tests/js/tos.nim index bfe3cd9b4..40fb52bcf 100644 --- a/tests/js/tos.nim +++ b/tests/js/tos.nim @@ -13,11 +13,9 @@ block: doAssert not "foo".isAbsolute doAssert relativePath("", "bar") == "" doAssert normalizedPath(".///foo//./") == "foo" - let cwd = getCurrentDir() - let isWindows = '\\' in cwd - # defined(windows) doesn't work with -d:nodejs but should - # these actually break because of that (see https://github.com/nim-lang/Nim/issues/13469) - if not isWindows: + when nimvm: discard + else: + let cwd = getCurrentDir() doAssert cwd.isAbsolute - doAssert relativePath(getCurrentDir() / "foo", "bar") == "../foo" + doAssert relativePath(getCurrentDir() / "foo", "bar") == ".." / "foo" diff --git a/tests/js/ttypedarray.nim b/tests/js/ttypedarray.nim index 08b5fcdde..4807cb103 100644 --- a/tests/js/ttypedarray.nim +++ b/tests/js/ttypedarray.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--jsbigint64:off; --jsbigint64:on" + matrix: "--jsbigint64:off -d:nimStringHash2; --jsbigint64:on" """ import std/private/jsutils diff --git a/tests/lent/tlent_from_var.nim b/tests/lent/tlent_from_var.nim index d61ff6dc0..1fb3d0c17 100644 --- a/tests/lent/tlent_from_var.nim +++ b/tests/lent/tlent_from_var.nim @@ -50,3 +50,58 @@ template get*[T: not void](self: Opt[T]): T = self.value() method connect*( self: Opt[(int, int)]) = discard self.get()[0] + +block: # bug #23454 + type + Letter = enum + A + + LetterPairs = object + values: seq[(Letter, string)] + + iterator items(list: var LetterPairs): lent (Letter, string) = + for item in list.values: + yield item + + var instance = LetterPairs(values: @[(A, "foo")]) + + for (a, _) in instance: + case a + of A: discard + +block: # bug #23454 + type + Letter = enum + A + + LetterPairs = object + values: seq[(Letter, string)] + + iterator items(list: var LetterPairs): var (Letter, string) = + for item in list.values.mItems: + yield item + + var instance = LetterPairs(values: @[(A, "foo")]) + + for (a, _) in instance: + case a + of A: discard + +block: # bug #24034 + type T = object + v: array[100, byte] + + + iterator pairs(t: T): (int, lent array[100, byte]) = + yield (0, t.v) + + + block: + for a, b in default(T): + doAssert a == 0 + doAssert b.len == 100 + + block: + for (a, b) in pairs(default(T)): + doAssert a == 0 + doAssert b.len == 100 diff --git a/tests/lent/tvm.nim b/tests/lent/tvm.nim new file mode 100644 index 000000000..5df1d1270 --- /dev/null +++ b/tests/lent/tvm.nim @@ -0,0 +1,21 @@ +block: # issue #17527 + iterator items2[IX, T](a: array[IX, T]): lent T {.inline.} = + var i = low(IX) + if i <= high(IX): + while true: + yield a[i] + if i >= high(IX): break + inc(i) + + proc main() = + var s: seq[string] = @[] + for i in 0..<3: + for (key, val) in items2([("any", "bar")]): + s.add $(i, key, val) + doAssert s == @[ + "(0, \"any\", \"bar\")", + "(1, \"any\", \"bar\")", + "(2, \"any\", \"bar\")" + ] + + static: main() diff --git a/tests/lookups/issue_23032/deep_scope.nim b/tests/lookups/issue_23032/deep_scope.nim new file mode 100644 index 000000000..3e25809a7 --- /dev/null +++ b/tests/lookups/issue_23032/deep_scope.nim @@ -0,0 +1,2 @@ +type A*[T] = object +proc foo*(a: A[int]): bool = false diff --git a/tests/lookups/issue_23172/m23172.nim b/tests/lookups/issue_23172/m23172.nim new file mode 100644 index 000000000..36af48761 --- /dev/null +++ b/tests/lookups/issue_23172/m23172.nim @@ -0,0 +1,6 @@ +type + Foo* = object + Bar* = object + +func `$`*(x: Foo | Bar): string = + "X" diff --git a/tests/lookups/mambsym3.nim b/tests/lookups/mambsym3.nim new file mode 100644 index 000000000..946a5ff29 --- /dev/null +++ b/tests/lookups/mambsym3.nim @@ -0,0 +1,4 @@ +# Module A +var x*: string +proc foo*(a: string) = + echo "A: ", a diff --git a/tests/lookups/mambsym4.nim b/tests/lookups/mambsym4.nim new file mode 100644 index 000000000..ed66cc16d --- /dev/null +++ b/tests/lookups/mambsym4.nim @@ -0,0 +1,4 @@ +# Module B +var x*: int +proc foo*(b: int) = + echo "B: ", b diff --git a/tests/lookups/mambtype1.nim b/tests/lookups/mambtype1.nim new file mode 100644 index 000000000..47046142e --- /dev/null +++ b/tests/lookups/mambtype1.nim @@ -0,0 +1 @@ +type K* = object diff --git a/tests/lookups/mambtype2.nim b/tests/lookups/mambtype2.nim new file mode 100644 index 000000000..cf622466b --- /dev/null +++ b/tests/lookups/mambtype2.nim @@ -0,0 +1,4 @@ +import ./mambtype1 +export mambtype1 +template K*(kind: static int): auto = typedesc[mambtype1.K] +template B*(kind: static int): auto = typedesc[mambtype1.K] diff --git a/tests/lookups/mdisambsym1.nim b/tests/lookups/mdisambsym1.nim new file mode 100644 index 000000000..b8beca035 --- /dev/null +++ b/tests/lookups/mdisambsym1.nim @@ -0,0 +1,2 @@ +proc count*(s: string): int = + s.len diff --git a/tests/lookups/mdisambsym2.nim b/tests/lookups/mdisambsym2.nim new file mode 100644 index 000000000..1e056311d --- /dev/null +++ b/tests/lookups/mdisambsym2.nim @@ -0,0 +1 @@ +var count*: int = 10 diff --git a/tests/lookups/mdisambsym3.nim b/tests/lookups/mdisambsym3.nim new file mode 100644 index 000000000..95bd19702 --- /dev/null +++ b/tests/lookups/mdisambsym3.nim @@ -0,0 +1 @@ +const count* = 3.142 diff --git a/tests/lookups/mmacroamb.nim b/tests/lookups/mmacroamb.nim new file mode 100644 index 000000000..107e51055 --- /dev/null +++ b/tests/lookups/mmacroamb.nim @@ -0,0 +1,10 @@ +# issue #12732 + +import std/macros +const getPrivate3_tmp* = 0 +const foobar1* = 0 # comment this or make private and it'll compile fine +macro foobar4*(): untyped = + newLit "abc" +template currentPkgDir2*: string = foobar4() +macro currentPkgDir2*(dir: string): untyped = + newLit "abc2" diff --git a/tests/lookups/t23032.nim b/tests/lookups/t23032.nim new file mode 100644 index 000000000..144abcb05 --- /dev/null +++ b/tests/lookups/t23032.nim @@ -0,0 +1,13 @@ +discard """ +action: "run" +outputsub: "proc (a: A[system.float]): bool{.noSideEffect, gcsafe.}" +""" + +import issue_23032/deep_scope + +proc foo(a: A[float]):bool = true + +let p: proc = foo +echo p.typeof +doAssert p(A[float]()) == true +doAssert compiles(doAssert p(A[int]()) == true) == false diff --git a/tests/lookups/t23172.nim b/tests/lookups/t23172.nim new file mode 100644 index 000000000..9edf9905a --- /dev/null +++ b/tests/lookups/t23172.nim @@ -0,0 +1,9 @@ +import issue_23172/m23172 + +type FooX = distinct Foo + +func `$`*(x: FooX): string = + $m23172.Foo(x) + +var a: FooX +doAssert $a == "X" diff --git a/tests/lookups/t23749.nim b/tests/lookups/t23749.nim new file mode 100644 index 000000000..650f04ea4 --- /dev/null +++ b/tests/lookups/t23749.nim @@ -0,0 +1,37 @@ +discard """ + action: compile +""" + +{.pragma: callback, gcsafe, raises: [].} + +type + DataProc* = proc(val: openArray[byte]) {.callback.} + GetProc = proc (db: RootRef, key: openArray[byte], onData: DataProc): bool {.nimcall, callback.} + KvStoreRef* = ref object + obj: RootRef + getProc: GetProc + +template get(dbParam: KvStoreRef, key: openArray[byte], onData: untyped): bool = + let db = dbParam + db.getProc(db.obj, key, onData) + +func decode(input: openArray[byte], maxSize = 128): seq[byte] = + @[] + +proc getSnappySSZ(db: KvStoreRef, key: openArray[byte]): string = + var status = "not found" + proc decode(data: openArray[byte]) = + status = + if true: "found" + else: "corrupted" + discard db.get(key, decode) + status + + +var ksr: KvStoreRef +var k = [byte(1), 2, 3, 4, 5] + +proc foo(): string = + getSnappySSZ(ksr, toOpenArray(k, 1, 3)) + +echo foo() diff --git a/tests/lookups/tambiguousemit.nim b/tests/lookups/tambiguousemit.nim index 0ebd0a255..4f4bacce4 100644 --- a/tests/lookups/tambiguousemit.nim +++ b/tests/lookups/tambiguousemit.nim @@ -7,6 +7,6 @@ proc foo(x: int) = discard proc foo(x: float) = discard {.emit: ["// ", foo].} #[tt.Error - ^ ambiguous identifier 'foo' -- use one of the following: + ^ ambiguous identifier: 'foo' -- use one of the following: tambiguousemit.foo: proc (x: int){.noSideEffect, gcsafe.} tambiguousemit.foo: proc (x: float){.noSideEffect, gcsafe.}]# diff --git a/tests/lookups/tambprocvar.nim b/tests/lookups/tambprocvar.nim index 33323fbb2..f5c3fde4f 100644 --- a/tests/lookups/tambprocvar.nim +++ b/tests/lookups/tambprocvar.nim @@ -2,7 +2,7 @@ discard """ action: reject cmd: "nim check $file" nimout: ''' -tambprocvar.nim(15, 11) Error: ambiguous identifier 'foo' -- use one of the following: +tambprocvar.nim(15, 11) Error: ambiguous identifier: 'foo' -- use one of the following: tambprocvar.foo: proc (x: int){.noSideEffect, gcsafe.} tambprocvar.foo: proc (x: float){.noSideEffect, gcsafe.} ''' @@ -16,4 +16,4 @@ block: block: let x = `+` #[tt.Error - ^ ambiguous identifier '+' -- use one of the following:]# + ^ ambiguous identifier: '+' -- use one of the following:]# diff --git a/tests/lookups/tambsym3.nim b/tests/lookups/tambsym3.nim index 6e7589cd8..6bbebca10 100644 --- a/tests/lookups/tambsym3.nim +++ b/tests/lookups/tambsym3.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "ambiguous identifier 'mDec' -- use one of the following:" + errormsg: "ambiguous identifier: 'mDec' -- use one of the following:" file: "tambsym3.nim" line: 11 """ diff --git a/tests/lookups/tambsymmanual.nim b/tests/lookups/tambsymmanual.nim new file mode 100644 index 000000000..3ad91b8cc --- /dev/null +++ b/tests/lookups/tambsymmanual.nim @@ -0,0 +1,25 @@ +discard """ + output: ''' +A: abc +B: 123 +A: def +4 +''' +""" +# Module C +import mambsym3, mambsym4 + +foo("abc") # A: abc +foo(123) # B: 123 +let inferred: proc (x: string) = foo +foo("def") # A: def + +doAssert not compiles(write(stdout, x)) # error: x is ambiguous +write(stdout, mambsym3.x) # no error: qualifier used + +proc bar(a: int): int = a + 1 +doAssert bar(x) == x + 1 # no error: only A.x of type int matches + +var x = 4 +write(stdout, x) # not ambiguous: uses the module C's x +echo() # for test output diff --git a/tests/lookups/tambtype.nim b/tests/lookups/tambtype.nim new file mode 100644 index 000000000..a292db83a --- /dev/null +++ b/tests/lookups/tambtype.nim @@ -0,0 +1,20 @@ +import ./mambtype2 + +block: # issue #23893 + discard default(K(0)) # works + discard default(mambtype2.B(0)) # works + discard default(mambtype2.K(0)) # doesn't work + +block: # issue #23898, in template + template r() = + discard default(B(0)) # compiles + discard default(mambtype2.B(0)) # compiles + discard default(K(0)) # does not compile + r() + +block: # in generics + proc foo[T]() = + discard default(B(0)) # compiles + discard default(mambtype2.B(0)) # compiles + discard default(K(0)) # does not compile + foo[int]() diff --git a/tests/lookups/tdisambsym.nim b/tests/lookups/tdisambsym.nim new file mode 100644 index 000000000..678528ad6 --- /dev/null +++ b/tests/lookups/tdisambsym.nim @@ -0,0 +1,8 @@ +# issue #15247 + +import mdisambsym1, mdisambsym2, mdisambsym3 + +proc twice(n: int): int = + n*2 + +doAssert twice(count) == 20 diff --git a/tests/lookups/tenumlocalsym.nim b/tests/lookups/tenumlocalsym.nim new file mode 100644 index 000000000..575227c07 --- /dev/null +++ b/tests/lookups/tenumlocalsym.nim @@ -0,0 +1,22 @@ +block: + type Enum = enum a, b + + block: + let a = b + let x: Enum = a + doAssert x == b + +block: + type + Enum = enum + a = 2 + b = 10 + + iterator items2(): Enum = + for a in [a, b]: + yield a + + var s = newSeq[Enum]() + for i in items2(): + s.add i + doAssert s == @[a, b] diff --git a/tests/lookups/tmacroamb.nim b/tests/lookups/tmacroamb.nim new file mode 100644 index 000000000..854017e72 --- /dev/null +++ b/tests/lookups/tmacroamb.nim @@ -0,0 +1,5 @@ +# issue #12732 + +import mmacroamb +const s0 = currentPkgDir2 #[tt.Error + ^ ambiguous identifier: 'currentPkgDir2' -- use one of the following:]# diff --git a/tests/lookups/tmoduleclash1.nim b/tests/lookups/tmoduleclash1.nim new file mode 100644 index 000000000..7058f691e --- /dev/null +++ b/tests/lookups/tmoduleclash1.nim @@ -0,0 +1,13 @@ +# issue #23596 + +import std/heapqueue +type Algo = enum heapqueue, quick +when false: + let x = heapqueue +let y: Algo = heapqueue +proc bar*(algo=quick) = + var x: HeapQueue[int] + case algo + of heapqueue: echo 1 # `Algo.heapqueue` works on devel + of quick: echo 2 + echo x.len diff --git a/tests/lookups/tmoduleclash2.nim b/tests/lookups/tmoduleclash2.nim new file mode 100644 index 000000000..958da2299 --- /dev/null +++ b/tests/lookups/tmoduleclash2.nim @@ -0,0 +1,6 @@ +import std/heapqueue +proc heapqueue(x: int) = discard +let x: proc (x: int) = heapqueue +let y: proc = heapqueue +when false: + let z = heapqueue diff --git a/tests/macros/t14329.nim b/tests/macros/t14329.nim new file mode 100644 index 000000000..b5606424a --- /dev/null +++ b/tests/macros/t14329.nim @@ -0,0 +1,4 @@ +import macros + +macro myMacro(n) = + let x = if true: newLit"test" else: error "error", n diff --git a/tests/macros/t15751.nim b/tests/macros/t15751.nim new file mode 100644 index 000000000..fcabb2f9e --- /dev/null +++ b/tests/macros/t15751.nim @@ -0,0 +1,11 @@ +discard """ + cmd: "nim c --hints:off $file" + nimout: "out" +""" + +# bug #15751 +macro print(n: untyped): untyped = + echo n.repr + +print: + out diff --git a/tests/macros/t23032_1.nim b/tests/macros/t23032_1.nim new file mode 100644 index 000000000..4e1707414 --- /dev/null +++ b/tests/macros/t23032_1.nim @@ -0,0 +1,19 @@ +import std/macros + +type A[T, H] = object + +proc `%*`(a: A): bool = true +proc `%*`[T](a: A[int, T]): bool = false + +macro collapse(s: untyped) = + result = newStmtList() + result.add quote do: + doAssert(`s`(A[float, int]()) == true) + +macro startHere(n: untyped): untyped = + result = newStmtList() + let s = n[0] + result.add quote do: + `s`.collapse() + +startHere(`a` %* `b`) diff --git a/tests/macros/t23032_2.nim b/tests/macros/t23032_2.nim new file mode 100644 index 000000000..8dde29e10 --- /dev/null +++ b/tests/macros/t23032_2.nim @@ -0,0 +1,20 @@ +discard """ + action: "reject" + errormsg: "ambiguous identifier: '%*'" +""" +import std/macros + +type A[T, H] = object + +proc `%*`[T](a: A) = discard +proc `%*`[T](a: A[int, T]) = discard + +macro collapse(s: typed) = discard + +macro startHere(n: untyped): untyped = + result = newStmtList() + let s = n[0] + result.add quote do: + collapse(`s`.typeof()) + +startHere(`a` %* `b`) diff --git a/tests/macros/t23547.nim b/tests/macros/t23547.nim new file mode 100644 index 000000000..9a2bff9ff --- /dev/null +++ b/tests/macros/t23547.nim @@ -0,0 +1,23 @@ +# https://github.com/nim-lang/Nim/issues/23547 + +type + A[T] = object + x: T + +proc mulCheckSparse[F](dummy: var A[F], xmulchecksparse: static A[F]) = + static: + echo "mulCheckSparse: ", typeof(dummy), ", ", typeof(xmulchecksparse) # when generic params not specified: A[system.int], A + +template sumImpl(xsumimpl: typed) = + static: + echo "sumImpl: ", typeof(xsumimpl) # A + var a = A[int](x: 55) + mulCheckSparse(a, xsumimpl) # fails here + +proc sum[T](xsum: static T) = + static: + echo "sum: ", typeof(xsum) # A[system.int] + sumImpl(xsum) + +const constA = A[int](x : 100) +sum[A[int]](constA) diff --git a/tests/macros/t23784.nim b/tests/macros/t23784.nim new file mode 100644 index 000000000..31b4544c6 --- /dev/null +++ b/tests/macros/t23784.nim @@ -0,0 +1,157 @@ +discard """ + joinable: false +""" + + +# debug ICE: genCheckedRecordField +# apparently after https://github.com/nim-lang/Nim/pull/23477 + +# bug #23784 + +import std/bitops, std/macros + +# -------------------------------------------------------------- + +type Algebra = enum + BN254_Snarks + +type SecretWord* = distinct uint64 +const WordBitWidth* = sizeof(SecretWord) * 8 + +func wordsRequired*(bits: int): int {.inline.} = + const divShiftor = fastLog2(WordBitWidth) + result = (bits + WordBitWidth - 1) shr divShiftor + +type + BigInt*[bits: static int] = object + limbs*: array[bits.wordsRequired, SecretWord] # <--- crash points to here + +# -------------------------------------------------------------- + +const CurveBitWidth = [ + BN254_Snarks: 254 +] + +const BN254_Snarks_Modulus = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x2, SecretWord 0x3, SecretWord 0x4]) +const BN254_Snarks_Order = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x2, SecretWord 0x2]) + +func montyOne*(M: BigInt[254]): BigInt[254] = + ## Returns "1 (mod M)" in the Montgomery domain. + ## This is equivalent to R (mod M) in the natural domain + BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x1, SecretWord 0x1]) + + +{.experimental: "dynamicBindSym".} + +type + DerivedConstantMode* = enum + kModulus + kOrder + +macro genDerivedConstants*(mode: static DerivedConstantMode): untyped = + ## Generate constants derived from the main constants + ## + ## For example + ## - the Montgomery magic constant "R^2 mod N" in ROM + ## For each curve under the private symbol "MyCurve_R2modP" + ## - the Montgomery magic constant -1/P mod 2^Wordbitwidth + ## For each curve under the private symbol "MyCurve_NegInvModWord + ## - ... + + # Now typedesc are NimNode and there is no way to translate + # NimNode -> typedesc easily so we can't + # "for curve in low(Curve) .. high(Curve):" + # As an ugly workaround, we count + # The item at position 0 is a pragma + result = newStmtList() + + template used(name: string): NimNode = + nnkPragmaExpr.newTree( + ident(name), + nnkPragma.newTree(ident"used") + ) + + let ff = if mode == kModulus: "_Fp" else: "_Fr" + + for curveSym in low(Algebra) .. high(Algebra): + let curve = $curveSym + let M = if mode == kModulus: bindSym(curve & "_Modulus") + else: bindSym(curve & "_Order") + + # const MyCurve_montyOne = montyOne(MyCurve_Modulus) + result.add newConstStmt( + used(curve & ff & "_MontyOne"), newCall( + bindSym"montyOne", + M + ) + ) + +# -------------------------------------------------------------- + +{.experimental: "dynamicBindSym".} + +genDerivedConstants(kModulus) +genDerivedConstants(kOrder) + +proc bindConstant(ff: NimNode, property: string): NimNode = + # Need to workaround https://github.com/nim-lang/Nim/issues/14021 + # which prevents checking if a type FF[Name] = Fp[Name] or Fr[Name] + # was instantiated with Fp or Fr. + # getTypeInst only returns FF and sameType doesn't work. + # so quote do + when checks. + let T = getTypeInst(ff) + T.expectKind(nnkBracketExpr) + doAssert T[0].eqIdent("typedesc") + + let curve = + if T[1].kind == nnkBracketExpr: # typedesc[Fp[BLS12_381]] as used internally + # doAssert T[1][0].eqIdent"Fp" or T[1][0].eqIdent"Fr", "Found ident: '" & $T[1][0] & "' instead of 'Fp' or 'Fr'" + T[1][1].expectKind(nnkIntLit) # static enum are ints in the VM + $Algebra(T[1][1].intVal) + else: # typedesc[bls12381_fp] alias as used for C exports + let T1 = getTypeInst(T[1].getImpl()[2]) + if T1.kind != nnkBracketExpr or + T1[1].kind != nnkIntLit: + echo T.repr() + echo T1.repr() + echo getTypeInst(T1).treerepr() + error "getTypeInst didn't return the full instantiation." & + " Dealing with types in macros is hard, complain at https://github.com/nim-lang/RFCs/issues/44" + $Algebra(T1[1].intVal) + + let curve_fp = bindSym(curve & "_Fp_" & property) + let curve_fr = bindSym(curve & "_Fr_" & property) + result = quote do: + when `ff` is Fp: + `curve_fp` + elif `ff` is Fr: + `curve_fr` + else: + {.error: "Unreachable, received type: " & $`ff`.} + +# -------------------------------------------------------------- + +template matchingBigInt*(Name: static Algebra): untyped = + ## BigInt type necessary to store the prime field Fp + # Workaround: https://github.com/nim-lang/Nim/issues/16774 + # as we cannot do array accesses in type section. + # Due to generic sandwiches, it must be exported. + BigInt[CurveBitWidth[Name]] + +type + Fp*[Name: static Algebra] = object + mres*: matchingBigInt(Name) + +macro getMontyOne*(ff: type Fp): untyped = + ## Get one in Montgomery representation (i.e. R mod P) + result = bindConstant(ff, "MontyOne") + +func getOne*(T: type Fp): T {.noInit, inline.} = + result = cast[ptr T](unsafeAddr getMontyOne(T))[] + +# -------------------------------------------------------------- +proc foo(T: Fp) = + discard T + +let a = Fp[BN254_Snarks].getOne() +foo(a) # oops this was a leftover that broke the bisect. diff --git a/tests/macros/tastrepr.nim b/tests/macros/tastrepr.nim index c04498a25..96a37c7a2 100644 --- a/tests/macros/tastrepr.nim +++ b/tests/macros/tastrepr.nim @@ -11,6 +11,8 @@ for i, (x, y) in pairs(data): var a = 1 b = 2 +type + A* = object var data = @[(1, "one"), (2, "two")] for (i, d) in pairs(data): @@ -20,6 +22,11 @@ for i, d in pairs(data): for i, (x, y) in pairs(data): discard var (a, b) = (1, 2) +type + A* = object + +var t04 = 1.0'f128 +t04 = 2.0'f128 ''' """ @@ -44,3 +51,8 @@ echoTypedAndUntypedRepr: for i, (x,y) in pairs(data): discard var (a,b) = (1,2) + type A* = object # issue #22933 + +echoUntypedRepr: + var t04 = 1'f128 + t04 = 2'f128 diff --git a/tests/macros/tfail_parse.nim b/tests/macros/tfail_parse.nim new file mode 100644 index 000000000..1925f2b69 --- /dev/null +++ b/tests/macros/tfail_parse.nim @@ -0,0 +1,15 @@ +discard """ +action: "reject" +cmd: "nim check $file" +errormsg: "expected expression, but got multiple statements [ValueError]" +file: "macros.nim" +""" + +import macros +static: + discard parseStmt("'") + discard parseExpr("'") + discard parseExpr(""" +proc foo() +proc foo() = discard +""") diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index 66722a234..ab33131b0 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -44,10 +44,11 @@ static: doAssert checkOwner(poo, 2) == "nskProc" doAssert checkOwner(poo, 3) == "nskModule" doAssert isSameOwner(foo, poo) - doAssert isSameOwner(foo, echo) == false - doAssert isSameOwner(poo, len) == false - -#--------------------------------------------------------------- + proc wrappedScope() = + proc dummyproc() = discard + doAssert isSameOwner(foo, dummyproc) == false + doAssert isSameOwner(poo, dummyproc) == false + wrappedScope() macro check_gen_proc(ex: typed): (bool, bool) = let lenChoice = bindsym"len" @@ -72,7 +73,9 @@ assert: check_gen_proc(len(a)) == (false, true) macro check(x: type): untyped = let z = getType(x) let y = getImpl(z[1]) - let sym = if y[0].kind == nnkSym: y[0] else: y[0][0] + var sym = y[0] + if sym.kind == nnkPragmaExpr: sym = sym[0] + if sym.kind == nnkPostfix: sym = sym[1] expectKind(z[1], nnkSym) expectKind(sym, nnkSym) expectKind(y[2], nnkObjectTy) diff --git a/tests/macros/tgettypeinst7737.nim b/tests/macros/tgettypeinst7737.nim new file mode 100644 index 000000000..e49f82562 --- /dev/null +++ b/tests/macros/tgettypeinst7737.nim @@ -0,0 +1,61 @@ +discard """ + nimout: ''' +seq[int] +CustomSeq[int] +''' +""" + +import macros, typetraits, sequtils + +block: # issue #7737 original + type + CustomSeq[T] = object + data: seq[T] + + proc getSubType(T: NimNode): NimNode = + echo getTypeInst(T).repr + result = getTypeInst(T)[1] + + macro typed_helper(x: varargs[typed]): untyped = + let foo = getSubType(x[0]) + result = quote do: discard + + macro untyped_heavylifting(x: varargs[untyped]): untyped = + var containers = nnkArgList.newTree() + for arg in x: + case arg.kind: + of nnkInfix: + if eqIdent(arg[0], "in"): + containers.add arg[2] + else: + discard + result = quote do: + typed_helper(`containers`) + var a, b, c: seq[int] + untyped_heavylifting z in c, x in a, y in b: + discard + ## The following gives me CustomSeq instead + ## of CustomSeq[int] in getTypeInst + var u, v, w: CustomSeq[int] + untyped_heavylifting z in u, x in v, y in w: + discard + +block: # issue #7737 comment + type + CustomSeq[T] = object + data: seq[T] + # when using just one argument, `foo` and `bar` should be exactly + # identical. + macro foo(arg: typed): string = + result = newLit(arg.getTypeInst.repr) + macro bar(args: varargs[typed]): untyped = + result = newTree(nnkBracket) + for arg in args: + result.add newLit(arg.getTypeInst.repr) + var + a: seq[int] + b: CustomSeq[int] + doAssert foo(a) == "seq[int]" + doAssert bar(a) == ["seq[int]"] + doAssert foo(b) == "CustomSeq[int]" + doAssert bar(b) == ["CustomSeq[int]"] diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim index 79be5f764..817bc8352 100644 --- a/tests/macros/tmacrostmt.nim +++ b/tests/macros/tmacrostmt.nim @@ -124,7 +124,7 @@ static: let fn4s = "proc fn4(x: int): int =\n if x mod 2 == 0:\n return x + 2\n else:\n return 0\n" let fn5s = "proc fn5(a, b: float): float =\n result = -a * a / (b * b)\n" let fn6s = "proc fn6() =\n var a = @[1.0, 2.0]\n let z = a{0, 1}\n a{2} = 5.0\n" - let fnAddr = "proc fn_unsafeaddr(x: int): int =\n result = cast[int](unsafeAddr(x))\n" + let fnAddr = "proc fn_unsafeaddr(x: int): int =\n result = cast[int](addr(x))\n" doAssert fn1.repr_to_string == fn1s doAssert fn2.repr_to_string == fn2s diff --git a/tests/macros/tmacrotypes.nim b/tests/macros/tmacrotypes.nim index 43819c81d..13b421303 100644 --- a/tests/macros/tmacrotypes.nim +++ b/tests/macros/tmacrotypes.nim @@ -1,9 +1,9 @@ discard """ - nimout: '''intProc; ntyProc; proc[int, int, float]; proc (a: int; b: float): int + nimout: '''intProc; ntyProc; proc[int, int, float]; proc (a: int; b: float): int {.nimcall.} void; ntyVoid; void; void int; ntyInt; int; int -proc (); ntyProc; proc[void]; proc () -voidProc; ntyProc; proc[void]; proc () +proc () {.nimcall.}; ntyProc; proc[void]; proc () {.nimcall.} +voidProc; ntyProc; proc[void]; proc () {.nimcall.} listing fields for ObjType a: string b: int diff --git a/tests/macros/tprocgettype.nim b/tests/macros/tprocgettype.nim new file mode 100644 index 000000000..0c1cc4270 --- /dev/null +++ b/tests/macros/tprocgettype.nim @@ -0,0 +1,28 @@ +discard """ + nimout: ''' +var x: proc () {.cdecl.} = foo +var x: iterator (): int {.closure.} = bar +''' +""" + +# issue #19010 + +import macros + +macro createVar(x: typed): untyped = + result = nnkVarSection.newTree: + newIdentDefs(ident"x", getTypeInst(x), copy(x)) + + echo repr result + +block: + proc foo() {.cdecl.} = discard + + createVar(foo) + x() + +block: + iterator bar(): int {.closure.} = discard + + createVar(bar) + for a in x(): discard diff --git a/tests/macros/ttryparseexpr.nim b/tests/macros/ttryparseexpr.nim index fc0ee61d0..e6e9e9880 100644 --- a/tests/macros/ttryparseexpr.nim +++ b/tests/macros/ttryparseexpr.nim @@ -18,3 +18,7 @@ const c = test("\"") # bug #2504 echo a, " ", b + +static: + # Issue #9918 + discard parseStmt("echo(1+1);") diff --git a/tests/metatype/tmetatype_issues.nim b/tests/metatype/tmetatype_issues.nim index 21c5c02f1..d33d8dd31 100644 --- a/tests/metatype/tmetatype_issues.nim +++ b/tests/metatype/tmetatype_issues.nim @@ -157,3 +157,12 @@ block t3338: var t2 = Bar[int32]() t2.add() doAssert t2.x == 5 + +block: # issue #24203 + proc b(G: typedesc) = + type U = G + template s(h: untyped) = h + s(b(typeof (0, 0))) + b(seq[int]) + b((int, int)) + b(typeof (0, 0)) diff --git a/tests/metatype/tmetatype_various.nim b/tests/metatype/tmetatype_various.nim index be70f37a7..45c74432d 100644 --- a/tests/metatype/tmetatype_various.nim +++ b/tests/metatype/tmetatype_various.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:refc; --mm:refc" + matrix: "--mm:refc; --mm:orc" output: '''[1, 0, 0, 0, 0, 0, 0, 0] CTBool[Ct[system.uint32]]''' """ @@ -66,3 +66,8 @@ var x: array[8, CTBool[Ct[uint32]]] x[0] = (CTBool[Ct[uint32]])(1) echo x.repr, " ", typeof(x[0]) +block: # bug #23139 + type Foo = enum a, b + + var x: range[a..b] + doAssert (repr x) == "a" diff --git a/tests/metatype/ttypedescnotnimnode.nim b/tests/metatype/ttypedescnotnimnode.nim new file mode 100644 index 000000000..52a04815b --- /dev/null +++ b/tests/metatype/ttypedescnotnimnode.nim @@ -0,0 +1,21 @@ +discard """ + errormsg: "type mismatch: got <NimNode> but expected 'typedesc'" + line: 14 +""" + +import macros + +# This is the same example as ttypeselectors but using a proc instead of a macro +# Instead of type mismatch for macro, proc just failed with internal error: getTypeDescAux(tyNone) +# https://github.com/nim-lang/Nim/issues/7231 + +proc getBase2*(bits: static[int]): typedesc = + if bits == 128: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64")) + else: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32")) + +type + MpUint2*[bits: static[int]] = getbase2(bits) +# technically shouldn't error until instantiation, so instantiate it +var x: MpUint2[123] diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index 5385a1be9..d0843511d 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -98,16 +98,3 @@ var c: Bar[32] echo sizeof(a) echo sizeof(b) echo sizeof(c) - -# This is the same example but using a proc instead of a macro -# Instead of type mismatch for macro, proc just failed with internal error: getTypeDescAux(tyNone) -# https://github.com/nim-lang/Nim/issues/7231 - -proc getBase2*(bits: static[int]): typedesc = - if bits == 128: - result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64")) - else: - result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32")) - -type - MpUint2*[bits: static[int]] = getbase2(bits) diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index bfaa23057..0523390a7 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -144,6 +144,32 @@ block distinctBase: doAssert($distinctBase(typeof(b2)) == "string") doAssert($distinctBase(typeof(c2)) == "int") +block: # rangeBase + {.push warningAsError[EnumConv]: on.} + proc foo[T: not range](x: T): string = + $T & "(" & $x & ")" + proc foo[T: range](x: T): string = + "ranged(" & $low(T) & ".." & $high(T) & " of " & $rangeBase(T) & ") " & foo(rangeBase(x)) + doAssert foo(123) == "int(123)" + type IntRange = range[0..3] + let x: IntRange = 2 + doAssert foo(x) == "ranged(0..3 of int) int(2)" + type E = enum a, b, c, d, e, f + type EnumRange = range[c..e] + let y: EnumRange = d + doAssert foo(y) == "ranged(c..e of E) E(d)" + let z: range['a'..'z'] = 'g' + doAssert foo(z) == "ranged(a..z of char) char(g)" + {.pop.} + + # works only with #24037: + var toChange: range[0..3] = 1 + proc bar[T: int and not range](y: var T) = + inc y + doAssert not compiles(bar(toChange)) + bar(rangeBase(toChange)) + doAssert toChange == 2 + block: # tupleLen doAssert not compiles(tupleLen(int)) @@ -365,3 +391,14 @@ block: # enum.len doAssert MyEnum.enumLen == 4 doAssert OtherEnum.enumLen == 3 doAssert MyFlag.enumLen == 4 + +when true: # Odd bug where alias can seep inside of `distinctBase` + import std/unittest + + type + AdtChild* = concept t + distinctBase(t) + + proc `$`*[T: AdtChild](adtChild: T): string = "" + + check 10 is int diff --git a/tests/metatype/twrong_same_type.nim b/tests/metatype/twrong_same_type.nim new file mode 100644 index 000000000..ea903b7a3 --- /dev/null +++ b/tests/metatype/twrong_same_type.nim @@ -0,0 +1,28 @@ +# bug #23418 + +template mapIt*(x: untyped): untyped = + type OutType {.gensym.} = typeof(x) #typeof(x, typeOfProc) + newSeq[OutType](5) + +type F[E] = object + +proc start(v: int): F[(ValueError,)] = discard +proc stop(v: int): F[tuple[]] = discard + +assert $typeof(mapIt(start(9))) == "seq[F[(ValueError,)]]" +assert $typeof(mapIt(stop(9))) == "seq[F[tuple[]]]" + +# bug #23445 + +type F2[T; I: static int] = distinct int + +proc start2(v: int): F2[void, 22] = discard +proc stop2(v: int): F2[void, 33] = discard + +var a = mapIt(start2(5)) + +assert $type(a) == "seq[F2[system.void, 22]]", $type(a) + +var b = mapIt(stop2(5)) + +assert $type(b) == "seq[F2[system.void, 33]]", $type(b) diff --git a/tests/misc/mjsondoc.nim b/tests/misc/mjsondoc.nim index e4642f0b4..016c8522d 100644 --- a/tests/misc/mjsondoc.nim +++ b/tests/misc/mjsondoc.nim @@ -9,3 +9,6 @@ const type MyEnum* = enum foo, bar + +proc foo2*[T: int, M: string, U](x: T, y: U, z: M) = + echo 1 diff --git a/tests/misc/t12869.nim b/tests/misc/t12869.nim index 731a4e95e..054e28a03 100644 --- a/tests/misc/t12869.nim +++ b/tests/misc/t12869.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch: got <bool> but expected 'int'" + errormsg: "type mismatch: got <openArray[int], proc (x: GenericParam, y: GenericParam): auto, SortOrder>" line: 12 """ diff --git a/tests/misc/t20883.nim b/tests/misc/t20883.nim index d98feaa14..92e7929f4 100644 --- a/tests/misc/t20883.nim +++ b/tests/misc/t20883.nim @@ -1,8 +1,9 @@ discard """ action: reject - errormsg: "type mismatch: got <float64> but expected 'typeof(U(0.000001))'" - line: 8 - column: 22 +nimout: ''' +t20883.nim(13, 4) template/generic instantiation of `foo` from here +t20883.nim(9, 11) Error: cannot instantiate: 'U' +''' """ proc foo*[U](x: U = U(1e-6)) = diff --git a/tests/misc/t23240.nim b/tests/misc/t23240.nim new file mode 100644 index 000000000..d5edcefe8 --- /dev/null +++ b/tests/misc/t23240.nim @@ -0,0 +1,6 @@ +discard """ + cmd: "nim c foo/bar.nim" + action: "reject" + errormsg: "cannot open 'foo/'" + file: "" +""" diff --git a/tests/misc/t8545.nim b/tests/misc/t8545.nim index 89957e1d3..48b886cb8 100644 --- a/tests/misc/t8545.nim +++ b/tests/misc/t8545.nim @@ -1,5 +1,6 @@ discard """ - targets: "c cpp js" + # just tests that this doesn't crash the compiler + errormsg: "cannot instantiate: 'a:type'" """ # bug #8545 diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim index 48d4928ac..64f95c7e3 100644 --- a/tests/misc/taddr.nim +++ b/tests/misc/taddr.nim @@ -273,6 +273,16 @@ proc test15939() = # bug #15939 (v2) else: # can't take address of cstring element in js when not defined(js): cstringTest() +block: # bug #23499 + template volatileStore[T](dest: ptr T, val: T) = + dest[] = val + + proc foo = + var ctr = 0 + volatileStore(addr ctr, 0) + + foo() + template main = # xxx wrap all other tests here like that so they're also tested in VM test14420() diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim index 6d67b1c52..73196e76c 100644 --- a/tests/misc/tcast.nim +++ b/tests/misc/tcast.nim @@ -1,6 +1,7 @@ discard """ output: ''' Hello World +Hello World Hello World''' joinable: false """ @@ -8,7 +9,7 @@ type MyProc = proc() {.cdecl.} type MyProc2 = proc() {.nimcall.} type MyProc3 = proc() #{.closure.} is implicit -proc testProc() = echo "Hello World" +proc testProc() {.exportc:"foo".} = echo "Hello World" template reject(x) = doAssert(not compiles(x)) @@ -23,6 +24,10 @@ proc callPointer(p: pointer) = ffunc0() ffunc1() + # bug #5901 + proc foo() {.importc.} + (cast[proc(a: int) {.cdecl.}](foo))(5) + callPointer(cast[pointer](testProc)) reject: discard cast[enum](0) diff --git a/tests/misc/tconv.nim b/tests/misc/tconv.nim index e4a99344a..90fae868b 100644 --- a/tests/misc/tconv.nim +++ b/tests/misc/tconv.nim @@ -88,6 +88,13 @@ block: # https://github.com/nim-lang/RFCs/issues/294 reject: Goo(k2) reject: k2.Goo + type KooRange = range[k2..k2] + accept: KooRange(k2) + accept: k2.KooRange + let k2ranged: KooRange = k2 + accept: Koo(k2ranged) + accept: k2ranged.Koo + reject: # bug #18550 proc f(c: char): cstring = diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index f0262f528..6e5487d1b 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -224,13 +224,15 @@ sub/mmain.idx""", context doAssert exitCode == 0, msg let data = parseJson(readFile(output))["entries"] - doAssert data.len == 4 + doAssert data.len == 5 let doSomething = data[0] doAssert doSomething["name"].getStr == "doSomething" doAssert doSomething["type"].getStr == "skProc" doAssert doSomething["line"].getInt == 1 doAssert doSomething["col"].getInt == 0 doAssert doSomething["code"].getStr == "proc doSomething(x, y: int): int {.raises: [], tags: [], forbids: [].}" + let foo2 = data[4] + doAssert $foo2["signature"] == """{"arguments":[{"name":"x","type":"T"},{"name":"y","type":"U"},{"name":"z","type":"M"}],"genericParams":[{"name":"T","types":"int"},{"name":"M","types":"string"},{"name":"U"}]}""" block: # nim jsondoc # bug #11953 let file = testsDir / "misc/mjsondoc.nim" @@ -241,7 +243,7 @@ sub/mmain.idx""", context doAssert exitCode == 0, msg let data = parseJson(readFile(destDir / "mjsondoc.json"))["entries"] - doAssert data.len == 4 + doAssert data.len == 5 let doSomething = data[0] doAssert doSomething["name"].getStr == "doSomething" doAssert doSomething["type"].getStr == "skProc" diff --git a/tests/misc/trunner_special.nim b/tests/misc/trunner_special.nim index e13810722..e08b419b0 100644 --- a/tests/misc/trunner_special.nim +++ b/tests/misc/trunner_special.nim @@ -14,6 +14,10 @@ xxx test all tests/untestable/* here, possibly with adjustments to make running import std/[strformat,os,unittest,compilesettings] import stdtest/specialpaths + +from stdtest/testutils import disableSSLTesting + + const nim = getCurrentCompilerExe() mode = querySetting(backend) @@ -29,4 +33,5 @@ proc main = block: # SSL certificate check integration tests runCmd fmt"{nim} r {options} -d:ssl --threads:on --mm:refc {testsDir}/untestable/thttpclient_ssl_remotenetwork.nim" -main() +when not disableSSLTesting(): + main() diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim index 0d96a5e04..ce5334664 100644 --- a/tests/misc/tsizeof.nim +++ b/tests/misc/tsizeof.nim @@ -732,3 +732,11 @@ type doAssert sizeof(PackedUnion) == 11 doAssert alignof(PackedUnion) == 1 + +# bug #22553 +type + ChunkObj = object + data: UncheckedArray[byte] + +doAssert sizeof(ChunkObj) == 1 +doAssert offsetOf(ChunkObj, data) == 1 diff --git a/tests/modules/mincludeprefix.nim b/tests/modules/mincludeprefix.nim new file mode 100644 index 000000000..6d557a430 --- /dev/null +++ b/tests/modules/mincludeprefix.nim @@ -0,0 +1 @@ +const bar = 456 diff --git a/tests/modules/mincludetemplate.nim b/tests/modules/mincludetemplate.nim new file mode 100644 index 000000000..febe9bfcf --- /dev/null +++ b/tests/modules/mincludetemplate.nim @@ -0,0 +1 @@ +const foo = 123 diff --git a/tests/modules/tincludeprefix.nim b/tests/modules/tincludeprefix.nim new file mode 100644 index 000000000..d45a6eff3 --- /dev/null +++ b/tests/modules/tincludeprefix.nim @@ -0,0 +1,3 @@ +include ./[mincludeprefix, mincludetemplate] +doAssert foo == 123 +doAssert bar == 456 diff --git a/tests/modules/tincludetemplate.nim b/tests/modules/tincludetemplate.nim new file mode 100644 index 000000000..77e409ee5 --- /dev/null +++ b/tests/modules/tincludetemplate.nim @@ -0,0 +1,5 @@ +# issue #12539 + +template includePath(n: untyped) = include ../modules/n # But `include n` works +includePath(mincludetemplate) +doAssert foo == 123 diff --git a/tests/modules/tmodulesymtype.nim b/tests/modules/tmodulesymtype.nim index b1378ab69..d17c4cca4 100644 --- a/tests/modules/tmodulesymtype.nim +++ b/tests/modules/tmodulesymtype.nim @@ -13,3 +13,10 @@ proc foo() = sequtils foo() + +# issue #23399 +when isMainModule: + sequtils #[tt.Error + ^ expression has no type: sequtils]# + +discard diff --git a/tests/modules/treorder.nim b/tests/modules/treorder.nim index 286b50e22..ff0b2e071 100644 --- a/tests/modules/treorder.nim +++ b/tests/modules/treorder.nim @@ -8,6 +8,8 @@ defined {.experimental: "codeReordering".} +{.push callconv: stdcall.} + proc bar(x: T) proc foo() = @@ -41,3 +43,5 @@ using my, omy: int goo(3, 4) + +{.pop.} diff --git a/tests/msgs/tused2.nim b/tests/msgs/tused2.nim index f80c198d8..5ccda7737 100644 --- a/tests/msgs/tused2.nim +++ b/tests/msgs/tused2.nim @@ -7,12 +7,12 @@ mused2a.nim(12, 6) Hint: 'fn1' is declared but not used [XDeclaredButNotUsed] mused2a.nim(16, 5) Hint: 'fn4' is declared but not used [XDeclaredButNotUsed] mused2a.nim(20, 7) Hint: 'fn7' is declared but not used [XDeclaredButNotUsed] mused2a.nim(23, 6) Hint: 'T1' is declared but not used [XDeclaredButNotUsed] -mused2a.nim(1, 11) Warning: imported and not used: 'strutils' [UnusedImport] -mused2a.nim(3, 9) Warning: imported and not used: 'os' [UnusedImport] +mused2a.nim(1, 12) Warning: imported and not used: 'strutils' [UnusedImport] +mused2a.nim(3, 10) Warning: imported and not used: 'os' [UnusedImport] mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport] -mused2a.nim(6, 9) Warning: imported and not used: 'setutils' [UnusedImport] +mused2a.nim(6, 10) Warning: imported and not used: 'setutils' [UnusedImport] tused2.nim(42, 8) Warning: imported and not used: 'mused2a' [UnusedImport] -tused2.nim(45, 11) Warning: imported and not used: 'strutils' [UnusedImport] +tused2.nim(45, 12) Warning: imported and not used: 'strutils' [UnusedImport] ''' """ diff --git a/tests/niminaction/Chapter8/sdl/sdl_test.nim b/tests/niminaction/Chapter8/sdl/sdl_test.nim index 1c4d258fb..db1700e0d 100644 --- a/tests/niminaction/Chapter8/sdl/sdl_test.nim +++ b/tests/niminaction/Chapter8/sdl/sdl_test.nim @@ -18,7 +18,7 @@ renderer.setDrawColor 29, 64, 153, 255 renderer.clear renderer.setDrawColor 255, 255, 255, 255 -when defined(c): +when false: # no long work with gcc 14! # just to ensure code from NimInAction still works, but # the `else` branch would work as well in C mode var points = [ diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim index 152b355f4..0cd05e4f3 100644 --- a/tests/objects/tobject_default_value.nim +++ b/tests/objects/tobject_default_value.nim @@ -121,7 +121,7 @@ template main {.dirty.} = rVal: R = default(R) # Works fine objVal = default(Obj) - doAssert rVal == 0 # it should be 1 + doAssert rVal == 1 doAssert objVal.r == 1 block: # bug #16744 @@ -134,7 +134,7 @@ template main {.dirty.} = rVal: R = default(R) # Works fine objVal = Obj() - doAssert rVal == 0 # it should be 1 + doAssert rVal == 1 # it should be 1 doAssert objVal.r == 1 block: # bug #3608 @@ -744,6 +744,37 @@ template main {.dirty.} = var b = default ArrayObj2 doAssert b.list[North] == 1 + block: + type limited_float = range[1.2..20.0] + doAssert default(limited_float) == 1.2 + + + block: + type + range1 = range[1..10] + range2 = range[-1..10] + + proc foo = + doAssert default(range1) == 1 + doAssert default(range2) == -1 + + let s = default(array[5, range1]) + doAssert s == [range1 1, 1, 1, 1, 1] + + foo() + + block: + type + Object = object + id: range[1.2..29.3] + + var s = default(Object) + doAssert s.id == 1.2 + + block: # bug #23943 + type limited_int = range[1..20] + var d: limited_int; + doAssert d == 1 static: main() main() diff --git a/tests/objects/trequireinit.nim b/tests/objects/trequireinit.nim new file mode 100644 index 000000000..202667b02 --- /dev/null +++ b/tests/objects/trequireinit.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "The MPlayerObj type doesn't have a default value. The following fields must be initialized: foo." +""" + +type + MPlayerObj* {.requiresInit.} = object + foo: range[5..10] = 5 + +var a: MPlayerObj +echo a.foo \ No newline at end of file diff --git a/tests/objvariant/tconstobjvariant.nim b/tests/objvariant/tconstobjvariant.nim new file mode 100644 index 000000000..45a647707 --- /dev/null +++ b/tests/objvariant/tconstobjvariant.nim @@ -0,0 +1,18 @@ +# This is a sample code, the first echo statement prints out the error +type + A = object + case w: uint8 + of 1: + n: int + else: + other: string + +const + a = A(w: 1, n: 5) + +proc foo = + + let c = [a] + doAssert c[0].n == 5 + +foo() \ No newline at end of file diff --git a/tests/openarray/topenarray.nim b/tests/openarray/topenarray.nim index c752becf2..25b983651 100644 --- a/tests/openarray/topenarray.nim +++ b/tests/openarray/topenarray.nim @@ -48,6 +48,41 @@ proc main = doAssert testing(mySeq) == mySeq doAssert testing(mySeq[2..^2]) == mySeq[2..^2] + block: # bug #23321 + block: + proc foo(x: openArray[int]) = + doAssert x[0] == 0 + + var d = new array[1, int] + foo d[].toOpenArray(0, 0) + + block: + proc foo(x: openArray[int]) = + doAssert x[0] == 0 + + proc task(x: var array[1, int]): var array[1, int] = + result = x + var d: array[1, int] + foo task(d).toOpenArray(0, 0) + + block: + proc foo(x: openArray[int]) = + doAssert x[0] == 0 + + proc task(x: var array[1, int]): lent array[1, int] = + result = x + var d: array[1, int] + foo task(d).toOpenArray(0, 0) + + block: + proc foo(x: openArray[int]) = + doAssert x[0] == 0 + + proc task(x: var array[1, int]): ptr array[1, int] = + result = addr x + var d: array[1, int] + foo task(d)[].toOpenArray(0, 0) + main() static: main() diff --git a/tests/openarray/tuncheckedarray.nim b/tests/openarray/tuncheckedarray.nim new file mode 100644 index 000000000..c8ca9d2d4 --- /dev/null +++ b/tests/openarray/tuncheckedarray.nim @@ -0,0 +1,19 @@ +discard """ + exitcode: 0 + targets: "c cpp" +""" + +proc main = + block: # issue 19171 + var a = ['A'] + proc mutB(x: var openArray[char]) = + x[0] = 'B' + mutB(toOpenArray(cast[ptr UncheckedArray[char]](addr a), 0, 0)) + doAssert a[0] == 'B' + proc mutC(x: var openArray[char]; c: char) = + x[0] = c + let p = cast[ptr UncheckedArray[char]](addr a) + mutC(toOpenArray(p, 0, 0), 'C') + doAssert p[0] == 'C' + +main() diff --git a/tests/osproc/tnoexe.nim b/tests/osproc/tnoexe.nim new file mode 100644 index 000000000..19a3cca67 --- /dev/null +++ b/tests/osproc/tnoexe.nim @@ -0,0 +1,27 @@ +discard """ + output: '''true +true''' +""" + +import std/osproc + +const command = "lsaaa -lah" + +try: + let process = startProcess(command, options = {poUsePath}) + discard process.waitForExit() +except OSError as e: + echo e.errorCode != 0 + +# `poEvalCommand`, invokes the system shell to run the specified command +try: + let process = startProcess(command, options = {poUsePath, poEvalCommand}) + # linux + let exitCode = process.waitForExit() + echo exitCode != 0 +except OSError as e: + # Because the implementation of `poEvalCommand` on different platforms is inconsistent, + # Linux will not throw an exception, but Windows will throw an exception + + # windows + echo e.errorCode != 0 diff --git a/tests/osproc/twaitforexit.nim b/tests/osproc/twaitforexit.nim new file mode 100644 index 000000000..535faca63 --- /dev/null +++ b/tests/osproc/twaitforexit.nim @@ -0,0 +1,38 @@ +import std/[osproc, os, times] + +block: # bug #5091 + when defined(linux): + const filename = "false" + var p = startProcess(filename, options = {poStdErrToStdOut, poUsePath}) + os.sleep(1000) # make sure process has exited already + + let atStart = getTime() + const msWait = 2000 + + try: + discard waitForExit(p, msWait) + except OSError: + discard + + # check that we don't have to wait msWait milliseconds + doAssert(getTime() < atStart + milliseconds(msWait)) + +block: # bug #23825 + + # the sleep command might not be available in all Windows installations + + when defined(linux): + + var thr: array[0..99, Thread[int]] + + proc threadFunc(i: int) {.thread.} = + let sleepTime = float(i) / float(thr.len + 1) + doAssert sleepTime < 1.0 + let p = startProcess("sleep", workingDir = "", args = @[$sleepTime], options = {poUsePath, poParentStreams}) + # timeout = 1_000_000 seconds ~= 278 hours ~= 11.5 days + doAssert p.waitForExit(timeout=1_000_000_000) == 0 + + for i in low(thr)..high(thr): + createThread(thr[i], threadFunc, i) + + joinThreads(thr) diff --git a/tests/overflow/twronginference.nim b/tests/overflow/twronginference.nim new file mode 100644 index 000000000..34a982976 --- /dev/null +++ b/tests/overflow/twronginference.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "cannot convert 256 to int8" + line: 9 +""" + +# issue #23177 + +var x: int8 +x = 256 +echo x # 0 diff --git a/tests/overload/issue22142/tfail_implicit_ambiguous.nim b/tests/overload/issue22142/tfail_implicit_ambiguous.nim new file mode 100644 index 000000000..2586e0877 --- /dev/null +++ b/tests/overload/issue22142/tfail_implicit_ambiguous.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "ambiguous call" +""" +type + A[T] = object + C = object + +proc test[T: A](param: T): bool = false +proc test(param: A): bool = true +doAssert test(A[C]()) == true # previously would pass diff --git a/tests/overload/issue22142/tfail_nested_pointers.nim b/tests/overload/issue22142/tfail_nested_pointers.nim new file mode 100644 index 000000000..1603d98cb --- /dev/null +++ b/tests/overload/issue22142/tfail_nested_pointers.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "ambiguous call" +""" + +type + A[T] = object + C = object + x:int +proc p[T: A[ptr]](x:ptr[T]):bool = false +proc p(x: ptr[A[ptr]]):bool = true +var a: A[ptr[C]] +doAssert p(a.addr) == true diff --git a/tests/overload/issue22142/tfail_object_is_generic.nim b/tests/overload/issue22142/tfail_object_is_generic.nim new file mode 100644 index 000000000..b46795bd5 --- /dev/null +++ b/tests/overload/issue22142/tfail_object_is_generic.nim @@ -0,0 +1,16 @@ +discard """ + errormsg: "ambiguous call" +""" + +#[ +As of the time of writing `object` needs some special +treament in order to be considered "generic" in the right +context when used implicitly +]# + +type + C = object + +proc test[T: object](param: T): bool = false +proc test(param: object): bool = true +doAssert test(C()) == true # previously would pass diff --git a/tests/overload/issue22142/tfail_typeclass_var_invar.nim b/tests/overload/issue22142/tfail_typeclass_var_invar.nim new file mode 100644 index 000000000..07db65fef --- /dev/null +++ b/tests/overload/issue22142/tfail_typeclass_var_invar.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "ambiguous call" +""" + +type C = object +proc test[T: ptr](param: var T): bool = false +proc test(param: var ptr): bool = true +var d: ptr[C] +doAssert test(d) == true # previously would pass diff --git a/tests/overload/issue22142/tissue22142_shouldpass.nim b/tests/overload/issue22142/tissue22142_shouldpass.nim new file mode 100644 index 000000000..90d4efe51 --- /dev/null +++ b/tests/overload/issue22142/tissue22142_shouldpass.nim @@ -0,0 +1,68 @@ +type + A[T] = object of RootObj + B[T] = object + C = object + x:int + +# change (previously true) +block: + proc test[J;H: A[J];T: B[H]](param: T): bool = false + proc test[T](param: B[T]): bool = true + doAssert test(B[A[int]]()) == false +block: # object is more specific then `T` + proc p[H:object;T:ptr[H]](param:T):bool = false + proc p[T](param:ptr[T]):bool= true + var l: ptr[C] + doAssert p(l) == false +block: + proc p[T:A[object]](param:T):bool = false + proc p[T](param: A[T]):bool= true + doAssert p(A[C]()) == false +block: + proc test[H;T: A[H]](param: T): bool = false + proc test(param: A): bool = true + doAssert test(A[C]()) == false + +# change (previously ambiguous) +block: + proc p[T](a: A[T]): bool = false + proc p[T: object](a: T): bool = true + doAssert p(A[int]()) == false +block: # A is more specific than `object` + proc test[T: A](param: T): bool = false + proc test[T: object](param: T): bool = true + doAssert test(A[int]()) == false +block: + proc test[T: A](param: T): bool = false + proc test(param: object): bool = true + doAssert test(A[int]()) == false +block: + proc test[H;T: A[H]](param: T): bool = false + proc test(param: object): bool = true + doAssert test(A[C]()) == false +block: + proc test[H;T: A[B[H]]](param: T): bool = false + proc test[T: object](param: T): bool = true + doAssert test(A[B[int]]()) == false +block: + #[ + This was referenced in the nim compiler source (`sumGeneric`) as a case + that was supposed to not be ambiguous, yet it was + ]# + proc test[J;H:A[J]; T: A[H]](param: T): bool = false + proc test[H;T: A[H]](param: T): bool = true + doAssert test(A[A[C]]()) == false +block: + proc test[J;T:A[J]](param: A[T]): bool = false + proc test[T](param: A[T]): bool = true + doAssert test(A[A[C]]()) == false +block: + proc test[T](param: A[T]): bool = false + proc test[T: object](param: A[T]): bool = true + doAssert test(A[C]()) == true + + +block: #anti-regression (object is more specific then `T`) + proc test[J;T:A[J]](param: A[T]): bool = false + proc test(param: A[A[object]]): bool = true + doAssert test(A[A[C]]()) == true \ No newline at end of file diff --git a/tests/overload/m19737.nim b/tests/overload/m19737.nim new file mode 100644 index 000000000..7f7ac98e2 --- /dev/null +++ b/tests/overload/m19737.nim @@ -0,0 +1,10 @@ +type + UInt128* = object + lo, hi: uint64 + +func `<`*(x, y: UInt128): bool = + (x.hi < y.hi) or ((x.hi == y.hi) and (x.lo < y.lo)) + +when not defined(works): + func `>`*(x, y: UInt128): bool = + (x.hi > y.hi) or ((x.hi == y.hi) and (x.lo > y.lo)) diff --git a/tests/overload/mvaruintconv.nim b/tests/overload/mvaruintconv.nim new file mode 100644 index 000000000..b889c90cf --- /dev/null +++ b/tests/overload/mvaruintconv.nim @@ -0,0 +1,145 @@ +import + std/[macros, tables, hashes] + +export + macros + +type + FieldDescription* = object + name*: NimNode + isPublic*: bool + isDiscriminator*: bool + typ*: NimNode + pragmas*: NimNode + caseField*: NimNode + caseBranch*: NimNode + +{.push raises: [].} + +func isTuple*(t: NimNode): bool = + t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple") + +macro isTuple*(T: type): untyped = + newLit(isTuple(getType(T)[1])) + +proc collectFieldsFromRecList(result: var seq[FieldDescription], + n: NimNode, + parentCaseField: NimNode = nil, + parentCaseBranch: NimNode = nil, + isDiscriminator = false) = + case n.kind + of nnkRecList: + for entry in n: + collectFieldsFromRecList result, entry, + parentCaseField, parentCaseBranch + of nnkRecWhen: + for branch in n: + case branch.kind: + of nnkElifBranch: + collectFieldsFromRecList result, branch[1], + parentCaseField, parentCaseBranch + of nnkElse: + collectFieldsFromRecList result, branch[0], + parentCaseField, parentCaseBranch + else: + doAssert false + + of nnkRecCase: + collectFieldsFromRecList result, n[0], + parentCaseField, + parentCaseBranch, + isDiscriminator = true + + for i in 1 ..< n.len: + let branch = n[i] + case branch.kind + of nnkOfBranch: + collectFieldsFromRecList result, branch[^1], n[0], branch + of nnkElse: + collectFieldsFromRecList result, branch[0], n[0], branch + else: + doAssert false + + of nnkIdentDefs: + let fieldType = n[^2] + for i in 0 ..< n.len - 2: + var field: FieldDescription + field.name = n[i] + field.typ = fieldType + field.caseField = parentCaseField + field.caseBranch = parentCaseBranch + field.isDiscriminator = isDiscriminator + + if field.name.kind == nnkPragmaExpr: + field.pragmas = field.name[1] + field.name = field.name[0] + + if field.name.kind == nnkPostfix: + field.isPublic = true + field.name = field.name[1] + + result.add field + + of nnkSym: + result.add FieldDescription( + name: n, + typ: getType(n), + caseField: parentCaseField, + caseBranch: parentCaseBranch, + isDiscriminator: isDiscriminator) + + of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty: + discard + + else: + doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr + +proc collectFieldsInHierarchy(result: var seq[FieldDescription], + objectType: NimNode) = + var objectType = objectType + + objectType.expectKind {nnkObjectTy, nnkRefTy} + + if objectType.kind == nnkRefTy: + objectType = objectType[0] + + objectType.expectKind nnkObjectTy + + var baseType = objectType[1] + if baseType.kind != nnkEmpty: + baseType.expectKind nnkOfInherit + baseType = baseType[0] + baseType.expectKind nnkSym + baseType = getImpl(baseType) + baseType.expectKind nnkTypeDef + baseType = baseType[2] + baseType.expectKind {nnkObjectTy, nnkRefTy} + collectFieldsInHierarchy result, baseType + + let recList = objectType[2] + collectFieldsFromRecList result, recList + +proc recordFields*(typeImpl: NimNode): seq[FieldDescription] = + if typeImpl.isTuple: + for i in 1 ..< typeImpl.len: + result.add FieldDescription(typ: typeImpl[i], name: ident("Field" & $(i - 1))) + return + + let objectType = case typeImpl.kind + of nnkObjectTy: typeImpl + of nnkTypeDef: typeImpl[2] + else: + macros.error("object type expected", typeImpl) + return + + collectFieldsInHierarchy(result, objectType) + +macro field*(obj: typed, fieldName: static string): untyped = + newDotExpr(obj, ident fieldName) + +proc skipPragma*(n: NimNode): NimNode = + if n.kind == nnkPragmaExpr: n[0] + else: n + + +{.pop.} diff --git a/tests/overload/t19737.nim b/tests/overload/t19737.nim new file mode 100644 index 000000000..b33ba9d8b --- /dev/null +++ b/tests/overload/t19737.nim @@ -0,0 +1,15 @@ +# issue #19737 + +import ./m19737 + +var m: seq[uint64] + +proc foo(x: bool) = discard + +proc test[T: uint64|uint32](s: var seq[T]) = + var tmp = newSeq[T](1) + s = newSeq[T](1) + + foo s[0] > tmp[0] + +test(m) diff --git a/tests/overload/t23249.nim b/tests/overload/t23249.nim new file mode 100644 index 000000000..f4657833b --- /dev/null +++ b/tests/overload/t23249.nim @@ -0,0 +1,17 @@ +# issue #23249 + +type Control* = object +proc onAction*(c: Control, handler: proc(e: int) {.gcsafe.}) = discard +proc onAction*(c: Control, handler: proc() {.gcsafe.}) = discard + +template setControlHandlerBlock(c: Control, p: untyped, a: untyped) = + when compiles(c.p(nil)): + c.p() do() {.gcsafe.}: a + else: + c.p = proc() {.gcsafe.} = + a + +proc mkLayout() = + var b: Control + setControlHandlerBlock(b, onAction): + echo "hi" diff --git a/tests/overload/t23755.nim b/tests/overload/t23755.nim new file mode 100644 index 000000000..de338a2ce --- /dev/null +++ b/tests/overload/t23755.nim @@ -0,0 +1,62 @@ +type + BigInt[bits: static int] = object + limbs: array[8, uint64] + +block: + proc view[N](a: array[N, uint64]) = + discard + + proc view[N](a: var array[N, uint64]) = + discard + + var r: BigInt[64] + r.limbs.view() + + +type Limbs[N: static int] = array[N, uint64] + +block: + proc view(a: Limbs) = + discard + + proc view(a: var Limbs) = + discard + + var r: BigInt[64] + r.limbs.view() + + +block: + type IntArray[N: static[int]] = array[N, int] + + proc p[T](a: IntArray[T]): bool= true + proc p(a: IntArray[5]): bool= false + + var s: IntArray[5] + doAssert s.p == false + +block: + type IntArray[N: static[int]] = array[N, int] + + proc `$`(a: IntArray): string = + return "test" + + var s: IntArray[5] = [1,1,1,1,1] + doAssert `$`(s) == "test" + +block: + proc p[n:static[int]](a: array[n, char]):bool=true + proc p[T, IDX](a: array[IDX, T]):bool=false + + var g: array[32, char] + doAssert p(g) + +block: # issue #23823 + func p[N,T](a, b: array[N,T]) = + discard + + func p[N: static int; T](x, y: array[N, T]) = + discard + + var a: array[5, int] + p(a,a) diff --git a/tests/overload/t8829.nim b/tests/overload/t8829.nim index fe0dbf2bf..85d87f136 100644 --- a/tests/overload/t8829.nim +++ b/tests/overload/t8829.nim @@ -2,7 +2,7 @@ block: let txt = "Hello World" template `[]`[T](p: ptr T, span: Slice[int]): untyped = - toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b) + toOpenArray(cast[ptr UncheckedArray[T]](p), span.a, span.b) doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]" @@ -12,7 +12,7 @@ block: let txt = "Hello World" template `[]`[T](p: ptr T, span: Slice[int]): untyped = - toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b) + toOpenArray(cast[ptr UncheckedArray[T]](p), span.a, span.b) doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]" diff --git a/tests/overload/tgenericalias.nim b/tests/overload/tgenericalias.nim new file mode 100644 index 000000000..50a44bd32 --- /dev/null +++ b/tests/overload/tgenericalias.nim @@ -0,0 +1,13 @@ +block: # issue #13799 + type + X[A, B] = object + a: A + b: B + + Y[A] = X[A, int] + template s(T: type X): X = T() + template t[A, B](T: type X[A, B]): X[A, B] = T() + proc works1(): Y[int] = s(X[int, int]) + proc works2(): Y[int] = t(X[int, int]) + proc works3(): Y[int] = t(Y[int]) + proc broken(): Y[int] = s(Y[int]) diff --git a/tests/overload/tor_isnt_better.nim b/tests/overload/tor_isnt_better.nim index ce92009d0..bee125386 100644 --- a/tests/overload/tor_isnt_better.nim +++ b/tests/overload/tor_isnt_better.nim @@ -7,7 +7,6 @@ block: # PR #22261 proc d(x: int | D[SomeInteger]):bool= true doAssert d(D[5]()) == false - block: # bug #8568 #[ Since PR #22261 and amendment has been made. Since D is a subset of D | E but @@ -17,3 +16,26 @@ block: # bug #8568 proc g(a: D|E): string = "foo D|E" proc g(a: D): string = "foo D" doAssert g(D[int]()) == "foo D" + +type Obj1[T] = object + v: T +converter toObj1[T](t: T): Obj1[T] = return Obj1[T](v: t) +block: # issue #10019 + proc fun1[T](elements: seq[T]): string = "fun1 seq" + proc fun1(o: object|tuple): string = "fun1 object|tuple" + proc fun2[T](elements: openArray[T]): string = "fun2 openarray" + proc fun2(o: object): string = "fun2 object" + proc fun_bug[T](elements: openArray[T]): string = "fun_bug openarray" + proc fun_bug(o: object|tuple):string = "fun_bug object|tuple" + proc main() = + var x = @["hello", "world"] + block: + # no ambiguity error shown here even though this would compile if we remove either 1st or 2nd overload of fun1 + doAssert fun1(x) == "fun1 seq" + block: + # ditto + doAssert fun2(x) == "fun2 openarray" + block: + # Error: ambiguous call; both t0065.fun_bug(elements: openarray[T])[declared in t0065.nim(17, 5)] and t0065.fun_bug(o: object or tuple)[declared in t0065.nim(20, 5)] match for: (array[0..1, string]) + doAssert fun_bug(x) == "fun_bug openarray" + main() diff --git a/tests/overload/toverload_issues.nim b/tests/overload/toverload_issues.nim index 5db7b54fa..26bf89091 100644 --- a/tests/overload/toverload_issues.nim +++ b/tests/overload/toverload_issues.nim @@ -77,27 +77,30 @@ testPred(1) -# bug #6526 -type - BaseObj = ref object of RootObj - DerivedObj = ref object of BaseObj - OtherDerivate = ref object of BaseObj - -proc `==`*[T1, T2: BaseObj](a: T1, b: T2): bool = - echo "baseobj ==" - return true - -let a = DerivedObj() -let b = DerivedObj() -echo a == b - -proc `==`*[T1, T2: OtherDerivate](a: T1, b: T2): bool = - echo "even better! ==" - return true - -let a2 = OtherDerivate() -let b2 = OtherDerivate() -echo a2 == b2 +block: # bug #6526 + type + BaseObj = ref object of RootObj + DerivedObj = ref object of BaseObj + OtherDerivate = ref object of BaseObj + + proc p[T](a: T, b: T): bool = + assert false + + proc p[T1, T2: BaseObj](a: T1, b: T2): bool = + echo "baseobj ==" + return true + + let a = DerivedObj() + let b = DerivedObj() + echo p(a,b) + + proc p[T1, T2: OtherDerivate](a: T1, b: T2): bool = + echo "even better! ==" + return true + + let a2 = OtherDerivate() + let b2 = OtherDerivate() + echo p(a2, b2) diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim index 0741fce60..d195a069d 100644 --- a/tests/overload/toverload_various.nim +++ b/tests/overload/toverload_various.nim @@ -506,3 +506,63 @@ block: doAssert(p2(F(float,1.0),F(float,2)) == 3.0) doAssert(p2(F(float,1.0),F(float,2.0)) == 3.0) #doAssert(p2(F(float,1),F(int,2.0)) == 3.0) + +block: # PR #23870 + type + A {.inheritable.} = object + B = object of A + C = object of B + + proc p[T: A](x: T): int = 0 + proc p[T: B](x: T): int = 1 + + proc d(x: A): int = 0 + proc d(x: B): int = 1 + + proc g[T:A](x: typedesc[T]): int = 0 + proc g[T: B](x: typedesc[T]): int = 1 + + proc f[T](x: typedesc[T]): int = 0 + proc f[T:B](x: typedesc[T]): int = 1 + + assert p(C()) == 1 + assert d(C()) == 1 + assert g(C) == 1 + assert f(C) == 1 + +block: # PR #23870 + type + A = object of RootObj + PT = proc(ev: A) {.closure.} + sdt = seq[(PT, PT)] + + proc encap() = + proc p(a: A) {.closure.} = + discard + + var s: sdt + s.add (p, nil) + + encap() + +block: # PR #23870 + type + A = object of RootObj + B = object of A + C = object of B + + proc p(a: B | RootObj): int = + 0 + + proc p(a: A | A): int = + 1 + + assert p(C()) == 0 + + proc d(a: RootObj | B): int = + 0 + + proc d(a: A | A): int = + 1 + + assert d(C()) == 0 diff --git a/tests/overload/tuntypedarg.nim b/tests/overload/tuntypedarg.nim new file mode 100644 index 000000000..9aa4fad3b --- /dev/null +++ b/tests/overload/tuntypedarg.nim @@ -0,0 +1,19 @@ +import macros + +block: # issue #7385 + type CustomSeq[T] = object + data: seq[T] + macro `[]`[T](s: CustomSeq[T], args: varargs[untyped]): untyped = + ## The end goal is to replace the joker "_" by something else + result = newIntLitNode(10) + proc foo1(): CustomSeq[int] = + result.data.newSeq(10) + # works since no overload matches first argument with type `CustomSeq` + # except magic `[]`, which always matches without checking arguments + doAssert result[_] == 10 + doAssert foo1() == CustomSeq[int](data: newSeq[int](10)) + proc foo2[T](): CustomSeq[T] = + result.data.newSeq(10) + # works fine with generic return type + doAssert result[_] == 10 + doAssert foo2[int]() == CustomSeq[int](data: newSeq[int](10)) diff --git a/tests/overload/tvaruintconv.nim b/tests/overload/tvaruintconv.nim new file mode 100644 index 000000000..87ebd285d --- /dev/null +++ b/tests/overload/tvaruintconv.nim @@ -0,0 +1,207 @@ +discard """ + action: compile +""" + +# https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102 +# failed with "for a 'var' type a variable needs to be passed; but 'uint64(result)' is immutable" + +import + std/[typetraits, macros] + +type + DefaultFlavor = object + +template serializationFormatImpl(Name: untyped) {.dirty.} = + type Name = object + +template serializationFormat(Name: untyped) = + serializationFormatImpl(Name) + +template setReader(Format, FormatReader: distinct type) = + when arity(FormatReader) > 1: + template Reader(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F] + else: + template ReaderType(T: type Format): type = FormatReader + template Reader(T: type Format): type = FormatReader + +template useDefaultReaderIn(T: untyped, Flavor: type) = + mixin Reader + + template readValue(r: var Reader(Flavor), value: var T) = + mixin readRecordValue + readRecordValue(r, value) + +import mvaruintconv + +type + FieldTag[RecordType: object; fieldName: static string] = distinct void + +func declval*(T: type): T {.compileTime.} = + default(ptr T)[] + +macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped = + var typeAst = getType(T)[1] + var typeImpl: NimNode + let isSymbol = not typeAst.isTuple + + if not isSymbol: + typeImpl = typeAst + else: + typeImpl = getImpl(typeAst) + result = newStmtList() + + var i = 0 + for field in recordFields(typeImpl): + let + fieldIdent = field.name + realFieldName = newLit($fieldIdent.skipPragma) + fieldName = realFieldName + fieldIndex = newLit(i) + + let fieldNameDefs = + if isSymbol: + quote: + const fieldName {.inject, used.} = `fieldName` + const realFieldName {.inject, used.} = `realFieldName` + else: + quote: + const fieldName {.inject, used.} = $`fieldIndex` + const realFieldName {.inject, used.} = $`fieldIndex` + + let field = + if isSymbol: + quote do: declval(`T`).`fieldIdent` + else: + quote do: declval(`T`)[`fieldIndex`] + + result.add quote do: + block: + `fieldNameDefs` + + template FieldType: untyped {.inject, used.} = typeof(`field`) + + `body` + + # echo repr(result) + +template enumAllSerializedFields(T: type, body): untyped = + enumAllSerializedFieldsImpl(T, body) + +type + FieldReader[RecordType, Reader] = tuple[ + fieldName: string, + reader: proc (rec: var RecordType, reader: var Reader) + {.gcsafe, nimcall.} + ] + +proc totalSerializedFieldsImpl(T: type): int = + mixin enumAllSerializedFields + enumAllSerializedFields(T): inc result + +template totalSerializedFields(T: type): int = + (static(totalSerializedFieldsImpl(T))) + +template GetFieldType(FT: type FieldTag): type = + typeof field(declval(FT.RecordType), FT.fieldName) + +proc makeFieldReadersTable(RecordType, ReaderType: distinct type, + numFields: static[int]): + array[numFields, FieldReader[RecordType, ReaderType]] = + mixin enumAllSerializedFields, handleReadException + var idx = 0 + + enumAllSerializedFields(RecordType): + proc readField(obj: var RecordType, reader: var ReaderType) + {.gcsafe, nimcall.} = + + mixin readValue + + type F = FieldTag[RecordType, realFieldName] + field(obj, realFieldName) = reader.readValue(GetFieldType(F)) + + result[idx] = (fieldName, readField) + inc idx + +proc fieldReadersTable(RecordType, ReaderType: distinct type): auto = + mixin readValue + type T = RecordType + const numFields = totalSerializedFields(T) + var tbl {.threadvar.}: ref array[numFields, FieldReader[RecordType, ReaderType]] + if tbl == nil: + tbl = new typeof(tbl) + tbl[] = makeFieldReadersTable(RecordType, ReaderType, numFields) + return addr(tbl[]) + +proc readValue(reader: var auto, T: type): T = + mixin readValue + reader.readValue(result) + +template decode(Format: distinct type, + input: string, + RecordType: distinct type): auto = + mixin Reader + block: # https://github.com/nim-lang/Nim/issues/22874 + var reader: Reader(Format) + reader.readValue(RecordType) + +template readValue(Format: type, + ValueType: type): untyped = + mixin Reader, init, readValue + var reader: Reader(Format) + readValue reader, ValueType + +template parseArrayImpl(numElem: untyped, + actionValue: untyped) = + actionValue + +serializationFormat Json +template createJsonFlavor(FlavorName: untyped, + skipNullFields = false) {.dirty.} = + type FlavorName = object + + template Reader(T: type FlavorName): type = Reader(Json, FlavorName) +type + JsonReader[Flavor = DefaultFlavor] = object + +Json.setReader JsonReader + +template parseArray(r: var JsonReader; body: untyped) = + parseArrayImpl(idx): body + +template parseArray(r: var JsonReader; idx: untyped; body: untyped) = + parseArrayImpl(idx): body + +proc readRecordValue[T](r: var JsonReader, value: var T) = + type + ReaderType {.used.} = type r + T = type value + + discard T.fieldReadersTable(ReaderType) + +proc readValue[T](r: var JsonReader, value: var T) = + mixin readValue + + when value is seq: + r.parseArray: + readValue(r, value[0]) + + elif value is object: + readRecordValue(r, value) + +type + RemoteSignerInfo = object + id: uint32 + RemoteKeystore = object + +proc readValue(reader: var JsonReader, value: var RemoteKeystore) = + discard reader.readValue(seq[RemoteSignerInfo]) + +createJsonFlavor RestJson +useDefaultReaderIn(RemoteSignerInfo, RestJson) +proc readValue(reader: var JsonReader[RestJson], value: var uint64) = + discard reader.readValue(string) + +discard Json.decode("", RemoteKeystore) +block: # https://github.com/nim-lang/Nim/issues/22874 + var reader: Reader(RestJson) + discard reader.readValue(RemoteSignerInfo) diff --git a/tests/parallel/tsendtwice.nim b/tests/parallel/tsendtwice.nim index 6bf5e5ebb..9f4a2e06e 100644 --- a/tests/parallel/tsendtwice.nim +++ b/tests/parallel/tsendtwice.nim @@ -1,18 +1,10 @@ discard """ - output: '''ob2 @[] -ob @[] -ob3 @[] -3 -ob2 @[] -ob @[] -ob3 @[] -''' matrix: "--mm:refc" """ # bug #4776 -import tables +import tables, algorithm type Base* = ref object of RootObj @@ -35,20 +27,21 @@ globalTable.add("ob", d) globalTable.add("ob2", d) globalTable.add("ob3", d) +proc `<`(x, y: seq[int]): bool = x.len < y.len +proc kvs(t: TableRef[string, Base]): seq[(string, seq[int])] = + for k, v in t.pairs: result.add (k, v.someSeq) + result.sort + proc testThread(channel: ptr TableChannel) {.thread.} = globalTable = channel[].recv() - for k, v in pairs globaltable: - echo k, " ", v.someSeq var myObj: Base deepCopy(myObj, globalTable["ob"]) myObj.someSeq = newSeq[int](100) let table = channel[].recv() # same table - echo table.len - for k, v in mpairs table: - echo k, " ", v.someSeq assert(table.contains("ob")) # fails! assert(table.contains("ob2")) # fails! assert(table.contains("ob3")) # fails! + assert table.kvs == globalTable.kvs # Last to see above spot checks first var channel: TableChannel diff --git a/tests/parser/tbinarynotindented.nim b/tests/parser/tbinarynotindented.nim new file mode 100644 index 000000000..8d124aade --- /dev/null +++ b/tests/parser/tbinarynotindented.nim @@ -0,0 +1,3 @@ +type Foo = ref int + not nil #[tt.Error + ^ invalid indentation]# diff --git a/tests/parser/tbinarynotsameline.nim b/tests/parser/tbinarynotsameline.nim new file mode 100644 index 000000000..ca417e023 --- /dev/null +++ b/tests/parser/tbinarynotsameline.nim @@ -0,0 +1,10 @@ +# issue #23565 + +func foo: bool = + true + +const bar = block: + type T = int + not foo() + +doAssert not bar diff --git a/tests/pragmas/monoff1.nim b/tests/pragmas/monoff1.nim new file mode 100644 index 000000000..85d6c57b3 --- /dev/null +++ b/tests/pragmas/monoff1.nim @@ -0,0 +1 @@ +proc on*() = discard diff --git a/tests/pragmas/mqualifiedmacro.nim b/tests/pragmas/mqualifiedmacro.nim new file mode 100644 index 000000000..908973206 --- /dev/null +++ b/tests/pragmas/mqualifiedmacro.nim @@ -0,0 +1,10 @@ +template t*(x:untyped): untyped = + echo "template t" + +import macros +macro m*(name: static string, x: untyped): untyped = + let newName = ident(name) + result = quote do: + type `newName` = object + if result.kind == nnkStmtList: + result = result[^1] diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 9ffa9a33d..11a6df813 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -531,3 +531,10 @@ block: check(a) check(b) + +block: # https://forum.nim-lang.org/t/12522, backticks + template `mypragma`() {.pragma.} + # Error: invalid pragma: `mypragma` + type Test = object + field {.`mypragma`.}: int + doAssert Test().field.hasCustomPragma(mypragma) diff --git a/tests/pragmas/tonoff1.nim b/tests/pragmas/tonoff1.nim new file mode 100644 index 000000000..20ba7def2 --- /dev/null +++ b/tests/pragmas/tonoff1.nim @@ -0,0 +1,8 @@ +# issue #23002 + +import monoff1 + +proc test() = + {.warning[ProveInit]: on.} + +test() diff --git a/tests/pragmas/tonoff2.nim b/tests/pragmas/tonoff2.nim new file mode 100644 index 000000000..9dff5ef11 --- /dev/null +++ b/tests/pragmas/tonoff2.nim @@ -0,0 +1,14 @@ +discard """ + action: compile +""" + +# issue #22841 + +import unittest + +proc on() = + discard + +suite "some suite": + test "some test": + discard diff --git a/tests/pragmas/tpush.nim b/tests/pragmas/tpush.nim index 8ebbfe3d3..9c6b85c4e 100644 --- a/tests/pragmas/tpush.nim +++ b/tests/pragmas/tpush.nim @@ -99,3 +99,46 @@ block: # bug #23019 k(w) {.pop.} {.pop.} + +{.push exportC.} + +block: + proc foo11() = + const factor = [1, 2, 3, 4] + doAssert factor[0] == 1 + proc foo21() = + const factor = [1, 2, 3, 4] + doAssert factor[0] == 1 + + foo11() + foo21() + +template foo31() = + let factor = [1, 2, 3, 4] + doAssert factor[0] == 1 +template foo41() = + let factor = [1, 2, 3, 4] + doAssert factor[0] == 1 + +foo31() +foo41() + +{.pop.} + +import macros + +block: + {.push deprecated.} + template test() = discard + test() + {.pop.} + macro foo(): bool = + let ast = getImpl(bindSym"test") + var found = false + if ast[4].kind == nnkPragma: + for x in ast[4]: + if x.eqIdent"deprecated": + found = true + break + result = newLit(found) + doAssert foo() diff --git a/tests/pragmas/tpushnotes.nim b/tests/pragmas/tpushnotes.nim new file mode 100644 index 000000000..27ba0bec4 --- /dev/null +++ b/tests/pragmas/tpushnotes.nim @@ -0,0 +1,13 @@ +discard """ + matrix: "--warningAsError:HoleEnumConv" +""" + +type + e = enum + a = 0 + b = 2 + +var i: int +{.push warning[HoleEnumConv]:off.} +discard i.e +{.pop.} diff --git a/tests/pragmas/tqualifiedmacro.nim b/tests/pragmas/tqualifiedmacro.nim new file mode 100644 index 000000000..bc95ec1ea --- /dev/null +++ b/tests/pragmas/tqualifiedmacro.nim @@ -0,0 +1,14 @@ +discard """ + output: ''' +template t +''' +""" + +# issue #12696 + +import mqualifiedmacro +proc p() {. mqualifiedmacro.t .} = # errors with identifier expected but a.t found + echo "proc p" + +type Foo {. mqualifiedmacro.m("Bar") .} = object +doAssert Bar is object diff --git a/tests/proc/t23874.nim b/tests/proc/t23874.nim new file mode 100644 index 000000000..940bc4ac8 --- /dev/null +++ b/tests/proc/t23874.nim @@ -0,0 +1,26 @@ +block: + type Head[T] = object + wasc: bool + + proc `=destroy`[T](x: var Head[T]) = + discard + + proc `=copy`[T](x: var Head[T], y: Head[T]) = + x.wasc = true + + proc `=dup`[T](x: Head[T]): Head[T] = + result.wasc = true + + proc update(h: var Head) = + discard + + proc digest(h: sink Head) = + assert h.wasc + + var h = Head[int](wasc: false) + h.digest() # sink h + h.update() # use after sink + +block: + proc two(a: sink auto) =discard + assert typeof(two[int]) is proc(a: sink int) {.nimcall.} diff --git a/tests/proc/texplicitgenericcount.nim b/tests/proc/texplicitgenericcount.nim new file mode 100644 index 000000000..8654a1d13 --- /dev/null +++ b/tests/proc/texplicitgenericcount.nim @@ -0,0 +1,24 @@ +discard """ + cmd: "nim check -d:testsConciseTypeMismatch $file" +""" + +proc foo[T, U](x: T, y: U): (T, U) = (x, y) + +let x = foo[int](1, 2) #[tt.Error + ^ type mismatch +Expression: foo[int](1, 2) + [1] 1: int literal(1) + [2] 2: int literal(2) + +Expected one of (first mismatch at [position]): +[2] proc foo[T, U](x: T; y: U): (T, U) + missing generic parameter: U]# +let y = foo[int, float, string](1, 2) #[tt.Error + ^ type mismatch +Expression: foo[int, float, string](1, 2) + [1] 1: int literal(1) + [2] 2: int literal(2) + +Expected one of (first mismatch at [position]): +[3] proc foo[T, U](x: T; y: U): (T, U) + extra generic param given]# diff --git a/tests/proc/texplicitgenericcountverbose.nim b/tests/proc/texplicitgenericcountverbose.nim new file mode 100644 index 000000000..76228eeaf --- /dev/null +++ b/tests/proc/texplicitgenericcountverbose.nim @@ -0,0 +1,22 @@ +discard """ + cmd: "nim check $file" +""" + +proc foo[T, U](x: T, y: U): (T, U) = (x, y) + +let x = foo[int](1, 2) #[tt.Error + ^ type mismatch: got <int literal(1), int literal(2)> +but expected one of: +proc foo[T, U](x: T; y: U): (T, U) + first type mismatch at position: 2 in generic parameters + missing generic parameter: U + +expression: foo[int](1, 2)]# +let y = foo[int, float, string](1, 2) #[tt.Error + ^ type mismatch: got <int literal(1), int literal(2)> +but expected one of: +proc foo[T, U](x: T; y: U): (T, U) + first type mismatch at position: 3 in generic parameters + extra generic param given + +expression: foo[int, float, string](1, 2)]# diff --git a/tests/proc/texplicitgenerics.nim b/tests/proc/texplicitgenerics.nim new file mode 100644 index 000000000..833d77b3b --- /dev/null +++ b/tests/proc/texplicitgenerics.nim @@ -0,0 +1,55 @@ +block: # issue #16376 + type + Matrix[T] = object + data: T + proc randMatrix[T](m, n: int, max: T): Matrix[T] = discard + proc randMatrix[T](m, n: int, x: Slice[T]): Matrix[T] = discard + template randMatrix[T](m, n: int): Matrix[T] = randMatrix[T](m, n, T(1.0)) + let B = randMatrix[float32](20, 10) + +block: # different generic param counts + type + Matrix[T] = object + data: T + proc randMatrix[T](m: T, n: T): Matrix[T] = Matrix[T](data: T(1.0)) + proc randMatrix[T; U: not T](m: T, n: U): (Matrix[T], U) = (Matrix[T](data: T(1.0)), default(U)) + let b = randMatrix[float32](20, 10) + doAssert b == Matrix[float32](data: 1.0) + +block: # above for templates + type + Matrix[T] = object + data: T + template randMatrix[T](m: T, n: T): Matrix[T] = Matrix[T](data: T(1.0)) + template randMatrix[T; U: not T](m: T, n: U): (Matrix[T], U) = (Matrix[T](data: T(1.0)), default(U)) + let b = randMatrix[float32](20, 10) + doAssert b == Matrix[float32](data: 1.0) + +block: # sigmatch can't handle this without pre-instantiating the type: + # minimized from numericalnim + type Foo[T] = proc (x: T) + proc foo[T](x: T) = discard + proc bar[T](f: Foo[T]) = discard + bar[int](foo) + +block: # ditto but may be wrong minimization + # minimized from measuremancer + type Foo[T] = object + proc foo[T](): Foo[T] = Foo[T]() + # this is the actual issue but there are other instantiation problems + proc bar[T](x = foo[T]()) = discard + bar[int](Foo[int]()) + bar[int]() + # alternative version, also causes instantiation issue + proc baz[T](x: typeof(foo[T]())) = discard + baz[int](Foo[int]()) + +block: # issue #21346 + type K[T] = object + template s[T](x: int) = doAssert T is K[K[int]] + proc b1(n: bool | bool) = s[K[K[int]]](3) + proc b2(n: bool) = s[K[K[int]]](3) + template b3(n: bool) = s[K[K[int]]](3) + b1(false) # Error: cannot instantiate K; got: <T> but expected: <T> + b2(false) # Builds, on its own + b3(false) diff --git a/tests/proc/tgenericdefaultparam.nim b/tests/proc/tgenericdefaultparam.nim new file mode 100644 index 000000000..7bce591ce --- /dev/null +++ b/tests/proc/tgenericdefaultparam.nim @@ -0,0 +1,98 @@ +block: # issue #16700 + type MyObject[T] = object + x: T + proc initMyObject[T](value = T.default): MyObject[T] = + MyObject[T](x: value) + var obj = initMyObject[int]() + +block: # issue #20916 + type + SomeX = object + v: int + var val = 0 + proc f(_: type int, x: SomeX, v = x.v) = + doAssert v == 42 + val = v + proc a(): proc() = + let v = SomeX(v: 42) + var tmp = proc() = + int.f(v) + tmp + a()() + doAssert val == 42 + +import std/typetraits + +block: # issue #24099, original example + type + ColorRGBU = distinct array[3, uint8] ## RGB range 0..255 + ColorRGBAU = distinct array[4, uint8] ## RGB range 0..255 + ColorRGBUAny = ColorRGBU | ColorRGBAU + template componentType(t: typedesc[ColorRGBUAny]): typedesc = + ## Returns component type of a given color type. + arrayType distinctBase t + func `~=`[T: ColorRGBUAny](a, b: T, e = componentType(T)(1.0e-11)): bool = + ## Compares colors with given accuracy. + abs(a[0] - b[0]) < e and abs(a[1] - b[1]) < e and abs(a[2] - b[2]) < e + +block: # issue #24099, modified to actually work + type + ColorRGBU = distinct array[3, uint8] ## RGB range 0..255 + ColorRGBAU = distinct array[4, uint8] ## RGB range 0..255 + ColorRGBUAny = ColorRGBU | ColorRGBAU + template arrayType[I, T](t: typedesc[array[I, T]]): typedesc = + T + template `[]`(a: ColorRGBUAny, i: untyped): untyped = distinctBase(a)[i] + proc abs(a: uint8): uint8 = a + template componentType(t: typedesc[ColorRGBUAny]): typedesc = + ## Returns component type of a given color type. + arrayType distinctBase t + func `~=`[T: ColorRGBUAny](a, b: T, e = componentType(T)(1.0e-11)): bool = + ## Compares colors with given accuracy. + abs(a[0] - b[0]) <= e and abs(a[1] - b[1]) <= e and abs(a[2] - b[2]) <= e + doAssert ColorRGBU([1.uint8, 1, 1]) ~= ColorRGBU([1.uint8, 1, 1]) + +block: # issue #24099, modified to work but using float32 + type + ColorRGBU = distinct array[3, float32] ## RGB range 0..255 + ColorRGBAU = distinct array[4, float32] ## RGB range 0..255 + ColorRGBUAny = ColorRGBU | ColorRGBAU + template arrayType[I, T](t: typedesc[array[I, T]]): typedesc = + T + template `[]`(a: ColorRGBUAny, i: untyped): untyped = distinctBase(a)[i] + template componentType(t: typedesc[ColorRGBUAny]): typedesc = + ## Returns component type of a given color type. + arrayType distinctBase t + func `~=`[T: ColorRGBUAny](a, b: T, e = componentType(T)(1.0e-11)): bool = + ## Compares colors with given accuracy. + abs(a[0] - b[0]) < e and abs(a[1] - b[1]) < e and abs(a[2] - b[2]) < e + doAssert ColorRGBU([1.float32, 1, 1]) ~= ColorRGBU([1.float32, 1, 1]) + +block: # issue #13270 + type + A = object + B = object + proc f(a: A) = discard + proc g[T](value: T, cb: (proc(a: T)) = f) = + cb value + g A() + # This should fail because there is no f(a: B) overload available + doAssert not compiles(g B()) + +block: # issue #24121 + type + Foo = distinct int + Bar = distinct int + FooBar = Foo | Bar + + proc foo[T: distinct](x: T): string = "a" + proc foo(x: Foo): string = "b" + proc foo(x: Bar): string = "c" + + proc bar(x: FooBar, y = foo(x)): string = y + doAssert bar(Foo(123)) == "b" + doAssert bar(Bar(123)) == "c" + + proc baz[T: FooBar](x: T, y = foo(x)): string = y + doAssert baz(Foo(123)) == "b" + doAssert baz(Bar(123)) == "c" diff --git a/tests/proc/tinferlambdareturn.nim b/tests/proc/tinferlambdareturn.nim new file mode 100644 index 000000000..e9e592871 --- /dev/null +++ b/tests/proc/tinferlambdareturn.nim @@ -0,0 +1,36 @@ +import std/[sugar, sequtils] + +block: # issue #23200 + proc dosomething(iter: int -> (iterator: int)) = + discard + proc dosomething(iter: int -> seq[int]) = + discard + proc makeSeq(x: int): seq[int] = + @[x] + # Works fine with 1.6.12 and 1.6.14 + dosomething(makeSeq) + # Works with 1.6.12, fails with 1.6.14 + dosomething((y) => makeSeq(y)) + dosomething(proc (y: auto): auto = makeSeq(y)) + proc foo(y: auto): auto = makeSeq(y) + dosomething(foo) + +block: # issue #18866 + proc somefn[T](list: openarray[T], op: proc (v: T): float) = + discard op(list[0]) + + type TimeD = object + year: Natural + month: 1..12 + day: 1..31 + + doAssert not compiles(@[TimeD()].somefn(proc (v: auto): auto = + v + )) + @[TimeD()].somefn(proc (v: auto): auto = + v.year.float + ) + proc foo(v: auto): auto = v + doAssert not compiles(@[TimeD()].somefn(foo)) + proc bar(v: auto): auto = v.year.float + @[TimeD()].somefn(bar) diff --git a/tests/proc/tstaticsignature.nim b/tests/proc/tstaticsignature.nim new file mode 100644 index 000000000..25aa09c5d --- /dev/null +++ b/tests/proc/tstaticsignature.nim @@ -0,0 +1,268 @@ +block: # issue #4228 + template seqType(t: typedesc): typedesc = + when t is int: + seq[int] + else: + seq[string] + + proc mkSeq[T: int|string](v: T): seqType(T) = + result = newSeq[T](1) + result[0] = v + + doAssert mkSeq("a") == @["a"] + doAssert mkSeq(1) == @[1] + +block: # expanded version of t8545 + template bar(a: static[bool]): untyped = + when a: + int + else: + float + + proc main() = + proc foo1(a: static[bool]): auto = 1 + doAssert foo1(true) == 1 + + proc foo2(a: static[bool]): bar(a) = 1 + doAssert foo2(true) == 1 + doAssert foo2(true) is int + doAssert foo2(false) == 1.0 + doAssert foo2(false) is float + + proc foo3(a: static[bool]): bar(cast[bool](a)) = 1 + doAssert foo3(true) == 1 + doAssert foo3(true) is int + doAssert foo3(false) == 1.0 + doAssert foo3(false) is float + + proc foo4(a: static[bool]): bar(static(a)) = 1 + doAssert foo4(true) == 1 + doAssert foo4(true) is int + doAssert foo4(false) == 1.0 + doAssert foo4(false) is float + + static: main() + main() + +block: # issue #8406 + macro f(x: static[int]): untyped = discard + proc g[X: static[int]](v: f(X)) = discard + +import macros + +block: # issue #8551 + macro distinctBase2(T: typedesc): untyped = + let typeNode = getTypeImpl(T) + expectKind(typeNode, nnkBracketExpr) + if typeNode[0].typeKind != ntyTypeDesc: + error "expected typeDesc, got " & $typeNode[0] + var typeSym = typeNode[1] + + typeSym = getTypeImpl(typeSym) + + if typeSym.typeKind != ntyDistinct: + error "type is not distinct: " & $typeSym.typeKind + + typeSym = typeSym[0] + typeSym + + func distinctBase[T](a: T): distinctBase2(T) = distinctBase2(T)(a) + + type T = distinct int + doAssert distinctBase(T(0)) is int + +block: + type Foo[T] = object + x: T + + proc foo(x: Foo): Foo[x.T] = + doAssert typeof(result) is typeof(x) + + var a: Foo[int] + let b: Foo[int] = foo(a) + doAssert b.x is int + +block: + type Foo[T: static int] = object + x: array[T, int] + + proc double(x: int): int = x * 2 + + proc foo[T: static int](x: Foo[T]): Foo[T.double] = + doAssert typeof(result).T == double(typeof(x).T) + + var a: Foo[3] + let b: Foo[6] = foo(a) + doAssert $typeof(foo(a)) == "Foo[6]" + +block: + type Foo[T: static int] = object + x: array[T, int] + + proc foo(x: Foo): Foo[x.T] = + doAssert typeof(result).T == typeof(x).T + doAssert typeof(result) is typeof(x) + + var a: Foo[3] + let b: Foo[3] = foo(a) + doAssert $typeof(foo(a)) == "Foo[3]" + +block: # issue #7006 + type + Node[T] = object + val: T + next: ref Node[T] + HHSet[T, Key] = object + data: seq[Node[T]] + proc rawGet(hhs:HHSet; key: hhs.Key): ptr Node[hhs.T] = + return nil # body doesn't matter + var hhs: HHSet[string, cstring] + discard hhs.rawGet("hello".cstring) + +block: # issue #7008 + type Node[T] = object + val: T + # Compiles fine + proc concreteProc(s: Node[cstring]; key: s.T) = discard + # Also fine + proc implicitGenericProc1(s: Node; key: s.T) = discard + # still fine + proc explicitGenericProc1[T](s: Node[T]; key: T) = discard + # Internal Compiler Error! + proc explicitGenericProc2[T](s: Node[T]; key: s.T) = discard + let n = Node[int](val: 5) + implicitGenericProc1(n, 5) # works + explicitGenericProc1(n, 5) # works + explicitGenericProc2(n, 5) # doesn't + +block: # issue #20027 + block: + type Test[T] = object + proc run(self: Test): self.T = discard + discard run(Test[int]()) + block: + type Test[T] = object + proc run[T](self: Test[T]): self.T = discard + discard run(Test[int]()) + block: + type Test[T] = object + proc run(self: Test[auto]): self.T = discard + discard run(Test[int]()) + +block: # issue #11112 + proc foo[A, B]: type(A.default + B.default) = + discard + doAssert foo[int, int]() is int + +block: # tyStatic and tyFromExpr instantiation mid-match + proc bar(x: int): int = x * 3 + proc bar2(x: static int): int = x * 4 + type Foo[T: static int] = distinct array[T, int] + proc foo[T: static int](x: Foo[T], y: Foo[bar(T)]) = discard + proc foo2[T: static int](x: Foo[T], y: Foo[bar2(T)]) = discard + foo(Foo[1]([1]), Foo[3]([1, 2, 3])) + foo2(Foo[1]([1]), Foo[4]([1, 2, 3, 4])) + +block: # issue #4990 + type Foo[I: static[int], A: static[array[I, int]]] = object + curIndex: int + + proc next[I: static[int], A: static[array[I, int]]](f: Foo[I, A]): string = + discard + const arr = [1, 2, 3] + var f: Foo[arr.len, arr] + discard next(f) + +block: # issue #4990 comment + type + Foo[A: static[int], B: static[int], TokenType: enum, EofToken: static[TokenType]] = object + curIndex: int + MyEnum = enum + meA, meB + Bar = Foo[2, 3, MyEnum, meA] + proc next[A: static[int], B: static[int], TokenType: enum, + EofToken: static[TokenType]](f: Foo[A, B, TokenType, EofToken], + a: static[(array[A, int], array[B, int])]): TokenType = + TokenType(a[0][f.curIndex]) + const + a = [1, 2] + b = [3, 4, 5] + template next(bar: Bar): MyEnum = + next(Foo[2, 3, MyEnum, meA](bar), (a, b)) + let bar = Bar(curIndex: 0) + doAssert bar.next() == meB + +block: # issue #14053 + template returnType(value: static[int]): typedesc = + when value == 1: + int + else: + float + proc fun(value: static[int]): returnType(value) = discard + doAssert fun(1) is int + template returnType2(value: static[int]): typedesc = + int + proc fun2(value: static[int]): returnType2(value) = discard + doAssert fun2(1) is int + +block: # issue #7547 + macro foo(N: static[int]): untyped = + result = getType(int) + type + Foo[N: static[int]] = foo(N) + ContainsFoo[N: static[int]] = object + Ffoo: Foo[N] + proc initFoo(N: static[int]): Foo[N] = discard + proc initContainsFoo(size: static[int]): ContainsFoo[size] = discard + var a: Foo[10] # Works + doAssert a is int + let b = initFoo(10) # Works + doAssert b is int + let c = ContainsFoo[5]() # Works + doAssert c.Ffoo is int + let z = initContainsFoo(5) # Error: undeclared identifier: 'N' + doAssert z.Ffoo is int + +block: # issue #22607, needs nkWhenStmt to be handled like nkRecWhen + proc test[x: static bool]( + t: ( + when x: + int + else: + float + ) + ) = discard + test[true](1.int) + test[false](1.0) + doAssert not compiles(test[]) + +block: # `when` in static signature + template ctAnd(a, b): bool = + when a: + when b: true + else: false + else: false + template test(): untyped = + when ctAnd(declared(SharedTable), typeof(result) is SharedTable): + result = SharedTable() + else: + result = 123 + proc foo[T](): T = test() + proc bar[T](x = foo[T]()): T = x + doAssert bar[int]() == 123 + +block: # issue #22276 + type Foo = enum A, B + macro test(y: static[Foo]): untyped = + if y == A: + result = parseExpr("proc (x: int)") + else: + result = parseExpr("proc (x: float)") + proc foo(y: static[Foo], x: test(y)) = # We want to make the type of `x` depend on what `y` is + x(9) + foo(A, proc (x: int) = doAssert x == 9) + var a: int + foo(A, proc (x: int) = + a = x * 2) + doAssert a == 18 + foo(B, proc (x: float) = doAssert x == 9) diff --git a/tests/proc/twrongdefaultvalue.nim b/tests/proc/twrongdefaultvalue.nim new file mode 100644 index 000000000..2c36c2247 --- /dev/null +++ b/tests/proc/twrongdefaultvalue.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim check $file" + action: reject + nimout: ''' +twrongdefaultvalue.nim(20, 12) template/generic instantiation of `doit` from here +twrongdefaultvalue.nim(17, 37) Error: type mismatch: got <proc (p: int): Item[initItem.T]> but expected 'Item[system.string]' +twrongdefaultvalue.nim(25, 3) template/generic instantiation of `foo` from here +twrongdefaultvalue.nim(23, 33) Error: type mismatch: got <string> but expected 'int' +''' +""" + +block: # issue #21258 + type Item[T] = object + pos: int + proc initItem[T](p:int=10000) : Item[T] = + result = Item[T](p) + proc doit[T](x:Item[T], s:Item[T]=initItem) : string = + return $x.pos + let x = Item[string](pos:100) + echo doit(x) + +block: # issue #21258, reduced case + proc foo[T](x: seq[T], y: T = "foo") = + discard + foo @[1, 2, 3] diff --git a/tests/range/tcompiletime_range_checks.nim b/tests/range/tcompiletime_range_checks.nim index 29e2c48a8..2d3f292ec 100644 --- a/tests/range/tcompiletime_range_checks.nim +++ b/tests/range/tcompiletime_range_checks.nim @@ -1,8 +1,8 @@ discard """ cmd: "nim check --hint:Processing:off --hint:Conf:off $file" errormsg: "18446744073709551615 can't be converted to int8" - nimout: '''tcompiletime_range_checks.nim(36, 21) Error: 2147483648 can't be converted to int32 -tcompiletime_range_checks.nim(37, 23) Error: -1 can't be converted to uint64 + nimout: ''' +tcompiletime_range_checks.nim(36, 21) Error: 2147483648 can't be converted to int32 tcompiletime_range_checks.nim(38, 34) Error: 255 can't be converted to FullNegativeRange tcompiletime_range_checks.nim(39, 34) Error: 18446744073709551615 can't be converted to HalfNegativeRange tcompiletime_range_checks.nim(40, 34) Error: 300 can't be converted to FullPositiveRange diff --git a/tests/range/texplicitvarconv.nim b/tests/range/texplicitvarconv.nim new file mode 100644 index 000000000..8da8a8878 --- /dev/null +++ b/tests/range/texplicitvarconv.nim @@ -0,0 +1,13 @@ +# related to issue #24032 + +proc `++`(n: var int) = + n += 1 + +type + r = range[ 0..15 ] + +var a: r = 14 + +++int(a) # this should be mutable + +doAssert a == 15 diff --git a/tests/range/toutofrangevarconv.nim b/tests/range/toutofrangevarconv.nim new file mode 100644 index 000000000..1ee4d340e --- /dev/null +++ b/tests/range/toutofrangevarconv.nim @@ -0,0 +1,14 @@ +discard """ + outputsub: "value out of range: 5 notin 0 .. 3 [RangeDefect]" + exitcode: "1" +""" + +# make sure out of bounds range conversion is detected for `var` conversions + +type R = range[0..3] + +proc foo(x: var R) = + doAssert x in 0..3 + +var x = 5 +foo(R(x)) diff --git a/tests/refc/tsinkbug.nim b/tests/refc/tsinkbug.nim new file mode 100644 index 000000000..de2ec98a5 --- /dev/null +++ b/tests/refc/tsinkbug.nim @@ -0,0 +1,26 @@ +discard """ + matrix: "--gc:refc; --gc:arc" + output: ''' +Value is: 42 +Value is: 42''' +""" + +type AnObject* = object of RootObj + value*: int + +proc mutate(a: sink AnObject) = + a.value = 1 + +var obj = AnObject(value: 42) +echo "Value is: ", obj.value +mutate(obj) +echo "Value is: ", obj.value + +proc p(x: sink string) = + var y = move(x) + doAssert x.len == 0 + doAssert y.len == 4 + +p("1234") +var s = "oooo" +p(s) diff --git a/tests/sets/trangeincompatible.nim b/tests/sets/trangeincompatible.nim new file mode 100644 index 000000000..554a50235 --- /dev/null +++ b/tests/sets/trangeincompatible.nim @@ -0,0 +1,32 @@ +block: # issue #20142 + let + s1: set['a' .. 'g'] = {'a', 'e'} + s2: set['a' .. 'g'] = {'b', 'c', 'd', 'f'} # this works fine + s3 = {'b', 'c', 'd', 'f'} + + doAssert s1 != s2 + doAssert s1 == {range['a'..'g'] 'a', 'e'} + doAssert s2 == {range['a'..'g'] 'b', 'c', 'd', 'f'} + # literal conversion: + doAssert s1 == {'a', 'e'} + doAssert s2 == {'b', 'c', 'd', 'f'} + doAssert s3 == {'b', 'c', 'd', 'f'} + doAssert not compiles(s1 == s3) + doAssert not compiles(s2 == s3) + # can't convert literal 'z', overload match fails + doAssert not compiles(s1 == {'a', 'z'}) + +block: # issue #18396 + var s1: set[char] = {'a', 'b'} + var s2: set['a'..'z'] = {'a', 'b'} + doAssert s1 == {'a', 'b'} + doAssert s2 == {range['a'..'z'] 'a', 'b'} + doAssert s2 == {'a', 'b'} + doAssert not compiles(s1 == s2) + +block: # issue #16270 + var s1: set[char] = {'a', 'b'} + var s2: set['a'..'z'] = {'a', 'c'} + doAssert not (compiles do: s2 = s2 + s1) + s2 = s2 + {'a', 'b'} + doAssert s2 == {'a', 'b', 'c'} diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim index 3c20a3907..6125a3715 100644 --- a/tests/sets/tsets.nim +++ b/tests/sets/tsets.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c cpp" +""" + # Test builtin sets # xxx these tests are not very good, this should be revisited. @@ -93,3 +97,37 @@ block: doAssert k99 notin s1 doAssert k99 notin s2 + +block: # bug #23422 + block: + var a: set[uint8] = {1'u8} + + proc printLen(x: set[uint8]): int = + doAssert x.len == card(x) + result = card(x) + + proc printLenVar(x: var set[uint8]): int = + doAssert x.len == card(x) + result = card(x) + + doAssert a.len == 1 + doAssert printLen(a) == 1 + doAssert printLenVar(a) == card(a) + + block: + type Fruit = enum + Apple, Banana, Melon + + var a: set[Fruit] = {Apple} + + proc printLen(x: set[Fruit]): int = + doAssert x.len == card(x) + result = card(x) + + proc printLenVar(x: var set[Fruit]): int = + doAssert x.len == card(x) + result = card(x) + + doAssert a.len == 1 + doAssert printLen(a) == 1 + doAssert printLenVar(a) == card(a) diff --git a/tests/sets/twrongenumrange.nim b/tests/sets/twrongenumrange.nim new file mode 100644 index 000000000..a8d64ac44 --- /dev/null +++ b/tests/sets/twrongenumrange.nim @@ -0,0 +1,50 @@ +discard """ + cmd: "nim check --hints:off $file" +""" + +# issue #17848 + +block: + # generate with: + # var a = "" + # for i in 0..<80: a.add "k" & $i & ", " + # echo a + type + TMsgKind = enum + k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79 + type + TNoteKind = range[k10..k79] + Conf = ref object + notes: set[TNoteKind] + proc bad(conf: Conf, noteSet: set[TMsgKind]) = + conf.notes = noteSet #[tt.Error + ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']# + var conf = Conf() + bad(conf, {k10..k60}) + +block: + type + TMsgKind = enum k0, k1, k2, k3 + TNoteKind = range[k1..k2] + TNoteKinds = set[TNoteKind] + type Conf = ref object + notes: TNoteKinds + proc fn(conf: Conf, b: set[TMsgKind]) = + conf.notes = b #[tt.Error + ^ type mismatch: got <set[TMsgKind]> but expected 'TNoteKinds = set[TNoteKind]']# + var conf = Conf() + conf.fn({k0..k3}) # BUG: this should give error + echo conf.notes # {k1, k2} + +block: + #[ + compiler/bitsets.nim(43, 9) `elem >= 0` [AssertionDefect] + ]# + type + TMsgKind = enum k0, k1, k2, k3 + TNoteKind = range[k1..k2] + var notes: set[TNoteKind] + notes = {k0} #[tt.Error + ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']# + notes = {k0..k3} #[tt.Error + ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']# diff --git a/tests/statictypes/tstaticprocparams.nim b/tests/statictypes/tstaticprocparams.nim index f0bb6fb5f..75d8288ac 100644 --- a/tests/statictypes/tstaticprocparams.nim +++ b/tests/statictypes/tstaticprocparams.nim @@ -1,9 +1,16 @@ proc consumer[T: static proc(i: int): int{.nimcall.}](i: int): int = T(i) +proc consumer(T: static proc(i: int): int{.nimcall.}, i: int): int = T(i) + proc addIt(i: int): int = i + i +proc add(i: int): int = i + i # Checks if we can use overloads proc squareIt(i: int): int = i * i assert consumer[addIt](10) == 20 +assert consumer[add](10) == 20 assert consumer[squareIt](30) == 900 assert consumer[proc(i: int): int{.nimcall.} = i * i + i](10) == 110 - +assert consumer(addIt, 10) == 20 +assert consumer(add, 10) == 20 +assert consumer(squareIt, 30) == 900 +assert consumer(proc(i: int): int{.nimcall.} = i * i + i, 10) == 110 diff --git a/tests/stdlib/concurrency/tatomics.nim b/tests/stdlib/concurrency/tatomics.nim index 3fb5197da..08f2e7d3e 100644 --- a/tests/stdlib/concurrency/tatomics.nim +++ b/tests/stdlib/concurrency/tatomics.nim @@ -1,5 +1,7 @@ discard """ - matrix: "--mm:refc; --mm:orc" + # test C with -d:nimUseCppAtomics as well to check nothing breaks + matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics" + targets: "c cpp" """ # test atomic operations diff --git a/tests/stdlib/concurrency/tatomics_size.nim b/tests/stdlib/concurrency/tatomics_size.nim index cfe568623..f64adb308 100644 --- a/tests/stdlib/concurrency/tatomics_size.nim +++ b/tests/stdlib/concurrency/tatomics_size.nim @@ -1,5 +1,6 @@ discard """ - matrix: "--mm:refc; --mm:orc" + # test C with -d:nimUseCppAtomics as well to check nothing breaks + matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics" targets: "c cpp" """ import std/atomics @@ -17,4 +18,4 @@ block testSize: # issue 12726 f: AtomicFlag static: doAssert sizeof(Node) == sizeof(pointer) - doAssert sizeof(MyChannel) == sizeof(pointer) * 2 \ No newline at end of file + doAssert sizeof(MyChannel) == sizeof(pointer) * 2 diff --git a/tests/stdlib/tbase64.nim b/tests/stdlib/tbase64.nim index 98388bb6c..c3bfb818e 100644 --- a/tests/stdlib/tbase64.nim +++ b/tests/stdlib/tbase64.nim @@ -18,6 +18,8 @@ template main() = doAssert encode("") == "" doAssert decode("") == "" + doAssert decode(" ") == "" + const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++" const testInputExpands = "++++++++++++++++++++++++++++++" const longText = """Man is distinguished, not only by his reason, but by this diff --git a/tests/stdlib/tclosures.nim b/tests/stdlib/tclosures.nim new file mode 100644 index 000000000..84b033fa8 --- /dev/null +++ b/tests/stdlib/tclosures.nim @@ -0,0 +1,47 @@ +discard """ + targets: "c js" +""" + +import std/assertions + +block: # bug #4299 + proc scopeProc() = + proc normalProc() = + discard + + proc genericProc[T]() = + normalProc() + + genericProc[string]() + + scopeProc() + +block: # bug #12492 + proc foo() = + var i = 0 + proc bar() = + inc i + + bar() + doAssert i == 1 + + foo() + static: + foo() + +block: # bug #10849 + type + Generic[T] = ref object + getState: proc(): T + + proc newGeneric[T](): Generic[T] = + var state: T + + proc getState[T](): T = + state + + Generic[T](getState: getState) + + let g = newGeneric[int]() + let state = g.getState() + doAssert state == 0 diff --git a/tests/stdlib/tcomplex.nim b/tests/stdlib/tcomplex.nim index 812bcdc77..ca83314b9 100644 --- a/tests/stdlib/tcomplex.nim +++ b/tests/stdlib/tcomplex.nim @@ -84,6 +84,9 @@ let t = polar(a) doAssert(rect(t.r, t.phi) =~ a) doAssert(rect(1.0, 2.0) =~ complex(-0.4161468365471424, 0.9092974268256817)) +doAssert(almostEqual(a, a + complex(1e-16, 1e-16))) +doAssert(almostEqual(a, a + complex(2e-15, 2e-15), unitsInLastPlace = 5)) + let i64: Complex32 = complex(0.0f, 1.0f) diff --git a/tests/stdlib/tdeques.nim b/tests/stdlib/tdeques.nim index 49072b150..39ff996d1 100644 --- a/tests/stdlib/tdeques.nim +++ b/tests/stdlib/tdeques.nim @@ -189,6 +189,55 @@ proc main() = a.shrink(fromFirst = 0, fromLast = 1) doAssert $a == "[10, 20, 30]" + block: + var a, b: Deque[int] + for i in 1 .. 256: + a.addLast(i) + for i in 1 .. 255: + a.popLast + b.addLast(1) + doAssert a == b + + block: + # Issue 23275 + # Test `==`. + block: + var a, b = initDeque[int]() + doAssert a == b + doAssert a.hash == b.hash + a.addFirst(1) + doAssert a != b + doAssert a.hash != b.hash + b.addLast(1) + doAssert a == b + doAssert a.hash == b.hash + a.popFirst + b.popLast + doAssert a == b + doAssert a.hash == b.hash + a.addLast 2 + doAssert a != b + doAssert a.hash != b.hash + b.addFirst 2 + doAssert a == b + doAssert a.hash == b.hash + + block: + var a, b = initDeque[int]() + for i in countDown(100, 1): + a.addFirst(i) + for i in 1..100: + b.addLast(i) + doAssert a == b + for i in 1..99: + a.popLast + let a1 = [1].toDeque + doAssert a == a1 + doAssert a.hash == a1.hash + var c = initDeque[int]() + c.addLast(1) + doAssert a == c + doAssert a.hash == c.hash static: main() main() diff --git a/tests/stdlib/tencodings.nim b/tests/stdlib/tencodings.nim index e5e89ef37..2f4daaba3 100644 --- a/tests/stdlib/tencodings.nim +++ b/tests/stdlib/tencodings.nim @@ -101,3 +101,7 @@ block: doAssert orig == "\195\182\195\164\195\188\195\159" doAssert ibm850 == "\148\132\129\225" doAssert convert(ibm850, current, "ibm850") == orig + +block: # fixes about #23481 + doAssertRaises EncodingError: + discard open(destEncoding="this is a invalid enc") diff --git a/tests/stdlib/tenumutils.nim b/tests/stdlib/tenumutils.nim index 67b98efe1..2662a660d 100644 --- a/tests/stdlib/tenumutils.nim +++ b/tests/stdlib/tenumutils.nim @@ -35,5 +35,15 @@ template main = doAssert $b == "kb0" static: doAssert B.high.symbolName == "b2" + block: + type + Color = enum + Red = "red", Yellow = "yellow", Blue = "blue" + + var s = Red + doAssert symbolName(s) == "Red" + var x: range[Red..Blue] = Yellow + doAssert symbolName(x) == "Yellow" + static: main() main() diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index b6fbbbdb7..4555fbcb3 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:on; --backend:js --jsbigint64:off" + matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:on; --backend:c -d:nimStringHash2; --backend:cpp -d:nimStringHash2; --backend:js -d:nimStringHash2" """ import std/hashes @@ -31,7 +31,8 @@ block hashes: doAssert hashWangYi1(123) == wy123 const wyNeg123 = hashWangYi1(-123) doAssert wyNeg123 != 0 - doAssert hashWangYi1(-123) == wyNeg123 + when not defined(js): # TODO: fixme it doesn't work for JS + doAssert hashWangYi1(-123) == wyNeg123 # "hashIdentity value incorrect at 456" @@ -45,20 +46,31 @@ block hashes: else: doAssert hashWangYi1(456) == -6421749900419628582 +template jsNoInt64: untyped = + when defined js: + when compiles(compileOption("jsbigint64")): + when not compileOption("jsbigint64"): true + else: false + else: false + else: false +const sHash2 = (when defined(nimStringHash2) or jsNoInt64(): true else: false) + block empty: + const emptyStrHash = # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash] + when sHash2: 0 else: cast[Hash](-7286425919675154353i64) var a = "" b = newSeq[char]() c = newSeq[int]() d = cstring"" e = "abcd" - doAssert hash(a) == 0 - doAssert hash(b) == 0 + doAssert hash(a) == emptyStrHash + doAssert hash(b) == emptyStrHash doAssert hash(c) == 0 - doAssert hash(d) == 0 + doAssert hash(d) == emptyStrHash doAssert hashIgnoreCase(a) == 0 doAssert hashIgnoreStyle(a) == 0 - doAssert hash(e, 3, 2) == 0 + doAssert hash(e, 3, 2) == emptyStrHash block sameButDifferent: doAssert hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) @@ -92,7 +104,10 @@ block largeSize: # longer than 4 characters proc main() = doAssert hash(0.0) == hash(0) # bug #16061 - doAssert hash(cstring"abracadabra") == 97309975 + when not sHash2: # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash] + doAssert hash(cstring"abracadabra") == cast[Hash](-1119910118870047694i64) + else: + doAssert hash(cstring"abracadabra") == 97309975 doAssert hash(cstring"abracadabra") == hash("abracadabra") when sizeof(int) == 8 or defined(js): diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index 00e728fa2..0bd479670 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -53,9 +53,9 @@ proc asyncTest() {.async.} = doAssert("<title>Example Domain</title>" in body) resp = await client.request("http://example.com/404") - doAssert(resp.code.is4xx) - doAssert(resp.code == Http404) - doAssert(resp.status == $Http404) + doAssert(resp.code.is4xx or resp.code.is5xx) + doAssert(resp.code == Http404 or resp.code == Http500) + doAssert(resp.status == $Http404 or resp.status == $Http500) when false: # occasionally does not give success code resp = await client.request("https://google.com/") @@ -115,9 +115,9 @@ proc syncTest() = doAssert("<title>Example Domain</title>" in resp.body) resp = client.request("http://example.com/404") - doAssert(resp.code.is4xx) - doAssert(resp.code == Http404) - doAssert(resp.status == $Http404) + doAssert(resp.code.is4xx or resp.code.is5xx) + doAssert(resp.code == Http404 or resp.code == Http500) + doAssert(resp.status == $Http404 or resp.status == $Http500) when false: # occasionally does not give success code resp = client.request("https://google.com/") diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim index feacd3e57..6b963f029 100644 --- a/tests/stdlib/thttpclient_ssl.nim +++ b/tests/stdlib/thttpclient_ssl.nim @@ -13,7 +13,10 @@ discard """ ## Test with: ## ./bin/nim c -d:ssl -p:. --threads:on -r tests/stdlib/thttpclient_ssl.nim -when not defined(windows): + +from stdtest/testutils import disableSSLTesting + +when not defined(windows) and not disableSSLTesting(): # Disabled on Windows due to old OpenSSL version import std/[formatfloat, syncio] import diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index 691bedeaa..e425501f6 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:refc; --backend:cpp --mm:refc; --backend:js --jsbigint64:off; --backend:js --jsbigint64:on" + matrix: "; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on" """ @@ -51,7 +51,7 @@ for i in 0 .. 10000: except: discard # memory diff should less than 4M -doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # todo fixme doesn;t work for ORC +doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # test `$` diff --git a/tests/stdlib/tmarshalsegfault.nim b/tests/stdlib/tmarshalsegfault.nim new file mode 100644 index 000000000..71f2766c8 --- /dev/null +++ b/tests/stdlib/tmarshalsegfault.nim @@ -0,0 +1,54 @@ +# issue #12405 + +import std/[marshal, streams, times, tables, os, assertions] + +type AiredEpisodeState * = ref object + airedAt * : DateTime + tvShowId * : string + seasonNumber * : int + number * : int + title * : string + +type ShowsWatchlistState * = ref object + aired * : seq[AiredEpisodeState] + +type UiState * = ref object + shows: ShowsWatchlistState + +# Helpers to marshal and unmarshal +proc load * ( state : var UiState, file : string ) = + var strm = newFileStream( file, fmRead ) + + strm.load( state ) + + strm.close() + +proc store * ( state : UiState, file : string ) = + var strm = newFileStream( file, fmWrite ) + + strm.store( state ) + + strm.close() + +# 1. We fill the state initially +var state : UiState = UiState( shows: ShowsWatchlistState( aired: @[] ) ) + +# VERY IMPORTANT: For some reason, small numbers (like 2 or 3) don't trigger the bug. Anything above 7 or 8 on my machine triggers though +for i in 0..30: + var episode = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" ) + + state.shows.aired.add( episode ) + +# 2. Store it in a file with the marshal module, and then load it back up +store( state, "tmarshalsegfault_data" ) +load( state, "tmarshalsegfault_data" ) +removeFile("tmarshalsegfault_data") + +# 3. VERY IMPORTANT: Without this line, for some reason, everything works fine +state.shows.aired[ 0 ] = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" ) + +# 4. And formatting the airedAt date will now trigger the exception +for ep in state.shows.aired: + let x = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")" + let y = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")" + doAssert x == y diff --git a/tests/stdlib/tmimetypes.nim b/tests/stdlib/tmimetypes.nim index e332cea40..fd66ebd97 100644 --- a/tests/stdlib/tmimetypes.nim +++ b/tests/stdlib/tmimetypes.nim @@ -11,10 +11,18 @@ template main() = var m = newMimetypes() doAssert m.getMimetype("mp4") == "video/mp4" doAssert m.getExt("application/json") == "json" + doAssert m.getMimetype("json") == "application/json" m.register("foo", "baa") doAssert m.getMimetype("foo") == "baa" + doAssert m.getMimetype("txt") == "text/plain" + doAssert m.getExt("text/plain") == "txt" # see also `runnableExamples`. # xxx we should have a way to avoid duplicating code between runnableExamples and tests + doAssert m.getMimetype("nim") == "text/nim" + doAssert m.getMimetype("nimble") == "text/nimble" + doAssert m.getMimetype("nimf") == "text/nim" + doAssert m.getMimetype("nims") == "text/nim" + static: main() main() diff --git a/tests/stdlib/tmisc_issues.nim b/tests/stdlib/tmisc_issues.nim index 33eb9655d..86dcf4162 100644 --- a/tests/stdlib/tmisc_issues.nim +++ b/tests/stdlib/tmisc_issues.nim @@ -18,3 +18,22 @@ type var x: Object = Object(data: Test(Data(id: 12))) doAssert Data(x.data).id == 12 + +block: # bug #16771 + type A = object + n: int + + proc foo(a, b: var A) = + swap a, b + + var a, b: A + a.n = 42 + b.n = 1 + doAssert a.n == 42 + doAssert b.n == 1 + a.swap b + doAssert a.n == 1 + doAssert b.n == 42 + a.foo b + doAssert a.n == 42 + doAssert b.n == 1 diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index ad34e479a..611659fdb 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -27,9 +27,8 @@ Raises """ # test os path creation, iteration, and deletion -import os, strutils, pathnorm from stdtest/specialpaths import buildDir -import std/[syncio, assertions] +import std/[syncio, assertions, osproc, os, strutils, pathnorm] block fileOperations: let files = @["these.txt", "are.x", "testing.r", "files.q"] @@ -161,6 +160,18 @@ block fileOperations: # createDir should not fail if `dir` is empty createDir("") + + when defined(linux): # bug #24174 + createDir("a/b") + open("a/file.txt", fmWrite).close + + if not fileExists("a/fifoFile"): + doAssert execCmd("mkfifo -m 600 a/fifoFile") == 0 + + copyDir("a/", "../dest/a/", skipSpecial = true) + copyDirWithPermissions("a/", "../dest2/a/", skipSpecial = true) + removeDir("a") + # Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`, # `copyDir`, `copyDirWithPermissions`, `moveFile`, and `moveDir`. block: diff --git a/tests/stdlib/tparseutils.nim b/tests/stdlib/tparseutils.nim index 020964446..b69900864 100644 --- a/tests/stdlib/tparseutils.nim +++ b/tests/stdlib/tparseutils.nim @@ -99,3 +99,14 @@ block: # With this included, static: test() crashes the compiler (from a checkParseSize " 12" , 0, 1 # Leading white # Value Edge cases checkParseSize "9223372036854775807", 19, int64.high + +block: # bug #23936 + func parsePyFloat( + a: openArray[char], # here must be openArray instead of string to reproduce this bug + res: var BiggestFloat): int = + result = parseFloat(a, res) + + static: + var f = 0.0 + doAssert "1.0".parsePyFloat(f) == 3 + doAssert f == 1.0 diff --git a/tests/stdlib/tpaths.nim b/tests/stdlib/tpaths.nim index 082c4937a..edb56209a 100644 --- a/tests/stdlib/tpaths.nim +++ b/tests/stdlib/tpaths.nim @@ -6,15 +6,12 @@ import std/paths import std/assertions import pathnorm from std/private/ospaths2 {.all.} import joinPathImpl -import std/sugar +import std/[sugar, sets] proc normalizePath*(path: Path; dirSep = DirSep): Path = result = Path(pathnorm.normalizePath(path.string, dirSep)) -func `==`(x, y: Path): bool = - x.string == y.string - func joinPath*(parts: varargs[Path]): Path = var estimatedLen = 0 var state = 0 @@ -231,4 +228,11 @@ block ospaths: when doslikeFileSystem: doAssert joinPath(Path"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\", Path"..\\..\\VC\\vcvarsall.bat") == r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat".Path doAssert joinPath(Path"C:\\foo", Path"..\\a") == r"C:\a".Path - doAssert joinPath(Path"C:\\foo\\", Path"..\\a") == r"C:\a".Path \ No newline at end of file + doAssert joinPath(Path"C:\\foo\\", Path"..\\a") == r"C:\a".Path + + +block: # bug #23663 + var s: HashSet[Path] + s.incl("/a/b/c/..".Path) + doAssert "/a/b/".Path in s + doAssert "/a/b/c".Path notin s diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim index 920d429d4..eb32f7757 100644 --- a/tests/stdlib/trandom.nim +++ b/tests/stdlib/trandom.nim @@ -1,6 +1,6 @@ discard """ joinable: false # to avoid messing with global rand state - matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:off; --backend:js --jsbigint64:on" + matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on" """ import std/[assertions, formatfloat] import std/[random, math, stats, sets, tables] @@ -47,6 +47,8 @@ block: type DiceRoll = range[0..6] when not defined(js): doAssert rand(DiceRoll).int == 3 + elif compileOption("jsbigint64"): + doAssert rand(DiceRoll).int == 1 else: doAssert rand(DiceRoll).int == 6 @@ -296,7 +298,13 @@ block: # bug #22360 else: inc fc - when defined(js): - doAssert (tc, fc) == (483, 517), $(tc, fc) + when defined(js) and not compileOption("jsbigint64"): + doAssert (tc, fc) == (515, 485), $(tc, fc) else: doAssert (tc, fc) == (510, 490), $(tc, fc) + +block: + when defined(js) and not compileOption("jsbigint64"): + doAssert rand(int32.high) == 335507522 + else: + doAssert rand(int32.high) == 607539621 diff --git a/tests/stdlib/trationals.nim b/tests/stdlib/trationals.nim index cd9954f61..22d7f5c2d 100644 --- a/tests/stdlib/trationals.nim +++ b/tests/stdlib/trationals.nim @@ -10,6 +10,7 @@ template main() = z = Rational[int](num: 0, den: 1) o = initRational(num = 1, den = 1) a = initRational(1, 2) + u = 3u // 2 b = -1 // -2 m1 = -1 // 1 tt = 10 // 2 @@ -104,5 +105,13 @@ template main() = when sizeof(int) == 8: doAssert almostEqual(PI.toRational.toFloat, PI) + # unsigned + doAssert u == u + doAssert u + u == 3u // 1 + doAssert 3u.toRational - u == u + doAssert u * 2 == 3u // 1 + + + static: main() main() diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index e39eae9c1..ceab34bc9 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -16,6 +16,8 @@ discard """ [Suite] RST escaping [Suite] RST inline markup + +[Suite] Misc isssues ''' matrix: "--mm:refc; --mm:orc" """ @@ -526,8 +528,7 @@ suite "RST parsing": rnFieldBody rnLeaf 'Nim' rnLiteralBlock - rnLeaf ' - let a = 1 + rnLeaf 'let a = 1 ```' """ @@ -637,8 +638,7 @@ suite "RST parsing": rnLeaf 'test' rnFieldBody rnLiteralBlock - rnLeaf ' - let a = 1' + rnLeaf 'let a = 1' """) check(dedent""" @@ -661,8 +661,7 @@ suite "RST parsing": rnFieldBody rnLeaf '1' rnLiteralBlock - rnLeaf ' - let a = 1' + rnLeaf 'let a = 1' """) test "additional indentation < 4 spaces is handled fine": @@ -682,8 +681,7 @@ suite "RST parsing": rnLeaf 'nim' [nil] rnLiteralBlock - rnLeaf ' - let a = 1' + rnLeaf ' let a = 1' """) # | | # | \ indentation of exactly two spaces before 'let a = 1' @@ -717,8 +715,7 @@ suite "RST parsing": rnFieldBody rnLeaf 'Nim' rnLiteralBlock - rnLeaf ' - CodeBlock()' + rnLeaf 'CodeBlock()' rnLeaf ' ' rnLeaf 'Other' rnLeaf ' ' @@ -1985,3 +1982,13 @@ suite "RST inline markup": rnLeaf ')' """) check(warnings[] == @["input(1, 5) Warning: broken link 'f'"]) + +suite "Misc isssues": + test "Markdown CodeblockFields in one line (lacking enclosing ```)": + let message = """ + ```llvm-profdata merge first.profraw second.profraw third.profraw <more stuff maybe> -output data.profdata```""" + + try: + echo rstgen.rstToHtml(message, {roSupportMarkdown}, nil) + except EParseError: + discard diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 934403665..6253e7146 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1246,7 +1246,7 @@ Test1 "input(8, 4) Warning: language 'anotherLang' not supported" ]) check(output == "<pre class = \"listing\">anything</pre>" & - "<p><pre class = \"listing\">\nsomeCode</pre> </p>") + "<p><pre class = \"listing\">someCode</pre> </p>") test "RST admonitions": # check that all admonitions are implemented diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index 0668d12bd..60c63b450 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -92,6 +92,10 @@ static: # Ensure streams it doesnt break with nimscript on arc/orc #19716 let s = newStringStream("a") doAssert s.data == "a" +static: # issue #24054, readStr + var s = newStringStream("foo bar baz") + doAssert s.readStr(3) == "foo" + template main = var strm = newStringStream("abcde") var buffer = "12345" diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index 3c0d55c1d..ff406f898 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -562,7 +562,7 @@ proc main() = doAssert &"""{(if true: "'" & "'" & ')' else: "")}""" == "'')" doAssert &"{(if true: \"\'\" & \"'\" & ')' else: \"\")}" == "'')" doAssert fmt"""{(if true: "'" & ')' else: "")}""" == "')" - + block: # issue #20381 var ss: seq[string] template myTemplate(s: string) = @@ -573,5 +573,18 @@ proc main() = foo() doAssert ss == @["hello", "hello"] + block: + proc noraises() {.raises: [].} = + const + flt = 0.0 + str = "str" + + doAssert fmt"{flt} {str}" == "0.0 str" + + noraises() + + block: + doAssert not compiles(fmt"{formatting errors detected at compile time") + static: main() main() diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 9cc65f218..35f6bc669 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:off; --backend:js --jsbigint64:on" + matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on" """ import std/strutils @@ -527,9 +527,9 @@ template main() = block: # toHex doAssert(toHex(100i16, 32) == "00000000000000000000000000000064") - doAssert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") whenJsNoBigInt64: discard do: + doAssert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") doAssert(toHex(high(uint64)) == "FFFFFFFFFFFFFFFF") doAssert(toHex(high(uint64), 16) == "FFFFFFFFFFFFFFFF") doAssert(toHex(high(uint64), 32) == "0000000000000000FFFFFFFFFFFFFFFF") diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim index b9cbdd3e3..2ea96cfbb 100644 --- a/tests/stdlib/tsugar.nim +++ b/tests/stdlib/tsugar.nim @@ -295,7 +295,8 @@ template main() = for i in 0..5: xs.add(i) - xs.apply(d => ys.add(d)) + xs.apply(proc (d: auto) = ys.add(d)) + # ^ can be turned into d => ys.add(d) when we can infer void return type, #16906 doAssert ys == @[0, 1, 2, 3, 4, 5] test() diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim index c1cadb49d..f634ce0c2 100644 --- a/tests/stdlib/tsystem.nim +++ b/tests/stdlib/tsystem.nim @@ -4,7 +4,7 @@ discard """ """ import stdtest/testutils -import std/assertions +import std/[assertions, formatfloat] # TODO: in future work move existing `system` tests here, where they belong @@ -83,24 +83,36 @@ block: X = object a: string b: set[char] + c: int + d: float + e: int64 - var y = X(b: {'a'}) - reset(y) + var x = X(b: {'a'}, e: 10) - doAssert y.b == {} + var y = move x -block: - type - X = object - a: string - b: int - - var y = X(b: 1314) + doAssert x.a == "" + doAssert x.b == {} + doAssert x.c == 0 + doAssert x.d == 0.0 + doAssert x.e == 0 reset(y) - doAssert y.b == 0 + doAssert y.a == "" + doAssert y.b == {} + doAssert y.c == 0 + doAssert y.d == 0.0 + doAssert y.e == 0 + +block: + var x = 2 + var y = move x + doAssert y == 2 + doAssert x == 0 + reset y + doAssert y == 0 block: type @@ -170,3 +182,19 @@ block: # bug #20516 when not defined(js): let a = create(Foo) + +block: # bug #6549 + when not defined(js): + block: + const v = 18446744073709551615'u64 + + doAssert $v == "18446744073709551615" + doAssert $float32(v) == "1.8446744e+19", $float32(v) + doAssert $float64(v) == "1.8446744073709552e+19", $float64(v) + + block: + let v = 18446744073709551615'u64 + + doAssert $v == "18446744073709551615" + doAssert $float32(v) == "1.8446744e+19" + doAssert $float64(v) == "1.8446744073709552e+19" diff --git a/tests/stdlib/ttasks.nim b/tests/stdlib/ttasks.nim index 347c3347a..ba65590d9 100644 --- a/tests/stdlib/ttasks.nim +++ b/tests/stdlib/ttasks.nim @@ -523,3 +523,39 @@ block: doAssert resB == "abcdef" testReturnValues() + + +block: # bug #23635 + block: + type + Store = object + run: proc (a: int) {.nimcall, gcsafe.} + + block: + var count = 0 + proc hello(a: int) = + inc count, a + + var store = Store() + store.run = hello + + let b = toTask store.run(13) + b.invoke() + doAssert count == 13 + + block: + type + Store = object + run: proc () {.nimcall, gcsafe.} + + block: + var count = 0 + proc hello() = + inc count, 1 + + var store = Store() + store.run = hello + + let b = toTask store.run() + b.invoke() + doAssert count == 1 diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index e01ab3a4f..0f04168dc 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:on; --backend:js --jsbigint64:off" + matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:on; --backend:js --jsbigint64:off -d:nimStringHash2" """ import times, strutils, unittest @@ -71,7 +71,7 @@ template runTimezoneTests() = "2006-01-12T22:04:05Z", 11) # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" parseTest("2006-01-12T15:04:05.999999999Z-07:00", - "yyyy-MM-dd'T'HH:mm:ss'.999999999Z'zzz", "2006-01-12T22:04:05Z", 11) + "yyyy-MM-dd'T'HH:mm:ss.'999999999Z'zzz", "2006-01-12T22:04:05Z", 11) for tzFormat in ["z", "zz", "zzz"]: # formatting timezone as 'Z' for UTC parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat, @@ -770,3 +770,15 @@ block: # ttimes proc test(): DateTime {.gcsafe.} = result = "1970".parse("yyyy") doAssert test().year == 1970 + + block: # test FormatLiterals + # since #23861 + block: + let dt = dateTime(2024, mJul, 21, 17, 01, 02, 123_321_123, utc()) + check dt.format("ss.fff") == "02.123" + check dt.format("fff.ffffff") == "123.123321" + block: + let dt = parse("2024.07.21", "yyyy.MM.dd") + check dt.year == 2024 + check dt.month == mJul + check dt.monthday == 21 diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim index 8d5061124..9bbc2e92c 100644 --- a/tests/stdlib/ttypeinfo.nim +++ b/tests/stdlib/ttypeinfo.nim @@ -74,3 +74,20 @@ block: doAssert getEnumOrdinal(y, "Hello") == 0 doAssert getEnumOrdinal(y, "hello") == 1 + +block: # bug #23556 + proc test = + var + t: seq[int] + aseq = toAny(t) + + invokeNewSeq(aseq, 0) + + # Got random value only when loop 8 times. + for i in 1 .. 8: + extendSeq(aseq) + + doAssert t == @[0, 0, 0, 0, 0, 0, 0, 0] + + for i in 1 .. 7: + test() diff --git a/tests/stdlib/tunicode.nim b/tests/stdlib/tunicode.nim index adc8d2078..b9e68b15b 100644 --- a/tests/stdlib/tunicode.nim +++ b/tests/stdlib/tunicode.nim @@ -57,6 +57,7 @@ doAssert isAlpha("r") doAssert isAlpha("α") doAssert isAlpha("ϙ") doAssert isAlpha("ஶ") +doAssert isAlpha("网") doAssert(not isAlpha("$")) doAssert(not isAlpha("")) @@ -66,6 +67,7 @@ doAssert isAlpha("𐌼𐌰𐌲𐌲𐌻𐌴𐍃𐍄𐌰𐌽") doAssert isAlpha("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") doAssert isAlpha("Јамогујестистаклоитоминештети") doAssert isAlpha("Կրնամապակիուտեևինծիանհանգիստչըներ") +doAssert isAlpha("编程语言") doAssert(not isAlpha("$Foo✓")) doAssert(not isAlpha("⠙⠕⠑⠎⠝⠞")) diff --git a/tests/stdlib/tunixsocket.nim b/tests/stdlib/tunixsocket.nim new file mode 100644 index 000000000..636fd08c6 --- /dev/null +++ b/tests/stdlib/tunixsocket.nim @@ -0,0 +1,35 @@ +import std/[assertions, net, os, osproc] + +# XXX: Make this test run on Windows too when we add support for Unix sockets on Windows +when defined(posix) and not defined(nimNetLite): + const nim = getCurrentCompilerExe() + let + dir = currentSourcePath().parentDir() + serverPath = dir / "unixsockettest" + + let (_, err) = execCmdEx(nim & " c " & quoteShell(dir / "unixsockettest.nim")) + doAssert err == 0 + + let svproc = startProcess(serverPath, workingDir = dir) + doAssert svproc.running() + # Wait for the server to open the socket and listen from it + sleep(400) + + block unixSocketSendRecv: + let + unixSocketPath = dir / "usox" + socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_NONE) + + socket.connectUnix(unixSocketPath) + # for a blocking Unix socket this should never fail + socket.send("data sent through the socket\c\l", maxRetries = 0) + var resp: string + socket.readLine(resp) + doAssert resp == "Hello from server" + + socket.send("bye\c\l") + socket.readLine(resp) + doAssert resp == "bye" + socket.close() + + svproc.close() diff --git a/tests/stdlib/tvarints.nim b/tests/stdlib/tvarints.nim index 35f1cd849..f9624ee5b 100644 --- a/tests/stdlib/tvarints.nim +++ b/tests/stdlib/tvarints.nim @@ -33,7 +33,7 @@ block: doAssert cast[float64](got) == test block: - var hugeIntArray: array[50, byte] + var hugeIntArray: array[9, byte] var readedInt: uint64 template chk(a) = diff --git a/tests/stdlib/tvolatile.nim b/tests/stdlib/tvolatile.nim new file mode 100644 index 000000000..c097f9723 --- /dev/null +++ b/tests/stdlib/tvolatile.nim @@ -0,0 +1,15 @@ +import std/[volatile, assertions] + +var st: int +var foo: ptr int = addr st +volatileStore(foo, 12) +doAssert volatileLoad(foo) == 12 + +# bug #14623 +proc bar = + var st: int + var foo: ptr int = addr st + volatileStore(foo, 12) + doAssert volatileLoad(foo) == 12 + +bar() diff --git a/tests/stdlib/twrongstattype.nim b/tests/stdlib/twrongstattype.nim new file mode 100644 index 000000000..4a1fc30c6 --- /dev/null +++ b/tests/stdlib/twrongstattype.nim @@ -0,0 +1,14 @@ +# issue #24076 + +when defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): + import std/posix + proc uid(x: uint32): Uid = Uid(x) + var y: uint32 + let myUid = geteuid() + discard myUid == uid(y) + proc dev(x: uint32): Dev = Dev(x) + let myDev = 1.Dev + discard myDev == dev(y) + proc nlink(x: uint32): Nlink = Nlink(x) + let myNlink = 1.Nlink + discard myNlink == nlink(y) diff --git a/tests/stdlib/unixsockettest.nim b/tests/stdlib/unixsockettest.nim new file mode 100644 index 000000000..8f95d0808 --- /dev/null +++ b/tests/stdlib/unixsockettest.nim @@ -0,0 +1,26 @@ +import std/[assertions, net, os] + +let unixSocketPath = getCurrentDir() / "usox" + +removeFile(unixSocketPath) + +let socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_NONE) +socket.bindUnix(unixSocketPath) +socket.listen() + +var + clientSocket: Socket + data: string + +socket.accept(clientSocket) +clientSocket.readLine(data) +doAssert data == "data sent through the socket" +clientSocket.send("Hello from server\c\l") + +clientSocket.readLine(data) +doAssert data == "bye" +clientSocket.send("bye\c\l") + +clientSocket.close() +socket.close() +removeFile(unixSocketPath) diff --git a/tests/system/tdollars.nim b/tests/system/tdollars.nim index 39337cca7..eabee81b3 100644 --- a/tests/system/tdollars.nim +++ b/tests/system/tdollars.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:off; --backend:js --jsbigint64:on" + matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on" """ #[ @@ -109,10 +109,10 @@ block: # if `uint8(a1)` changes meaning to `cast[uint8](a1)` in future, update this test; # until then, this is the correct semantics. let a3 = $a2 - doAssert a2 < 3 - doAssert a3 == "-1" + doAssert a2 == 255'u8 + doAssert a3 == "255" proc intToStr(a: uint8): cstring {.importjs: "(# + \"\")".} - doAssert $intToStr(a2) == "-1" + doAssert $intToStr(a2) == "255" else: block: let x = -1'i8 diff --git a/tests/system/tensuremove.nim b/tests/system/tensuremove.nim index 52d9a43a8..668d5aed1 100644 --- a/tests/system/tensuremove.nim +++ b/tests/system/tensuremove.nim @@ -1,5 +1,5 @@ discard """ - target: "c js" + targets: "c js" matrix: "--cursorinference:on; --cursorinference:off" """ diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim index 7f5914725..1debb7c48 100644 --- a/tests/system/tsystem_misc.nim +++ b/tests/system/tsystem_misc.nim @@ -212,3 +212,16 @@ block: doAssert not compiles(echo p.rawProc.repr) doAssert not compiles(echo p.rawEnv.repr) doAssert not compiles(echo p.finished) + +proc bug23223 = # bug #23223 + var stuff = "hello" + stuff.insert "" + doAssert stuff == "hello" + +bug23223() + +block: # bug #23894 + let v = high(uint) div 2 + let s = v + 1 # 9223372036854775808 + let m = succ v + doAssert s == m diff --git a/tests/template/m19277_1.nim b/tests/template/m19277_1.nim new file mode 100644 index 000000000..840bd4767 --- /dev/null +++ b/tests/template/m19277_1.nim @@ -0,0 +1,2 @@ +template foo*(x: untyped) = + echo "got: ", x diff --git a/tests/template/m19277_2.nim b/tests/template/m19277_2.nim new file mode 100644 index 000000000..de72dad45 --- /dev/null +++ b/tests/template/m19277_2.nim @@ -0,0 +1,2 @@ +proc foo*(a: string) = + echo "got string: ", a diff --git a/tests/template/mqualifiedtype1.nim b/tests/template/mqualifiedtype1.nim new file mode 100644 index 000000000..46569107f --- /dev/null +++ b/tests/template/mqualifiedtype1.nim @@ -0,0 +1,2 @@ +type A* = object + x*: int diff --git a/tests/template/mqualifiedtype2.nim b/tests/template/mqualifiedtype2.nim new file mode 100644 index 000000000..6a61c14bd --- /dev/null +++ b/tests/template/mqualifiedtype2.nim @@ -0,0 +1,2 @@ +type A* = object + x*: array[1000, byte] diff --git a/tests/template/t13426.nim b/tests/template/t13426.nim new file mode 100644 index 000000000..f7f44749c --- /dev/null +++ b/tests/template/t13426.nim @@ -0,0 +1,87 @@ +discard """ + cmd: "nim check --hints:off $file" + errormsg: "" + nimout: ''' +t13426.nim(81, 6) template/generic instantiation of `fun` from here +t13426.nim(80, 24) Error: type mismatch: got <int> but expected 'string' +t13426.nim(81, 6) template/generic instantiation of `fun` from here +t13426.nim(80, 17) Error: type mismatch: got <uint, string> +but expected one of: +proc `and`(x, y: uint): uint + first type mismatch at position: 2 + required type for y: uint + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint64): uint64 + first type mismatch at position: 2 + required type for y: uint64 + but expression 'high(@[1])' is of type: string +10 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them + +expression: 1'u and high(@[1]) +t13426.nim(81, 6) template/generic instantiation of `fun` from here +t13426.nim(80, 17) Error: expression '' has no type (or is ambiguous) +t13426.nim(87, 6) template/generic instantiation of `fun` from here +t13426.nim(86, 22) Error: type mismatch: got <int> but expected 'string' +t13426.nim(87, 6) template/generic instantiation of `fun` from here +t13426.nim(86, 15) Error: type mismatch: got <int literal(1), string> +but expected one of: +proc `and`(x, y: int): int + first type mismatch at position: 2 + required type for y: int + but expression 'high(@[1])' is of type: string +proc `and`(x, y: int16): int16 + first type mismatch at position: 2 + required type for y: int16 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: int32): int32 + first type mismatch at position: 2 + required type for y: int32 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: int64): int64 + first type mismatch at position: 2 + required type for y: int64 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: int8): int8 + first type mismatch at position: 2 + required type for y: int8 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint): uint + first type mismatch at position: 2 + required type for y: uint + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint16): uint16 + first type mismatch at position: 2 + required type for y: uint16 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint32): uint32 + first type mismatch at position: 2 + required type for y: uint32 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint64): uint64 + first type mismatch at position: 2 + required type for y: uint64 + but expression 'high(@[1])' is of type: string +proc `and`(x, y: uint8): uint8 + first type mismatch at position: 2 + required type for y: uint8 + but expression 'high(@[1])' is of type: string +2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them + +expression: 1 and high(@[1]) +t13426.nim(87, 6) template/generic instantiation of `fun` from here +t13426.nim(86, 15) Error: expression '' has no type (or is ambiguous) +''' +""" + +# bug # #13426 +block: + template bar(t): string = high(t) + proc fun[A](key: A) = + var h = 1'u and bar(@[1]) + fun(0) + +block: + template bar(t): string = high(t) + proc fun[A](key: A) = + var h = 1 and bar(@[1]) + fun(0) diff --git a/tests/template/t19277.nim b/tests/template/t19277.nim new file mode 100644 index 000000000..16435a09c --- /dev/null +++ b/tests/template/t19277.nim @@ -0,0 +1,19 @@ +discard """ + output: ''' +got: 0 +''' +""" + +# issue #19277 + +import m19277_1, m19277_2 + +template injector(val: untyped): untyped = + template subtemplate: untyped = val + subtemplate() + +template methodCall(val: untyped): untyped = val + +{.push raises: [Defect].} + +foo(injector(0).methodCall()) diff --git a/tests/template/t24112.nim b/tests/template/t24112.nim new file mode 100644 index 000000000..175fc7d5e --- /dev/null +++ b/tests/template/t24112.nim @@ -0,0 +1,19 @@ +discard """ + matrix: "--skipParentCfg --filenames:legacyRelProj --hints:off" + action: reject +""" + +# issue #24112, needs --experimental:openSym disabled + +block: # simplified + type + SomeObj = ref object # Doesn't error if you make SomeObj be non-ref + template foo = yield SomeObj() + when compiles(foo): discard + +import std/asyncdispatch +block: + proc someProc(): Future[void] {.async.} = discard + proc foo() = + await someProc() #[tt.Error + ^ Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead]# diff --git a/tests/template/tdefaultparam.nim b/tests/template/tdefaultparam.nim new file mode 100644 index 000000000..7ea0b2b25 --- /dev/null +++ b/tests/template/tdefaultparam.nim @@ -0,0 +1,56 @@ +block: + template foo(a: untyped, b: untyped = a(0)): untyped = + let x = a(0) + let y = b + (x, y) + proc bar(x: int): int = x + 1 + doAssert foo(bar, b = bar(0)) == (1, 1) + doAssert foo(bar) == (1, 1) + +block: # issue #23506 + var a: string + template foo(x: int; y = x) = + a = $($x, $y) + foo(1) + doAssert a == "(\"1\", \"1\")" + +block: # untyped params with default value + macro foo(x: typed): untyped = + result = x + template test(body: untyped, alt: untyped = (;), maxTries = 3): untyped {.foo.} = + body + alt + var s = "a" + test: + s.add "b" + do: + s.add "c" + doAssert s == "abc" + template test2(body: untyped, alt: untyped = s.add("e"), maxTries = 3): untyped = + body + alt + test2: + s.add "d" + doAssert s == "abcde" + template test3(body: untyped = willNotCompile) = + discard + test3() + +block: # typed params with `void` default value + macro foo(x: typed): untyped = + result = x + template test(body: untyped, alt: typed = (;), maxTries = 3): untyped {.foo.} = + body + alt + var s = "a" + test: + s.add "b" + do: + s.add "c" + doAssert s == "abc" + template test2(body: untyped, alt: typed = s.add("e"), maxTries = 3): untyped = + body + alt + test2: + s.add "d" + doAssert s == "abcde" diff --git a/tests/template/tgensymhijack.nim b/tests/template/tgensymhijack.nim new file mode 100644 index 000000000..72ff3d495 --- /dev/null +++ b/tests/template/tgensymhijack.nim @@ -0,0 +1,37 @@ +# issue #23326 + +type Result*[E] = object + e*: E + +proc error*[E](v: Result[E]): E = discard + +template valueOr*[E](self: Result[E], def: untyped): int = + when E isnot void: + when false: + # Comment line below to make it work + template error(): E {.used, gensym.} = s.e + discard + else: + template error(): E {.used, inject.} = + self.e + + def + else: + def + + +block: + let rErr = Result[string](e: "a") + let rErrV = rErr.valueOr: + ord(error[0]) + +block: + template foo(x: static bool): untyped = + when x: + let a = 123 + else: + template a: untyped {.gensym.} = 456 + a + + doAssert foo(false) == 456 + doAssert foo(true) == 123 diff --git a/tests/template/tinnerouterproc.nim b/tests/template/tinnerouterproc.nim index 1f15fb13e..56e0d02df 100644 --- a/tests/template/tinnerouterproc.nim +++ b/tests/template/tinnerouterproc.nim @@ -6,3 +6,15 @@ block: # #20002 discard 3.bar # evaluates to 10 but only check if it compiles for now block: foo() + +block: # issue #23813 + template r(body: untyped) = + proc x() {.gensym.} = + body + template g() = + r: + let y = 0 + r: + proc y() = discard + y() + g() diff --git a/tests/template/tnested.nim b/tests/template/tnested.nim new file mode 100644 index 000000000..81e416a76 --- /dev/null +++ b/tests/template/tnested.nim @@ -0,0 +1,38 @@ +block: # issue #22775 + proc h(c: int) = discard + template k(v: int) = + template p() = v.h() + p() + let a = @[0] + k(0 and not a[0]) + +block: # issue #22775 case 2 + proc h(c: int, q: int) = discard + template k(v: int) = + template p() = h(v, v) + p() + let a = [0] + k(0 and not a[0]) + +block: # issue #22775 minimal cases + proc h(c: int) = discard + template k(v: int) = + template p() = h(v) + p() + let a = [0] + k(not a[0]) + block: + k(-a[0]) + block: + proc f(x: int): int = x + k(f a[0]) + +block: # bracket assignment case of above tests + proc h(c: int) = discard + template k(v: int) = + template p() = h(v) + p() + var a = [0] + k(not (block: + a[0] = 1 + 1)) diff --git a/tests/template/topensym.nim b/tests/template/topensym.nim new file mode 100644 index 000000000..2f930407b --- /dev/null +++ b/tests/template/topensym.nim @@ -0,0 +1,209 @@ +{.experimental: "openSym".} + +block: # issue #24002 + type Result[T, E] = object + func value[T, E](self: Result[T, E]): T {.inline.} = + discard + func value[T: not void, E](self: var Result[T, E]): var T {.inline.} = + discard + template unrecognizedFieldWarning = + doAssert value == 123 + let x = value + doAssert value == x + proc readValue(value: var int) = + unrecognizedFieldWarning() + var foo: int = 123 + readValue(foo) + +block: # issue #22605 for templates, normal call syntax + const error = "bad" + + template valueOr(self: int, def: untyped): untyped = + case false + of true: "" + of false: + template error: untyped {.used, inject.} = "good" + def + + template g(T: type): string = + var res = "ok" + let x = valueOr 123: + res = $error + "dummy" + res + + doAssert g(int) == "good" + + template g2(T: type): string = + bind error # use the bad version on purpose + var res = "ok" + let x = valueOr 123: + res = $error + "dummy" + res + + doAssert g2(int) == "bad" + +block: # issue #22605 for templates, method call syntax + const error = "bad" + + template valueOr(self: int, def: untyped): untyped = + case false + of true: "" + of false: + template error: untyped {.used, inject.} = "good" + def + + template g(T: type): string = + var res = "ok" + let x = 123.valueOr: + res = $error + "dummy" + res + + doAssert g(int) == "good" + + template g2(T: type): string = + bind error # use the bad version on purpose + var res = "ok" + let x = 123.valueOr: + res = $error + "dummy" + res + + doAssert g2(int) == "bad" + +block: # issue #22605 for templates, original complex example + type Xxx = enum + error + value + + type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + + template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + + proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + + template g(T: type): string = + var res = "ok" + let x = f().valueOr: + res = $error + 123 + res + + doAssert g(int) == "f" + + template g2(T: type): string = + bind error # use the bad version on purpose + var res = "ok" + let x = f().valueOr: + res = $error + 123 + res + + doAssert g2(int) == "error" + +block: # issue #23865 for templates + type Xxx = enum + error + value + + type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate: bool + else: + case oResultPrivate: bool + of false: + eResultPrivate: E + of true: + discard + else: + when E is void: + case oResultPrivate: bool + of false: + discard + of true: + vResultPrivate: T + else: + case oResultPrivate: bool + of false: + eResultPrivate: E + of true: + vResultPrivate: T + + func error[T, E](self: Result[T, E]): E = + ## Fetch error of result if set, or raise Defect + case self.oResultPrivate + of true: + when T isnot void: + raiseResultDefect("Trying to access error when value is set", self.vResultPrivate) + else: + raiseResultDefect("Trying to access error when value is set") + of false: + when E isnot void: + self.eResultPrivate + + template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + template g(T: type): string = + var res = "ok" + let x = f().valueOr: + res = $error + 123 + res + doAssert g(int) == "f" + +import std/sequtils + +block: # issue #15314 + var it: string + var nums = @[1,2,3] + + template doubleNums() = + nums.applyIt(it * 2) + + doubleNums() + doAssert nums == @[2, 4, 6] diff --git a/tests/template/topensymoverride.nim b/tests/template/topensymoverride.nim new file mode 100644 index 000000000..3d4bb59f1 --- /dev/null +++ b/tests/template/topensymoverride.nim @@ -0,0 +1,39 @@ +discard """ + matrix: "--skipParentCfg --filenames:legacyRelProj" +""" + +const value = "captured" +template fooOld(x: int, body: untyped): untyped = + let value {.inject.} = "injected" + body +template foo(x: int, body: untyped): untyped = + let value {.inject.} = "injected" + {.push experimental: "genericsOpenSym".} + body + {.pop.} + +proc old[T](): string = + fooOld(123): + return value +doAssert old[int]() == "captured" + +template oldTempl(): string = + block: + var res: string + fooOld(123): + res = value + res +doAssert oldTempl() == "captured" + +proc bar[T](): string = + foo(123): + return value +doAssert bar[int]() == "injected" + +template barTempl(): string = + block: + var res: string + foo(123): + res = value + res +doAssert barTempl() == "injected" diff --git a/tests/template/topensymwarning.nim b/tests/template/topensymwarning.nim new file mode 100644 index 000000000..0bbe0a9fb --- /dev/null +++ b/tests/template/topensymwarning.nim @@ -0,0 +1,60 @@ +discard """ + matrix: "--skipParentCfg --filenames:legacyRelProj" +""" + +type Xxx = enum + error + value + +type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + +template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + +proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + +template g(T: type): string = + var res = "ok" + let x = f().valueOr: + {.push warningAsError[IgnoredSymbolInjection]: on.} + # test spurious error + discard true + let _ = f + {.pop.} + res = $error #[tt.Warning + ^ a new symbol 'error' has been injected during template or generic instantiation, however 'error' [enumField declared in topensymwarning.nim(6, 3)] captured at the proc declaration will be used instead; either enable --experimental:openSym to use the injected symbol, or `bind` this captured symbol explicitly [IgnoredSymbolInjection]]# + 123 + res + +discard g(int) diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index b68d7e253..b559c2d9e 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -16,6 +16,7 @@ wth (total: 6) S1 5 +abc ''' """ # bug #1915 @@ -394,3 +395,11 @@ proc chunkedReadLoop2 = test2 test1(); test2() + +block: # bug #22846 + template foo2(x: proc (y: string)) = + let f = x + f("abc") + + foo2(proc (y: string) = echo y) + diff --git a/tests/template/tqualifiedtype.nim b/tests/template/tqualifiedtype.nim new file mode 100644 index 000000000..6497af6ee --- /dev/null +++ b/tests/template/tqualifiedtype.nim @@ -0,0 +1,25 @@ +# issue #19866 + +# Switch module import order to switch which of last two +# doAsserts fails +import mqualifiedtype1 +import mqualifiedtype2 + +# this isn't officially supported but needed to point out the issue: +template f(moduleName: untyped): int = sizeof(`moduleName`.A) +template g(someType: untyped): int = sizeof(someType) + +# These are legitimately true. +doAssert sizeof(mqualifiedtype1.A) != sizeof(mqualifiedtype2.A) +doAssert g(mqualifiedtype1.A) != g(mqualifiedtype2.A) + +# Which means that this should not be true, but is in Nim 1.6 +doAssert f(`mqualifiedtype1`) != f(`mqualifiedtype2`) +doAssert f(mqualifiedtype1) != f(mqualifiedtype2) + +# These should be true, but depending on import order, exactly one +# fails in Nim 1.2, 1.6 and devel. +doAssert f(`mqualifiedtype1`) == g(mqualifiedtype1.A) +doAssert f(`mqualifiedtype2`) == g(mqualifiedtype2.A) +doAssert f(mqualifiedtype1) == g(mqualifiedtype1.A) +doAssert f(mqualifiedtype2) == g(mqualifiedtype2.A) diff --git a/tests/threads/tmembug.nim b/tests/threads/tmembug.nim new file mode 100644 index 000000000..3618f0ecc --- /dev/null +++ b/tests/threads/tmembug.nim @@ -0,0 +1,51 @@ + +import std / [atomics, strutils, sequtils] + +type + BackendMessage* = object + field*: seq[int] + +var + chan1: Channel[BackendMessage] + chan2: Channel[BackendMessage] + +chan1.open() +chan2.open() + +proc routeMessage*(msg: BackendMessage) = + discard chan2.trySend(msg) + +var + recv: Thread[void] + stopToken: Atomic[bool] + +proc recvMsg() = + while not stopToken.load(moRelaxed): + let resp = chan1.tryRecv() + if resp.dataAvailable: + routeMessage(resp.msg) + echo "child consumes ", formatSize getOccupiedMem() + +createThread[void](recv, recvMsg) + +const MESSAGE_COUNT = 100 + +proc main() = + let msg: BackendMessage = BackendMessage(field: (0..500).toSeq()) + for j in 0..0: #100: + echo "New iteration" + + for _ in 1..MESSAGE_COUNT: + chan1.send(msg) + echo "After sending" + + var counter = 0 + while counter < MESSAGE_COUNT: + let resp = recv(chan2) + counter.inc + echo "After receiving ", formatSize getOccupiedMem() + + stopToken.store true, moRelaxed + joinThreads(recv) + +main() diff --git a/tests/tools/tunused_imports.nim b/tests/tools/tunused_imports.nim index 31d6cf7d7..539608ad6 100644 --- a/tests/tools/tunused_imports.nim +++ b/tests/tools/tunused_imports.nim @@ -1,9 +1,12 @@ discard """ cmd: '''nim c --hint:Processing:off $file''' nimout: ''' -tunused_imports.nim(11, 10) Warning: BEGIN [User] -tunused_imports.nim(36, 10) Warning: END [User] -tunused_imports.nim(34, 8) Warning: imported and not used: 'strutils' [UnusedImport] +tunused_imports.nim(14, 10) Warning: BEGIN [User] +tunused_imports.nim(41, 10) Warning: END [User] +tunused_imports.nim(37, 8) Warning: imported and not used: 'strutils' [UnusedImport] +tunused_imports.nim(38, 13) Warning: imported and not used: 'strtabs' [UnusedImport] +tunused_imports.nim(38, 22) Warning: imported and not used: 'cstrutils' [UnusedImport] +tunused_imports.nim(39, 12) Warning: imported and not used: 'macrocache' [UnusedImport] ''' action: "compile" """ @@ -32,5 +35,7 @@ macro bar(): untyped = bar() import strutils +import std/[strtabs, cstrutils] +import std/macrocache {.warning: "END".} diff --git a/tests/trmacros/tor.nim b/tests/trmacros/tor.nim index 6b4e71216..9defc4d1b 100644 --- a/tests/trmacros/tor.nim +++ b/tests/trmacros/tor.nim @@ -1,7 +1,7 @@ discard """ output: ''' 3 -0 +30 true ''' """ @@ -15,7 +15,9 @@ echo z template arithOps: untyped = (`+` | `-` | `*`) -template testOr{ (arithOps{f})(a, b) }(a, b, f: untyped): untyped = f(a mod 10, b) +template testOr{ (arithOps{f})(a, b) }(a, b, f: untyped): untyped = + {.noRewrite.}: + f(a mod 10, b) let xx = 10 echo 10*xx diff --git a/tests/trmacros/trmacros_various2.nim b/tests/trmacros/trmacros_various2.nim index 257ee8ba6..981df4ca6 100644 --- a/tests/trmacros/trmacros_various2.nim +++ b/tests/trmacros/trmacros_various2.nim @@ -70,7 +70,9 @@ block tstar: for i in 1..len(s)-1: result.add s[i] inc calls - template optConc{ `&&` * a }(a: string): string = &&a + template optConc{ `&&` * a }(a: string): string = + {.noRewrite.}: + &&a let space = " " echo "my" && (space & "awe" && "some " ) && "concat" diff --git a/tests/tuples/t18125_1.nim b/tests/tuples/t18125_1.nim new file mode 100644 index 000000000..74fdfe8f5 --- /dev/null +++ b/tests/tuples/t18125_1.nim @@ -0,0 +1,14 @@ +# issue #18125 solved with type inference + +type + Parent = ref object of RootObj + + Child = ref object of Parent + c: char + +func foo(c: char): (Parent, int) = + # Works if you use (Parent(Child(c: c)), 0) + (Child(c: c), 0) + +let x = foo('x')[0] +doAssert Child(x).c == 'x' diff --git a/tests/tuples/t18125_2.nim b/tests/tuples/t18125_2.nim new file mode 100644 index 000000000..fe0a4a8bb --- /dev/null +++ b/tests/tuples/t18125_2.nim @@ -0,0 +1,20 @@ +discard """ + errormsg: "type mismatch: got <(Child, int)> but expected '(Parent, int)'" + line: 17 +""" + +# issue #18125 solved with correct type relation + +type + Parent = ref object of RootObj + + Child = ref object of Parent + c: char + +func foo(c: char): (Parent, int) = + # Works if you use (Parent(Child(c: c)), 0) + let x = (Child(c: c), 0) + x + +let x = foo('x')[0] +doAssert Child(x).c == 'x' diff --git a/tests/typerel/tregionptrs.nim b/tests/typerel/tregionptrs.nim deleted file mode 100644 index 504ec1011..000000000 --- a/tests/typerel/tregionptrs.nim +++ /dev/null @@ -1,16 +0,0 @@ -discard """ - errormsg: "type mismatch: got <BPtr> but expected 'APtr = ptr[RegionA, int]'" - line: 16 -""" - -type - RegionA = object - APtr = RegionA ptr int - RegionB = object - BPtr = RegionB ptr int - -var x,xx: APtr -var y: BPtr -x = nil -x = xx -x = y diff --git a/tests/typerel/tuncheckedarray_eq.nim b/tests/typerel/tuncheckedarray_eq.nim new file mode 100644 index 000000000..c07696b5f --- /dev/null +++ b/tests/typerel/tuncheckedarray_eq.nim @@ -0,0 +1,2 @@ +type p = ptr UncheckedArray[char] +doAssert p is ptr UncheckedArray \ No newline at end of file diff --git a/tests/types/t15836.nim b/tests/types/t15836.nim index 9c0c26dec..27d3ad0d0 100644 --- a/tests/types/t15836.nim +++ b/tests/types/t15836.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch: got <string> but expected 'int'" + errormsg: "type mismatch: got <int literal(1), proc (a: GenericParam): auto>" line: 11 """ diff --git a/tests/types/t15836_2.nim b/tests/types/t15836_2.nim index 9afef416a..6a16e2d22 100644 --- a/tests/types/t15836_2.nim +++ b/tests/types/t15836_2.nim @@ -1,8 +1,3 @@ - -discard """ - action: "compile" - disabled: true -""" import std/sugar type Tensor[T] = object diff --git a/tests/types/tinheritgenericparameter.nim b/tests/types/tinheritgenericparameter.nim index c88c50b7b..d0d313635 100644 --- a/tests/types/tinheritgenericparameter.nim +++ b/tests/types/tinheritgenericparameter.nim @@ -4,11 +4,11 @@ discard """ nimout:''' tinheritgenericparameter.nim(36, 15) Error: Cannot inherit from: 'MyObject' tinheritgenericparameter.nim(36, 15) Error: Cannot inherit from: 'MyObject' -tinheritgenericparameter.nim(36, 23) Error: object constructor needs an object type [proxy] +tinheritgenericparameter.nim(36, 23) Error: object constructor needs an object type [error] tinheritgenericparameter.nim(36, 23) Error: expression '' has no type (or is ambiguous) tinheritgenericparameter.nim(37, 15) Error: Cannot inherit from: 'int' tinheritgenericparameter.nim(37, 15) Error: Cannot inherit from: 'int' -tinheritgenericparameter.nim(37, 23) Error: object constructor needs an object type [proxy] +tinheritgenericparameter.nim(37, 23) Error: object constructor needs an object type [error] tinheritgenericparameter.nim(37, 23) Error: expression '' has no type (or is ambiguous) ''' """ diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 64b3d03c0..c95b63c90 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -165,3 +165,7 @@ block: # previously tisop.nim doAssert f.y.typeof is float doAssert f.z.typeof is float p(A, f) + +block: # issue #22850 + doAssert not (type is int) + doAssert not (typedesc is int) diff --git a/tests/types/tissues_types.nim b/tests/types/tissues_types.nim index eab4e8e9b..6bb1258f4 100644 --- a/tests/types/tissues_types.nim +++ b/tests/types/tissues_types.nim @@ -96,3 +96,23 @@ block: # issue #12582 x: foo(int) # error let b = Bar() let b2 = Bar(x: [123]) + +block: + when true: # bug #14710 + type Foo[T] = object + x1: int + when T.sizeof == 4: discard # SIGSEGV + when sizeof(T) == 4: discard # ok + let t = Foo[float](x1: 1) + doAssert t.x1 == 1 + +block: + template s(d: varargs[typed])=discard + + proc something(x:float)=discard + proc something(x:int)=discard + proc otherthing()=discard + + s(something) + s(otherthing, something) + s(something, otherthing) diff --git a/tests/types/ttopdowninference.nim b/tests/types/ttopdowninference.nim index 2a26e0e34..765761e99 100644 --- a/tests/types/ttopdowninference.nim +++ b/tests/types/ttopdowninference.nim @@ -325,3 +325,9 @@ block: # bug #22180 else: (ref A)(nil) doAssert y.isNil + +block: # issue #24164, related regression + proc foo(x: proc ()) = discard + template bar(x: untyped = nil) = + foo(x) + bar() diff --git a/tests/varres/tprevent_forloopvar_mutations.nim b/tests/varres/tprevent_forloopvar_mutations.nim index b27c327a9..c9aeb94d8 100644 --- a/tests/varres/tprevent_forloopvar_mutations.nim +++ b/tests/varres/tprevent_forloopvar_mutations.nim @@ -2,7 +2,7 @@ discard """ errormsg: "type mismatch: got <int>" nimout: '''tprevent_forloopvar_mutations.nim(16, 3) Error: type mismatch: got <int> but expected one of: -proc inc[T: Ordinal](x: var T; y: int = 1) +proc inc[T, V: Ordinal](x: var T; y: V = 1) first type mismatch at position: 1 required type for x: var T: Ordinal but expression 'i' is immutable, not 'var' diff --git a/tests/varstmt/tvardecl.nim b/tests/varstmt/tvardecl.nim index 37bc4bad7..d5ccfafb7 100644 --- a/tests/varstmt/tvardecl.nim +++ b/tests/varstmt/tvardecl.nim @@ -2,6 +2,7 @@ discard """ output: "44" """ # Test the new variable declaration syntax +import std/sequtils var x = 0 @@ -10,3 +11,9 @@ var write(stdout, a) writeLine(stdout, b) #OUT 44 + +proc p() = # bug #18104 + var x, y = newSeqWith(10, newString(3)) + discard (x, y) + +p() diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim index 6662e3e5a..9785d25e5 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -105,3 +105,32 @@ block: # bug #22117 result = aref doAssert main() == 10 + +type + Slice*[T] = object + first, last: int + p: ptr UncheckedArray[T] + +var i = 0 + +converter autoToOpenArray*[T](s: Slice[T]): openArray[T] = + inc i + result = toOpenArray(s.p, s.first, s.last) + +proc acceptOpenArray(s: openArray[byte]) = discard + +proc bug22597 = # bug #22597 + acceptOpenArray(Slice[byte]()) + doAssert i == 1 + +bug22597() + +block: # bug #20048 + type + Test = object + tokens: openArray[string] + + func init(Self: typedesc[Test], tokens: openArray[string]): Self = Self(tokens: tokens) + + let data = Test.init(["123"]) + doAssert @(data.tokens) == @["123"] diff --git a/tests/views/tviews2.nim b/tests/views/tviews2.nim new file mode 100644 index 000000000..29aff76df --- /dev/null +++ b/tests/views/tviews2.nim @@ -0,0 +1,90 @@ +discard """ + targets: "c js" +""" + +{.experimental: "views".} + +block: + type + Foo = object + id: openArray[char] + + proc foo(): Foo = + var source = "1245" + result = Foo(id: source.toOpenArray(0, 1)) + + doAssert foo().id == @['1', '2'] + +block: # bug #15778 + type + Reader = object + data: openArray[char] + current: int + + var count = 0 + + proc read(data: var Reader, length: int): openArray[char] = + inc count + let start = data.current + data.current.inc length + return data.data.toOpenArray(start, data.current-1) + + var data = "hello there" + var reader = Reader(data: data.toOpenArray(0, data.len-1), current: 0) + doAssert @(reader.read(2)) == @['h', 'e'] + doAssert @(reader.read(3)) == @['l', 'l', 'o'] + doAssert count == 2 + +block: # bug #16671 + block: + type X = ref object of RootObj + type Y = ref object of X + field: openArray[int] + + var s: seq[X] + proc f() = + s.add(Y(field: [1])) + + f() + + block: + type X = ref object of RootObj + type Y = ref object of X + field: openArray[int] + + var s: seq[X] + proc f() = + s.add(Y(field: toOpenArray([1, 2, 3], 0, 1))) + + f() + +block: # bug #15746 + type + Reader = object + data: openArray[char] + current: int + + proc initReader(data: openArray[char], offset = 0): Reader = + result = Reader(data: data, current: offset) + + let s = "\x01\x00\x00\x00" + doAssert initReader(s).data[0].int == 1 + +block: + proc foo(x: openArray[char]) = + discard x + + foo("12254") + foo(@['a', 'b']) + + var a1 = "12254" + foo(a1) + + var a2 = @['a', 'b'] + foo(a2) + + var s = "138443" + var ooo: openArray[char] = s + var xxx: openArray[char] = ooo + foo(ooo) + foo(xxx) diff --git a/tests/vm/tconst.nim b/tests/vm/tconst.nim index c2bcf78b5..5cfe7533e 100644 --- a/tests/vm/tconst.nim +++ b/tests/vm/tconst.nim @@ -42,6 +42,16 @@ template main() = discard (x, increment, a) mytemp() + block: # bug #12334 + block: + const b: cstring = "foo" + var c = b + doAssert c == "foo" + block: + const a = "foo" + const b: cstring = a + var c = b + doAssert c == "foo" static: main() diff --git a/tests/vm/tconstarrayresem.nim b/tests/vm/tconstarrayresem.nim new file mode 100644 index 000000000..6701cfe4d --- /dev/null +++ b/tests/vm/tconstarrayresem.nim @@ -0,0 +1,29 @@ +# issue #23010 + +type + Result[T, E] = object + case oResult: bool + of false: + discard + of true: + vResult: T + + Opt[T] = Result[T, void] + +template ok[T, E](R: type Result[T, E], x: untyped): R = + R(oResult: true, vResult: x) + +template c[T](v: T): Opt[T] = Opt[T].ok(v) + +type + FixedBytes[N: static[int]] = distinct array[N, byte] + + H = object + d: FixedBytes[2] + +const b = default(H) +template g(): untyped = + const t = default(H) + b + +discard c(g()) diff --git a/tests/vm/tconstscope1.nim b/tests/vm/tconstscope1.nim new file mode 100644 index 000000000..41c45a28f --- /dev/null +++ b/tests/vm/tconstscope1.nim @@ -0,0 +1,5 @@ +# issue #5395 + +const a = (var b = 3; b) +echo b #[tt.Error + ^ undeclared identifier: 'b']# diff --git a/tests/vm/tconstscope2.nim b/tests/vm/tconstscope2.nim new file mode 100644 index 000000000..d858e96c2 --- /dev/null +++ b/tests/vm/tconstscope2.nim @@ -0,0 +1,5 @@ +const + a = (var x = 3; x) + # should we allow this? + b = x #[tt.Error + ^ undeclared identifier: 'x']# diff --git a/tests/vm/tconsttable.nim b/tests/vm/tconsttable.nim index 64a74a59d..152a33cba 100644 --- a/tests/vm/tconsttable.nim +++ b/tests/vm/tconsttable.nim @@ -17,3 +17,18 @@ x = "ah" echo foo[x] x = "possible." echo foo[x] + +block: # bug #19840 + const testBytes = [byte 0xD8, 0x08, 0xDF, 0x45, 0x00, 0x3D, 0x00, 0x52, 0x00, 0x61] + var tempStr = "__________________" + + tempStr.prepareMutation + copyMem(addr tempStr[0], addr testBytes[0], testBytes.len) + +block: # bug #22389 + func foo(): ptr UncheckedArray[byte] = + const bar = [77.byte] + cast[ptr UncheckedArray[byte]](addr bar[0]) + + doAssert foo()[0] == 77 + diff --git a/tests/vm/tconvaddr.nim b/tests/vm/tconvaddr.nim new file mode 100644 index 000000000..9762a9e59 --- /dev/null +++ b/tests/vm/tconvaddr.nim @@ -0,0 +1,49 @@ +block: # issue #24097 + type Foo = distinct int + proc foo(x: var Foo) = + int(x) += 1 + proc bar(x: var int) = + x += 1 + static: + var x = Foo(1) + int(x) = int(x) + 1 + doAssert x.int == 2 + int(x) += 1 + doAssert x.int == 3 + foo(x) + doAssert x.int == 4 + bar(int(x)) # need vmgen flags propagated for this + doAssert x.int == 5 + type Bar = object + x: Foo + static: + var obj = Bar(x: Foo(1)) + int(obj.x) = int(obj.x) + 1 + doAssert obj.x.int == 2 + int(obj.x) += 1 + doAssert obj.x.int == 3 + foo(obj.x) + doAssert obj.x.int == 4 + bar(int(obj.x)) # need vmgen flags propagated for this + doAssert obj.x.int == 5 + static: + var arr = @[Foo(1)] + int(arr[0]) = int(arr[0]) + 1 + doAssert arr[0].int == 2 + int(arr[0]) += 1 + doAssert arr[0].int == 3 + foo(arr[0]) + doAssert arr[0].int == 4 + bar(int(arr[0])) # need vmgen flags propagated for this + doAssert arr[0].int == 5 + proc testResult(): Foo = + result = Foo(1) + int(result) = int(result) + 1 + doAssert result.int == 2 + int(result) += 1 + doAssert result.int == 3 + foo(result) + doAssert result.int == 4 + bar(int(result)) # need vmgen flags propagated for this + doAssert result.int == 5 + doAssert testResult().int == 5 diff --git a/tests/vm/tgenericcompiletimeproc.nim b/tests/vm/tgenericcompiletimeproc.nim new file mode 100644 index 000000000..08099ebbe --- /dev/null +++ b/tests/vm/tgenericcompiletimeproc.nim @@ -0,0 +1,36 @@ +block: # issue #10753 + proc foo(x: int): int {.compileTime.} = x + const a = foo(123) + doAssert foo(123) == a + + proc bar[T](x: T): T {.compileTime.} = x + const b = bar(123) + doAssert bar(123) == b + const c = bar("abc") + doAssert bar("abc") == c + +block: # issue #22021 + proc foo(x: static int): int {.compileTime.} = x + 1 + doAssert foo(123) == 124 + +block: # issue #19365 + proc f[T](x: static T): T {.compileTime.} = x + x + doAssert f(123) == 246 + doAssert f(1.0) == 2.0 + +block: + # don't fold compile time procs in typeof + proc fail[T](x: T): T {.compileTime.} = + doAssert false + x + doAssert typeof(fail(123)) is typeof(123) + proc p(x: int): int = x + + type Foo = typeof(p(fail(123))) + +block: # issue #24150, related regression + proc w(T: type): T {.compileTime.} = default(ptr T)[] + template y(v: auto): auto = typeof(v) is int + discard compiles(y(w int)) + proc s(): int {.compileTime.} = discard + discard s() diff --git a/tests/vm/tnilclosurecall.nim b/tests/vm/tnilclosurecall.nim new file mode 100644 index 000000000..449865b9c --- /dev/null +++ b/tests/vm/tnilclosurecall.nim @@ -0,0 +1,8 @@ +discard """ + errormsg: "attempt to call nil closure" + line: 8 +""" + +static: + let x: proc () = nil + x() diff --git a/tests/vm/tnilclosurecallstacktrace.nim b/tests/vm/tnilclosurecallstacktrace.nim new file mode 100644 index 000000000..879060e8e --- /dev/null +++ b/tests/vm/tnilclosurecallstacktrace.nim @@ -0,0 +1,23 @@ +discard """ + action: reject + nimout: ''' +stack trace: (most recent call last) +tnilclosurecallstacktrace.nim(23, 6) tnilclosurecallstacktrace +tnilclosurecallstacktrace.nim(20, 6) baz +tnilclosurecallstacktrace.nim(17, 6) bar +tnilclosurecallstacktrace.nim(14, 4) foo +tnilclosurecallstacktrace.nim(14, 4) Error: attempt to call nil closure +''' +""" + +proc foo(x: proc ()) = + x() + +proc bar(x: proc ()) = + foo(x) + +proc baz(x: proc ()) = + bar(x) + +static: + baz(nil) diff --git a/tests/vm/topenarrays.nim b/tests/vm/topenarrays.nim index 0a822f583..375d2523d 100644 --- a/tests/vm/topenarrays.nim +++ b/tests/vm/topenarrays.nim @@ -67,3 +67,23 @@ template fn= doAssert test([0,1,2,3,4,5]).id == 0 fn() # ok static: fn() + + +block: # bug #22095 + type + StUint = object + limbs: array[4, uint64] + + func shlAddMod(a: var openArray[uint64]) = + a[0] = 10 + + func divRem(r: var openArray[uint64]) = + shlAddMod(r.toOpenArray(0, 3)) + + func fn(): StUint = + divRem(result.limbs) + + const + z = fn() + + doAssert z.limbs[0] == 10 diff --git a/tests/vm/ttypedesc.nim b/tests/vm/ttypedesc.nim index a112584c5..d799e5adb 100644 --- a/tests/vm/ttypedesc.nim +++ b/tests/vm/ttypedesc.nim @@ -16,3 +16,16 @@ block: # issue #15760 doAssert x[SpecialBanana]() == "SpecialBanana" doAssert y(SpecialBanana) == "SpecialBanana" + +import macros + +block: # issue #23112 + type Container = object + foo: string + + proc canBeImplicit(t: typedesc) {.compileTime.} = + let tDesc = getType(t) + doAssert tDesc.kind == nnkObjectTy + + static: + canBeImplicit(Container) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index cade68577..6aeac5529 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -4,8 +4,7 @@ import os # bug #4462 block: proc foo(t: typedesc) {.compileTime.} = - assert sameType(getType(t), getType(typedesc[int])) - assert sameType(getType(t), getType(type int)) + assert sameType(getType(t), getType(int)) static: foo(int) @@ -302,14 +301,6 @@ block: # bug #10815 const a = P() doAssert $a == "" -when defined osx: # xxx bug #13481 - block: - type CharSet {.union.} = object - cs: set[char] - vs: array[4, uint64] - const a = Charset(cs: {'a'..'z'}) - doAssert a.repr.len > 0 - import tables block: # bug #8007 @@ -364,7 +355,7 @@ block: # bug #8007 block: # bug #14340 block: proc opl3EnvelopeCalcSin0() = discard - type EnvelopeSinfunc = proc() + type EnvelopeSinfunc = proc() {.nimcall.} # todo: fixme # const EnvelopeCalcSin0 = opl3EnvelopeCalcSin0 # ok const EnvelopeCalcSin0: EnvelopeSinfunc = opl3EnvelopeCalcSin0 # was bug const envelopeSin = [EnvelopeCalcSin0] @@ -575,7 +566,7 @@ block: block: static: let x = int 1 - doAssert $(x.type) == "Foo" # Foo + doAssert $(x.type) == "int" # Foo block: static: @@ -775,3 +766,31 @@ block: # issue #15730 static: # more nil cstring issues let x = cstring(nil) doAssert x.len == 0 + +block: # bug #23925 + type Foo = enum A = -1 + proc foo = + doAssert cast[Foo](-1) == A + doAssert ord(A) == -1 + + static: foo() + foo() + + type E = enum + e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 e10 e11 e12 e13 e14 e15 e16 e17 e18 e19 e20 + e21 e22 e23 e24 e25 e26 e27 e28 e29 e30 e31 e32 e33 e34 e35 e36 e37 e38 + e39 e40 e41 e42 e43 e44 e45 e46 e47 e48 e49 e50 e51 e52 e53 e54 e55 e56 + e57 e58 e59 e60 e61 e62 e63 e64 e65 e66 e67 e68 e69 e70 e71 e72 e73 e74 + e75 e76 e77 e78 e79 e80 e81 e82 e83 e84 e85 e86 e87 e88 e89 e90 e91 e92 + e93 e94 e95 e96 e97 e98 e99 e100 e101 e102 e103 e104 e105 e106 e107 e108 + e109 e110 e111 e112 e113 e114 e115 e116 e117 e118 e119 e120 e121 e122 + e123 e124 e125 e126 e127 e128 + proc bar = + doAssert cast[E](int(e128)) == e128 + + static: bar() + bar() + +static: # bug #21353 + var s: proc () = default(proc ()) + doAssert s == nil diff --git a/tests/vm/tvmopsDanger.nim b/tests/vm/tvmopsDanger.nim index 5e7bac29a..966feffe6 100644 --- a/tests/vm/tvmopsDanger.nim +++ b/tests/vm/tvmopsDanger.nim @@ -3,8 +3,11 @@ discard """ """ when defined(nimPreviewSlimSystem): import std/assertions -import std/times +import std/[times, os] const foo = getTime() let bar = foo -doAssert bar > low(Time) \ No newline at end of file +doAssert bar > low(Time) + +static: # bug #23932 + doAssert getCurrentDir().len > 0 diff --git a/tools/debug/nim-gdb.py b/tools/debug/nim-gdb.py index 1050197c9..8c9854bda 100644 --- a/tools/debug/nim-gdb.py +++ b/tools/debug/nim-gdb.py @@ -151,7 +151,7 @@ class DollarPrintFunction (gdb.Function): "Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)" dollar_functions = re.findall( - '(?:NimStringDesc \*|NimStringV2)\s?(dollar__[A-z0-9_]+?)\(([^,)]*)\);', + r'(?:NimStringDesc \*|NimStringV2)\s?(dollar__[A-z0-9_]+?)\(([^,)]*)\);', gdb.execute("info functions dollar__", True, True) ) @@ -168,11 +168,11 @@ class DollarPrintFunction (gdb.Function): # this way of overload resolution cannot deal with type aliases, # therefore it won't find all overloads. if arg_typ == argTypeName: - func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value() + func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTION_DOMAIN).value() return func_value(arg) elif arg_typ == argTypeName + " *": - func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value() + func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTION_DOMAIN).value() return func_value(arg.address) if not ignore_errors: @@ -387,7 +387,7 @@ def enumNti(typeNimName, idString): class NimEnumPrinter: pattern = re.compile(r'^tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$') - enumReprProc = gdb.lookup_global_symbol("reprEnum", gdb.SYMBOL_FUNCTIONS_DOMAIN) + enumReprProc = gdb.lookup_global_symbol("reprEnum", gdb.SYMBOL_FUNCTION_DOMAIN) def __init__(self, val): self.val = val @@ -677,7 +677,7 @@ def makematcher(klass): return matcher def register_nim_pretty_printers_for_object(objfile): - nimMainSym = gdb.lookup_global_symbol("NimMain", gdb.SYMBOL_FUNCTIONS_DOMAIN) + nimMainSym = gdb.lookup_global_symbol("NimMain", gdb.SYMBOL_FUNCTION_DOMAIN) if nimMainSym and nimMainSym.symtab.objfile == objfile: print("set Nim pretty printers for ", objfile.filename) diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index 0753248bc..946945346 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -328,8 +328,8 @@ proc copyToClipboard*() {.exportc.} = function updatePreTags() { - const allPreTags = document.querySelectorAll("pre") - + const allPreTags = document.querySelectorAll("pre:not(.line-nums)") + allPreTags.forEach((e) => { const div = document.createElement("div") diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index bd5c4ecbe..477fb29fa 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -325,7 +325,8 @@ proc nim2pdf(src: string, dst: string, nimArgs: string) = exec(cmd) moveFile(texFile.changeFileExt("pdf"), dst) -proc buildPdfDoc*(nimArgs, destPath: string) = +proc buildPdfDoc*(args: string, destPath: string) = + let args = nimArgs & " " & args var pdfList: seq[string] createDir(destPath) if os.execShellCmd("xelatex -version") != 0: @@ -334,7 +335,7 @@ proc buildPdfDoc*(nimArgs, destPath: string) = for src in items(mdPdfList): let dst = destPath / src.lastPathPart.changeFileExt("pdf") pdfList.add dst - nim2pdf(src, dst, nimArgs) + nim2pdf(src, dst, args) echo "\nOutput PDF files: \n ", pdfList.join(" ") # because `nim2pdf` is a bit verbose proc buildJS(): string = diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 6b99c49ee..063a02779 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -122,6 +122,7 @@ case $uos in *netbsd* ) myos="netbsd" LINK_FLAGS="$LINK_FLAGS -lm" + ucpu=`uname -p` ;; *darwin* ) myos="macosx" diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index b392ab472..002bc0592 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -45,6 +45,7 @@ endif ifeq ($(uos),netbsd) myos = netbsd LDFLAGS += -lm + ucpu = $(shell sh -c 'uname -p') endif ifeq ($(uos),darwin) myos = macosx diff --git a/tools/unicode_parsedata.nim b/tools/unicode_parsedata.nim index cca377f51..bd12998d1 100644 --- a/tools/unicode_parsedata.nim +++ b/tools/unicode_parsedata.nim @@ -26,34 +26,54 @@ var proc parseData(data: seq[string]) = - for line in data: - let - fields = line.split(';') - code = fields[0].parseHexInt() - category = fields[2] - uc = fields[12] - lc = fields[13] - tc = fields[14] - + proc doAdd(firstCode, lastCode: int, category, uc, lc, tc: string) = if category notin spaces and category notin letters: - continue + return + if firstCode != lastCode: + doAssert uc == "" and lc == "" and tc == "" if uc.len > 0: - let diff = 500 + uc.parseHexInt() - code - toUpper.add (code, diff) + let diff = 500 + uc.parseHexInt() - firstCode + toUpper.add (firstCode, diff) if lc.len > 0: - let diff = 500 + lc.parseHexInt() - code - toLower.add (code, diff) + let diff = 500 + lc.parseHexInt() - firstCode + toLower.add (firstCode, diff) if tc.len > 0 and tc != uc: # if titlecase is different than uppercase - let diff = 500 + tc.parseHexInt() - code + let diff = 500 + tc.parseHexInt() - firstCode if diff != 500: - toTitle.add (code, diff) + toTitle.add (firstCode, diff) - if category in spaces: - unispaces.add code + for code in firstCode..lastCode: + if category in spaces: + unispaces.add code + else: + alphas.add code + + var idx = 0 + while idx < data.len: + let + line = data[idx] + fields = line.split(';') + code = fields[0].parseHexInt() + name = fields[1] + category = fields[2] + uc = fields[12] + lc = fields[13] + tc = fields[14] + inc(idx) + if name.endsWith(", First>"): + doAssert idx < data.len + let + nextLine = data[idx] + nextFields = nextLine.split(';') + nextCode = nextFields[0].parseHexInt() + nextName = nextFields[1] + inc(idx) + doAssert nextName.endsWith(", Last>") + doAdd(code, nextCode, category, uc, lc, tc) else: - alphas.add code + doAdd(code, code, category, uc, lc, tc) proc splitRanges(a: seq[Singlets], r: var seq[Ranges], s: var seq[Singlets]) = ## Splits `toLower`, `toUpper` and `toTitle` into separate sequences: @@ -153,18 +173,18 @@ proc createHeader(output: var string) = proc `$`(r: Ranges): string = let - start = "0x" & toHex(r.start, 5) - stop = "0x" & toHex(r.stop, 5) + start = "0x" & toHex(r.start, 5) & "'i32" + stop = "0x" & toHex(r.stop, 5) & "'i32" result = "$#, $#, $#,\n" % [start, stop, $r.diff] proc `$`(r: Singlets): string = - let code = "0x" & toHex(r.code, 5) + let code = "0x" & toHex(r.code, 5) & "'i32" result = "$#, $#,\n" % [code, $r.diff] proc `$`(r: NonLetterRanges): string = let - start = "0x" & toHex(r.start, 5) - stop = "0x" & toHex(r.stop, 5) + start = "0x" & toHex(r.start, 5) & "'i32" + stop = "0x" & toHex(r.stop, 5) & "'i32" result = "$#, $#,\n" % [start, stop] @@ -178,7 +198,7 @@ proc outputSeq(s: seq[Ranges|Singlets|NonLetterRanges], name: string, proc outputSeq(s: seq[int], name: string, output: var string) = output.add " $# = [\n" % name for i in s: - output.add " 0x$#,\n" % toHex(i, 5) + output.add " 0x$#'i32,\n" % toHex(i, 5) output.add " ]\n\n" proc outputSpaces(s: seq[int], name: string, output: var string) = |