mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 10:53:40 +00:00
Compare commits
5 Commits
pr_table_s
...
pr_shadowi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e6a2a13d9 | ||
|
|
8af81e3d24 | ||
|
|
aca59572c7 | ||
|
|
07463c00fc | ||
|
|
1c79ef1090 |
2
.github/workflows/ci_docs.yml
vendored
2
.github/workflows/ci_docs.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
target: [linux, windows, osx]
|
||||
include:
|
||||
- target: linux
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-20.04
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
|
||||
76
.github/workflows/ci_gcc14.yml
vendored
Normal file
76
.github/workflows/ci_gcc14.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
name: GCC 14
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt update -qq
|
||||
sudo apt remove needrestart
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
- name: 'Install dependencies (Linux amd64 gcc 14)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo add-apt-repository universe
|
||||
sudo apt update -qq
|
||||
sudo apt install -y gcc-14 g++-14 libpcre3 liblapack-dev
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'NIM_TESTAMENT_DISABLE_SSL'
|
||||
shell: bash
|
||||
run: echo "NIM_TESTAMENT_DISABLE_SSL=1" >> $GITHUB_ENV
|
||||
|
||||
- name: 'System information'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimCiSystemInfo
|
||||
|
||||
- name: 'Build csourcesAny'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
||||
|
||||
- name: 'koch, Run CI'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
3
.github/workflows/ci_packages.yml
vendored
3
.github/workflows/ci_packages.yml
vendored
@@ -4,7 +4,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
- 'version-2-2'
|
||||
- 'version-2-0'
|
||||
- 'version-1-6'
|
||||
- 'version-1-2'
|
||||
@@ -18,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-13]
|
||||
os: [ubuntu-20.04, macos-13]
|
||||
cpu: [amd64]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
|
||||
17
.github/workflows/ci_publish.yml
vendored
17
.github/workflows/ci_publish.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
os: [ubuntu-20.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -21,10 +21,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js'
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ''
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
@@ -34,6 +34,17 @@ jobs:
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-24.04'
|
||||
vmImage: 'ubuntu-20.04'
|
||||
CPU: amd64
|
||||
# regularly breaks, refs bug #17325
|
||||
# Linux_i386:
|
||||
@@ -80,12 +80,10 @@ jobs:
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo add-apt-repository universe
|
||||
echo_run sudo apt-get update -qq
|
||||
echo_run sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
echo_run sudo apt-get install --no-install-recommends -yq \
|
||||
gcc-14 g++-14 libpcre3 liblapack-dev libpcre3 liblapack-dev libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
echo_run sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
echo_run sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
displayName: 'Install dependencies (amd64 Linux)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
|
||||
|
||||
@@ -102,16 +100,15 @@ jobs:
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
|
||||
# echo_run sudo apt-get update -qq
|
||||
echo_run sudo apt-get update -qq || echo "failed, see bug #17343"
|
||||
# echo_run sudo apt-fast update -qq
|
||||
echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
|
||||
# `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
|
||||
# `could not load: libffi.so` during dynamic loading.
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
echo_run sudo apt-get install --no-install-recommends --allow-downgrades -yq \
|
||||
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
|
||||
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
|
||||
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
|
||||
|
||||
|
||||
cat << EOF > bin/gcc
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
41
changelog.md
41
changelog.md
@@ -12,13 +12,6 @@ rounding guarantees (via the
|
||||
avoid conflicts with `system.default`, so named argument usage for this
|
||||
parameter like `getOrDefault(..., default = ...)` will have to be changed.
|
||||
|
||||
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
|
||||
|
||||
- Unknown warnings and hints now gives warnings `warnUnknownNotes` instead of
|
||||
errors.
|
||||
|
||||
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Additions:"
|
||||
@@ -31,40 +24,6 @@ errors.
|
||||
|
||||
## Language changes
|
||||
|
||||
- An experimental option `--experimental:typeBoundOps` has been added that
|
||||
implements the RFC https://github.com/nim-lang/RFCs/issues/380.
|
||||
This makes the behavior of interfaces like `hash`, `$`, `==` etc. more
|
||||
reliable for nominal types across indirect/restricted imports.
|
||||
|
||||
```nim
|
||||
# objs.nim
|
||||
import std/hashes
|
||||
|
||||
type
|
||||
Obj* = object
|
||||
x*, y*: int
|
||||
z*: string # to be ignored for equality
|
||||
|
||||
proc `==`*(a, b: Obj): bool =
|
||||
a.x == b.x and a.y == b.y
|
||||
|
||||
proc hash*(a: Obj): Hash =
|
||||
$!(hash(a.x) &! hash(a.y))
|
||||
```
|
||||
|
||||
```nim
|
||||
# main.nim
|
||||
{.experimental: "typeBoundOps".}
|
||||
from objs import Obj # objs.hash, objs.`==` not imported
|
||||
import std/tables
|
||||
|
||||
var t: Table[Obj, int]
|
||||
t[Obj(x: 3, y: 4, z: "debug")] = 34
|
||||
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
|
||||
```
|
||||
|
||||
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#typeminusbound-overloads)
|
||||
for more information.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
- [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
|
||||
|
||||
- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
|
||||
now produces a `CountTable` with values corresponding to the number of occurrences
|
||||
now produces a `CountTable` with values correspoding to the number of occurrences
|
||||
of the key in the input. It used to produce a table with all values set to `1`.
|
||||
|
||||
Counting occurrences in a sequence used to be:
|
||||
@@ -339,7 +339,7 @@ for i in a..b:
|
||||
|
||||
- Fixed "ReraiseError when using try/except within finally block"
|
||||
([#5871](https://github.com/nim-lang/Nim/issues/5871))
|
||||
- Fixed "Range type inference leads to counter-intuitive behaviour"
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "JSON % operator can fail in extern procs with dynamic types"
|
||||
([#6385](https://github.com/nim-lang/Nim/issues/6385))
|
||||
@@ -403,7 +403,7 @@ for i in a..b:
|
||||
([#6589](https://github.com/nim-lang/Nim/issues/6589))
|
||||
- Fixed "Generated c code calls function twice"
|
||||
([#6292](https://github.com/nim-lang/Nim/issues/6292))
|
||||
- Fixed "Range type inference leads to counter-intuitive behaviour"
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "New backward indexing is too limited"
|
||||
([#6631](https://github.com/nim-lang/Nim/issues/6631))
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
- We removed `unicode.Rune16` without any deprecation period as the name
|
||||
was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
|
||||
and we didn't find any usage of it in the wild. If you still need it, add this
|
||||
and we didn't find any usages of it in the wild. If you still need it, add this
|
||||
piece of code to your project:
|
||||
```nim
|
||||
type
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
|
||||
* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
|
||||
* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
|
||||
* Fix how `relativePath` handles case sensitivity
|
||||
* Fix how `relativePath` handle case sensitiviy
|
||||
* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
|
||||
* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
|
||||
* VM: no special casing for big endian machines
|
||||
@@ -43,7 +43,7 @@
|
||||
* threadpool: fix link in docs (#12258)
|
||||
* Fix spellings (#12277)
|
||||
* fix #12278, don't expose internal PCRE documentation
|
||||
* Fixed "Documentation of quitprocs is wrong" [#12279](https://github.com/nim-lang/Nim/issues/12279)
|
||||
* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
|
||||
* Fix typo in docs
|
||||
* Fix reference to parseSpec proc in readme
|
||||
* [doc/tut1] removed discard discussion in comments
|
||||
|
||||
@@ -169,7 +169,7 @@ echo f
|
||||
- The Nim compiler now supports a new pragma called ``.localPassc`` to
|
||||
pass specific compiler options to the C(++) backend for the C(++) file
|
||||
that was produced from the current Nim module.
|
||||
- The compiler now infers "sink parameters". To disable this for a specific routine,
|
||||
- The compiler now inferes "sink parameters". To disable this for a specific routine,
|
||||
annotate it with `.nosinks`. To disable it for a section of code, use
|
||||
`{.push sinkInference: off.}`...`{.pop.}`.
|
||||
- The compiler now supports a new switch `--panics:on` that turns runtime
|
||||
@@ -261,7 +261,7 @@ echo f
|
||||
([#12812](https://github.com/nim-lang/Nim/issues/12812))
|
||||
- Fixed "Produce static/const initializations for variables when possible"
|
||||
([#12216](https://github.com/nim-lang/Nim/issues/12216))
|
||||
- Fixed "Assigning discriminator field leads to internal assert with --gc:destructors"
|
||||
- Fixed "Assigning descriminator field leads to internal assert with --gc:destructors"
|
||||
([#12821](https://github.com/nim-lang/Nim/issues/12821))
|
||||
- Fixed "nimsuggest `use` command does not return all instances of symbol"
|
||||
([#12832](https://github.com/nim-lang/Nim/issues/12832))
|
||||
|
||||
@@ -283,7 +283,7 @@ The definition of `"strictFuncs"` was changed.
|
||||
The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
|
||||
The new definition is: "A store to a ref/ptr deref is forbidden."
|
||||
|
||||
This new definition is much easier to understand, the price is some expressiveness. The following code used to be
|
||||
This new definition is much easier to understand, the price is some expressitivity. The following code used to be
|
||||
accepted:
|
||||
|
||||
```nim
|
||||
|
||||
@@ -279,7 +279,7 @@ const
|
||||
GcTypeKinds* = {tyRef, tySequence, tyString}
|
||||
|
||||
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyConcept,
|
||||
tyUserTypeClass, tyUserTypeClassInst,
|
||||
tyAnd, tyOr, tyNot, tyAnything}
|
||||
|
||||
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
|
||||
@@ -447,8 +447,6 @@ const
|
||||
tfReturnsNew* = tfInheritable
|
||||
tfNonConstExpr* = tfExplicitCallConv
|
||||
## tyFromExpr where the expression shouldn't be evaluated as a static value
|
||||
tfGenericHasDestructor* = tfExplicitCallConv
|
||||
## tyGenericBody where an instance has a generated destructor
|
||||
skError* = skUnknown
|
||||
|
||||
var
|
||||
@@ -1647,7 +1645,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||
if mask != {} and propagateHasAsgn:
|
||||
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if o2.kind in {tyTuple, tyObject, tyArray,
|
||||
tySequence, tyString, tySet, tyDistinct}:
|
||||
tySequence, tySet, tyDistinct}:
|
||||
o2.flags.incl mask
|
||||
owner.flags.incl mask
|
||||
|
||||
|
||||
@@ -1,164 +1,6 @@
|
||||
import ropes, int128
|
||||
|
||||
type
|
||||
Snippet* = string
|
||||
Builder* = object
|
||||
buf*: string
|
||||
indents*: int
|
||||
Snippet = string
|
||||
Builder = string
|
||||
|
||||
template newBuilder*(s: string): Builder =
|
||||
Builder(buf: s)
|
||||
|
||||
proc extract*(builder: Builder): Snippet =
|
||||
builder.buf
|
||||
|
||||
proc add*(builder: var Builder, s: string) =
|
||||
builder.buf.add(s)
|
||||
|
||||
proc add*(builder: var Builder, s: char) =
|
||||
builder.buf.add(s)
|
||||
|
||||
proc addNewline*(builder: var Builder) =
|
||||
builder.add('\n')
|
||||
for i in 0 ..< builder.indents:
|
||||
builder.add('\t')
|
||||
|
||||
proc addLineEnd*(builder: var Builder, s: string) =
|
||||
builder.add(s)
|
||||
builder.addNewline()
|
||||
|
||||
proc addLineEndIndent*(builder: var Builder, s: string) =
|
||||
inc builder.indents
|
||||
builder.add(s)
|
||||
builder.addNewline()
|
||||
|
||||
proc addDedent*(builder: var Builder, s: string) =
|
||||
if builder.buf.len > 0 and builder.buf[^1] == '\t':
|
||||
builder.buf.setLen(builder.buf.len - 1)
|
||||
builder.add(s)
|
||||
dec builder.indents
|
||||
|
||||
proc addLineEndDedent*(builder: var Builder, s: string) =
|
||||
builder.addDedent(s)
|
||||
builder.addNewline()
|
||||
|
||||
proc addLineComment*(builder: var Builder, comment: string) =
|
||||
# probably no-op on nifc
|
||||
builder.add("// ")
|
||||
builder.add(comment)
|
||||
builder.addNewline()
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: int) =
|
||||
builder.buf.addInt(val)
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: int64) =
|
||||
builder.buf.addInt(val)
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: uint64) =
|
||||
builder.buf.addInt(val)
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: Int128) =
|
||||
builder.buf.addInt128(val)
|
||||
|
||||
template cIntValue*(val: int): Snippet = $val
|
||||
template cIntValue*(val: int64): Snippet = $val
|
||||
template cIntValue*(val: uint64): Snippet = $val
|
||||
template cIntValue*(val: Int128): Snippet = $val
|
||||
|
||||
template cUintValue*(val: uint): Snippet = $val & "U"
|
||||
|
||||
import std/formatfloat
|
||||
|
||||
proc addFloatValue*(builder: var Builder, val: float) =
|
||||
builder.buf.addFloat(val)
|
||||
|
||||
template cFloatValue*(val: float): Snippet = $val
|
||||
|
||||
proc addInt64Literal*(result: var Builder; i: BiggestInt) =
|
||||
if i > low(int64):
|
||||
result.add "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result.add "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc addUint64Literal*(result: var Builder; i: uint64) =
|
||||
result.add rope($i & "ULL")
|
||||
|
||||
proc addIntLiteral*(result: var Builder; i: BiggestInt) =
|
||||
if i > low(int32) and i <= high(int32):
|
||||
result.addIntValue(i)
|
||||
elif i == low(int32):
|
||||
# Nim has the same bug for the same reasons :-)
|
||||
result.add "(-2147483647 -1)"
|
||||
elif i > low(int64):
|
||||
result.add "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result.add "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc addIntLiteral*(result: var Builder; i: Int128) =
|
||||
addIntLiteral(result, toInt64(i))
|
||||
|
||||
proc cInt64Literal*(i: BiggestInt): Snippet =
|
||||
if i > low(int64):
|
||||
result = "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result = "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc cUint64Literal*(i: uint64): Snippet =
|
||||
result = $i & "ULL"
|
||||
|
||||
proc cIntLiteral*(i: BiggestInt): Snippet =
|
||||
if i > low(int32) and i <= high(int32):
|
||||
result = rope(i)
|
||||
elif i == low(int32):
|
||||
# Nim has the same bug for the same reasons :-)
|
||||
result = "(-2147483647 -1)"
|
||||
elif i > low(int64):
|
||||
result = "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result = "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc cIntLiteral*(i: Int128): Snippet =
|
||||
result = cIntLiteral(toInt64(i))
|
||||
|
||||
const
|
||||
NimInt* = "NI"
|
||||
NimInt8* = "NI8"
|
||||
NimInt16* = "NI16"
|
||||
NimInt32* = "NI32"
|
||||
NimInt64* = "NI64"
|
||||
CInt* = "int"
|
||||
NimUint* = "NU"
|
||||
NimUint8* = "NU8"
|
||||
NimUint16* = "NU16"
|
||||
NimUint32* = "NU32"
|
||||
NimUint64* = "NU64"
|
||||
NimFloat* = "NF"
|
||||
NimFloat32* = "NF32"
|
||||
NimFloat64* = "NF64"
|
||||
NimFloat128* = "NF128" # not actually defined
|
||||
NimNan* = "NAN"
|
||||
NimInf* = "INF"
|
||||
NimBool* = "NIM_BOOL"
|
||||
NimTrue* = "NIM_TRUE"
|
||||
NimFalse* = "NIM_FALSE"
|
||||
NimChar* = "NIM_CHAR"
|
||||
CChar* = "char"
|
||||
NimCstring* = "NCSTRING"
|
||||
NimNil* = "NIM_NIL"
|
||||
CNil* = "NULL"
|
||||
NimStrlitFlag* = "NIM_STRLIT_FLAG"
|
||||
CVoid* = "void"
|
||||
CPointer* = "void*"
|
||||
CConstPointer* = "NIM_CONST void*"
|
||||
|
||||
proc cIntType*(bits: BiggestInt): Snippet =
|
||||
"NI" & $bits
|
||||
|
||||
proc cUintType*(bits: BiggestInt): Snippet =
|
||||
"NU" & $bits
|
||||
|
||||
type
|
||||
IfBuilderState* = enum
|
||||
WaitingIf, WaitingElseIf, InBlock
|
||||
IfBuilder* = object
|
||||
state*: IfBuilderState
|
||||
template newBuilder(s: string): Builder =
|
||||
s
|
||||
|
||||
@@ -32,7 +32,7 @@ proc addVar(builder: var Builder, kind: VarKind = Local, name: string, typ: Snip
|
||||
if initializer.len != 0:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
builder.add(";\n")
|
||||
|
||||
template addVarWithType(builder: var Builder, kind: VarKind = Local, name: string, body: typed) =
|
||||
## adds a variable declaration to the builder, with the `body` building the type
|
||||
@@ -40,19 +40,7 @@ template addVarWithType(builder: var Builder, kind: VarKind = Local, name: strin
|
||||
body
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string,
|
||||
typ: Snippet, initializerBody: typed) =
|
||||
## adds a variable declaration to the builder, with
|
||||
## `initializerBody` building the initializer. initializer must be provided
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(typ)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
initializerBody
|
||||
builder.addLineEnd(";")
|
||||
builder.add(";\n")
|
||||
|
||||
template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
|
||||
typeBody, initializerBody: typed) =
|
||||
@@ -64,7 +52,7 @@ template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Loca
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
initializerBody
|
||||
builder.addLineEnd(";")
|
||||
builder.add(";\n")
|
||||
|
||||
proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
|
||||
## adds an array variable declaration to the builder
|
||||
@@ -73,12 +61,12 @@ proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elem
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addIntValue(len)
|
||||
builder.addInt(len)
|
||||
builder.add("]")
|
||||
if initializer.len != 0:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
builder.add(";\n")
|
||||
|
||||
template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
|
||||
## adds an array variable declaration to the builder with the initializer built according to `body`
|
||||
@@ -87,10 +75,10 @@ template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local,
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addIntValue(len)
|
||||
builder.addInt(len)
|
||||
builder.add("] = ")
|
||||
body
|
||||
builder.addLineEnd(";")
|
||||
builder.add(";\n")
|
||||
|
||||
template addTypedef(builder: var Builder, name: string, typeBody: typed) =
|
||||
## adds a typedef declaration to the builder with name `name` and type as
|
||||
@@ -99,20 +87,9 @@ template addTypedef(builder: var Builder, name: string, typeBody: typed) =
|
||||
typeBody
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.addLineEnd(";")
|
||||
builder.add(";\n")
|
||||
|
||||
proc addProcTypedef(builder: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
|
||||
builder.add("typedef ")
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addArrayTypedef(builder: var Builder, name: string, len: BiggestInt, typeBody: typed) =
|
||||
template addArrayTypedef(builder: var Builder, name: string, len: int, typeBody: typed) =
|
||||
## adds an array typedef declaration to the builder with name `name`,
|
||||
## length `len`, and element type as built in `typeBody`
|
||||
builder.add("typedef ")
|
||||
@@ -120,8 +97,8 @@ template addArrayTypedef(builder: var Builder, name: string, len: BiggestInt, ty
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addIntValue(len)
|
||||
builder.addLineEnd("];")
|
||||
builder.addInt(len)
|
||||
builder.add("];\n")
|
||||
|
||||
type
|
||||
StructInitializerKind = enum
|
||||
@@ -196,7 +173,7 @@ proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initi
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
obj.add("[")
|
||||
obj.addIntValue(len)
|
||||
obj.addInt(len)
|
||||
obj.add("]")
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
@@ -207,7 +184,7 @@ proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bo
|
||||
obj.add('\t')
|
||||
if field.alignment > 0:
|
||||
obj.add("NIM_ALIGN(")
|
||||
obj.addIntValue(field.alignment)
|
||||
obj.addInt(field.alignment)
|
||||
obj.add(") ")
|
||||
obj.add(typ)
|
||||
if sfNoalias in field.flags:
|
||||
@@ -218,21 +195,11 @@ proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bo
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if field.bitsize != 0:
|
||||
obj.add(":")
|
||||
obj.addIntValue(field.bitsize)
|
||||
obj.addInt(field.bitsize)
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addProcField(obj: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
|
||||
obj.add(CallingConvToStr[callConv])
|
||||
obj.add("_PTR(")
|
||||
obj.add(rettype)
|
||||
obj.add(", ")
|
||||
obj.add(name)
|
||||
obj.add(")")
|
||||
obj.add(params)
|
||||
obj.add(";\n")
|
||||
|
||||
type
|
||||
BaseClassKind = enum
|
||||
## denotes how and whether or not the base class/RTTI should be stored
|
||||
@@ -264,14 +231,14 @@ proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Sni
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
result.preFieldsLen = obj.buf.len
|
||||
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.buf.len:
|
||||
if info.baseKind == bcNone and info.preFieldsLen == obj.len:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = CChar)
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
if info.named:
|
||||
obj.add("};\n")
|
||||
else:
|
||||
@@ -320,7 +287,7 @@ proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType:
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
result.preFieldsLen = obj.buf.len
|
||||
result.preFieldsLen = obj.len
|
||||
case result.baseKind
|
||||
of bcNone:
|
||||
# rest of the options add a field or don't need it due to inheritance,
|
||||
@@ -330,7 +297,7 @@ proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType:
|
||||
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 = CChar)
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
of bcCppInherit: discard
|
||||
of bcNoneRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
|
||||
@@ -340,10 +307,10 @@ proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType:
|
||||
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.buf.len and
|
||||
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 = CChar)
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
if info.named:
|
||||
obj.add("};\n")
|
||||
else:
|
||||
@@ -373,296 +340,10 @@ template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType;
|
||||
obj.add(fieldName)
|
||||
obj.add(";\n")
|
||||
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
obj.add("#pragma pack(pop)\n")
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addAnonUnion(obj: var Builder; body: typed) =
|
||||
## adds an anonymous union i.e. `union { ... };` with fields according to `body`
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("};\n")
|
||||
|
||||
template addUnionType(obj: var Builder; body: typed) =
|
||||
## adds a union type i.e. `union { ... }` with fields according to `body`
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("}")
|
||||
|
||||
type DeclVisibility = enum
|
||||
None
|
||||
Extern
|
||||
ExternC
|
||||
ImportLib
|
||||
ExportLib
|
||||
ExportLibVar
|
||||
Private
|
||||
StaticProc
|
||||
|
||||
proc addVisibilityPrefix(builder: var Builder, visibility: DeclVisibility) =
|
||||
# internal proc
|
||||
case visibility
|
||||
of None: discard
|
||||
of Extern:
|
||||
builder.add("extern ")
|
||||
of ExternC:
|
||||
builder.add("NIM_EXTERNC ")
|
||||
of ImportLib:
|
||||
builder.add("N_LIB_IMPORT ")
|
||||
of ExportLib:
|
||||
builder.add("N_LIB_EXPORT ")
|
||||
of ExportLibVar:
|
||||
builder.add("N_LIB_EXPORT_VAR ")
|
||||
of Private:
|
||||
builder.add("N_LIB_PRIVATE ")
|
||||
of StaticProc:
|
||||
builder.add("static ")
|
||||
|
||||
template addDeclWithVisibility(builder: var Builder, visibility: DeclVisibility, declBody: typed) =
|
||||
## adds a declaration as in `declBody` with the given visibility
|
||||
builder.addVisibilityPrefix(visibility)
|
||||
declBody
|
||||
|
||||
type ProcParamBuilder = object
|
||||
needsComma: bool
|
||||
|
||||
proc initProcParamBuilder(builder: var Builder): ProcParamBuilder =
|
||||
result = ProcParamBuilder(needsComma: false)
|
||||
builder.add("(")
|
||||
|
||||
proc finishProcParamBuilder(builder: var Builder, params: ProcParamBuilder) =
|
||||
if params.needsComma:
|
||||
builder.add(")")
|
||||
else:
|
||||
builder.add("void)")
|
||||
|
||||
template cgDeclFrmt*(s: PSym): string =
|
||||
s.constraint.strVal
|
||||
|
||||
proc addParam(builder: var Builder, params: var ProcParamBuilder, name: string, typ: Snippet) =
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
builder.add(typ)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
|
||||
proc addParam(builder: var Builder, params: var ProcParamBuilder, param: PSym, typ: Snippet) =
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
var modifiedTyp = typ
|
||||
if sfNoalias in param.flags:
|
||||
modifiedTyp.add(" NIM_NOALIAS")
|
||||
if sfCodegenDecl notin param.flags:
|
||||
builder.add(modifiedTyp)
|
||||
builder.add(" ")
|
||||
builder.add(param.loc.snippet)
|
||||
else:
|
||||
builder.add runtimeFormat(param.cgDeclFrmt, [modifiedTyp, param.loc.snippet])
|
||||
|
||||
proc addUnnamedParam(builder: var Builder, params: var ProcParamBuilder, typ: Snippet) =
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
builder.add(typ)
|
||||
|
||||
proc addProcTypedParam(builder: var Builder, paramBuilder: var ProcParamBuilder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
|
||||
if paramBuilder.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
paramBuilder.needsComma = true
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
|
||||
proc addVarargsParam(builder: var Builder, params: var ProcParamBuilder) =
|
||||
# does not exist in NIFC, needs to be proc pragma
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
builder.add("...")
|
||||
|
||||
template addProcParams(builder: var Builder, params: out ProcParamBuilder, body: typed) =
|
||||
params = initProcParamBuilder(builder)
|
||||
body
|
||||
finishProcParamBuilder(builder, params)
|
||||
|
||||
type SimpleProcParam = tuple
|
||||
name, typ: string
|
||||
|
||||
proc cProcParams(params: varargs[SimpleProcParam]): Snippet =
|
||||
if params.len == 0: return "(void)"
|
||||
result = "("
|
||||
for i in 0 ..< params.len:
|
||||
if i != 0: result.add(", ")
|
||||
result.add(params[i].typ)
|
||||
if params[i].name.len != 0:
|
||||
result.add(" ")
|
||||
result.add(params[i].name)
|
||||
result.add(")")
|
||||
|
||||
template addProcHeaderWithParams(builder: var Builder, callConv: TCallingConvention,
|
||||
name: string, rettype: Snippet, paramBuilder: typed) =
|
||||
# on nifc should build something like (proc name params type pragmas
|
||||
# with no body given
|
||||
# or enforce this with secondary builder object
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
paramBuilder
|
||||
|
||||
proc addProcHeader(builder: var Builder, callConv: TCallingConvention,
|
||||
name: string, rettype, params: Snippet) =
|
||||
# on nifc should build something like (proc name params type pragmas
|
||||
# with no body given
|
||||
# or enforce this with secondary builder object
|
||||
addProcHeaderWithParams(builder, callConv, name, rettype):
|
||||
builder.add(params)
|
||||
|
||||
proc addProcHeader(builder: var Builder, name: string, rettype, params: Snippet, isConstructor = false) =
|
||||
# no callconv
|
||||
builder.add(rettype)
|
||||
builder.add(" ")
|
||||
if isConstructor:
|
||||
builder.add("__attribute__((constructor)) ")
|
||||
builder.add(name)
|
||||
builder.add(params)
|
||||
|
||||
proc addProcHeader(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet, addAttributes: bool) =
|
||||
# on nifc should build something like (proc name params type pragmas
|
||||
# with no body given
|
||||
# or enforce this with secondary builder object
|
||||
let noreturn = isNoReturn(m, prc)
|
||||
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(naked) ")
|
||||
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(noreturn) ")
|
||||
builder.add(CallingConvToStr[prc.typ.callConv])
|
||||
builder.add("(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
if addAttributes:
|
||||
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((naked))")
|
||||
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((noreturn))")
|
||||
|
||||
proc finishProcHeaderAsProto(builder: var Builder) =
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template finishProcHeaderWithBody(builder: var Builder, body: typed) =
|
||||
builder.addLineEndIndent(" {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
builder.addNewline
|
||||
|
||||
proc addProcVar(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet,
|
||||
isStatic = false, ignoreAttributes = false) =
|
||||
# on nifc, builds full variable
|
||||
if isStatic:
|
||||
builder.add("static ")
|
||||
let noreturn = isNoReturn(m, prc)
|
||||
if not ignoreAttributes:
|
||||
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(naked) ")
|
||||
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(noreturn) ")
|
||||
builder.add(CallingConvToStr[prc.typ.callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
if not ignoreAttributes:
|
||||
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((naked))")
|
||||
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((noreturn))")
|
||||
# ensure we are just adding a variable:
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addProcVar(builder: var Builder, callConv: TCallingConvention,
|
||||
name: string, params, rettype: Snippet,
|
||||
isStatic = false, isVolatile = false) =
|
||||
# on nifc, builds full variable
|
||||
if isStatic:
|
||||
builder.add("static ")
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
if isVolatile:
|
||||
builder.add("volatile ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
# ensure we are just adding a variable:
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addProcVar(builder: var Builder,
|
||||
name: string, params, rettype: Snippet,
|
||||
isStatic = false, isVolatile = false) =
|
||||
# no callconv
|
||||
if isStatic:
|
||||
builder.add("static ")
|
||||
builder.add(rettype)
|
||||
builder.add(" (*")
|
||||
if isVolatile:
|
||||
builder.add("volatile ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
# ensure we are just adding a variable:
|
||||
builder.addLineEnd(";")
|
||||
|
||||
type VarInitializerKind = enum
|
||||
Assignment, CppConstructor
|
||||
|
||||
proc addVar(builder: var Builder, m: BModule, s: PSym, name: string, typ: Snippet, kind = Local, visibility: DeclVisibility = None, initializer: Snippet = "", initializerKind: VarInitializerKind = Assignment) =
|
||||
if sfCodegenDecl in s.flags:
|
||||
builder.add(runtimeFormat(s.cgDeclFrmt, [typ, name]))
|
||||
if initializer.len != 0:
|
||||
if initializerKind == Assignment:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
return
|
||||
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
|
||||
builder.add("NIM_ALIGN(" & $s.alignment & ") ")
|
||||
builder.addVisibilityPrefix(visibility)
|
||||
if kind == Threadvar:
|
||||
if optThreads in m.config.globalOptions:
|
||||
let sym = s.typ.sym
|
||||
if sym != nil and sfCppNonPod in sym.flags:
|
||||
builder.add("NIM_THREAD_LOCAL ")
|
||||
else: builder.add("NIM_THREADVAR ")
|
||||
else:
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(typ)
|
||||
if sfRegister in s.flags: builder.add(" register")
|
||||
if sfVolatile in s.flags: builder.add(" volatile")
|
||||
if sfNoalias in s.flags: builder.add(" NIM_NOALIAS")
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
if initializer.len != 0:
|
||||
if initializerKind == Assignment:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addInclude(builder: var Builder, value: Snippet) =
|
||||
builder.addLineEnd("#include " & value)
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
proc constType(t: Snippet): Snippet =
|
||||
# needs manipulation of `t` in nifc
|
||||
"NIM_CONST " & t
|
||||
|
||||
proc constPtrType(t: Snippet): Snippet =
|
||||
t & "* NIM_CONST"
|
||||
|
||||
proc ptrConstType(t: Snippet): Snippet =
|
||||
"NIM_CONST " & t & "*"
|
||||
# XXX make complex ones like bitOr use builder instead
|
||||
# XXX add stuff like NI, NIM_NIL as constants
|
||||
|
||||
proc ptrType(t: Snippet): Snippet =
|
||||
t & "*"
|
||||
|
||||
proc cppRefType(t: Snippet): Snippet =
|
||||
t & "&"
|
||||
|
||||
const
|
||||
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
|
||||
"N_STDCALL", "N_CDECL", "N_SAFECALL",
|
||||
@@ -23,237 +13,14 @@ const
|
||||
"N_NOCONV" #ccMember is N_NOCONV
|
||||
]
|
||||
|
||||
proc procPtrTypeUnnamed(rettype, params: Snippet): Snippet =
|
||||
rettype & "(*)" & params
|
||||
|
||||
proc procPtrTypeUnnamedNimCall(rettype, params: Snippet): Snippet =
|
||||
rettype & "(N_RAW_NIMCALL*)" & params
|
||||
|
||||
proc procPtrTypeUnnamed(callConv: TCallingConvention, rettype, params: Snippet): Snippet =
|
||||
CallingConvToStr[callConv] & "_PTR(" & rettype & ", )" & params
|
||||
|
||||
type CppCaptureKind = enum None, ByReference, ByCopy
|
||||
|
||||
template addCppLambda(builder: var Builder, captures: CppCaptureKind, params: Snippet, body: typed) =
|
||||
builder.add("[")
|
||||
case captures
|
||||
of None: discard
|
||||
of ByReference: builder.add("&")
|
||||
of ByCopy: builder.add("=")
|
||||
builder.add("] ")
|
||||
builder.add(params)
|
||||
builder.addLineEndIndent(" {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
proc procPtrType(conv: TCallingConvention, rettype: Snippet, name: string): Snippet =
|
||||
CallingConvToStr[conv] & "_PTR(" & rettype & ", " & name & ")"
|
||||
|
||||
proc cCast(typ, value: Snippet): Snippet =
|
||||
"((" & typ & ") " & value & ")"
|
||||
|
||||
proc wrapPar(value: Snippet): Snippet =
|
||||
# used for expression group, no-op on sexp
|
||||
"(" & value & ")"
|
||||
|
||||
proc removeSinglePar(value: Snippet): Snippet =
|
||||
# removes a single paren layer expected to exist, to silence Wparentheses-equality
|
||||
assert value[0] == '(' and value[^1] == ')'
|
||||
value[1..^2]
|
||||
|
||||
template addCast(builder: var Builder, typ: Snippet, valueBody: typed) =
|
||||
## adds a cast to `typ` with value built by `valueBody`
|
||||
builder.add "(("
|
||||
builder.add typ
|
||||
builder.add ") "
|
||||
valueBody
|
||||
builder.add ")"
|
||||
|
||||
proc cAddr(value: Snippet): Snippet =
|
||||
"(&" & value & ")"
|
||||
"&" & value
|
||||
|
||||
proc cLabelAddr(value: TLabel): Snippet =
|
||||
"&&" & value
|
||||
|
||||
proc cDeref(value: Snippet): Snippet =
|
||||
"(*" & value & ")"
|
||||
|
||||
proc subscript(a, b: Snippet): Snippet =
|
||||
a & "[" & b & "]"
|
||||
|
||||
proc dotField(a, b: Snippet): Snippet =
|
||||
a & "." & b
|
||||
|
||||
proc derefField(a, b: Snippet): Snippet =
|
||||
a & "->" & b
|
||||
|
||||
type CallBuilder = object
|
||||
needsComma: bool
|
||||
|
||||
proc initCallBuilder(builder: var Builder, callee: Snippet): CallBuilder =
|
||||
result = CallBuilder(needsComma: false)
|
||||
builder.add(callee)
|
||||
builder.add("(")
|
||||
|
||||
const cArgumentSeparator = ", "
|
||||
|
||||
proc addArgumentSeparator(builder: var Builder) =
|
||||
# no-op on NIFC
|
||||
# used by "single argument" builders
|
||||
builder.add(cArgumentSeparator)
|
||||
|
||||
template addArgument(builder: var Builder, call: var CallBuilder, valueBody: typed) =
|
||||
if call.needsComma:
|
||||
builder.addArgumentSeparator()
|
||||
else:
|
||||
call.needsComma = true
|
||||
valueBody
|
||||
|
||||
proc finishCallBuilder(builder: var Builder, call: CallBuilder) =
|
||||
builder.add(")")
|
||||
|
||||
template addCall(builder: var Builder, call: out CallBuilder, callee: Snippet, body: typed) =
|
||||
call = initCallBuilder(builder, callee)
|
||||
body
|
||||
finishCallBuilder(builder, call)
|
||||
|
||||
proc addCall(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
|
||||
builder.add(callee)
|
||||
builder.add("(")
|
||||
if args.len != 0:
|
||||
builder.add(args[0])
|
||||
for i in 1 ..< args.len:
|
||||
builder.add(", ")
|
||||
builder.add(args[i])
|
||||
builder.add(")")
|
||||
|
||||
proc cCall(callee: Snippet, args: varargs[Snippet]): Snippet =
|
||||
result = callee
|
||||
result.add("(")
|
||||
if args.len != 0:
|
||||
result.add(args[0])
|
||||
for i in 1 ..< args.len:
|
||||
result.add(", ")
|
||||
result.add(args[i])
|
||||
result.add(")")
|
||||
|
||||
proc addSizeof(builder: var Builder, val: Snippet) =
|
||||
builder.add("sizeof(")
|
||||
builder.add(val)
|
||||
builder.add(")")
|
||||
|
||||
proc addAlignof(builder: var Builder, val: Snippet) =
|
||||
builder.add("NIM_ALIGNOF(")
|
||||
builder.add(val)
|
||||
builder.add(")")
|
||||
|
||||
proc addOffsetof(builder: var Builder, val, member: Snippet) =
|
||||
builder.add("offsetof(")
|
||||
builder.add(val)
|
||||
builder.add(", ")
|
||||
builder.add(member)
|
||||
builder.add(")")
|
||||
|
||||
template cSizeof(val: Snippet): Snippet =
|
||||
"sizeof(" & val & ")"
|
||||
|
||||
template cAlignof(val: Snippet): Snippet =
|
||||
"NIM_ALIGNOF(" & val & ")"
|
||||
|
||||
template cOffsetof(val, member: Snippet): Snippet =
|
||||
"offsetof(" & val & ", " & member & ")"
|
||||
|
||||
type TypedBinaryOp = enum
|
||||
Add, Sub, Mul, Div, Mod
|
||||
Shr, Shl, BitAnd, BitOr, BitXor
|
||||
|
||||
const typedBinaryOperators: array[TypedBinaryOp, string] = [
|
||||
Add: "+",
|
||||
Sub: "-",
|
||||
Mul: "*",
|
||||
Div: "/",
|
||||
Mod: "%",
|
||||
Shr: ">>",
|
||||
Shl: "<<",
|
||||
BitAnd: "&",
|
||||
BitOr: "|",
|
||||
BitXor: "^"
|
||||
]
|
||||
|
||||
type TypedUnaryOp = enum
|
||||
Neg, BitNot
|
||||
|
||||
const typedUnaryOperators: array[TypedUnaryOp, string] = [
|
||||
Neg: "-",
|
||||
BitNot: "~",
|
||||
]
|
||||
|
||||
type UntypedBinaryOp = enum
|
||||
LessEqual, LessThan, GreaterEqual, GreaterThan, Equal, NotEqual
|
||||
And, Or
|
||||
|
||||
const untypedBinaryOperators: array[UntypedBinaryOp, string] = [
|
||||
LessEqual: "<=",
|
||||
LessThan: "<",
|
||||
GreaterEqual: ">=",
|
||||
GreaterThan: ">",
|
||||
Equal: "==",
|
||||
NotEqual: "!=",
|
||||
And: "&&",
|
||||
Or: "||"
|
||||
]
|
||||
|
||||
type UntypedUnaryOp = enum
|
||||
Not
|
||||
|
||||
const untypedUnaryOperators: array[UntypedUnaryOp, string] = [
|
||||
Not: "!"
|
||||
]
|
||||
|
||||
proc addOp(builder: var Builder, binOp: TypedBinaryOp, t: Snippet, a, b: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(typedBinaryOperators[binOp])
|
||||
builder.add(' ')
|
||||
builder.add(b)
|
||||
builder.add(')')
|
||||
|
||||
proc addOp(builder: var Builder, unOp: TypedUnaryOp, t: Snippet, a: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(typedUnaryOperators[unOp])
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add("))")
|
||||
|
||||
proc addOp(builder: var Builder, binOp: UntypedBinaryOp, a, b: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(untypedBinaryOperators[binOp])
|
||||
builder.add(' ')
|
||||
builder.add(b)
|
||||
builder.add(')')
|
||||
|
||||
proc addOp(builder: var Builder, unOp: UntypedUnaryOp, a: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(untypedUnaryOperators[unOp])
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add("))")
|
||||
|
||||
template cOp(binOp: TypedBinaryOp, t: Snippet, a, b: Snippet): Snippet =
|
||||
'(' & a & ' ' & typedBinaryOperators[binOp] & ' ' & b & ')'
|
||||
|
||||
template cOp(binOp: TypedUnaryOp, t: Snippet, a: Snippet): Snippet =
|
||||
'(' & typedUnaryOperators[binOp] & '(' & a & "))"
|
||||
|
||||
template cOp(binOp: UntypedBinaryOp, a, b: Snippet): Snippet =
|
||||
'(' & a & ' ' & untypedBinaryOperators[binOp] & ' ' & b & ')'
|
||||
|
||||
template cOp(binOp: UntypedUnaryOp, a: Snippet): Snippet =
|
||||
'(' & untypedUnaryOperators[binOp] & '(' & a & "))"
|
||||
|
||||
template cIfExpr(cond, a, b: Snippet): Snippet =
|
||||
# XXX used for `min` and `max`, maybe add nifc primitives for these
|
||||
"(" & cond & " ? " & a & " : " & b & ")"
|
||||
|
||||
template cUnlikely(val: Snippet): Snippet =
|
||||
"NIM_UNLIKELY(" & val & ")"
|
||||
proc bitOr(a, b: Snippet): Snippet =
|
||||
"(" & a & " | " & b & ")"
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
template addAssignmentWithValue(builder: var Builder, lhs: Snippet, valueBody: typed) =
|
||||
builder.add(lhs)
|
||||
builder.add(" = ")
|
||||
valueBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addFieldAssignmentWithValue(builder: var Builder, lhs: Snippet, name: string, valueBody: typed) =
|
||||
builder.add(lhs)
|
||||
builder.add("." & name & " = ")
|
||||
valueBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addAssignment(builder: var Builder, lhs, rhs: Snippet) =
|
||||
builder.addAssignmentWithValue(lhs):
|
||||
builder.add(rhs)
|
||||
|
||||
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
|
||||
builder.addFieldAssignmentWithValue(lhs, name):
|
||||
builder.add(rhs)
|
||||
|
||||
template addMutualFieldAssignment(builder: var Builder, lhs, rhs: Snippet, name: string) =
|
||||
builder.addFieldAssignmentWithValue(lhs, name):
|
||||
builder.add(rhs)
|
||||
builder.add("." & name)
|
||||
|
||||
template addAssignment(builder: var Builder, lhs: Snippet, rhs: int | int64 | uint64 | Int128) =
|
||||
builder.addAssignmentWithValue(lhs):
|
||||
builder.addIntValue(rhs)
|
||||
|
||||
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: int | int64 | uint64 | Int128) =
|
||||
builder.addFieldAssignmentWithValue(lhs, name):
|
||||
builder.addIntValue(rhs)
|
||||
|
||||
template addDerefFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
|
||||
builder.add(lhs)
|
||||
builder.add("->" & name & " = ")
|
||||
builder.add(rhs)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addSubscriptAssignment(builder: var Builder, lhs: Snippet, index: Snippet, rhs: Snippet) =
|
||||
builder.add(lhs)
|
||||
builder.add("[" & index & "] = ")
|
||||
builder.add(rhs)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addStmt(builder: var Builder, stmtBody: typed) =
|
||||
## makes an expression built by `stmtBody` into a statement
|
||||
stmtBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addCallStmt(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
|
||||
builder.addStmt():
|
||||
builder.addCall(callee, args)
|
||||
|
||||
# XXX blocks need indent tracker in `Builder` object
|
||||
|
||||
template addSingleIfStmt(builder: var Builder, cond: Snippet, body: typed) =
|
||||
builder.add("if (")
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
template addSingleIfStmtWithCond(builder: var Builder, condBody: typed, body: typed) =
|
||||
builder.add("if (")
|
||||
condBody
|
||||
builder.addLineEndIndent(") {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
proc initIfStmt(builder: var Builder): IfBuilder =
|
||||
IfBuilder(state: WaitingIf)
|
||||
|
||||
proc finishIfStmt(builder: var Builder, stmt: IfBuilder) =
|
||||
assert stmt.state != InBlock
|
||||
builder.addNewline()
|
||||
|
||||
template addIfStmt(builder: var Builder, stmt: out IfBuilder, body: typed) =
|
||||
stmt = initIfStmt(builder)
|
||||
body
|
||||
finishIfStmt(builder, stmt)
|
||||
|
||||
proc initElifBranch(builder: var Builder, stmt: var IfBuilder, cond: Snippet) =
|
||||
case stmt.state
|
||||
of WaitingIf:
|
||||
builder.add("if (")
|
||||
of WaitingElseIf:
|
||||
builder.add(" else if (")
|
||||
else: assert false, $stmt.state
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
stmt.state = InBlock
|
||||
|
||||
proc initElseBranch(builder: var Builder, stmt: var IfBuilder) =
|
||||
assert stmt.state == WaitingElseIf, $stmt.state
|
||||
builder.addLineEndIndent(" else {")
|
||||
stmt.state = InBlock
|
||||
|
||||
proc finishBranch(builder: var Builder, stmt: var IfBuilder) =
|
||||
builder.addDedent("}")
|
||||
stmt.state = WaitingElseIf
|
||||
|
||||
template addElifBranch(builder: var Builder, stmt: var IfBuilder, cond: Snippet, body: typed) =
|
||||
initElifBranch(builder, stmt, cond)
|
||||
body
|
||||
finishBranch(builder, stmt)
|
||||
|
||||
template addElseBranch(builder: var Builder, stmt: var IfBuilder, body: typed) =
|
||||
initElseBranch(builder, stmt)
|
||||
body
|
||||
finishBranch(builder, stmt)
|
||||
|
||||
proc initForRange(builder: var Builder, i, start, bound: Snippet, inclusive: bool = false) =
|
||||
builder.add("for (")
|
||||
builder.add(i)
|
||||
builder.add(" = ")
|
||||
builder.add(start)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
if inclusive:
|
||||
builder.add(" <= ")
|
||||
else:
|
||||
builder.add(" < ")
|
||||
builder.add(bound)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
builder.addLineEndIndent("++) {")
|
||||
|
||||
proc initForStep(builder: var Builder, i, start, bound, step: Snippet, inclusive: bool = false) =
|
||||
builder.add("for (")
|
||||
builder.add(i)
|
||||
builder.add(" = ")
|
||||
builder.add(start)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
if inclusive:
|
||||
builder.add(" <= ")
|
||||
else:
|
||||
builder.add(" < ")
|
||||
builder.add(bound)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
builder.add(" += ")
|
||||
builder.add(step)
|
||||
builder.addLineEndIndent(") {")
|
||||
|
||||
proc finishFor(builder: var Builder) {.inline.} =
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
template addForRangeExclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
|
||||
initForRange(builder, i, start, bound, false)
|
||||
body
|
||||
finishFor(builder)
|
||||
|
||||
template addForRangeInclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
|
||||
initForRange(builder, i, start, bound, true)
|
||||
body
|
||||
finishFor(builder)
|
||||
|
||||
template addSwitchStmt(builder: var Builder, val: Snippet, body: typed) =
|
||||
builder.add("switch (")
|
||||
builder.add(val)
|
||||
builder.addLineEnd(") {") # no indent
|
||||
body
|
||||
builder.addLineEnd("}")
|
||||
|
||||
template addSingleSwitchCase(builder: var Builder, val: Snippet, body: typed) =
|
||||
builder.add("case ")
|
||||
builder.add(val)
|
||||
builder.addLineEndIndent(":")
|
||||
body
|
||||
builder.addLineEndDedent("")
|
||||
|
||||
type
|
||||
SwitchCaseState = enum
|
||||
None, Of, Else, Finished
|
||||
SwitchCaseBuilder = object
|
||||
state: SwitchCaseState
|
||||
|
||||
proc addCase(builder: var Builder, info: var SwitchCaseBuilder, val: Snippet) =
|
||||
if info.state != Of:
|
||||
assert info.state == None
|
||||
info.state = Of
|
||||
builder.add("case ")
|
||||
builder.add(val)
|
||||
builder.addLineEndIndent(":")
|
||||
|
||||
proc addCaseRange(builder: var Builder, info: var SwitchCaseBuilder, first, last: Snippet) =
|
||||
if info.state != Of:
|
||||
assert info.state == None
|
||||
info.state = Of
|
||||
builder.add("case ")
|
||||
builder.add(first)
|
||||
builder.add(" ... ")
|
||||
builder.add(last)
|
||||
builder.addLineEndIndent(":")
|
||||
|
||||
proc addCaseElse(builder: var Builder, info: var SwitchCaseBuilder) =
|
||||
assert info.state == None
|
||||
info.state = Else
|
||||
builder.addLineEndIndent("default:")
|
||||
|
||||
template addSwitchCase(builder: var Builder, info: out SwitchCaseBuilder, caseBody, body: typed) =
|
||||
info = SwitchCaseBuilder(state: None)
|
||||
caseBody
|
||||
info.state = Finished
|
||||
body
|
||||
builder.addLineEndDedent("")
|
||||
|
||||
template addSwitchElse(builder: var Builder, body: typed) =
|
||||
builder.addLineEndIndent("default:")
|
||||
body
|
||||
builder.addLineEndDedent("")
|
||||
|
||||
proc addBreak(builder: var Builder) =
|
||||
builder.addLineEnd("break;")
|
||||
|
||||
type ScopeBuilder = object
|
||||
inside: bool
|
||||
|
||||
proc initScope(builder: var Builder): ScopeBuilder =
|
||||
builder.addLineEndIndent("{")
|
||||
result = ScopeBuilder(inside: true)
|
||||
|
||||
proc finishScope(builder: var Builder, scope: var ScopeBuilder) =
|
||||
assert scope.inside, "scope not inited"
|
||||
builder.addLineEndDedent("}")
|
||||
scope.inside = false
|
||||
|
||||
template addScope(builder: var Builder, body: typed) =
|
||||
builder.addLineEndIndent("{")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
type WhileBuilder = object
|
||||
inside: bool
|
||||
|
||||
proc initWhileStmt(builder: var Builder, cond: Snippet): WhileBuilder =
|
||||
builder.add("while (")
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
result = WhileBuilder(inside: true)
|
||||
|
||||
proc finishWhileStmt(builder: var Builder, stmt: var WhileBuilder) =
|
||||
assert stmt.inside, "while stmt not inited"
|
||||
builder.addLineEndDedent("}")
|
||||
stmt.inside = false
|
||||
|
||||
template addWhileStmt(builder: var Builder, cond: Snippet, body: typed) =
|
||||
builder.add("while (")
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
proc addLabel(builder: var Builder, name: TLabel) =
|
||||
builder.add(name)
|
||||
builder.addLineEnd(": ;")
|
||||
|
||||
proc addReturn(builder: var Builder) =
|
||||
builder.addLineEnd("return;")
|
||||
|
||||
proc addReturn(builder: var Builder, value: Snippet) =
|
||||
builder.add("return ")
|
||||
builder.add(value)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addGoto(builder: var Builder, label: TLabel) =
|
||||
builder.add("goto ")
|
||||
builder.add(label)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addComputedGoto(builder: var Builder, value: Snippet) =
|
||||
builder.add("goto *")
|
||||
builder.add(value)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addIncr(builder: var Builder, val: Snippet) =
|
||||
builder.add(val)
|
||||
builder.addLineEnd("++;")
|
||||
|
||||
proc addDecr(builder: var Builder, val: Snippet) =
|
||||
builder.add(val)
|
||||
builder.addLineEnd("--;")
|
||||
|
||||
proc addInPlaceOp(builder: var Builder, binOp: TypedBinaryOp, t: Snippet, a, b: Snippet) =
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(typedBinaryOperators[binOp])
|
||||
builder.add("= ")
|
||||
builder.add(b)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addInPlaceOp(builder: var Builder, binOp: UntypedBinaryOp, a, b: Snippet) =
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(untypedBinaryOperators[binOp])
|
||||
builder.add("= ")
|
||||
builder.add(b)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc cInPlaceOp(binOp: TypedBinaryOp, t: Snippet, a, b: Snippet): Snippet =
|
||||
result = ""
|
||||
result.add(a)
|
||||
result.add(' ')
|
||||
result.add(typedBinaryOperators[binOp])
|
||||
result.add("= ")
|
||||
result.add(b)
|
||||
result.add(";\n")
|
||||
|
||||
proc cInPlaceOp(binOp: UntypedBinaryOp, a, b: Snippet): Snippet =
|
||||
result = ""
|
||||
result.add(a)
|
||||
result.add(' ')
|
||||
result.add(untypedBinaryOperators[binOp])
|
||||
result.add("= ")
|
||||
result.add(b)
|
||||
result.add(";\n")
|
||||
|
||||
template addCPragma(builder: var Builder, val: Snippet) =
|
||||
builder.addNewline()
|
||||
builder.add("#pragma ")
|
||||
builder.add(val)
|
||||
builder.addNewline()
|
||||
|
||||
proc addDiscard(builder: var Builder, val: Snippet) =
|
||||
builder.add("(void)")
|
||||
builder.add(val)
|
||||
builder.addLineEnd(";")
|
||||
@@ -84,21 +84,20 @@ proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
|
||||
let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor)
|
||||
var op = initLocExpr(p, newSymNode(dtor))
|
||||
var callee = rdLoc(op)
|
||||
let destroyArg =
|
||||
if dtor.typ.firstParamType.kind == tyVar:
|
||||
cAddr(rdLoc(tmp))
|
||||
let destroy = if dtor.typ.firstParamType.kind == tyVar:
|
||||
callee & "(&" & rdLoc(tmp) & ")"
|
||||
else:
|
||||
rdLoc(tmp)
|
||||
let destroy = cCall(callee, destroyArg)
|
||||
callee & "(" & rdLoc(tmp) & ")"
|
||||
raiseExitCleanup(p, destroy)
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
result: var Builder, call: var CallBuilder) =
|
||||
callee, params: Rope) =
|
||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||
genLineDir(p, ri)
|
||||
var pl = callee & "(" & params
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if typ.returnType != nil:
|
||||
@@ -107,6 +106,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
# 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':
|
||||
@@ -114,39 +114,33 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
discard "resetLoc(p, d)"
|
||||
let rad = addrLoc(p.config, d)
|
||||
result.addArgument(call):
|
||||
result.add(rad)
|
||||
result.finishCallBuilder(call)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(result))
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add(");\n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
let ratmp = addrLoc(p.config, tmp)
|
||||
result.addArgument(call):
|
||||
result.add(ratmp)
|
||||
result.finishCallBuilder(call)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(result))
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add(");\n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
result.finishCallBuilder(call)
|
||||
pl.add(")")
|
||||
if p.module.compileToCpp:
|
||||
if lfSingleUse in d.flags:
|
||||
# do not generate spurious temporaries for C++! For C we're better off
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
d.snippet = extract(result)
|
||||
d.snippet = pl
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
|
||||
d = getTempCpp(p, typ.returnType, extract(result))
|
||||
if d.k == locNone and p.splitDecls == 0:
|
||||
d = getTempCpp(p, typ.returnType, pl)
|
||||
else:
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
var list = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.snippet = extract(result)
|
||||
list.snippet = pl
|
||||
genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
@@ -157,7 +151,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.snippet = extract(result)
|
||||
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)):
|
||||
@@ -165,16 +159,15 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
var list = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.snippet = extract(result)
|
||||
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:
|
||||
finishCallBuilder(result, call)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(result))
|
||||
pl.add(");\n")
|
||||
line(p, cpsStmts, pl)
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
|
||||
@@ -206,49 +199,49 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, a, b, c, ty)
|
||||
if prepareForMutation:
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let dest = getTypeDesc(p.module, destType)
|
||||
let ra = rdLoc(a)
|
||||
let rb = rdLoc(b)
|
||||
let rc = rdLoc(c)
|
||||
let lengthExpr = cOp(Add, NimInt, cOp(Sub, NimInt, rc, rb), cIntValue(1))
|
||||
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
||||
case ty.kind
|
||||
of tyArray:
|
||||
let first = toInt64(firstOrd(p.config, ty))
|
||||
if first == 0:
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, rb)), lengthExpr)
|
||||
result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
else:
|
||||
let lit = cIntLiteral(first)
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, cOp(Sub, NimInt, rb, lit))), lengthExpr)
|
||||
var lit = newRopeAppender()
|
||||
intLiteral(first, lit)
|
||||
result = ("($4*)($1)+(($2)-($3))" %
|
||||
[rdLoc(a), rdLoc(b), lit, dest],
|
||||
lengthExpr)
|
||||
of tyOpenArray, tyVarargs:
|
||||
let data = if reifiedOpenArray(q[1]): dotField(ra, "Field0") else: ra
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, data, rb)), lengthExpr)
|
||||
if reifiedOpenArray(q[1]):
|
||||
result = ("($3*)($1.Field0)+($2)" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
else:
|
||||
result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
of tyUncheckedArray, tyCstring:
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, rb)), lengthExpr)
|
||||
result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
of tyString, tySequence:
|
||||
let atyp = skipTypes(a.t, abstractInst)
|
||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
|
||||
optSeqDestructors in p.config.globalOptions:
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
var val: Snippet
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
if atyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||
val = cDeref(ra)
|
||||
result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
|
||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
|
||||
lengthExpr)
|
||||
else:
|
||||
val = ra
|
||||
result = (
|
||||
cIfExpr(dataFieldAccessor(p, val),
|
||||
cCast(ptrType(dest), cOp(Add, NimInt, dataField(p, val), rb)),
|
||||
NimNil),
|
||||
lengthExpr)
|
||||
result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
|
||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
|
||||
lengthExpr)
|
||||
else:
|
||||
result = ("", "")
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
|
||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
|
||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||
var q = skipConv(n)
|
||||
var skipped = false
|
||||
while q.kind == nkStmtListExpr and q.len > 0:
|
||||
@@ -263,66 +256,42 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
|
||||
genStmts(p, q[i])
|
||||
q = q.lastSon
|
||||
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType)
|
||||
result.add(x)
|
||||
result.addArgumentSeparator()
|
||||
result.add(y)
|
||||
result.add x & ", " & y
|
||||
else:
|
||||
var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
|
||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
let ra = rdLoc(a)
|
||||
if reifiedOpenArray(n):
|
||||
if a.t.kind in {tyVar, tyLent}:
|
||||
result.add(derefField(ra, "Field0"))
|
||||
result.addArgumentSeparator()
|
||||
result.add(derefField(ra, "Field1"))
|
||||
result.add "$1->Field0, $1->Field1" % [rdLoc(a)]
|
||||
else:
|
||||
result.add(dotField(ra, "Field0"))
|
||||
result.addArgumentSeparator()
|
||||
result.add(dotField(ra, "Field1"))
|
||||
result.add "$1.Field0, $1.Field1" % [rdLoc(a)]
|
||||
else:
|
||||
result.add(ra)
|
||||
result.addArgumentSeparator()
|
||||
result.add(ra & "Len_0")
|
||||
result.add "$1, $1Len_0" % [rdLoc(a)]
|
||||
of tyString, tySequence:
|
||||
let ntyp = skipTypes(n.typ, abstractInst)
|
||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
|
||||
optSeqDestructors in p.config.globalOptions:
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||
let ra = a.rdLoc
|
||||
var t = TLoc(snippet: cDeref(ra))
|
||||
let lt = lenExpr(p, t)
|
||||
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
|
||||
result.addArgumentSeparator()
|
||||
result.add(lt)
|
||||
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)]
|
||||
else:
|
||||
let ra = a.rdLoc
|
||||
let la = lenExpr(p, a)
|
||||
result.add(cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
|
||||
result.addArgumentSeparator()
|
||||
result.add(la)
|
||||
result.add "($4) ? ($1$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
|
||||
of tyArray:
|
||||
let ra = rdLoc(a)
|
||||
result.add(ra)
|
||||
result.addArgumentSeparator()
|
||||
result.addIntValue(lengthOrd(p.config, a.t))
|
||||
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
|
||||
of tyPtr, tyRef:
|
||||
case elementType(a.t).kind
|
||||
of tyString, tySequence:
|
||||
let ra = a.rdLoc
|
||||
var t = TLoc(snippet: cDeref(ra))
|
||||
let lt = lenExpr(p, t)
|
||||
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
|
||||
result.addArgumentSeparator()
|
||||
result.add(lt)
|
||||
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:
|
||||
let ra = rdLoc(a)
|
||||
result.add(ra)
|
||||
result.addArgumentSeparator()
|
||||
result.addIntValue(lengthOrd(p.config, elementType(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))
|
||||
@@ -342,12 +311,11 @@ proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||
result = getTemp(p, a.lode.typ, needsInit=false)
|
||||
genAssignment(p, result, a, {})
|
||||
|
||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
|
||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
|
||||
var a = initLocExpr(p, n[0])
|
||||
let ra = withTmpIfNeeded(p, a, needsTmp).rdLoc
|
||||
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
|
||||
appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
|
||||
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
genArgStringToCString(p, n, result, needsTmp)
|
||||
@@ -388,11 +356,12 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; 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 = cCast(getTypeDesc(p.module, param.typ), rdCharLoc(a))
|
||||
a.snippet = "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
|
||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
||||
#assert result != nil
|
||||
|
||||
proc genArgNoParam(p: BProc, n: PNode; result: var Builder; needsTmp = false) =
|
||||
proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
genArgStringToCString(p, n, result, needsTmp)
|
||||
@@ -455,7 +424,7 @@ proc getPotentialReads(n: PNode; result: var seq[PNode]) =
|
||||
for s in n:
|
||||
getPotentialReads(s, result)
|
||||
|
||||
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Builder, argBuilder: var CallBuilder) =
|
||||
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
||||
# We must generate temporaries in cases like #14396
|
||||
# to keep the strict Left-To-Right evaluation
|
||||
var needTmp = newSeq[bool](ri.len - 1)
|
||||
@@ -477,22 +446,21 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Builder, argBuilder:
|
||||
# Optimization: don't use a temp, if we would only take the address anyway
|
||||
needTmp[i - 1] = false
|
||||
|
||||
var oldLen = result.len
|
||||
for i in 1..<ri.len:
|
||||
if i < typ.n.len:
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
let paramType = typ.n[i]
|
||||
if not paramType.typ.isCompileTimeOnly:
|
||||
var arg = newBuilder("")
|
||||
genArg(p, ri[i], paramType.sym, ri, arg, needTmp[i-1])
|
||||
if arg.buf.len != 0:
|
||||
result.addArgument(argBuilder):
|
||||
result.add(extract(arg))
|
||||
if oldLen != result.len:
|
||||
result.add(", ")
|
||||
oldLen = result.len
|
||||
genArg(p, ri[i], paramType.sym, ri, result, needTmp[i-1])
|
||||
else:
|
||||
var arg = newBuilder("")
|
||||
genArgNoParam(p, ri[i], arg, needTmp[i-1])
|
||||
if arg.buf.len != 0:
|
||||
result.addArgument(argBuilder):
|
||||
result.add(extract(arg))
|
||||
if oldLen != result.len:
|
||||
result.add(", ")
|
||||
oldLen = result.len
|
||||
genArgNoParam(p, ri[i], result, needTmp[i-1])
|
||||
|
||||
proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
|
||||
if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
|
||||
@@ -506,39 +474,21 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var typ = skipTypes(ri[0].typ, abstractInstOwned)
|
||||
assert(typ.kind == tyProc)
|
||||
|
||||
var params = newRopeAppender()
|
||||
genParams(p, ri, typ, params)
|
||||
|
||||
var callee = rdLoc(op)
|
||||
if p.hcrOn and ri[0].kind == nkSym:
|
||||
callee.addActualSuffixForHCR(p.module.module, ri[0].sym)
|
||||
|
||||
var res = newBuilder("")
|
||||
var call = initCallBuilder(res, callee)
|
||||
genParams(p, ri, typ, res, call)
|
||||
fixupCall(p, le, ri, d, res, call)
|
||||
fixupCall(p, le, ri, d, callee, params)
|
||||
|
||||
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
|
||||
template callProc(rp, params, pTyp: Snippet): Snippet =
|
||||
let e = dotField(rp, "ClE_0")
|
||||
let p = dotField(rp, "ClP_0")
|
||||
let eCall =
|
||||
# note `params` here is actually multiple params
|
||||
if params.len == 0:
|
||||
cCall(p, e)
|
||||
else:
|
||||
cCall(p, params, e)
|
||||
cIfExpr(e,
|
||||
eCall,
|
||||
cCall(cCast(pTyp, p), params))
|
||||
proc addComma(r: Rope): Rope =
|
||||
if r.len == 0: r else: r & ", "
|
||||
|
||||
template callIter(rp, params: Snippet): Snippet =
|
||||
# we know the env exists
|
||||
let e = dotField(rp, "ClE_0")
|
||||
let p = dotField(rp, "ClP_0")
|
||||
# note `params` here is actually multiple params
|
||||
if params.len == 0:
|
||||
cCall(p, e)
|
||||
else:
|
||||
cCall(p, params, e)
|
||||
const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
|
||||
const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
|
||||
|
||||
var op = initLocExpr(p, ri[0])
|
||||
|
||||
@@ -546,23 +496,20 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var typ = skipTypes(ri[0].typ, abstractInstOwned)
|
||||
assert(typ.kind == tyProc)
|
||||
|
||||
var params = newBuilder("")
|
||||
var argBuilder = default(CallBuilder) # not initCallBuilder, we just want the params
|
||||
genParams(p, ri, typ, params, argBuilder)
|
||||
var pl = newRopeAppender()
|
||||
genParams(p, ri, typ, pl)
|
||||
|
||||
template genCallPattern {.dirty.} =
|
||||
let rp = rdLoc(op)
|
||||
let pars = extract(params)
|
||||
p.s(cpsStmts).addStmt():
|
||||
if tfIterator in typ.flags:
|
||||
p.s(cpsStmts).add(callIter(rp, pars))
|
||||
else:
|
||||
p.s(cpsStmts).add(callProc(rp, pars, rawProc))
|
||||
if tfIterator in typ.flags:
|
||||
lineF(p, cpsStmts, PatIter & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
|
||||
else:
|
||||
lineF(p, cpsStmts, PatProc & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
|
||||
|
||||
let rawProc = getClosureType(p.module, typ, clHalf)
|
||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||
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':
|
||||
@@ -571,14 +518,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
discard "resetLoc(p, d)"
|
||||
params.addArgument(argBuilder):
|
||||
params.add(addrLoc(p.config, d))
|
||||
pl.add(addrLoc(p.config, d))
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
params.addArgument(argBuilder):
|
||||
params.add(addrLoc(p.config, tmp))
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
@@ -586,24 +531,20 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
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)
|
||||
let rp = rdLoc(op)
|
||||
let pars = extract(params)
|
||||
if tfIterator in typ.flags:
|
||||
list.snippet = callIter(rp, pars)
|
||||
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
else:
|
||||
list.snippet = callProc(rp, pars, 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.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
|
||||
let rp = rdLoc(op)
|
||||
let pars = extract(params)
|
||||
if tfIterator in typ.flags:
|
||||
list.snippet = callIter(rp, pars)
|
||||
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
else:
|
||||
list.snippet = callProc(rp, pars, rawProc)
|
||||
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
genAssignment(p, tmp, list, {})
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
@@ -611,8 +552,8 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder;
|
||||
argBuilder: var CallBuilder) =
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
|
||||
argsCounter: var int) =
|
||||
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'.
|
||||
@@ -621,17 +562,20 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder;
|
||||
if paramType.typ.isCompileTimeOnly:
|
||||
discard
|
||||
elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
|
||||
result.addArgument(argBuilder):
|
||||
genArgNoParam(p, ri[i][0], result)
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i][0], result)
|
||||
inc argsCounter
|
||||
else:
|
||||
result.addArgument(argBuilder):
|
||||
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
|
||||
inc argsCounter
|
||||
else:
|
||||
if tfVarargs notin typ.flags:
|
||||
localError(p.config, ri.info, "wrong argument count")
|
||||
else:
|
||||
result.addArgument(argBuilder):
|
||||
genArgNoParam(p, ri[i], result)
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i], result)
|
||||
inc argsCounter
|
||||
|
||||
discard """
|
||||
Dot call syntax in C++
|
||||
@@ -688,7 +632,7 @@ proc skipAddrDeref(node: PNode): PNode =
|
||||
else:
|
||||
result = node
|
||||
|
||||
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
|
||||
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.
|
||||
@@ -723,15 +667,15 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
|
||||
genArgNoParam(p, ri, result) #, typ.n[i].sym)
|
||||
result.add(".")
|
||||
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Builder) =
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) =
|
||||
var i = 0
|
||||
var j = 1
|
||||
while i < pat.len:
|
||||
case pat[i]
|
||||
of '@':
|
||||
var callBuilder = default(CallBuilder) # not init call builder
|
||||
var argsCounter = 0
|
||||
for k in j..<ri.len:
|
||||
genOtherArg(p, ri, k, typ, result, callBuilder)
|
||||
genOtherArg(p, ri, k, typ, result, argsCounter)
|
||||
inc i
|
||||
of '#':
|
||||
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
|
||||
@@ -741,11 +685,11 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
|
||||
if pat[i+1] == '+': genArgNoParam(p, ri[0], result)
|
||||
result.add("(")
|
||||
if 1 < ri.len:
|
||||
var callBuilder: CallBuilder = default(CallBuilder)
|
||||
genOtherArg(p, ri, 1, typ, result, callBuilder)
|
||||
var argsCounterB = 0
|
||||
genOtherArg(p, ri, 1, typ, result, argsCounterB)
|
||||
for k in j+1..<ri.len:
|
||||
var callBuilder: CallBuilder = default(CallBuilder)
|
||||
genOtherArg(p, ri, k, typ, result, callBuilder)
|
||||
var argsCounterB = 0
|
||||
genOtherArg(p, ri, k, typ, result, argsCounterB)
|
||||
result.add(")")
|
||||
else:
|
||||
localError(p.config, ri.info, "call expression expected for C++ pattern")
|
||||
@@ -759,15 +703,15 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
|
||||
genArgNoParam(p, arg, result)
|
||||
#result.add debugTree(arg, 0, 10)
|
||||
else:
|
||||
var callBuilder = default(CallBuilder) # not init call builder
|
||||
genOtherArg(p, ri, j, typ, result, callBuilder)
|
||||
var argsCounter = 0
|
||||
genOtherArg(p, ri, j, typ, result, argsCounter)
|
||||
inc j
|
||||
inc i
|
||||
of '\'':
|
||||
var idx, stars: int = 0
|
||||
if scanCppGenericSlot(pat, i, idx, stars):
|
||||
var t = resolveStarsInCppType(typ, idx, stars)
|
||||
if t == nil: result.add(CVoid)
|
||||
if t == nil: result.add("void")
|
||||
else: result.add(getTypeDesc(p.module, t))
|
||||
else:
|
||||
let start = i
|
||||
@@ -786,7 +730,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
let pat = $ri[0].sym.loc.snippet
|
||||
internalAssert p.config, pat.len > 0
|
||||
if pat.contains({'#', '(', '@', '\''}):
|
||||
var pl = newBuilder("")
|
||||
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)
|
||||
@@ -796,32 +740,32 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
d.snippet = extract(pl)
|
||||
d.snippet = pl
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
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.snippet = extract(pl)
|
||||
list.snippet = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
pl.add(";\n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var pl = newBuilder("")
|
||||
var pl = newRopeAppender()
|
||||
var argsCounter = 0
|
||||
if 1 < ri.len:
|
||||
genThisArg(p, ri, 1, typ, pl)
|
||||
pl.add(op.snippet)
|
||||
var res = newBuilder("")
|
||||
var call = initCallBuilder(res, extract(pl))
|
||||
var params = newRopeAppender()
|
||||
for i in 2..<ri.len:
|
||||
genOtherArg(p, ri, i, typ, res, call)
|
||||
fixupCall(p, le, ri, d, res, call)
|
||||
genOtherArg(p, ri, i, typ, params, argsCounter)
|
||||
fixupCall(p, le, ri, d, pl, params)
|
||||
|
||||
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
# generates a crappy ObjC call
|
||||
var op = initLocExpr(p, ri[0])
|
||||
var pl = newBuilder("[")
|
||||
var pl = "["
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
@@ -863,27 +807,24 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add("Result: ")
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add("]")
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
pl.add("];\n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add("]")
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
pl.add("];\n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add("]")
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, ri, OnUnknown)
|
||||
list.snippet = extract(pl)
|
||||
list.snippet = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add("]")
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
pl.add("];\n")
|
||||
line(p, cpsStmts, pl)
|
||||
|
||||
proc notYetAlive(n: PNode): bool {.inline.} =
|
||||
let r = getRoot(n)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,7 +40,7 @@ proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
|
||||
res.addVarWithTypeAndInitializer(AlwaysConst, name = tmp):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "Sup", typ = "TGenericSeq")
|
||||
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
|
||||
res.addArrayField(name = "data", elementType = "NIM_CHAR", len = s.len + 1)
|
||||
do:
|
||||
var strInit: StructInitializer
|
||||
res.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
@@ -48,16 +48,16 @@ proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
|
||||
var seqInit: StructInitializer
|
||||
res.addStructInitializer(seqInit, kind = siOrderedStruct):
|
||||
res.addField(seqInit, name = "len"):
|
||||
res.addIntValue(s.len)
|
||||
res.add(rope(s.len))
|
||||
res.addField(seqInit, name = "reserved"):
|
||||
res.add(cCast(NimInt, cOp(BitOr, NimUint, cCast(NimUint, cIntValue(s.len)), NimStrlitFlag)))
|
||||
res.add(cCast("NI", bitOr(cCast("NU", rope(s.len)), "NIM_STRLIT_FLAG")))
|
||||
res.addField(strInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
m.s[cfsStrData].add(res)
|
||||
|
||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Builder) =
|
||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
|
||||
if s.isNil:
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), "NIM_NIL"))
|
||||
else:
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var name: string = ""
|
||||
@@ -76,18 +76,18 @@ proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bo
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = result):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "cap", typ = NimInt)
|
||||
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
|
||||
res.addField(name = "cap", typ = "NI")
|
||||
res.addArrayField(name = "data", elementType = "NIM_CHAR", len = s.len + 1)
|
||||
do:
|
||||
var structInit: StructInitializer
|
||||
res.addStructInitializer(structInit, kind = siOrderedStruct):
|
||||
res.addField(structInit, name = "cap"):
|
||||
res.add(cOp(BitOr, NimInt, cIntValue(s.len), NimStrlitFlag))
|
||||
res.add(bitOr(rope(s.len), "NIM_STRLIT_FLAG"))
|
||||
res.addField(structInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
m.s[cfsStrData].add(res)
|
||||
|
||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Builder) =
|
||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var litName: string
|
||||
if id == m.labels:
|
||||
@@ -101,19 +101,20 @@ proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Builder
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
var res = newBuilder("")
|
||||
res.addVarWithInitializer(
|
||||
res.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = tmp,
|
||||
typ = "NimStringV2"):
|
||||
name = tmp):
|
||||
res.add("NimStringV2")
|
||||
do:
|
||||
var strInit: StructInitializer
|
||||
res.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
res.addField(strInit, name = "len"):
|
||||
res.addIntValue(n.strVal.len)
|
||||
res.add(rope(n.strVal.len))
|
||||
res.addField(strInit, name = "p"):
|
||||
res.add(cCast(ptrType("NimStrPayload"), cAddr(litName)))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
m.s[cfsStrData].add(res)
|
||||
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var pureLit: Rope
|
||||
if id == m.labels:
|
||||
@@ -127,7 +128,7 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Bu
|
||||
var strInit: StructInitializer
|
||||
result.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
result.addField(strInit, name = "len"):
|
||||
result.addIntValue(n.strVal.len)
|
||||
result.add(rope(n.strVal.len))
|
||||
result.addField(strInit, name = "p"):
|
||||
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
|
||||
|
||||
@@ -144,10 +145,10 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
else:
|
||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
||||
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Builder) =
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), "NIM_NIL"))
|
||||
|
||||
proc genStringLiteral(m: BModule; n: PNode; result: var Builder) =
|
||||
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: genStringLiteralV1(m, n, result)
|
||||
of 2: genStringLiteralV2(m, n, isConst = true, result)
|
||||
|
||||
@@ -27,28 +27,25 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
||||
if disc.loc.snippet == "": fillObjectFields(p.module, typ)
|
||||
if disc.loc.t == nil:
|
||||
internalError(p.config, n.info, "specializeResetN()")
|
||||
let discField = dotField(accessor, disc.loc.snippet)
|
||||
p.s(cpsStmts).addSwitchStmt(discField):
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
var caseBuilder: SwitchCaseBuilder
|
||||
p.s(cpsStmts).addSwitchCase(caseBuilder):
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(p, branch, caseBuilder)
|
||||
else:
|
||||
p.s(cpsStmts).addCaseElse(caseBuilder)
|
||||
do:
|
||||
specializeResetN(p, accessor, lastSon(branch), typ)
|
||||
p.s(cpsStmts).addBreak()
|
||||
specializeResetT(p, discField, disc.loc.t)
|
||||
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}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(p, branch)
|
||||
else:
|
||||
lineF(p, cpsStmts, "default:$n", [])
|
||||
specializeResetN(p, accessor, lastSon(branch), typ)
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
lineF(p, cpsStmts, "} $n", [])
|
||||
specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t)
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.snippet == "": fillObjectFields(p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(p.config, n.info, "specializeResetN()")
|
||||
specializeResetT(p, dotField(accessor, field.loc.snippet), 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) =
|
||||
@@ -61,8 +58,10 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
of tyArray:
|
||||
let arraySize = lengthOrd(p.config, typ.indexType)
|
||||
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
|
||||
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
|
||||
specializeResetT(p, subscript(accessor, i.snippet), typ.elementType)
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.snippet, arraySize])
|
||||
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
of tyObject:
|
||||
var x = typ.baseClass
|
||||
if x != nil: x = x.skipTypes(skipPtrs)
|
||||
@@ -71,34 +70,28 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i, a in typ.ikids:
|
||||
specializeResetT(p, dotField(accessor, "Field" & $i), a)
|
||||
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
|
||||
|
||||
of tyString, tyRef, tySequence:
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
|
||||
cCast(ptrType(CPointer), cAddr(accessor)),
|
||||
NimNil)
|
||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
|
||||
|
||||
of tyProc:
|
||||
if typ.callConv == ccClosure:
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
|
||||
cCast(ptrType(CPointer), cAddr(dotField(accessor, "ClE_0"))),
|
||||
NimNil)
|
||||
p.s(cpsStmts).addFieldAssignment(accessor, "ClP_0", NimNil)
|
||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1.ClE_0, NIM_NIL);$n", [accessor])
|
||||
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
|
||||
else:
|
||||
p.s(cpsStmts).addAssignment(accessor, NimNil)
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
|
||||
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
|
||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
||||
p.s(cpsStmts).addAssignment(accessor, NimNil)
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
of tySet:
|
||||
case mapSetType(p.config, typ)
|
||||
of ctArray:
|
||||
let t = getTypeDesc(p.module, typ)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
|
||||
accessor,
|
||||
cSizeof(t))
|
||||
lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
|
||||
[accessor, getTypeDesc(p.module, typ)])
|
||||
of ctInt8, ctInt16, ctInt32, ctInt64:
|
||||
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
|
||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||
else:
|
||||
raiseAssert "unexpected set type kind"
|
||||
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,12 +19,9 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
|
||||
if emulatedThreadVars(p.config) and threadVarAccessed notin p.flags:
|
||||
p.flags.incl threadVarAccessed
|
||||
incl p.module.flags, usesThreadVars
|
||||
p.procSec(cpsLocals).addVar(kind = Local,
|
||||
name = "NimTV_",
|
||||
typ = ptrType("NimThreadVars"))
|
||||
p.procSec(cpsInit).addAssignment("NimTV_",
|
||||
cCast(ptrType("NimThreadVars"),
|
||||
cCall(cgsymValue(p.module, "GetThreadLocalVars"))))
|
||||
p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", [])
|
||||
p.procSec(cpsInit).add(
|
||||
ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n", []))
|
||||
|
||||
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||
if emulatedThreadVars(m.config):
|
||||
@@ -33,32 +30,34 @@ 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.addField(name = s.loc.snippet, typ = getTypeDesc(m, s.loc.t))
|
||||
m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.snippet])
|
||||
else:
|
||||
let vis =
|
||||
if isExtern: Extern
|
||||
elif lfExportLib in s.loc.flags: ExportLibVar
|
||||
else: Private
|
||||
m.s[cfsVars].addVar(m, s,
|
||||
name = s.loc.snippet,
|
||||
typ = getTypeDesc(m, s.loc.t),
|
||||
kind = Threadvar,
|
||||
visibility = vis)
|
||||
if isExtern: m.s[cfsVars].add("extern ")
|
||||
elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
|
||||
else: m.s[cfsVars].add("N_LIB_PRIVATE ")
|
||||
if optThreads in m.config.globalOptions:
|
||||
let sym = s.typ.sym
|
||||
if sym != nil and sfCppNonPod in sym.flags:
|
||||
m.s[cfsVars].add("NIM_THREAD_LOCAL ")
|
||||
else: m.s[cfsVars].add("NIM_THREADVAR ")
|
||||
m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
|
||||
m.s[cfsVars].addf(" $1;$n", [s.loc.snippet])
|
||||
|
||||
proc generateThreadLocalStorage(m: BModule) =
|
||||
if m.g.nimtv.buf.len != 0 and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
|
||||
finishTypeDescriptions(m)
|
||||
m.s[cfsSeqTypes].addTypedef(name = "NimThreadVars"):
|
||||
m.s[cfsSeqTypes].addSimpleStruct(m, name = "", baseType = ""):
|
||||
m.s[cfsSeqTypes].add(extract(m.g.nimtv))
|
||||
var typedef = newBuilder("")
|
||||
typedef.addTypedef(name = "NimThreadVars"):
|
||||
typedef.addSimpleStruct(m, name = "", baseType = ""):
|
||||
typedef.add(m.g.nimtv)
|
||||
m.s[cfsSeqTypes].add(typedef)
|
||||
|
||||
proc generateThreadVarsSize(m: BModule) =
|
||||
if m.g.nimtv.buf.len != 0:
|
||||
if m.g.nimtv != "":
|
||||
let externc = if m.config.backend == backendCpp or
|
||||
sfCompileToCpp in m.module.flags: ExternC
|
||||
else: None
|
||||
m.s[cfsProcs].addDeclWithVisibility(externc):
|
||||
m.s[cfsProcs].addProcHeader("NimThreadVarsSize", NimInt, cProcParams())
|
||||
m.s[cfsProcs].finishProcHeaderWithBody():
|
||||
m.s[cfsProcs].addReturn(cCast(NimInt, cSizeof("NimThreadVars")))
|
||||
sfCompileToCpp in m.module.flags: "extern \"C\" "
|
||||
else: ""
|
||||
m.s[cfsProcs].addf(
|
||||
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
|
||||
[externc.rope])
|
||||
|
||||
@@ -16,16 +16,13 @@ type
|
||||
p: BProc
|
||||
visitorFrmt: string
|
||||
|
||||
const
|
||||
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
|
||||
|
||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
|
||||
proc genCaseRange(p: BProc, branch: PNode, info: var SwitchCaseBuilder)
|
||||
proc genCaseRange(p: BProc, branch: PNode)
|
||||
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
|
||||
|
||||
proc visit(p: BProc, data, visitor: Snippet) =
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisit"),
|
||||
cCast(CPointer, data),
|
||||
visitor)
|
||||
|
||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
typ: PType) =
|
||||
if n == nil: return
|
||||
@@ -40,32 +37,29 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
||||
if disc.loc.t == nil:
|
||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
let discField = dotField(accessor, disc.loc.snippet)
|
||||
p.s(cpsStmts).addSwitchStmt(discField):
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
var caseBuilder: SwitchCaseBuilder
|
||||
p.s(cpsStmts).addSwitchCase(caseBuilder):
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch, caseBuilder)
|
||||
else:
|
||||
p.s(cpsStmts).addCaseElse(caseBuilder)
|
||||
do:
|
||||
genTraverseProc(c, accessor, lastSon(branch), typ)
|
||||
p.s(cpsStmts).addBreak()
|
||||
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}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch)
|
||||
else:
|
||||
lineF(p, cpsStmts, "default:$n", [])
|
||||
genTraverseProc(c, accessor, lastSon(branch), typ)
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
lineF(p, cpsStmts, "} $n", [])
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
genTraverseProc(c, dotField(accessor, field.loc.snippet), 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.} =
|
||||
if not m.compileToCpp:
|
||||
result = dotField(accessor, "Sup")
|
||||
result = "$1.Sup" % [accessor]
|
||||
else:
|
||||
result = accessor
|
||||
|
||||
@@ -82,14 +76,16 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
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)
|
||||
var oldLen, newLen: int
|
||||
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
|
||||
oldLen = p.s(cpsStmts).buf.len
|
||||
genTraverseProc(c, subscript(accessor, i.snippet), typ.elementType)
|
||||
newLen = p.s(cpsStmts).buf.len
|
||||
if oldLen == newLen:
|
||||
freeze oldCode
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.snippet, arraySize])
|
||||
let oldLen = p.s(cpsStmts).len
|
||||
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:
|
||||
var x = typ.baseClass
|
||||
if x != nil: x = x.skipTypes(skipPtrs)
|
||||
@@ -98,25 +94,23 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i, a in typ.ikids:
|
||||
genTraverseProc(c, dotField(accessor, "Field" & $i), a)
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a)
|
||||
of tyRef:
|
||||
visit(p, accessor, c.visitorFrmt)
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
of tySequence:
|
||||
if optSeqDestructors notin c.p.module.config.globalOptions:
|
||||
visit(p, accessor, c.visitorFrmt)
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
elif containsGarbageCollectedRef(typ.elementType):
|
||||
# destructor based seqs are themselves not traced but their data is, if
|
||||
# they contain a GC'ed type:
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisitSeq"),
|
||||
cCast(CPointer, accessor),
|
||||
c.visitorFrmt)
|
||||
lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
|
||||
#genTraverseProcSeq(c, accessor, typ)
|
||||
of tyString:
|
||||
if tfHasAsgn notin typ.flags:
|
||||
visit(p, accessor, c.visitorFrmt)
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
of tyProc:
|
||||
if typ.callConv == ccClosure:
|
||||
visit(p, dotField(accessor, "ClE_0"), c.visitorFrmt)
|
||||
lineCg(p, cpsStmts, visitorFrmt, [ropecg(c.p.module, "$1.ClE_0", [accessor]), c.visitorFrmt])
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -125,17 +119,18 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
assert typ.kind == tySequence
|
||||
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
|
||||
var oldCode = p.s(cpsStmts)
|
||||
var oldLen, newLen: int
|
||||
freeze oldCode
|
||||
var a = TLoc(snippet: accessor)
|
||||
let le = lenExpr(c.p, a)
|
||||
|
||||
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), le):
|
||||
oldLen = p.s(cpsStmts).buf.len
|
||||
genTraverseProc(c, subscript(dataField(c.p, accessor), i.snippet), typ.elementType)
|
||||
newLen = p.s(cpsStmts).buf.len
|
||||
if newLen == oldLen:
|
||||
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.snippet, lenExpr(c.p, a)])
|
||||
let oldLen = p.s(cpsStmts).len
|
||||
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
|
||||
else:
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
|
||||
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
var p = newProc(nil, m)
|
||||
@@ -144,10 +139,11 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
hcrOn = m.hcrOn
|
||||
typ = origTyp.skipTypes(abstractInstOwned)
|
||||
markerName = if hcrOn: result & "_actual" else: result
|
||||
header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [markerName]
|
||||
t = getTypeDesc(m, typ)
|
||||
|
||||
p.s(cpsLocals).addVar(kind = Local, name = "a", typ = t)
|
||||
p.s(cpsInit).addAssignment("a", cCast(t, "p"))
|
||||
lineF(p, cpsLocals, "$1 a;$n", [t])
|
||||
lineF(p, cpsInit, "a = ($1)p;$n", [t])
|
||||
|
||||
var c = TTraversalClosure(p: p,
|
||||
visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
|
||||
@@ -161,40 +157,18 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
# C's arrays are broken beyond repair:
|
||||
genTraverseProc(c, "a".rope, typ.elementType)
|
||||
else:
|
||||
genTraverseProc(c, cDeref("a"), typ.elementType)
|
||||
genTraverseProc(c, "(*a)".rope, typ.elementType)
|
||||
|
||||
var headerBuilder = newBuilder("")
|
||||
headerBuilder.addProcHeaderWithParams(ccNimCall, markerName, CVoid):
|
||||
var paramBuilder: ProcParamBuilder
|
||||
headerBuilder.addProcParams(paramBuilder):
|
||||
headerBuilder.addParam(paramBuilder, name = "p", typ = CPointer)
|
||||
headerBuilder.addParam(paramBuilder, name = "op", typ = NimInt)
|
||||
let header = extract(headerBuilder)
|
||||
let generatedProc = "$1 {$n$2$3$4}\n" %
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||
|
||||
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcHeaders].add(header)
|
||||
m.s[cfsProcHeaders].finishProcHeaderAsProto()
|
||||
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcs].add(header)
|
||||
m.s[cfsProcs].finishProcHeaderWithBody():
|
||||
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsInit)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
|
||||
m.s[cfsProcHeaders].addf("$1;\n", [header])
|
||||
m.s[cfsProcs].add(generatedProc)
|
||||
|
||||
if hcrOn:
|
||||
var desc = newBuilder("")
|
||||
var unnamedParamBuilder: ProcParamBuilder
|
||||
desc.addProcParams(unnamedParamBuilder):
|
||||
desc.addUnnamedParam(unnamedParamBuilder, CPointer)
|
||||
desc.addUnnamedParam(unnamedParamBuilder, NimInt)
|
||||
let unnamedParams = extract(desc)
|
||||
m.s[cfsProcHeaders].addProcVar(ccNimCall, result, unnamedParams, CVoid)
|
||||
m.s[cfsDynLibInit].addAssignmentWithValue(result):
|
||||
m.s[cfsDynLibInit].addCast(procPtrTypeUnnamed(ccNimCall, CVoid, unnamedParams)):
|
||||
m.s[cfsDynLibInit].addCall("hcrRegisterProc",
|
||||
getModuleDllPath(m),
|
||||
'"' & result & '"',
|
||||
cCast(CPointer, markerName))
|
||||
m.s[cfsProcHeaders].addf("N_NIMCALL_PTR(void, $1)(void*, NI);\n", [result])
|
||||
m.s[cfsDynLibInit].addf("\t$1 = (N_NIMCALL_PTR(void, )(void*, NI)) hcrRegisterProc($3, \"$1\", (void*)$2);\n",
|
||||
[result, markerName, getModuleDllPath(m)])
|
||||
|
||||
proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
|
||||
discard genTypeInfoV1(m, s.loc.t, info)
|
||||
@@ -205,28 +179,17 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
|
||||
|
||||
if sfThread in s.flags and emulatedThreadVars(m.config):
|
||||
accessThreadLocalVar(p, s)
|
||||
sLoc = derefField("NimTV_", sLoc)
|
||||
sLoc = "NimTV_->" & sLoc
|
||||
|
||||
var c = TTraversalClosure(p: p,
|
||||
visitorFrmt: cIntValue(0) # "#nimGCvisit((void*)$1, 0);$n"
|
||||
visitorFrmt: "0" # "#nimGCvisit((void*)$1, 0);$n"
|
||||
)
|
||||
|
||||
let header = "static N_NIMCALL(void, $1)(void)" % [result]
|
||||
genTraverseProc(c, sLoc, s.loc.t)
|
||||
|
||||
var headerBuilder = newBuilder("")
|
||||
headerBuilder.addProcHeaderWithParams(ccNimCall, result, CVoid):
|
||||
var paramBuilder: ProcParamBuilder
|
||||
headerBuilder.addProcParams(paramBuilder):
|
||||
# (void)
|
||||
discard
|
||||
let header = extract(headerBuilder)
|
||||
let generatedProc = "$1 {$n$2$3$4}$n" %
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||
|
||||
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcHeaders].add(header)
|
||||
m.s[cfsProcHeaders].finishProcHeaderAsProto()
|
||||
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcs].add(header)
|
||||
m.s[cfsProcs].finishProcHeaderWithBody():
|
||||
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsInit)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
|
||||
m.s[cfsProcHeaders].addf("$1;$n", [header])
|
||||
m.s[cfsProcs].add(generatedProc)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1422
compiler/cgen.nim
1422
compiler/cgen.nim
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, ropes, options,
|
||||
lineinfos, pathutils, modulegraphs, cbuilderbase
|
||||
ndi, lineinfos, pathutils, modulegraphs
|
||||
|
||||
import std/[intsets, tables, sets]
|
||||
|
||||
@@ -43,12 +43,12 @@ type
|
||||
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
|
||||
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
|
||||
ctCString
|
||||
TCFileSections* = array[TCFileSection, Builder] # represents a generated C file
|
||||
TCFileSections* = array[TCFileSection, Rope] # represents a generated C file
|
||||
TCProcSection* = enum # the sections a generated C proc consists of
|
||||
cpsLocals, # section of local variables for C proc
|
||||
cpsInit, # section for init of variables for C proc
|
||||
cpsStmts # section of local statements for C proc
|
||||
TCProcSections* = array[TCProcSection, Builder] # represents a generated C proc
|
||||
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
|
||||
BModule* = ref TCGen
|
||||
BProc* = ref TCProc
|
||||
TBlock* = object
|
||||
@@ -115,7 +115,7 @@ type
|
||||
# computing alive data on our own.
|
||||
|
||||
BModuleList* = ref object of RootObj
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Builder
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||
mapping*: Rope # the generated mapping file (if requested)
|
||||
modules*: seq[BModule] # list of all compiled modules
|
||||
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
||||
@@ -127,7 +127,7 @@ type
|
||||
graph*: ModuleGraph
|
||||
strVersion*, seqVersion*: int # version of the string/seq implementation to use
|
||||
|
||||
nimtv*: Builder # Nim thread vars; the struct body
|
||||
nimtv*: Rope # Nim thread vars; the struct body
|
||||
nimtvDeps*: seq[PType] # type deps: every module needs whole struct
|
||||
nimtvDeclared*: IntSet # so that every var/field exists only once
|
||||
# in the struct
|
||||
@@ -161,18 +161,18 @@ type
|
||||
typeInfoMarkerV2*: TypeCache
|
||||
initProc*: BProc # code for init procedure
|
||||
preInitProc*: BProc # code executed before the init proc
|
||||
hcrCreateTypeInfosProc*: Builder # type info globals are in here when HCR=on
|
||||
hcrCreateTypeInfosProc*: Rope # type info globals are in here when HCR=on
|
||||
inHcrInitGuard*: bool # We are currently within a HCR reloading guard.
|
||||
hcrInitGuard*: IfBuilder
|
||||
typeStack*: TTypeSeq # used for type generation
|
||||
dataCache*: TNodeTable
|
||||
typeNodes*, nimTypes*: int # used for type info generation
|
||||
typeNodesName*, nimTypesName*: Rope # used for type info generation
|
||||
labels*: Natural # for generating unique module-scope names
|
||||
extensionLoaders*: array['0'..'9', Builder] # special procs for the
|
||||
extensionLoaders*: array['0'..'9', Rope] # special procs for the
|
||||
# OpenGL wrapper
|
||||
sigConflicts*: CountTable[SigHash]
|
||||
g*: BModuleList
|
||||
ndi*: NdiFile
|
||||
|
||||
template config*(m: BModule): ConfigRef = m.g.config
|
||||
template config*(p: BProc): ConfigRef = p.module.g.config
|
||||
@@ -182,18 +182,18 @@ proc includeHeader*(this: BModule; header: string) =
|
||||
if not this.headerFiles.contains header:
|
||||
this.headerFiles.add header
|
||||
|
||||
proc s*(p: BProc, s: TCProcSection): var Builder {.inline.} =
|
||||
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
# section in the current block
|
||||
result = p.blocks[^1].sections[s]
|
||||
|
||||
proc procSec*(p: BProc, s: TCProcSection): var Builder {.inline.} =
|
||||
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
# top level proc sections
|
||||
result = p.blocks[0].sections[s]
|
||||
|
||||
proc initBlock*(): TBlock =
|
||||
result = TBlock()
|
||||
for i in low(result.sections)..high(result.sections):
|
||||
result.sections[i] = newBuilder("")
|
||||
result.sections[i] = newRopeAppender()
|
||||
|
||||
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
result = BProc(
|
||||
|
||||
@@ -203,14 +203,13 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
else: invalidCmdLineOption(conf, pass, orig, info)
|
||||
|
||||
let isSomeHint = state in {wHint, wHintAsError}
|
||||
let isSomeWarning = state in {wWarning, wWarningAsError}
|
||||
template findNote(noteMin, noteMax, name) =
|
||||
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
|
||||
let x = findStr(noteMin, noteMax, id, errUnknown)
|
||||
if x != errUnknown: notes = {TNoteKind(x)}
|
||||
else:
|
||||
if isSomeHint or isSomeWarning:
|
||||
message(conf, info, warnUnknownNotes, "unknown $#: $#" % [name, id])
|
||||
if isSomeHint:
|
||||
message(conf, info, hintUnknownHint, id)
|
||||
else:
|
||||
localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
case id.normalize
|
||||
@@ -920,12 +919,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
|
||||
conf.maxLoopIterationsVM = value
|
||||
of "maxcalldepthvm":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
var value: int = 2_000
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
if value <= 0: localError(conf, info, "maxCallDepthVM must be a positive integer greater than zero")
|
||||
conf.maxCallDepthVM = value
|
||||
of "errormax":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
# Note: `nim check` (etc) can overwrite this.
|
||||
|
||||
@@ -78,7 +78,6 @@ type
|
||||
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
|
||||
## cannot be fixed that easily.
|
||||
## Thus we special case it here.
|
||||
concpt: PType
|
||||
|
||||
proc existingBinding(m: MatchCon; key: PType): PType =
|
||||
## checks if we bound the type variable 'key' already to some
|
||||
@@ -89,52 +88,22 @@ proc existingBinding(m: MatchCon; key: PType): PType =
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||
|
||||
proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool
|
||||
|
||||
proc acceptsAllTypes(t: PType): bool=
|
||||
result = false
|
||||
if t.kind == tyAnything:
|
||||
result = true
|
||||
elif t.kind == tyGenericParam:
|
||||
if tfImplicitTypeParam in t.flags:
|
||||
result = true
|
||||
if not t.hasElementType or t.elementType.kind == tyNone:
|
||||
result = true
|
||||
|
||||
proc matchKids(c: PContext; f, a: PType; m: var MatchCon, start=0): bool=
|
||||
result = true
|
||||
for i in start..<f.kidsLen - ord(f.kind == tyGenericInst):
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
|
||||
proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
|
||||
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
## The heart of the concept matching process. 'f' is the formal parameter of some
|
||||
## routine inside the concept that we're looking for. 'a' is the formal parameter
|
||||
## of a routine that might match.
|
||||
const
|
||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
|
||||
|
||||
var a = ao
|
||||
|
||||
case a.kind
|
||||
of tyGenericParam:
|
||||
let binding = m.existingBinding(a)
|
||||
if binding != nil:
|
||||
a = binding
|
||||
else:
|
||||
discard
|
||||
|
||||
case f.kind
|
||||
of tyAlias:
|
||||
result = matchType(c, f.skipModifier, a, m)
|
||||
of tyTypeDesc:
|
||||
if isSelf(f):
|
||||
if m.magic in {mArrPut, mArrGet}:
|
||||
result = false
|
||||
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
|
||||
m.inferred.add((a, last m.potentialImplementation))
|
||||
result = true
|
||||
else:
|
||||
result = matchType(c, a, m.potentialImplementation, m)
|
||||
#let oldLen = m.inferred.len
|
||||
result = matchType(c, a, m.potentialImplementation, m)
|
||||
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
|
||||
#m.inferred.setLen oldLen
|
||||
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
else:
|
||||
if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
|
||||
if f.hasElementType:
|
||||
@@ -143,11 +112,14 @@ proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
|
||||
result = true # both lack it
|
||||
else:
|
||||
result = false
|
||||
|
||||
of tyGenericInvocation:
|
||||
result = false
|
||||
if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
|
||||
if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
|
||||
result = matchKids(c, f, a, m, start=FirstGenericParamAt)
|
||||
for i in FirstGenericParamAt ..< f.kidsLen:
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
return true
|
||||
of tyGenericParam:
|
||||
let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
|
||||
if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
|
||||
@@ -164,7 +136,7 @@ proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
|
||||
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, " ", last ak
|
||||
when logBindings: echo "B adding ", f, " ", lastSon ak
|
||||
m.inferred.add((f, last ak))
|
||||
result = true
|
||||
else:
|
||||
@@ -179,6 +151,7 @@ proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
|
||||
else:
|
||||
result = false
|
||||
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
|
||||
of tyVar, tySink, tyLent, tyOwned:
|
||||
# modifiers in the concept must be there in the actual implementation
|
||||
# too but not vice versa.
|
||||
@@ -197,43 +170,22 @@ proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
|
||||
result = ak.kind == f.kind or ak.kind == tyOrdinal or
|
||||
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
|
||||
of tyConcept:
|
||||
if a.kind == tyConcept and f.n == a.n:
|
||||
result = true
|
||||
elif m.concpt.size == szIllegalRecursion:
|
||||
result = false
|
||||
else:
|
||||
let oldLen = m.inferred.len
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = a
|
||||
m.concpt.size = szIllegalRecursion
|
||||
let oldConcept = m.concpt
|
||||
m.concpt = f
|
||||
result = conceptMatchNode(c, f.n.lastSon, m)
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
m.concpt = oldConcept
|
||||
m.concpt.size = szUnknownSize
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
of tyGenericBody:
|
||||
var ak = a
|
||||
if a.kind == tyGenericBody:
|
||||
ak = last(a)
|
||||
result = matchType(c, last(f), ak, m)
|
||||
of tyCompositeTypeClass:
|
||||
var ak = if a.kind == tyCompositeTypeClass: a.last else: a
|
||||
result = matchType(c, last(f), ak, m)
|
||||
let oldLen = m.inferred.len
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = a
|
||||
result = conceptMatchNode(c, f.n.lastSon, m)
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
|
||||
tyGenericInst:
|
||||
# ^ XXX Rewrite this logic, it's more complex than it needs to be.
|
||||
if f.kind == tyArray and f.kidsLen == 3:
|
||||
# XXX: this is a work-around!
|
||||
# system.nim creates these for the magic array typeclass
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
|
||||
result = matchKids(c, f, ak, m)
|
||||
result = false
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
|
||||
for i in 0..<ak.kidsLen:
|
||||
if not matchType(c, f[i], ak[i], m): return false
|
||||
return true
|
||||
of tyOr:
|
||||
let oldLen = m.inferred.len
|
||||
if a.kind == tyOr:
|
||||
@@ -269,16 +221,6 @@ proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
|
||||
result = true
|
||||
of tyOrdinal:
|
||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
||||
of tyStatic:
|
||||
result = false
|
||||
var scomp = f.base
|
||||
if scomp.kind == tyGenericParam:
|
||||
if f.base.kidsLen > 0:
|
||||
scomp = scomp.base
|
||||
if a.kind == tyStatic:
|
||||
result = matchType(c, scomp, a.base, m)
|
||||
else:
|
||||
result = matchType(c, scomp, a, m)
|
||||
else:
|
||||
result = false
|
||||
|
||||
@@ -329,8 +271,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
|
||||
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
|
||||
## 'n' is the nkProcDef or similar from the concept that we try to match.
|
||||
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
|
||||
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
|
||||
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
|
||||
for candidate in candidates:
|
||||
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
||||
m.magic = candidate.magic
|
||||
@@ -377,9 +318,7 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable
|
||||
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
|
||||
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
|
||||
## we do not add any bindings at all on an unsuccessful match!
|
||||
if arg.containsUnresolvedType:
|
||||
return false
|
||||
var m = MatchCon(inferred: @[], potentialImplementation: arg, concpt: concpt)
|
||||
var m = MatchCon(inferred: @[], potentialImplementation: arg)
|
||||
result = conceptMatchNode(c, concpt.n.lastSon, m)
|
||||
if result:
|
||||
for (a, b) in m.inferred:
|
||||
|
||||
@@ -159,7 +159,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasEnsureMove")
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimUseStrictDefs") # deadcode
|
||||
defineSymbol("nimUseStrictDefs")
|
||||
defineSymbol("nimHasNolineTooLong")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
@@ -171,4 +171,3 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasJsNoLambdaLifting")
|
||||
defineSymbol("nimHasDefaultFloatRoundtrip")
|
||||
defineSymbol("nimHasXorSet")
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
|
||||
for i in 0..n.safeLen-1:
|
||||
importForwarded(c, n[i], exceptSet, fromMod, importSet)
|
||||
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, trackUnusedImport: bool): PSym =
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
||||
result = realModule
|
||||
template createModuleAliasImpl(ident): untyped =
|
||||
createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
|
||||
@@ -246,10 +246,8 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, track
|
||||
result = createModuleAliasImpl(realModule.name)
|
||||
if importHidden:
|
||||
result.options.incl optImportHidden
|
||||
let moduleIdent = if n.kind in {nkInfix, nkImportAs}: n[^1] else: n
|
||||
result.info = moduleIdent.info
|
||||
if trackUnusedImport:
|
||||
c.unusedImports.add((result, result.info))
|
||||
let moduleIdent = if n.kind == nkInfix: n[^1] else: n
|
||||
c.unusedImports.add((result, moduleIdent.info))
|
||||
c.importModuleMap[result.id] = realModule.id
|
||||
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
|
||||
|
||||
@@ -290,11 +288,10 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
toFullPath(c.config, c.graph.importStack[i+1])
|
||||
c.recursiveDep = err
|
||||
|
||||
let trackUnusedImport = warnUnusedImportX in c.config.notes
|
||||
var realModule: PSym
|
||||
discard pushOptionEntry(c)
|
||||
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
|
||||
result = importModuleAs(c, n, realModule, transf.importHidden, trackUnusedImport)
|
||||
result = importModuleAs(c, n, realModule, transf.importHidden)
|
||||
popOptionEntry(c)
|
||||
|
||||
#echo "set back to ", L
|
||||
@@ -338,7 +335,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
|
||||
let m = myImportModule(c, it, importStmtResult)
|
||||
if m != nil:
|
||||
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
addDecl(c, m, it.info) # add symbol to symbol table of module
|
||||
importAllSymbols(c, m)
|
||||
#importForwarded(c, m.ast, emptySet, m)
|
||||
afterImport(c, m)
|
||||
@@ -375,7 +372,7 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
|
||||
var m = myImportModule(c, n[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
||||
|
||||
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
|
||||
for i in 1..<n.len:
|
||||
@@ -390,7 +387,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
||||
var m = myImportModule(c, n[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
||||
importAllSymbolsExcept(c, m, readExceptSet(c, n))
|
||||
#importForwarded(c, m.ast, exceptSet, m)
|
||||
afterImport(c, m)
|
||||
|
||||
@@ -802,7 +802,15 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result = passCopyToSink(n, c, s)
|
||||
elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
|
||||
nkCallKinds + nkLiterals:
|
||||
result = p(n, c, s, consumed)
|
||||
if n.kind in nkCallKinds and n[0].kind == nkSym:
|
||||
if n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
||||
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
@@ -878,6 +886,12 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
return
|
||||
|
||||
let inSpawn = c.inSpawn
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
|
||||
c.inSpawn.inc
|
||||
@@ -895,19 +909,13 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
isDangerous = true
|
||||
|
||||
result = shallowCopy(n)
|
||||
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
for i in 1..<n.len:
|
||||
if i < L and isCompileTimeOnly(parameters[i]):
|
||||
result[i] = n[i]
|
||||
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
|
||||
result[i] = p(n[i], c, s, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
for i in 1..<n.len:
|
||||
if i < L and isCompileTimeOnly(parameters[i]):
|
||||
result[i] = n[i]
|
||||
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
|
||||
result[i] = p(n[i], c, s, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
|
||||
when false:
|
||||
if isDangerous:
|
||||
|
||||
@@ -166,7 +166,7 @@ proc containsVariable(n: PNode): bool =
|
||||
proc checkIsolate*(n: PNode): bool =
|
||||
if types.containsTyRef(n.typ):
|
||||
# XXX Maybe require that 'n.typ' is acyclic. This is not much
|
||||
# worse than the already existing inheritance and closure restrictions.
|
||||
# worse than the already exisiting inheritance and closure restrictions.
|
||||
case n.kind
|
||||
of nkCharLit..nkNilLit:
|
||||
result = true
|
||||
|
||||
@@ -1585,8 +1585,15 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
|
||||
of nkObjDownConv:
|
||||
gen(p, n[0], r)
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
of nkHiddenDeref:
|
||||
gen(p, n[0], r)
|
||||
of nkDerefExpr:
|
||||
var x = n[0]
|
||||
if n.kind == nkHiddenAddr:
|
||||
x = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
x.typ() = n.typ
|
||||
gen(p, x, r)
|
||||
of nkHiddenAddr:
|
||||
gen(p, n[0], r)
|
||||
of nkConv:
|
||||
|
||||
@@ -1207,8 +1207,6 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
result.ast[pragmasPos].add newTree(nkExprColonExpr,
|
||||
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))
|
||||
|
||||
if kind == attachedDestructor:
|
||||
incl result.options, optQuirky
|
||||
completePartialOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
|
||||
@@ -1278,10 +1276,6 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
## The later 'injectdestructors' pass depends on it.
|
||||
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
|
||||
incl orig.flags, tfCheckedForDestructor
|
||||
# for user defined generic destructors:
|
||||
let origRoot = genericRoot(orig)
|
||||
if origRoot != nil:
|
||||
incl origRoot.flags, tfGenericHasDestructor
|
||||
|
||||
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
|
||||
|
||||
@@ -94,7 +94,6 @@ type
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
|
||||
warnStdPrefix = "StdPrefix"
|
||||
warnUnknownNotes = "UnknownNotes"
|
||||
warnUser = "User",
|
||||
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
|
||||
# hints
|
||||
@@ -112,6 +111,7 @@ type
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
hintUnknownHint = "UnknownHint"
|
||||
|
||||
const
|
||||
MsgKindToStr*: array[TMsgKind, string] = [
|
||||
@@ -200,7 +200,6 @@ const
|
||||
warnImplicitDefaultValue: "$1",
|
||||
warnIgnoredSymbolInjection: "$1",
|
||||
warnStdPrefix: "$1 needs the 'std' prefix",
|
||||
warnUnknownNotes: "$1",
|
||||
warnUser: "$1",
|
||||
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
|
||||
hintSuccess: "operation successful: $#",
|
||||
@@ -237,7 +236,8 @@ const
|
||||
hintUserRaw: "$1",
|
||||
hintExtendedContext: "$1",
|
||||
hintMsgOrigin: "$1",
|
||||
hintDeclaredLoc: "$1"
|
||||
hintDeclaredLoc: "$1",
|
||||
hintUnknownHint: "unknown hint: $1"
|
||||
]
|
||||
|
||||
const
|
||||
|
||||
@@ -221,14 +221,7 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
|
||||
if i == limit: return
|
||||
inc i
|
||||
|
||||
proc searchImportsAll*(c: PContext, s: PIdent, filter: TSymKinds, holding: var seq[PSym]) =
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
holding.add s
|
||||
|
||||
proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
@@ -238,12 +231,14 @@ proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result.add candidate
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
|
||||
proc searchScopesAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = searchScopes(c,s,filter)
|
||||
if result.len == 0:
|
||||
searchImportsAll(c, s, filter, result)
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
@@ -257,7 +252,11 @@ proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PS
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
|
||||
if result.len == 0:
|
||||
searchImportsAll(c, s, filter, result)
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc cmpScopes*(ctx: PContext, s: PSym): int =
|
||||
# Do not return a negative number
|
||||
@@ -645,7 +644,7 @@ const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
|
||||
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
|
||||
includePureEnum = false): seq[PSym] =
|
||||
result = selectFromScopesElseAll(c, ident, filter)
|
||||
result = searchInScopesFilterBy(c, ident, filter)
|
||||
if skEnumField in filter and (result.len == 0 or includePureEnum):
|
||||
result.add allPureEnumFields(c, ident)
|
||||
|
||||
@@ -726,7 +725,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
if n.kind == nkOpenSym:
|
||||
# maybe the logic in semexprs should be mirrored here instead
|
||||
# for now it only seems this is called for `pickSym` in `getTypeIdent`
|
||||
# for now it only seems this is called for `pickSym` in `getTypeIdent`
|
||||
return initOverloadIter(o, c, n[0])
|
||||
o.importIdx = -1
|
||||
o.marked = initIntSet()
|
||||
|
||||
52
compiler/ndi.nim
Normal file
52
compiler/ndi.nim
Normal file
@@ -0,0 +1,52 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2017 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the generation of ``.ndi`` files for better debugging
|
||||
## support of Nim code. "ndi" stands for "Nim debug info".
|
||||
|
||||
import ast, msgs, ropes, options, pathutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
type
|
||||
NdiFile* = object
|
||||
enabled: bool
|
||||
f: File
|
||||
buf: string
|
||||
filename: AbsoluteFile
|
||||
syms: seq[PSym]
|
||||
|
||||
proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
|
||||
f.buf.setLen 0
|
||||
f.buf.addInt s.info.line.int
|
||||
f.buf.add "\t"
|
||||
f.buf.addInt s.info.col.int
|
||||
f.f.write(s.name.s, "\t")
|
||||
f.f.writeRope(s.loc.snippet)
|
||||
f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
|
||||
|
||||
template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
|
||||
if f.enabled: f.syms.add s
|
||||
|
||||
proc open*(f: var NdiFile; filename: AbsoluteFile; conf: ConfigRef) =
|
||||
f.enabled = not filename.isEmpty
|
||||
if f.enabled:
|
||||
f.filename = filename
|
||||
f.buf = newStringOfCap(20)
|
||||
|
||||
proc close*(f: var NdiFile, conf: ConfigRef) =
|
||||
if f.enabled:
|
||||
f.f = open(f.filename.string, fmWrite, 8000)
|
||||
doAssert f.f != nil, f.filename.string
|
||||
for s in f.syms:
|
||||
doWrite(f, s, conf)
|
||||
close(f.f)
|
||||
f.syms.reset
|
||||
f.filename.reset
|
||||
@@ -9,8 +9,6 @@ define:nimPreviewCstringConversion
|
||||
define:nimPreviewProcConversion
|
||||
define:nimPreviewRangeDefault
|
||||
define:nimPreviewNonVarDestructor
|
||||
define:nimPreviewCheckedClose
|
||||
define:nimPreviewAsmSemSymbol
|
||||
threads:off
|
||||
|
||||
#import:"$projectpath/testability"
|
||||
|
||||
@@ -25,7 +25,7 @@ const
|
||||
useEffectSystem* = true
|
||||
useWriteTracking* = false
|
||||
hasFFI* = defined(nimHasLibFFI)
|
||||
copyrightYear* = "2025"
|
||||
copyrightYear* = "2024"
|
||||
|
||||
nimEnableCovariance* = defined(nimEnableCovariance)
|
||||
|
||||
@@ -222,14 +222,13 @@ type
|
||||
strictEffects,
|
||||
unicodeOperators, # deadcode
|
||||
flexibleOptionalParams,
|
||||
strictDefs, # deadcode
|
||||
strictDefs,
|
||||
strictCaseObjects,
|
||||
inferGenericTypes,
|
||||
openSym, # remove nfDisabledOpenSym when this is default
|
||||
# alternative to above:
|
||||
genericsOpenSym
|
||||
vtables
|
||||
typeBoundOps
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
@@ -383,7 +382,6 @@ type
|
||||
warnCounter*: int
|
||||
errorMax*: int
|
||||
maxLoopIterationsVM*: int ## VM: max iterations of all loops
|
||||
maxCallDepthVM*: int ## VM: max call depth
|
||||
isVmTrace*: bool
|
||||
configVars*: StringTableRef
|
||||
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
|
||||
@@ -599,7 +597,6 @@ proc newConfigRef*(): ConfigRef =
|
||||
arguments: "",
|
||||
suggestMaxResults: 10_000,
|
||||
maxLoopIterationsVM: 10_000_000,
|
||||
maxCallDepthVM: 2_000,
|
||||
vmProfileData: newProfileData(),
|
||||
spellSuggestMax: spellSuggestSecretSauce,
|
||||
currentConfigDir: ""
|
||||
|
||||
@@ -42,7 +42,10 @@ proc makePass*(open: TPassOpen = nil,
|
||||
process: TPassProcess = nil,
|
||||
close: TPassClose = nil,
|
||||
isFrontend = false): TPass =
|
||||
result = (open, process, close, isFrontend)
|
||||
result.open = open
|
||||
result.close = close
|
||||
result.process = process
|
||||
result.isFrontend = isFrontend
|
||||
|
||||
const
|
||||
maxPasses = 10
|
||||
@@ -97,8 +100,8 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
||||
stream: PLLStream): bool {.discardable.} =
|
||||
if graph.stopCompile(): return true
|
||||
var
|
||||
p: Parser = default(Parser)
|
||||
a: TPassContextArray = default(TPassContextArray)
|
||||
p: Parser
|
||||
a: TPassContextArray
|
||||
s: PLLStream
|
||||
fileIdx = module.fileIdx
|
||||
prepareConfigNotes(graph, module)
|
||||
|
||||
@@ -590,7 +590,7 @@ proc processCompile(c: PContext, n: PNode) =
|
||||
var customArgs = ""
|
||||
if n.kind in nkCallKinds:
|
||||
s = getStrLit(c, n, 1)
|
||||
if n.len == 3:
|
||||
if n.len <= 3:
|
||||
customArgs = getStrLit(c, n, 2)
|
||||
else:
|
||||
localError(c.config, n.info, "'.compile' pragma takes up 2 arguments")
|
||||
@@ -637,10 +637,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
||||
# XXX what to do here if 'amb' is true?
|
||||
if e != nil:
|
||||
incl(e.flags, sfUsed)
|
||||
if isDefined(con.config, "nimPreviewAsmSemSymbol"):
|
||||
result.add con.semExprWithType(con, newSymNode(e), {efTypeAllowed})
|
||||
else:
|
||||
result.add newSymNode(e)
|
||||
result.add newSymNode(e)
|
||||
else:
|
||||
result.add newStrNode(nkStrLit, sub)
|
||||
else:
|
||||
@@ -1318,12 +1315,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
pragmaProposition(c, it)
|
||||
of wEnsures:
|
||||
pragmaEnsures(c, it)
|
||||
of wEnforceNoRaises:
|
||||
of wEnforceNoRaises, wQuirky:
|
||||
sym.flags.incl sfNeverRaises
|
||||
of wQuirky:
|
||||
sym.flags.incl sfNeverRaises
|
||||
if sym.kind in {skProc, skMethod, skConverter, skFunc, skIterator}:
|
||||
sym.options.incl optQuirky
|
||||
of wSystemRaisesDefect:
|
||||
sym.flags.incl sfSystemRaisesDefect
|
||||
of wVirtual:
|
||||
|
||||
@@ -500,21 +500,6 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
dec(c.config.evalTemplateCounter)
|
||||
discard c.friendModules.pop()
|
||||
|
||||
proc getLineInfo(n: PNode): TLineInfo =
|
||||
case n.kind
|
||||
of nkPostfix:
|
||||
if len(n) > 1:
|
||||
result = getLineInfo(n[1])
|
||||
else:
|
||||
result = n.info
|
||||
of nkAccQuoted, nkPragmaExpr:
|
||||
if len(n) > 0:
|
||||
result = getLineInfo(n[0])
|
||||
else:
|
||||
result = n.info
|
||||
else:
|
||||
result = n.info
|
||||
|
||||
const
|
||||
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
|
||||
|
||||
@@ -541,9 +526,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
if efNoSemCheck notin flags:
|
||||
result = semAfterMacroCall(c, n, result, sym, flags, expectedType)
|
||||
if c.config.macrosToExpand.hasKey(sym.name.s):
|
||||
message(c.config, nOrig.info, hintExpandMacro, renderTree(result, {
|
||||
renderNonExportedFields, renderDocComments, renderNoComments
|
||||
}))
|
||||
message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
|
||||
result = wrapInComesFrom(nOrig.info, sym, result)
|
||||
popInfoContext(c.config)
|
||||
|
||||
@@ -688,9 +671,8 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
|
||||
if child != nil:
|
||||
let node = newNode(nkIntLit)
|
||||
node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
|
||||
let typeNode = newNode(nkType)
|
||||
typeNode.typ() = makeTypeDesc(c, aTypSkip[1])
|
||||
result = semExpr(c, newTree(nkCall, newTree(nkBracketExpr, newSymNode(getSysSym(c.graph, a.info, "arrayWithDefault"), a.info), typeNode),
|
||||
result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
|
||||
semExprWithType(c, child),
|
||||
node
|
||||
))
|
||||
result.typ() = aTyp
|
||||
@@ -751,7 +733,6 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
|
||||
result.semInferredLambda = semInferredLambda
|
||||
result.semGenerateInstance = generateInstance
|
||||
result.instantiateOnlyProcType = instantiateOnlyProcType
|
||||
result.fitDefaultNode = fitDefaultNode
|
||||
result.semTypeNode = semTypeNode
|
||||
result.instTypeBoundOp = sigmatch.instTypeBoundOp
|
||||
result.hasUnresolvedArgs = hasUnresolvedArgs
|
||||
@@ -872,9 +853,9 @@ proc semWithPContext*(c: PContext, n: PNode): PNode =
|
||||
|
||||
proc reportUnusedModules(c: PContext) =
|
||||
if c.config.cmd == cmdM: return
|
||||
for (s, info) in c.unusedImports:
|
||||
if sfUsed notin s.flags:
|
||||
message(c.config, info, warnUnusedImportX, s.name.s)
|
||||
for i in 0..high(c.unusedImports):
|
||||
if sfUsed notin c.unusedImports[i][0].flags:
|
||||
message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
|
||||
|
||||
proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
|
||||
if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
|
||||
|
||||
@@ -56,7 +56,7 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
|
||||
proc name[T: static proc()]() = T()
|
||||
name[proc() = echo"hello"]()
|
||||
]#
|
||||
for paramSym in searchScopesAll(c, symx.name, {skConst}):
|
||||
for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}):
|
||||
let paramTyp = paramSym.typ
|
||||
if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter:
|
||||
result.add((paramTyp.n.sym, o.lastOverloadScope))
|
||||
@@ -69,39 +69,6 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
|
||||
result[0].scope, diagnostics)
|
||||
best.state = csNoMatch
|
||||
|
||||
proc isAttachableRoutineTo(prc: PSym, arg: PType): bool =
|
||||
result = false
|
||||
if arg.owner != prc.owner: return false
|
||||
for i in 1 ..< prc.typ.len:
|
||||
if prc.typ.n[i].kind == nkSym and prc.typ.n[i].sym.ast != nil:
|
||||
# has default value, parameter is not considered in type attachment
|
||||
continue
|
||||
let t = nominalRoot(prc.typ[i])
|
||||
if t != nil and t.itemId == arg.itemId:
|
||||
# parameter `i` is a nominal type in this module
|
||||
# attachable if the nominal root `t` has the same id as `arg`
|
||||
return true
|
||||
|
||||
proc addTypeBoundSymbols(graph: ModuleGraph, arg: PType, name: PIdent,
|
||||
filter: TSymKinds, marker: var IntSet,
|
||||
syms: var seq[tuple[s: PSym, scope: int]]) =
|
||||
# add type bound ops for `name` based on the argument type `arg`
|
||||
if arg != nil:
|
||||
# argument must be typed first, meaning arguments always
|
||||
# matching `untyped` are ignored
|
||||
let t = nominalRoot(arg)
|
||||
if t != nil and t.owner.kind == skModule:
|
||||
# search module for routines attachable to `t`
|
||||
let module = t.owner
|
||||
var iter = default(ModuleIter)
|
||||
var s = initModuleIter(iter, graph, module, name)
|
||||
while s != nil:
|
||||
if s.kind in filter and s.isAttachableRoutineTo(t) and
|
||||
not containsOrIncl(marker, s.id):
|
||||
# least priority scope, less than explicit imports:
|
||||
syms.add((s, -2))
|
||||
s = nextModuleIter(iter, graph)
|
||||
|
||||
proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
n, orig: PNode,
|
||||
initialBinding: PNode,
|
||||
@@ -121,23 +88,10 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
best, alt, o, diagnosticsFlag)
|
||||
if len(syms) == 0:
|
||||
return
|
||||
let allowTypeBoundOps = typeBoundOps in c.features and
|
||||
# qualified or bound symbols cannot refer to type bound ops
|
||||
headSymbol.kind in {nkIdent, nkAccQuoted, nkOpenSymChoice, nkOpenSym}
|
||||
var symMarker = initIntSet()
|
||||
for s in syms:
|
||||
symMarker.incl(s.s.id)
|
||||
# current overload being considered
|
||||
var sym = syms[0].s
|
||||
let name = sym.name
|
||||
var scope = syms[0].scope
|
||||
|
||||
if allowTypeBoundOps:
|
||||
for a in 1 ..< n.len:
|
||||
# for every already typed argument, add type bound ops
|
||||
let arg = n[a]
|
||||
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
|
||||
|
||||
# starts at 1 because 0 is already done with setup, only needs checking
|
||||
var nextSymIndex = 1
|
||||
var z: TCandidate # current candidate
|
||||
@@ -152,14 +106,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# may introduce new symbols with caveats described in recalc branch
|
||||
matches(c, n, orig, z)
|
||||
|
||||
if allowTypeBoundOps:
|
||||
# this match may have given some arguments new types,
|
||||
# in which case add their type bound ops as well
|
||||
# type bound ops of arguments always matching `untyped` are not considered
|
||||
for x in z.newlyTypedOperands:
|
||||
let arg = n[x]
|
||||
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
|
||||
|
||||
if z.state == csMatch:
|
||||
# little hack so that iterators are preferred over everything else:
|
||||
if sym.kind == skIterator:
|
||||
@@ -190,14 +136,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# before any further candidate init and compare. SLOW, but rare case.
|
||||
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
|
||||
best, alt, o, diagnosticsFlag)
|
||||
symMarker = initIntSet()
|
||||
for s in syms:
|
||||
symMarker.incl(s.s.id)
|
||||
if allowTypeBoundOps:
|
||||
for a in 1 ..< n.len:
|
||||
# for every already typed argument, add type bound ops
|
||||
let arg = n[a]
|
||||
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
|
||||
|
||||
# reset counter because syms may be in a new order
|
||||
symCount = c.currentScope.symbols.counter
|
||||
nextSymIndex = 0
|
||||
@@ -973,14 +912,8 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
|
||||
# common case; check the only candidate has the right
|
||||
# number of generic type parameters:
|
||||
result = explicitGenericSym(c, n, s, errors, doError)
|
||||
if result == nil:
|
||||
if c.inGenericContext > 0:
|
||||
# same as in semOverloadedCall, make expression untyped,
|
||||
# may have failed match due to unresolved types
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
elif doError:
|
||||
notFoundError(c, n, errors)
|
||||
if doError and result == nil:
|
||||
notFoundError(c, n, errors)
|
||||
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
|
||||
# choose the generic proc with the proper number of type parameters.
|
||||
result = newNodeI(a.kind, getCallLineInfo(n))
|
||||
@@ -990,14 +923,6 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
|
||||
skFunc, skIterator}:
|
||||
let x = explicitGenericSym(c, n, candidate, errors, doError)
|
||||
if x != nil: result.add(x)
|
||||
elif c.inGenericContext > 0:
|
||||
# same as in semOverloadedCall, make expression untyped,
|
||||
# may have failed match due to unresolved types
|
||||
# any failing match stops building the symchoice for correctness,
|
||||
# can also make it untyped from the start
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
return
|
||||
# get rid of nkClosedSymChoice if not ambiguous:
|
||||
if result.len == 0:
|
||||
result = nil
|
||||
|
||||
@@ -142,7 +142,6 @@ type
|
||||
instantiateOnlyProcType*: proc (c: PContext, pt: LayeredIdTable,
|
||||
prc: PSym, info: TLineInfo): PType
|
||||
# used by sigmatch for explicit generic instantiations
|
||||
fitDefaultNode*: proc (c: PContext, n: var PNode, expectedType: PType)
|
||||
includedFiles*: IntSet # used to detect recursive include files
|
||||
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
|
||||
userPragmas*: TStrTable
|
||||
|
||||
@@ -1006,9 +1006,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
n.typ.flags.incl tfUnresolved
|
||||
|
||||
# optimization pass: not necessary for correctness of the semantic pass
|
||||
if (callee.kind == skConst or
|
||||
if callee.kind == skConst or
|
||||
{sfNoSideEffect, sfCompileTime} * callee.flags != {} and
|
||||
{sfForward, sfImportc} * callee.flags == {}) and n.typ != nil:
|
||||
{sfForward, sfImportc} * callee.flags == {} and n.typ != nil:
|
||||
|
||||
if callee.kind != skConst and
|
||||
sfCompileTime notin callee.flags and
|
||||
@@ -1492,8 +1492,6 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
|
||||
markUsed(c, n.info, s)
|
||||
onUse(n.info, s)
|
||||
if s.typ == nil:
|
||||
return localErrorNode(c, n, "symbol '$1' has no type" % [s.name.s])
|
||||
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
|
||||
return s.typ.n
|
||||
result = newSymNode(s, n.info)
|
||||
@@ -1680,46 +1678,29 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
|
||||
result = tryReadingGenericParam(c, n, i, t)
|
||||
flags.incl efCannotBeDotCall
|
||||
|
||||
proc hiddenDerefDepth(n: PNode): int =
|
||||
result = 0
|
||||
var n = n
|
||||
while n.kind == nkHiddenDeref:
|
||||
inc result
|
||||
n = n[0]
|
||||
|
||||
proc dotTransformation(c: PContext, n: PNode, initialDerefs: int): PNode =
|
||||
var root = n[0]
|
||||
let currentDerefs = hiddenDerefDepth(root)
|
||||
if currentDerefs > initialDerefs:
|
||||
# hidden derefs were inserted by `builtinFieldAccess` for fields of
|
||||
# `ref object` etc.
|
||||
# undo the derefs for overload resolution
|
||||
for _ in initialDerefs ..< currentDerefs:
|
||||
root = root[0]
|
||||
root = copyTree(root)
|
||||
proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||
if isSymChoice(n[1]) or
|
||||
# generics usually leave field names as symchoices, but not types
|
||||
(n[1].kind == nkSym and n[1].sym.kind == skType):
|
||||
result = newNodeI(nkDotCall, n.info)
|
||||
result.add n[1]
|
||||
result.add root
|
||||
result.add copyTree(n[0])
|
||||
else:
|
||||
var i = considerQuotedIdent(c, n[1], n)
|
||||
result = newNodeI(nkDotCall, n.info)
|
||||
result.flags.incl nfDotField
|
||||
result.add newIdentNode(i, n[1].info)
|
||||
result.add root
|
||||
result.add copyTree(n[0])
|
||||
|
||||
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# this is difficult, because the '.' is used in many different contexts
|
||||
# in Nim. We first allow types in the semantic checking.
|
||||
var f = flags - {efIsDotCall}
|
||||
let initialDerefDepth = hiddenDerefDepth(n[0])
|
||||
result = builtinFieldAccess(c, n, f)
|
||||
if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and
|
||||
efIsDotCall in flags and callOperator notin c.features and
|
||||
efCannotBeDotCall notin f):
|
||||
result = dotTransformation(c, n, initialDerefDepth)
|
||||
result = dotTransformation(c, n)
|
||||
|
||||
proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
|
||||
result = newNodeI(nkCall, n.info)
|
||||
@@ -1833,9 +1814,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags, afterOverloading = f
|
||||
# type parameters: partial generic specialization
|
||||
n[0] = semSymGenericInstantiation(c, n[0], s)
|
||||
result = maybeInstantiateGeneric(c, n, s, doError = afterOverloading)
|
||||
if result != nil and
|
||||
# leave untyped generic expression alone:
|
||||
(result.typ == nil or result.typ.kind != tyFromExpr):
|
||||
if result != nil:
|
||||
# check newly created sym/symchoice
|
||||
result = semExpr(c, result, flags)
|
||||
of skMacro, skTemplate:
|
||||
@@ -2021,14 +2000,13 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
# --> `f=` (r, x)
|
||||
let nOrig = n.copyTree
|
||||
var flags = {efLValue}
|
||||
let initialDerefDepth = hiddenDerefDepth(a[0])
|
||||
a = builtinFieldAccess(c, a, flags)
|
||||
if a == nil:
|
||||
a = propertyWriteAccess(c, n, nOrig, n[0])
|
||||
if a != nil: return a
|
||||
# we try without the '='; proc that return 'var' or macros are still
|
||||
# possible:
|
||||
a = dotTransformation(c, n[0], initialDerefDepth)
|
||||
a = dotTransformation(c, n[0])
|
||||
if a.kind == nkDotCall:
|
||||
a.transitionSonsKind(nkCall)
|
||||
a = semExprWithType(c, a, {efLValue})
|
||||
@@ -2928,10 +2906,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
|
||||
# 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))`
|
||||
let conversion = indexTypesMatch(c, expectedElemType, n[i][1].typ, n[i][1])
|
||||
# ignore matching error, full tuple will be matched later which may call converter, see #24609
|
||||
if conversion != nil:
|
||||
n[i][1] = conversion
|
||||
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.")
|
||||
|
||||
@@ -17,17 +17,9 @@ type
|
||||
field: PSym
|
||||
replaceByFieldName: bool
|
||||
c: PContext
|
||||
leftPartOfDefinition: bool
|
||||
|
||||
proc wrapNewScope(c: PContext, n: PNode): PNode {.inline.} =
|
||||
# use `if true` to not interfere with `break`
|
||||
# just opening scope via `openScope(c)` isn't enough,
|
||||
# a scope has to be opened in the codegen as well for reused
|
||||
# template instantiations
|
||||
let trueLit = newIntLit(c.graph, n.info, 1)
|
||||
trueLit.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
result = newTreeI(nkIfStmt, n.info, newTreeI(nkElifBranch, n.info, trueLit, n))
|
||||
|
||||
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
proc instFieldLoopBody(c: var TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
if c.field != nil and isEmptyType(c.field.typ):
|
||||
result = newNode(nkEmpty)
|
||||
return
|
||||
@@ -38,6 +30,9 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
let ident = considerQuotedIdent(c.c, n)
|
||||
if c.replaceByFieldName:
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
|
||||
if c.leftPartOfDefinition:
|
||||
localError(c.c.config, n.info,
|
||||
"redefine field variable '$1' in a 'fields' loop" % [ident.s])
|
||||
let fieldName = if c.tupleType.isNil: c.field.name.s
|
||||
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
|
||||
else: c.tupleType.n[c.tupleIndex].sym.name.s
|
||||
@@ -46,6 +41,9 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
# other fields:
|
||||
for i in ord(c.replaceByFieldName)..<forLoop.len-2:
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
|
||||
if c.leftPartOfDefinition:
|
||||
localError(c.c.config, n.info,
|
||||
"redefine field variable '$1' in a 'fields' loop" % [ident.s])
|
||||
var call = forLoop[^2]
|
||||
var tupl = call[i+1-ord(c.replaceByFieldName)]
|
||||
if c.field.isNil:
|
||||
@@ -57,6 +55,13 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
result.add(tupl)
|
||||
result.add(newSymNode(c.field, n.info))
|
||||
break
|
||||
of nkIdentDefs, nkVarTuple, nkConstDef:
|
||||
result = shallowCopy(n)
|
||||
c.leftPartOfDefinition = true
|
||||
result[0] = instFieldLoopBody(c, n[0], forLoop)
|
||||
c.leftPartOfDefinition = false
|
||||
for i in 1..<n.len:
|
||||
result[i] = instFieldLoopBody(c, n[i], forLoop)
|
||||
else:
|
||||
if n.kind == nkContinueStmt:
|
||||
localError(c.c.config, n.info,
|
||||
@@ -81,9 +86,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
|
||||
)
|
||||
openScope(c.c)
|
||||
inc c.c.inUnrolledContext
|
||||
var body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
# new scope for each field that codegen should know about:
|
||||
body = wrapNewScope(c.c, body)
|
||||
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
father.add(semStmt(c.c, body, {}))
|
||||
dec c.c.inUnrolledContext
|
||||
closeScope(c.c)
|
||||
@@ -159,8 +162,6 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
replaceByFieldName: m == mFieldPairs
|
||||
)
|
||||
var body = instFieldLoopBody(fc, loopBody, n)
|
||||
# new scope for each field that codegen should know about:
|
||||
body = wrapNewScope(c, body)
|
||||
inc c.inUnrolledContext
|
||||
stmts.add(semStmt(c, body, {}))
|
||||
dec c.inUnrolledContext
|
||||
|
||||
@@ -589,7 +589,7 @@ proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
raise newException(ValueError, "invalid enum value: " & str)
|
||||
else:
|
||||
localError(g.config, s.info, "unsupported type $1 for define '$2'" %
|
||||
[typeToString(rawTyp), name])
|
||||
[name, typeToString(rawTyp)])
|
||||
except ValueError as e:
|
||||
localError(g.config, s.info,
|
||||
"could not process define '$1' of type $2; $3" %
|
||||
|
||||
@@ -218,7 +218,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
let ident = considerQuotedIdent(c, n)
|
||||
# could be type conversion if like a.T and not a.T()
|
||||
let symKinds = if inCall: routineKinds else: routineKinds+{skType}
|
||||
var candidates = selectFromScopesElseAll(c, ident, symKinds)
|
||||
var candidates = searchInScopesFilterBy(c, ident, symKinds)
|
||||
if candidates.len > 0:
|
||||
let s = candidates[0] # XXX take into account the other candidates!
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
|
||||
@@ -10,28 +10,9 @@
|
||||
## Implements type sanity checking for ASTs resulting from macros. Lots of
|
||||
## room for improvement here.
|
||||
|
||||
import ast, msgs, types, options, trees, nimsets
|
||||
import ast, msgs, types, options
|
||||
|
||||
type
|
||||
FieldTracker = object
|
||||
index: int
|
||||
remaining: int
|
||||
constr: PNode
|
||||
delete: bool # to delete fields from inactive case branches
|
||||
FieldInfo = ref object
|
||||
sym: PSym
|
||||
delete: bool
|
||||
|
||||
proc caseBranchMatchesExpr(branch, matched: PNode): bool =
|
||||
# copied from sem
|
||||
result = false
|
||||
for i in 0 ..< branch.len-1:
|
||||
if branch[i].kind == nkRange:
|
||||
if overlap(branch[i], matched): return true
|
||||
elif exprStructuralEquivalent(branch[i], matched):
|
||||
return true
|
||||
|
||||
proc ithField(n: PNode, field: var FieldTracker): FieldInfo =
|
||||
proc ithField(n: PNode, field: var int): PSym =
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
@@ -42,42 +23,18 @@ proc ithField(n: PNode, field: var FieldTracker): FieldInfo =
|
||||
if n[0].kind != nkSym: return
|
||||
result = ithField(n[0], field)
|
||||
if result != nil: return
|
||||
# value of the discriminator field, from (index - remaining - 1 + 1):
|
||||
# - 1 because the `ithField` call above decreased it by 1,
|
||||
# + 1 because the constructor node has an initial type child
|
||||
let val = field.constr[field.index - field.remaining][1]
|
||||
var branchFound = false
|
||||
for i in 1..<n.len:
|
||||
let previousDelete = field.delete
|
||||
case n[i].kind
|
||||
of nkOfBranch:
|
||||
if branchFound or previousDelete or
|
||||
not caseBranchMatchesExpr(n[i], val):
|
||||
# if this is not the active case branch,
|
||||
# mark all fields inside as deleted
|
||||
field.delete = true
|
||||
else:
|
||||
branchFound = true
|
||||
of nkOfBranch, nkElse:
|
||||
result = ithField(lastSon(n[i]), field)
|
||||
if result != nil: return
|
||||
field.delete = previousDelete
|
||||
of nkElse:
|
||||
if branchFound:
|
||||
# if this is not the active case branch,
|
||||
# mark all fields inside as deleted
|
||||
field.delete = true
|
||||
result = ithField(lastSon(n[i]), field)
|
||||
if result != nil: return
|
||||
field.delete = previousDelete
|
||||
else: discard
|
||||
of nkSym:
|
||||
if field.remaining == 0:
|
||||
result = FieldInfo(sym: n.sym, delete: field.delete)
|
||||
else:
|
||||
dec(field.remaining)
|
||||
if field == 0: result = n.sym
|
||||
else: dec(field)
|
||||
else: discard
|
||||
|
||||
proc ithField(t: PType, field: var FieldTracker): FieldInfo =
|
||||
proc ithField(t: PType, field: var int): PSym =
|
||||
var base = t.baseClass
|
||||
while base != nil:
|
||||
let b = skipTypes(base, skipPtrs)
|
||||
@@ -96,16 +53,13 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
n.typ() = t
|
||||
n[0].typ() = t
|
||||
for i in 1..<n.len:
|
||||
var tracker = FieldTracker(index: i-1, remaining: i-1, constr: n, delete: false)
|
||||
let field = x.ithField(tracker)
|
||||
var j = i-1
|
||||
let field = x.ithField(j)
|
||||
if field.isNil:
|
||||
globalError conf, n.info, "invalid field at index " & $i
|
||||
else:
|
||||
internalAssert(conf, n[i].kind == nkExprColonExpr)
|
||||
annotateType(n[i][1], field.sym.typ, conf)
|
||||
if field.delete:
|
||||
# only codegen fields from active case branches
|
||||
incl(n[i].flags, nfPreventCg)
|
||||
annotateType(n[i][1], field.typ, conf)
|
||||
of nkPar, nkTupleConstr:
|
||||
if x.kind == tyTuple:
|
||||
n.typ() = t
|
||||
|
||||
@@ -536,33 +536,31 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
|
||||
else:
|
||||
if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move
|
||||
setOwner(fin, fin.instantiatedFrom)
|
||||
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
|
||||
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
|
||||
selfSymNode.typ() = fin.typ.firstParamType
|
||||
wrapperSym.flags.incl sfUsed
|
||||
|
||||
if fin.typ[1].skipTypes(abstractInst).kind != tyRef:
|
||||
bindTypeHook(c, fin, n, attachedDestructor)
|
||||
else:
|
||||
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
|
||||
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
|
||||
selfSymNode.typ() = fin.typ.firstParamType
|
||||
wrapperSym.flags.incl sfUsed
|
||||
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
|
||||
params = nkFormalParams.newTree(c.graph.emptyNode,
|
||||
newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
|
||||
fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
|
||||
),
|
||||
name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
|
||||
genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
|
||||
|
||||
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
|
||||
params = nkFormalParams.newTree(c.graph.emptyNode,
|
||||
newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
|
||||
fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
|
||||
),
|
||||
name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
|
||||
genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
|
||||
|
||||
var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
|
||||
setOwner(transFormedSym, fin)
|
||||
if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
|
||||
let origParamType = transFormedSym.ast[bodyPos][1].typ
|
||||
let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
|
||||
let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
|
||||
selfPtr.add transFormedSym.ast[bodyPos][1]
|
||||
selfPtr.typ() = selfSymbolType
|
||||
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor)
|
||||
var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
|
||||
setOwner(transFormedSym, fin)
|
||||
if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
|
||||
let origParamType = transFormedSym.ast[bodyPos][1].typ
|
||||
let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
|
||||
let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
|
||||
selfPtr.add transFormedSym.ast[bodyPos][1]
|
||||
selfPtr.typ() = selfSymbolType
|
||||
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
|
||||
# TODO: suppress var destructor warnings; if newFinalizer is not
|
||||
# TODO: deprecated, try to implement plain T destructor
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor, suppressVarDestructorWarning = true)
|
||||
result = addDefaultFieldForNew(c, n)
|
||||
|
||||
proc semPrivateAccess(c: PContext, n: PNode): PNode =
|
||||
|
||||
@@ -79,7 +79,7 @@ proc semConstrField(c: PContext, flags: TExprFlags,
|
||||
if nfSkipFieldChecking in assignment[1].flags:
|
||||
discard
|
||||
elif not fieldVisible(c, field):
|
||||
localError(c.config, assignment[0].info,
|
||||
localError(c.config, initExpr.info,
|
||||
"the field '$1' is not accessible." % [field.name.s])
|
||||
return
|
||||
|
||||
@@ -465,14 +465,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
if t == nil:
|
||||
return localErrorNode(c, result, "object constructor needs an object type")
|
||||
|
||||
when false:
|
||||
# attempted type inference for generic object types,
|
||||
# doesn't work since n[0] isn't set and seems underspecified
|
||||
if t.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
|
||||
expectedType != nil and expectedType.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
|
||||
t = expectedType
|
||||
if t.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
|
||||
expectedType != nil and expectedType.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
|
||||
t = expectedType
|
||||
|
||||
t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if t.kind == tyRef:
|
||||
@@ -521,7 +518,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
for j in 1..<i:
|
||||
let prevId = considerQuotedIdent(c, result[j][0])
|
||||
if prevId.id == id.id:
|
||||
localError(c.config, field[0].info, errFieldInitTwice % id.s)
|
||||
localError(c.config, field.info, errFieldInitTwice % id.s)
|
||||
hasError = true
|
||||
break
|
||||
# 2) No such field exists in the constructed type
|
||||
|
||||
@@ -124,11 +124,10 @@ proc collectObjectTree(graph: ModuleGraph, n: PNode) =
|
||||
else:
|
||||
graph.objectTree[root].add (depthLevel, typ)
|
||||
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit = false) =
|
||||
if typ == nil or (sfGeneratedOp in tracked.owner.flags and not explicit):
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
|
||||
if typ == nil or sfGeneratedOp in tracked.owner.flags:
|
||||
# don't create type bound ops for anything in a function with a `nodestroy` pragma
|
||||
# bug #21987
|
||||
# unless this is an explicit call, bug #24626
|
||||
return
|
||||
when false:
|
||||
let realType = typ.skipTypes(abstractInst)
|
||||
@@ -213,7 +212,6 @@ proc varDecl(a: PEffects; n: PNode) {.inline.} =
|
||||
proc skipHiddenDeref(n: PNode): PNode {.inline.} =
|
||||
result = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
|
||||
|
||||
proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
|
||||
let n = skipHiddenDeref(n)
|
||||
if n.kind != nkSym: return
|
||||
@@ -1073,7 +1071,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
# rebind type bounds operations after createTypeBoundOps call
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar})
|
||||
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
|
||||
createTypeBoundOps(tracked, t, n.info, explicit = true)
|
||||
createTypeBoundOps(tracked, t, n.info)
|
||||
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
|
||||
if op != nil:
|
||||
n[0].sym = op
|
||||
@@ -1303,12 +1301,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
let last = lastSon(child)
|
||||
track(tracked, last)
|
||||
of nkCaseStmt: trackCase(tracked, n)
|
||||
of nkWhen: # This should be a "when nimvm" node.
|
||||
let oldState = tracked.init.len
|
||||
track(tracked, n[0][1])
|
||||
tracked.init.setLen(oldState)
|
||||
track(tracked, n[1][0])
|
||||
of nkIfStmt, nkIfExpr: trackIf(tracked, n)
|
||||
of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
|
||||
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
|
||||
of nkWhileStmt:
|
||||
# 'while true' loop?
|
||||
@@ -1645,9 +1638,6 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod}:
|
||||
var res = s.ast[resultPos].sym # get result symbol
|
||||
t.scopes[res.id] = t.currentBlock
|
||||
if sfNoInit in s.flags:
|
||||
# marks result "noinit"
|
||||
incl res.flags, sfNoInit
|
||||
|
||||
track(t, body)
|
||||
|
||||
@@ -1660,14 +1650,13 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
(t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
(isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
|
||||
createTypeBoundOps(t, typ, param.info)
|
||||
if isOutParam(typ) and param.id notin t.init and s.magic == mNone:
|
||||
if isOutParam(typ) and param.id notin t.init:
|
||||
message(g.config, param.info, warnProveInit, param.name.s)
|
||||
|
||||
if not isEmptyType(s.typ.returnType) and
|
||||
(s.typ.returnType.requiresInit or s.typ.returnType.skipTypes(abstractInst).kind == tyVar or
|
||||
strictDefs in c.features) and
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone and
|
||||
sfNoInit notin s.flags:
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
|
||||
var res = s.ast[resultPos].sym # get result symbol
|
||||
if res.id notin t.init and breaksBlock(body) != bsNoReturn:
|
||||
if tfRequiresInit in s.typ.returnType.flags:
|
||||
|
||||
@@ -492,8 +492,19 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = tru
|
||||
incl(result.flags, sfGlobal)
|
||||
result.options = c.config.options
|
||||
|
||||
proc getLineInfo(n: PNode): TLineInfo =
|
||||
case n.kind
|
||||
of nkPostfix:
|
||||
if len(n) > 1:
|
||||
return getLineInfo(n[1])
|
||||
of nkAccQuoted, nkPragmaExpr:
|
||||
if len(n) > 0:
|
||||
return getLineInfo(n[0])
|
||||
else:
|
||||
discard
|
||||
result = n.info
|
||||
let info = getLineInfo(n)
|
||||
if reportToNimsuggest:
|
||||
let info = getLineInfo(n)
|
||||
suggestSym(c.graph, info, result, c.graph.usageSym)
|
||||
|
||||
proc checkNilable(c: PContext; v: PSym) =
|
||||
@@ -601,38 +612,11 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
|
||||
else:
|
||||
localError(c.config, n.info, "nkDotNode requires 2 children")
|
||||
|
||||
proc checkDefineType(c: PContext; v: PSym; t: PType) =
|
||||
# see semfold.foldDefine for acceptable types
|
||||
let typeKinds =
|
||||
case v.magic
|
||||
of mStrDefine: {tyString, tyCstring}
|
||||
# this used to be not typechecked, so anything that accepts int nodes for compatbility:
|
||||
of mIntDefine: {tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyChar, tyEnum}
|
||||
of mBoolDefine: {tyBool}
|
||||
of mGenericDefine: {tyString, tyCstring, tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyEnum}
|
||||
else: raiseAssert("unreachable")
|
||||
var skipped = abstractVarRange
|
||||
if v.magic == mGenericDefine:
|
||||
# no distinct types for generic define
|
||||
skipped.excl tyDistinct
|
||||
if t.skipTypes(skipped).kind notin typeKinds:
|
||||
let name =
|
||||
case v.magic
|
||||
of mStrDefine: "strdefine"
|
||||
of mIntDefine: "intdefine"
|
||||
of mBoolDefine: "booldefine"
|
||||
of mGenericDefine: "define"
|
||||
else: raiseAssert("unreachable")
|
||||
localError(c.config, v.info, "unsupported type for constant '" & v.name.s &
|
||||
"' with ." & name & " pragma: " & typeToString(t))
|
||||
|
||||
proc setVarType(c: PContext; v: PSym, typ: PType) =
|
||||
if v.typ != nil and not sameTypeOrNil(v.typ, typ):
|
||||
localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" &
|
||||
v.name.s & "': previous type was: " & typeToString(v.typ, preferDesc) &
|
||||
"; new type is: " & typeToString(typ, preferDesc))
|
||||
if v.kind == skConst and v.magic in {mGenericDefine, mIntDefine, mStrDefine, mBoolDefine}:
|
||||
checkDefineType(c, v, typ)
|
||||
v.typ = typ
|
||||
|
||||
proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
|
||||
@@ -1714,14 +1698,12 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
obj.ast[0] = a[0].shallowCopy
|
||||
if a[0][0].kind == nkPostfix:
|
||||
obj.ast[0][0] = a[0][0].shallowCopy
|
||||
obj.ast[0][0][0] = a[0][0][0] # ident "*"
|
||||
obj.ast[0][0][1] = symNode
|
||||
else:
|
||||
obj.ast[0][0] = symNode
|
||||
obj.ast[0][1] = a[0][1]
|
||||
of nkPostfix:
|
||||
obj.ast[0] = a[0].shallowCopy
|
||||
obj.ast[0][0] = a[0][0] # ident "*"
|
||||
obj.ast[0][1] = symNode
|
||||
else: assert(false)
|
||||
obj.ast[1] = a[1]
|
||||
@@ -1776,17 +1758,6 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
# check the style here after the pragmas have been processed:
|
||||
styleCheckDef(c, s)
|
||||
# compute the type's size and check for illegal recursions:
|
||||
if a[0].kind == nkPragmaExpr:
|
||||
let pragmas = a[0][1]
|
||||
for i in 0 ..< pragmas.len:
|
||||
if pragmas[i].kind == nkExprColonExpr and
|
||||
pragmas[i][0].kind == nkIdent and
|
||||
whichKeyword(pragmas[i][0].ident) == wSize:
|
||||
if s.typ.kind != tyEnum and sfImportc notin s.flags:
|
||||
# EventType* {.size: sizeof(uint32).} = enum
|
||||
# AtomicFlag* {.importc: "atomic_flag", header: "<stdatomic.h>", size: 1.} = object
|
||||
localError(c.config, pragmas[i].info, "size pragma only allowed for enum types and imported types")
|
||||
|
||||
if a[1].kind == nkEmpty:
|
||||
var x = a[2]
|
||||
if x.kind in nkCallKinds and nfSem in x.flags:
|
||||
@@ -2064,27 +2035,14 @@ proc canonType(c: PContext, t: PType): PType =
|
||||
else:
|
||||
result = t
|
||||
|
||||
proc prevDestructor(c: PContext; op: TTypeAttachedOp; prevOp: PSym; obj: PType; info: TLineInfo) =
|
||||
var msg = "cannot bind another '" & AttachedOpToStr[op] & "' to: " & typeToString(obj)
|
||||
if prevOp == nil:
|
||||
# happens if the destructor was implicitly constructed for a specific instance,
|
||||
# not the entire generic type
|
||||
msg.add "; previous declaration was constructed implicitly"
|
||||
elif sfOverridden notin prevOp.flags:
|
||||
proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
|
||||
var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
|
||||
if sfOverridden notin prevOp.flags:
|
||||
msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
|
||||
else:
|
||||
msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
|
||||
localError(c.config, info, errGenerated, msg)
|
||||
|
||||
proc checkedForDestructor(t: PType): bool =
|
||||
if tfCheckedForDestructor in t.flags:
|
||||
return true
|
||||
# maybe another instance was instantiated, marking the generic root:
|
||||
let root = genericRoot(t)
|
||||
if root != nil and tfGenericHasDestructor in root.flags:
|
||||
return true
|
||||
result = false
|
||||
|
||||
proc whereToBindTypeHook(c: PContext; t: PType): PType =
|
||||
result = t
|
||||
while true:
|
||||
@@ -2118,10 +2076,10 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
let ao = getAttachedOp(c.graph, obj, op)
|
||||
if ao == s:
|
||||
discard "forward declared destructor"
|
||||
elif ao.isNil and not checkedForDestructor(obj):
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
setAttachedOp(c.graph, c.module.position, obj, op, s)
|
||||
else:
|
||||
prevDestructor(c, op, ao, obj, n.info)
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
noError = true
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
@@ -2134,7 +2092,7 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverridden)
|
||||
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) =
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
let cond = case op
|
||||
@@ -2158,17 +2116,17 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||
if op == attachedDestructor and t.firstParamType.kind == tyVar and
|
||||
if (not suppressVarDestructorWarning) and op == attachedDestructor and t.firstParamType.kind == tyVar and
|
||||
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter")
|
||||
obj = canonType(c, obj)
|
||||
let ao = getAttachedOp(c.graph, obj, op)
|
||||
if ao == s:
|
||||
discard "forward declared destructor"
|
||||
elif ao.isNil and not checkedForDestructor(obj):
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
setAttachedOp(c.graph, c.module.position, obj, op, s)
|
||||
else:
|
||||
prevDestructor(c, op, ao, obj, n.info)
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
noError = true
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
@@ -2259,10 +2217,10 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
let ao = getAttachedOp(c.graph, obj, k)
|
||||
if ao == s:
|
||||
discard "forward declared op"
|
||||
elif ao.isNil and not checkedForDestructor(obj):
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
setAttachedOp(c.graph, c.module.position, obj, k, s)
|
||||
else:
|
||||
prevDestructor(c, k, ao, obj, n.info)
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
@@ -2351,7 +2309,7 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) =
|
||||
isInitializer = false
|
||||
break
|
||||
var j = 0
|
||||
while p[j].kind == nkSym and p[j].sym.kind == skParam:
|
||||
while p[j].sym.kind == skParam:
|
||||
initializerCall.add val
|
||||
inc j
|
||||
if isInitializer:
|
||||
@@ -2401,7 +2359,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
of nkEmpty:
|
||||
s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info)
|
||||
s.flags.incl sfUsed
|
||||
s.flags.incl sfGenSym
|
||||
n[namePos] = newSymNode(s)
|
||||
of nkSym:
|
||||
s = n[namePos].sym
|
||||
@@ -2690,7 +2647,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
|
||||
incl(s.typ.flags, tfCapturesEnv)
|
||||
else:
|
||||
s.typ.callConv = ccInline
|
||||
if result[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
|
||||
if n[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
|
||||
localError(c.config, n.info, errImplOfXexpected % s.name.s)
|
||||
if optOwnedRefs in c.config.globalOptions and result.typ != nil:
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
|
||||
@@ -67,12 +67,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
|
||||
# for instance 'nextTry' is both in tables.nim and astalgo.nim ...
|
||||
if not isField or sfGenSym notin s.flags:
|
||||
result = newSymNode(s, info)
|
||||
if isField:
|
||||
# possibly not final field sym
|
||||
incl(s.flags, sfUsed)
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
else:
|
||||
markUsed(c, info, s)
|
||||
markUsed(c, info, s)
|
||||
onUse(info, s)
|
||||
else:
|
||||
result = n
|
||||
@@ -696,9 +691,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
s = semIdentVis(c, skTemplate, n[namePos], {})
|
||||
assert s.kind == skTemplate
|
||||
|
||||
let info = getLineInfo(n[namePos])
|
||||
suggestSym(c.graph, info, s, c.graph.usageSym)
|
||||
|
||||
styleCheckDef(c, s)
|
||||
onDef(n[namePos].info, s)
|
||||
# check parameter list:
|
||||
|
||||
@@ -84,7 +84,6 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
let isPure = result.sym != nil and sfPure in result.sym.flags
|
||||
var symbols: TStrTable = initStrTable()
|
||||
var hasNull = false
|
||||
var needsReorder = false
|
||||
for i in 1..<n.len:
|
||||
if n[i].kind == nkEmpty: continue
|
||||
var useAutoCounter = false
|
||||
@@ -123,9 +122,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
else:
|
||||
localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc))
|
||||
if i != 1:
|
||||
if x != counter:
|
||||
needsReorder = true
|
||||
incl(result.flags, tfEnumHasHoles)
|
||||
if x != counter: incl(result.flags, tfEnumHasHoles)
|
||||
e.ast = strVal # might be nil
|
||||
counter = x
|
||||
of nkSym:
|
||||
@@ -176,13 +173,6 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
localError(c.config, n[i].info, errOverflowInEnumX % [e.name.s, $high(typeof(counter))])
|
||||
else:
|
||||
inc(counter)
|
||||
|
||||
if needsReorder:
|
||||
result.n.sons.sort(
|
||||
proc (x, y: PNode): int =
|
||||
result = cmp(x.sym.position, y.sym.position)
|
||||
)
|
||||
|
||||
if isPure and sfExported in result.sym.flags:
|
||||
addPureEnum(c, LazySym(sym: result.sym))
|
||||
if tfNotNil in e.typ.flags and not hasNull:
|
||||
@@ -257,39 +247,27 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
|
||||
else:
|
||||
return false
|
||||
|
||||
proc annotateClosureConv(n: PNode) =
|
||||
case n.kind
|
||||
of {nkNone..nkNilLit}:
|
||||
discard
|
||||
of nkTupleConstr:
|
||||
if n.typ.kind == tyProc and n.typ.callConv == ccClosure and
|
||||
n[0].typ.kind == tyProc and n[0].typ.callConv != ccClosure:
|
||||
# restores `transf.generateThunk`
|
||||
n[0] = newTreeIT(nkHiddenSubConv, n[0].info, n.typ,
|
||||
newNodeI(nkEmpty, n[0].info), n[0])
|
||||
n.transitionSonsKind(nkClosure)
|
||||
n.flags.incl nfTransf
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
annotateClosureConv(n[i])
|
||||
|
||||
proc fitDefaultNode(c: PContext, n: var PNode, expectedType: PType) =
|
||||
proc fitDefaultNode(c: PContext, n: PNode): PType =
|
||||
inc c.inStaticContext
|
||||
n = semConstExpr(c, n, expectedType = expectedType)
|
||||
let oldType = n.typ
|
||||
n.flags.incl nfSem
|
||||
if expectedType != nil and oldType != expectedType:
|
||||
n = fitNodeConsiderViewType(c, expectedType, n, n.info)
|
||||
changeType(c, n, expectedType, true) # infer types for default fields value
|
||||
# bug #22926; be cautious that it uses `semConstExpr` to
|
||||
# evaulate the default fields; it's only natural to use
|
||||
# `changeType` to infer types for constant values
|
||||
# that's also the reason why we don't use `semExpr` to check
|
||||
# the type since two overlapping error messages might be produced
|
||||
annotateClosureConv(n)
|
||||
let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
|
||||
n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
|
||||
let oldType = n[^1].typ
|
||||
n[^1].flags.incl nfSem
|
||||
if n[^2].kind != nkEmpty:
|
||||
if expectedType != nil and oldType != expectedType:
|
||||
n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info)
|
||||
changeType(c, n[^1], expectedType, true) # infer types for default fields value
|
||||
# bug #22926; be cautious that it uses `semConstExpr` to
|
||||
# evaulate the default fields; it's only natural to use
|
||||
# `changeType` to infer types for constant values
|
||||
# that's also the reason why we don't use `semExpr` to check
|
||||
# the type since two overlapping error messages might be produced
|
||||
result = n[^1].typ
|
||||
else:
|
||||
result = n[^1].typ
|
||||
# xxx any troubles related to defaults fields, consult `semConst` for a potential answer
|
||||
if n.kind != nkNilLit:
|
||||
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
|
||||
if n[^1].kind != nkNilLit:
|
||||
typeAllowedCheck(c, n.info, result, skConst, {taProcContextIsNotMacro, taIsDefaultField})
|
||||
dec c.inStaticContext
|
||||
|
||||
proc isRecursiveType*(t: PType): bool =
|
||||
@@ -409,12 +387,8 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
result = makeRangeWithStaticExpr(c, e.typ.n)
|
||||
elif e.kind in {nkIntLit..nkUInt64Lit}:
|
||||
if e.intVal < 0:
|
||||
if e.kind in {nkIntLit..nkInt64Lit}:
|
||||
localError(c.config, n.info,
|
||||
"Array length can't be negative, but was " & $e.intVal)
|
||||
else:
|
||||
localError(c.config, n.info,
|
||||
"Array length can't exceed its maximum value (9223372036854775807), but was " & $cast[BiggestUInt](e.intVal))
|
||||
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):
|
||||
if e.typ.kind == tyStatic:
|
||||
@@ -520,14 +494,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
var hasDefaultField = a[^1].kind != nkEmpty
|
||||
if hasDefaultField:
|
||||
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
|
||||
if c.inGenericContext > 0:
|
||||
a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ)
|
||||
if typ == nil:
|
||||
typ = a[^1].typ
|
||||
else:
|
||||
fitDefaultNode(c, a[^1], typ)
|
||||
typ = a[^1].typ
|
||||
typ = fitDefaultNode(c, a)
|
||||
elif a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
|
||||
@@ -891,15 +858,8 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
var typ: PType
|
||||
var hasDefaultField = n[^1].kind != nkEmpty
|
||||
if hasDefaultField:
|
||||
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
|
||||
if c.inGenericContext > 0:
|
||||
n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ)
|
||||
if typ == nil:
|
||||
typ = n[^1].typ
|
||||
else:
|
||||
fitDefaultNode(c, n[^1], typ)
|
||||
typ = n[^1].typ
|
||||
propagateToOwner(rectype, typ)
|
||||
typ = fitDefaultNode(c, n)
|
||||
propagateToOwner(rectype, typ)
|
||||
elif n[^2].kind == nkEmpty:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
typ = errorType(c)
|
||||
@@ -1501,10 +1461,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
addParamOrResult(c, arg, kind)
|
||||
styleCheckDef(c, a[j].info, arg)
|
||||
onDef(a[j].info, arg)
|
||||
if a[j].kind == nkPragmaExpr:
|
||||
a[j][0] = newSymNode(arg)
|
||||
else:
|
||||
a[j] = newSymNode(arg)
|
||||
a[j] = newSymNode(arg)
|
||||
|
||||
var r: PType = nil
|
||||
if n[0].kind != nkEmpty:
|
||||
@@ -1674,11 +1631,6 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
var err = "cannot instantiate "
|
||||
err.addTypeHeader(c.config, t)
|
||||
err.add "\ngot: <$1>\nbut expected: <$2>" % [describeArgs(c, n), describeArgs(c, t.n, 0)]
|
||||
if m.firstMismatch.kind == kTypeMismatch and m.firstMismatch.arg < n.len:
|
||||
let nArg = n[m.firstMismatch.arg]
|
||||
if nArg.kind in nkSymChoices:
|
||||
err.add "\n"
|
||||
err.add ambiguousIdentifierMsg(nArg)
|
||||
localError(c.config, n.info, errGenerated, err)
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
|
||||
@@ -1879,7 +1831,7 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
|
||||
x.add(operand.copyTreeWithoutNode(p))
|
||||
# recursion assures that this works for multiple macro annotations too:
|
||||
var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
|
||||
if r != nil and (r.typ == nil or r.typ.kind != tyFromExpr):
|
||||
if r != nil:
|
||||
doAssert r[0].kind == nkSym
|
||||
let m = r[0].sym
|
||||
case m.kind
|
||||
|
||||
@@ -80,7 +80,7 @@ type
|
||||
owner*: PSym # where this instantiation comes from
|
||||
recursionLimit: int
|
||||
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym
|
||||
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode
|
||||
|
||||
@@ -95,8 +95,8 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code
|
||||
debug t
|
||||
writeStackTrace()
|
||||
|
||||
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType, isInstValue = false): PType =
|
||||
result = replaceTypeVarsTAux(cl, t, isInstValue)
|
||||
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
|
||||
result = replaceTypeVarsTAux(cl, t)
|
||||
checkMetaInvariants(cl, result)
|
||||
|
||||
proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
@@ -276,11 +276,6 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
|
||||
replaceTypeVarsS(cl, n.sym, result.typ)
|
||||
else:
|
||||
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
|
||||
if result.sym.kind == skField and result.sym.ast != nil and
|
||||
(cl.owner == nil or result.sym.owner == cl.owner):
|
||||
# instantiate default value of object/tuple field
|
||||
cl.c.fitDefaultNode(cl.c, result.sym.ast, result.sym.typ)
|
||||
result.sym.typ = result.sym.ast.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
|
||||
@@ -344,7 +339,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
|
||||
#[
|
||||
|
||||
We cannot naively check for symbol recursions, because otherwise
|
||||
object types A, B would share their fields!
|
||||
object types A, B whould share their fields!
|
||||
|
||||
import tables
|
||||
|
||||
@@ -481,7 +476,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
return
|
||||
|
||||
let bbody = last body
|
||||
var newbody = replaceTypeVarsT(cl, bbody, isInstValue = true)
|
||||
var newbody = replaceTypeVarsT(cl, bbody)
|
||||
cl.skipTypedesc = oldSkipTypedesc
|
||||
newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
|
||||
result.flags = result.flags + newbody.flags - tfInstClearedFlags
|
||||
@@ -578,7 +573,7 @@ proc propagateFieldFlags(t: PType, n: PNode) =
|
||||
propagateFieldFlags(t, son)
|
||||
else: discard
|
||||
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType =
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
template bailout =
|
||||
if (t.sym == nil) or (t.sym != nil and sfGeneratedType in t.sym.flags):
|
||||
# In the first case 't.sym' can be 'nil' if the type is a ref/ptr, see
|
||||
@@ -712,17 +707,13 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
propagateToOwner(result, result.last)
|
||||
|
||||
else:
|
||||
if containsGenericType(t) or
|
||||
# nominal types as direct generic instantiation values
|
||||
# are re-instantiated even if they don't contain generic fields
|
||||
(isInstValue and (t.kind in {tyDistinct, tyObject} or isRefPtrObject(t))):
|
||||
if containsGenericType(t):
|
||||
#if not cl.allowMetaTypes:
|
||||
bailout()
|
||||
result = instCopyType(cl, t)
|
||||
result.size = -1 # needs to be recomputed
|
||||
#if not cl.allowMetaTypes:
|
||||
cl.localCache[t.itemId] = result
|
||||
let propagateInstValue = isInstValue and isRefPtrObject(t)
|
||||
|
||||
for i, resulti in result.ikids:
|
||||
if resulti != nil:
|
||||
@@ -732,7 +723,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
typeToString(result[i], preferDesc) &
|
||||
"' inside of type definition: '" &
|
||||
t.owner.name.s & "'; Maybe generic arguments are missing?")
|
||||
var r = replaceTypeVarsT(cl, resulti, isInstValue = propagateInstValue)
|
||||
var r = replaceTypeVarsT(cl, resulti)
|
||||
if result.kind == tyObject:
|
||||
# carefully coded to not skip the precious tyGenericInst:
|
||||
let r2 = r.skipTypes({tyAlias, tySink, tyOwned})
|
||||
|
||||
@@ -85,9 +85,6 @@ type
|
||||
inheritancePenalty: int
|
||||
firstMismatch*: MismatchInfo # mismatch info for better error messages
|
||||
diagnosticsEnabled*: bool
|
||||
newlyTypedOperands*: seq[int]
|
||||
## indexes of arguments that are newly typechecked in this match
|
||||
## used for type bound op additions
|
||||
|
||||
TTypeRelFlag* = enum
|
||||
trDontBind
|
||||
@@ -595,6 +592,45 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
|
||||
else: result = isIntConv
|
||||
else: result = isNone
|
||||
|
||||
proc reduceToBase(f: PType): PType =
|
||||
#[
|
||||
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
|
||||
]#
|
||||
case f.kind:
|
||||
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 = 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.skipModifier
|
||||
of tyStatic, tyOwned, tyVar, tyLent, tySink:
|
||||
result = reduceToBase(f.base)
|
||||
of tyInferred:
|
||||
# This is not true "After a candidate type is selected"
|
||||
result = reduceToBase(f.base)
|
||||
of tyRange:
|
||||
result = f.elementType
|
||||
else:
|
||||
result = f
|
||||
|
||||
proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
|
||||
if fGenericOrigin != nil and last.kind == tyGenericInst and
|
||||
last.kidsLen-1 == fGenericOrigin.kidsLen:
|
||||
@@ -603,22 +639,12 @@ proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
|
||||
if x == nil:
|
||||
put(c, fGenericOrigin[i], last[i])
|
||||
|
||||
proc isGenericObjectOf(f, a: PType): bool =
|
||||
## checks if `f` is an unparametrized generic type
|
||||
## that `a` is an instance of
|
||||
if not (f.sym != nil and f.sym.typ.kind == tyGenericBody):
|
||||
# covers the case where `f` is the last child (body) of the `tyGenericBody`
|
||||
return false
|
||||
let aRoot = genericRoot(a)
|
||||
# use sym equality to check if the `tyGenericBody` types are equal
|
||||
result = aRoot != nil and f.sym == aRoot.sym
|
||||
|
||||
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
|
||||
var t = a
|
||||
assert t.kind == tyObject
|
||||
var depth = 0
|
||||
var last = a
|
||||
while t != nil and not (sameObjectTypes(f, t) or isGenericObjectOf(f, t)):
|
||||
while t != nil and not sameObjectTypes(f, t):
|
||||
if t.kind != tyObject: # avoid entering generic params etc
|
||||
return -1
|
||||
t = t.baseClass
|
||||
@@ -1692,8 +1718,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if aAsObject.kind == tyObject and trIsOutParam notin flags:
|
||||
let baseType = aAsObject.base
|
||||
if baseType != nil:
|
||||
if tfFinal notin aAsObject.flags:
|
||||
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
|
||||
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
|
||||
let ret = typeRel(c, f, baseType, flags)
|
||||
return if ret in {isEqual,isGeneric}: isSubtype else: ret
|
||||
|
||||
@@ -1734,10 +1759,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let tr = typeRel(c, f[i], x[i], flags)
|
||||
if tr <= isSubtype: return
|
||||
result = isGeneric
|
||||
let impl = last(f[0])
|
||||
if impl.kind == tyObject and tfFinal notin impl.flags:
|
||||
# match non-invocation case
|
||||
inc c.inheritancePenalty, 0 + int(c.inheritancePenalty < 0)
|
||||
elif x.kind == tyGenericInst and f[0] == x[0] and
|
||||
x.len - 1 == f.len:
|
||||
for i in 1..<f.len:
|
||||
@@ -1794,8 +1815,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
depth = -1
|
||||
|
||||
if depth >= 0:
|
||||
if aobj.kind == tyObject and tfFinal notin aobj.flags:
|
||||
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
|
||||
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
|
||||
@@ -1845,12 +1865,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let target = f.genericHead
|
||||
let targetKind = target.kind
|
||||
var effectiveArgType = reduceToBase(a)
|
||||
# the skipped child of tyBuiltInTypeClass can be structured differently,
|
||||
# newConstraint constructs them with no children
|
||||
let typeClassArg = effectiveArgType.kind == tyBuiltInTypeClass
|
||||
effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
|
||||
if targetKind == effectiveArgType.kind:
|
||||
if not typeClassArg and effectiveArgType.isEmptyContainer:
|
||||
if effectiveArgType.isEmptyContainer:
|
||||
return isNone
|
||||
if targetKind == tyProc:
|
||||
if target.flags * {tfIterator} != effectiveArgType.flags * {tfIterator}:
|
||||
@@ -2711,7 +2728,7 @@ proc setSon(father: PNode, at: int, son: PNode) =
|
||||
# father[i] = newNodeIT(nkEmpty, son.info, getSysType(tyVoid))
|
||||
|
||||
# we are allowed to modify the calling node in the 'prepare*' procs:
|
||||
proc prepareOperand(c: PContext; formal: PType; a: PNode, newlyTyped: var bool): PNode =
|
||||
proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
|
||||
if formal.kind == tyUntyped and formal.len != 1:
|
||||
# {tyTypeDesc, tyUntyped, tyTyped, tyError}:
|
||||
# a.typ == nil is valid
|
||||
@@ -2729,17 +2746,15 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode, newlyTyped: var bool):
|
||||
#elif formal.kind == tyTyped: {efDetermineType, efWantStmt}
|
||||
#else: {efDetermineType}
|
||||
result = c.semOperand(c, a, flags)
|
||||
newlyTyped = true
|
||||
else:
|
||||
result = a
|
||||
considerGenSyms(c, result)
|
||||
if result.kind != nkHiddenDeref and result.typ.kind in {tyVar, tyLent} and c.matchedConcept == nil:
|
||||
result = newDeref(result)
|
||||
|
||||
proc prepareOperand(c: PContext; a: PNode, newlyTyped: var bool): PNode =
|
||||
proc prepareOperand(c: PContext; a: PNode): PNode =
|
||||
if a.typ.isNil:
|
||||
result = c.semOperand(c, a, {efDetermineType})
|
||||
newlyTyped = true
|
||||
else:
|
||||
result = a
|
||||
considerGenSyms(c, result)
|
||||
@@ -2865,9 +2880,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
noMatch()
|
||||
m.baseTypeMatch = false
|
||||
m.typedescMatched = false
|
||||
var newlyTyped = false
|
||||
n[a][1] = prepareOperand(c, formal.typ, n[a][1], newlyTyped)
|
||||
if newlyTyped: m.newlyTypedOperands.add(a)
|
||||
n[a][1] = prepareOperand(c, formal.typ, n[a][1])
|
||||
n[a].typ() = n[a][1].typ
|
||||
arg = paramTypesMatch(m, formal.typ, n[a].typ,
|
||||
n[a][1], n[a][1])
|
||||
@@ -2891,9 +2904,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
if tfVarargs in m.callee.flags:
|
||||
# is ok... but don't increment any counters...
|
||||
# we have no formal here to snoop at:
|
||||
var newlyTyped = false
|
||||
n[a] = prepareOperand(c, n[a], newlyTyped)
|
||||
if newlyTyped: m.newlyTypedOperands.add(a)
|
||||
n[a] = prepareOperand(c, n[a])
|
||||
if skipTypes(n[a].typ, abstractVar-{tyTypeDesc}).kind==tyString:
|
||||
m.call.add implicitConv(nkHiddenStdConv,
|
||||
getSysType(c.graph, n[a].info, tyCstring),
|
||||
@@ -2907,9 +2918,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
m.baseTypeMatch = false
|
||||
m.typedescMatched = false
|
||||
incl(marker, formal.position)
|
||||
var newlyTyped = false
|
||||
n[a] = prepareOperand(c, formal.typ, n[a], newlyTyped)
|
||||
if newlyTyped: m.newlyTypedOperands.add(a)
|
||||
n[a] = prepareOperand(c, formal.typ, n[a])
|
||||
arg = paramTypesMatch(m, formal.typ, n[a].typ,
|
||||
n[a], nOrig[a])
|
||||
if arg != nil and m.baseTypeMatch and container != nil:
|
||||
@@ -2945,9 +2954,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
else:
|
||||
m.baseTypeMatch = false
|
||||
m.typedescMatched = false
|
||||
var newlyTyped = false
|
||||
n[a] = prepareOperand(c, formal.typ, n[a], newlyTyped)
|
||||
if newlyTyped: m.newlyTypedOperands.add(a)
|
||||
n[a] = prepareOperand(c, formal.typ, n[a])
|
||||
arg = paramTypesMatch(m, formal.typ, n[a].typ,
|
||||
n[a], nOrig[a])
|
||||
if arg == nil:
|
||||
|
||||
@@ -106,13 +106,6 @@ proc transformSons(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
for i in 0..<n.len:
|
||||
result[i] = transform(c, n[i], noConstFold)
|
||||
|
||||
proc transformSonsAfterType(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
result = newTransNode(n)
|
||||
assert n.len != 0
|
||||
result[0] = copyTree(n[0])
|
||||
for i in 1..<n.len:
|
||||
result[i] = transform(c, n[i], noConstFold)
|
||||
|
||||
proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode =
|
||||
result = newTransNode(kind, ri.info, 2)
|
||||
result[0] = le
|
||||
@@ -373,19 +366,6 @@ proc transformAsgn(c: PTransf, n: PNode): PNode =
|
||||
result[0] = letSection
|
||||
result[1] = asgnNode
|
||||
|
||||
template assignTupleUnpacking(c: PTransf, e: PNode) =
|
||||
for i in 0..<c.transCon.forStmt.len - 2:
|
||||
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, e, i), j))
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
else:
|
||||
let lhs = c.transCon.forStmt[i]
|
||||
let rhs = transform(c, newTupleAccess(c.graph, e, i))
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
|
||||
|
||||
proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
proc asgnTo(lhs: PNode, rhs: PNode): PNode =
|
||||
# Choose the right assignment instruction according to the given ``lhs``
|
||||
@@ -420,8 +400,7 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
let lhs = c.transCon.forStmt[i]
|
||||
let rhs = transform(c, v)
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
elif e.kind notin {nkAddr, nkHiddenAddr} and e.kind != nkSym:
|
||||
# no need to generate temp for address operation + nodes without sideeffects
|
||||
elif e.kind notin {nkAddr, nkHiddenAddr}: # no need to generate temp for address operation
|
||||
# TODO do not use temp for nodes which cannot have side-effects
|
||||
var tmp = newTemp(c, e.typ, e.info)
|
||||
let v = newNodeI(nkVarSection, e.info)
|
||||
@@ -429,9 +408,21 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
|
||||
result.add transform(c, v)
|
||||
|
||||
assignTupleUnpacking(c, tmp)
|
||||
for i in 0..<c.transCon.forStmt.len - 2:
|
||||
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:
|
||||
assignTupleUnpacking(c, e)
|
||||
for i in 0..<c.transCon.forStmt.len - 2:
|
||||
let lhs = c.transCon.forStmt[i]
|
||||
let rhs = transform(c, newTupleAccess(c.graph, e, i))
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
else:
|
||||
if c.transCon.forStmt[0].kind == nkVarTuple:
|
||||
var notLiteralTuple = false # we don't generate temp for tuples with const value: (1, 2, 3)
|
||||
@@ -444,8 +435,7 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
else:
|
||||
notLiteralTuple = true
|
||||
|
||||
if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple and e.kind != nkSym:
|
||||
# no need to generate temp for address operation + nodes without sideeffects
|
||||
if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple:
|
||||
# TODO do not use temp for nodes which cannot have side-effects
|
||||
var tmp = newTemp(c, e.typ, e.info)
|
||||
let v = newNodeI(nkVarSection, e.info)
|
||||
@@ -517,9 +507,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds, isAddr = false)
|
||||
) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and
|
||||
n.typ.kind == tyVar and
|
||||
n.typ.skipTypes(abstractVar).kind == tyOpenArray and
|
||||
n[0][0].typ.skipTypes(abstractVar).kind == tyString) and
|
||||
not (isAddr and n.typ.kind == tyVar and n[0][0].typ.kind == tyRef and
|
||||
n[0][0].kind == nkObjConstr)
|
||||
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
|
||||
@@ -649,12 +637,6 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
|
||||
case arg.kind
|
||||
of nkStmtListExpr:
|
||||
return paComplexOpenarray
|
||||
of nkCall:
|
||||
if skipTypes(arg.typ, abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
# XXX incorrect, causes #13417 when `arg` has side effects.
|
||||
return paDirectMapping
|
||||
else:
|
||||
return paComplexOpenarray
|
||||
of nkBracket:
|
||||
return paFastAsgnTakeTypeFromArg
|
||||
else:
|
||||
@@ -821,7 +803,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
|
||||
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, addrExp, true))
|
||||
newC.mapping[formal.itemId] = newDeref(temp)
|
||||
of paComplexOpenarray:
|
||||
# XXX arrays will deep copy here (pretty bad).
|
||||
# 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))
|
||||
@@ -875,18 +857,9 @@ proc transformArrayAccess(c: PTransf, n: PNode): PNode =
|
||||
if n[0].kind == nkSym and n[0].sym.kind == skType:
|
||||
result = n
|
||||
else:
|
||||
result = transformSons(c, n)
|
||||
if n.len >= 2 and result[1].kind in {nkChckRange, nkChckRange64} and
|
||||
n[1].kind in {nkHiddenStdConv, nkHiddenSubConv}:
|
||||
# implicit conversion, was transformed into range check
|
||||
# remove in favor of index check if conversion to array index type
|
||||
# has to be done here because the array index type needs to be relaxed
|
||||
# i.e. a uint32 index can implicitly convert to range[0..3] but not int
|
||||
let arr = skipTypes(n[0].typ, abstractVarRange)
|
||||
if arr.kind == tyArray and
|
||||
firstOrd(c.graph.config, arr) == getOrdValue(result[1][1]) and
|
||||
lastOrd(c.graph.config, arr) == getOrdValue(result[1][2]):
|
||||
result[1] = result[1].skipConv
|
||||
result = newTransNode(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = transform(c, skipConv(n[i]))
|
||||
|
||||
proc getMergeOp(n: PNode): PSym =
|
||||
case n.kind
|
||||
@@ -1096,7 +1069,9 @@ proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
of nkBreakStmt: result = transformBreak(c, n)
|
||||
of nkCallKinds:
|
||||
result = transformCall(c, n)
|
||||
of nkAddr, nkHiddenAddr:
|
||||
of nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, {nkHiddenDeref}, isAddr = true)
|
||||
of nkAddr:
|
||||
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}, isAddr = true)
|
||||
of nkDerefExpr:
|
||||
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
|
||||
@@ -1109,9 +1084,6 @@ proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = transformConv(c, n)
|
||||
of nkObjConstr, nkCast:
|
||||
# don't try to transform type node
|
||||
result = transformSonsAfterType(c, n)
|
||||
of nkDiscardStmt:
|
||||
result = n
|
||||
if n[0].kind != nkEmpty:
|
||||
|
||||
@@ -209,6 +209,9 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
result = typeAllowedAux(marker, t.skipModifier, kind, c, flags+{taHeap})
|
||||
else:
|
||||
result = t
|
||||
of tyConcept:
|
||||
if kind != skParam: result = t
|
||||
else: result = nil
|
||||
|
||||
proc typeAllowed*(t: PType, kind: TSymKind; c: PContext; flags: TTypeAllowedFlags = {}): PType =
|
||||
# returns 'nil' on success and otherwise the part of the type that is
|
||||
|
||||
@@ -102,9 +102,6 @@ const
|
||||
# typedescX is used if we're sure tyTypeDesc should be included (or skipped)
|
||||
typedescPtrs* = abstractPtrs + {tyTypeDesc}
|
||||
typedescInst* = abstractInst + {tyTypeDesc, tyOwned, tyUserTypeClass}
|
||||
|
||||
# incorrect definition of `[]` and `[]=` for these types in system.nim
|
||||
arrPutGetMagicApplies* = {tyArray, tyOpenArray, tyString, tySequence, tyCstring, tyTuple}
|
||||
|
||||
proc invalidGenericInst*(f: PType): bool =
|
||||
result = f.kind == tyGenericInst and skipModifier(f) == nil
|
||||
@@ -1204,19 +1201,17 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
|
||||
if not result: return
|
||||
|
||||
proc isGenericAlias*(t: PType): bool =
|
||||
return t.kind == tyGenericInst and t.skipModifier.skipTypes({tyAlias}).kind == tyGenericInst
|
||||
return t.kind == tyGenericInst and t.skipModifier.kind == tyGenericInst
|
||||
|
||||
proc genericAliasDepth*(t: PType): int =
|
||||
result = 0
|
||||
var it = t.skipTypes({tyAlias})
|
||||
var it = t
|
||||
while it.isGenericAlias:
|
||||
it = it.skipModifier.skipTypes({tyAlias})
|
||||
it = it.skipModifier
|
||||
inc result
|
||||
|
||||
proc skipGenericAlias*(t: PType): PType =
|
||||
result = t.skipTypes({tyAlias})
|
||||
if result.isGenericAlias:
|
||||
result = result.skipModifier.skipTypes({tyAlias})
|
||||
return if t.isGenericAlias: t.skipModifier else: t
|
||||
|
||||
proc sameFlags*(a, b: PType): bool {.inline.} =
|
||||
result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
|
||||
@@ -1309,34 +1304,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
cycleCheck()
|
||||
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
|
||||
of tyObject:
|
||||
result = sameFlags(a, b)
|
||||
if result:
|
||||
withoutShallowFlags:
|
||||
ifFastObjectTypeCheckFailed(a, b):
|
||||
cycleCheck()
|
||||
# should be generic, and belong to the same generic head type:
|
||||
assert a.typeInst != nil, "generic object " & $a & " has no typeInst"
|
||||
assert b.typeInst != nil, "generic object " & $b & " has no typeInst"
|
||||
if result:
|
||||
withoutShallowFlags:
|
||||
# this is required because of generic `ref object`s,
|
||||
# the value of their dereferences are not wrapped in `tyGenericInst`,
|
||||
# so we need to check the generic parameters here
|
||||
for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
result = sameObjectStructures(a, b, c) and sameFlags(a, b)
|
||||
of tyDistinct:
|
||||
cycleCheck()
|
||||
if c.cmp == dcEq:
|
||||
result = sameFlags(a, b)
|
||||
if result:
|
||||
if sameFlags(a, b):
|
||||
ifFastObjectTypeCheckFailed(a, b):
|
||||
# should be generic, and belong to the same generic head type:
|
||||
assert a.typeInst != nil, "generic distinct type " & $a & " has no typeInst"
|
||||
assert b.typeInst != nil, "generic distinct type " & $b & " has no typeInst"
|
||||
withoutShallowFlags:
|
||||
# just in case `tyGenericInst` was skipped at some point,
|
||||
# we need to check the generic parameters here
|
||||
for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
result = sameTypeAux(a.elementType, b.elementType, c)
|
||||
else:
|
||||
result = sameTypeAux(a.elementType, b.elementType, c) and sameFlags(a, b)
|
||||
of tyEnum, tyForward:
|
||||
@@ -1958,125 +1935,3 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
|
||||
result = false
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc isRefPtrObject*(t: PType): bool =
|
||||
t.kind in {tyRef, tyPtr} and tfRefsAnonObj in t.flags
|
||||
|
||||
proc nominalRoot*(t: PType): PType =
|
||||
## the "name" type of a given instance of a nominal type,
|
||||
## i.e. the type directly associated with the symbol where the root
|
||||
## nominal type of `t` was defined, skipping things like generic instances,
|
||||
## aliases, `var`/`sink`/`typedesc` modifiers
|
||||
##
|
||||
## instead of returning the uninstantiated body of a generic type,
|
||||
## returns the type of the symbol instead (with tyGenericBody type)
|
||||
result = nil
|
||||
case t.kind
|
||||
of tyAlias, tyVar, tySink:
|
||||
# varargs?
|
||||
result = nominalRoot(t.skipModifier)
|
||||
of tyTypeDesc:
|
||||
# for proc foo(_: type T)
|
||||
result = nominalRoot(t.skipModifier)
|
||||
of tyGenericInvocation, tyGenericInst:
|
||||
result = t
|
||||
# skip aliases, so this works in the same module but not in another module:
|
||||
# type Foo[T] = object
|
||||
# type Bar[T] = Foo[T]
|
||||
# proc foo[T](x: Bar[T]) = ... # attached to type
|
||||
while result.skipModifier.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
result = result.skipModifier
|
||||
result = nominalRoot(result[0])
|
||||
of tyGenericBody:
|
||||
result = t
|
||||
# this time skip the aliases but take the generic body
|
||||
while result.skipModifier.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
result = result.skipModifier[0]
|
||||
let val = result.skipModifier
|
||||
if val.kind in {tyDistinct, tyEnum, tyObject} or
|
||||
isRefPtrObject(val):
|
||||
# atomic nominal types, this generic body is attached to them
|
||||
discard
|
||||
else:
|
||||
result = nominalRoot(val)
|
||||
of tyCompositeTypeClass:
|
||||
# parameter with type Foo
|
||||
result = nominalRoot(t.skipModifier)
|
||||
of tyGenericParam:
|
||||
if t.genericParamHasConstraints:
|
||||
# T: Foo
|
||||
result = nominalRoot(t.genericConstraint)
|
||||
else:
|
||||
result = nil
|
||||
of tyDistinct, tyEnum, tyObject:
|
||||
result = t
|
||||
of tyPtr, tyRef:
|
||||
if tfRefsAnonObj in t.flags:
|
||||
# in the case that we have `type Foo = ref object` etc
|
||||
result = t
|
||||
else:
|
||||
# we could allow this in general, but there's things like `seq[Foo]`
|
||||
#result = nominalRoot(t.skipModifier)
|
||||
result = nil
|
||||
of tyStatic:
|
||||
result = nominalRoot(t.base)
|
||||
else:
|
||||
# skips all typeclasses
|
||||
# is this correct for `concept`?
|
||||
result = nil
|
||||
|
||||
proc genericRoot*(t: PType): PType =
|
||||
## gets the root generic type (`tyGenericBody`) from `t`,
|
||||
## if `t` is a generic type or the body of a generic instantiation
|
||||
case t.kind
|
||||
of tyGenericBody:
|
||||
result = t
|
||||
of tyGenericInst, tyGenericInvocation:
|
||||
result = t.genericHead
|
||||
else:
|
||||
if t.typeInst != nil:
|
||||
result = t.typeInst.genericHead
|
||||
elif t.sym != nil and t.sym.typ.kind == tyGenericBody:
|
||||
# can happen if `t` is the last child (body) of the generic body
|
||||
result = t.sym.typ
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc reduceToBase*(f: PType): PType =
|
||||
#[
|
||||
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
|
||||
]#
|
||||
case f.kind:
|
||||
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 = 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.skipModifier
|
||||
of tyStatic, tyOwned, tyVar, tyLent, tySink:
|
||||
result = reduceToBase(f.base)
|
||||
of tyInferred:
|
||||
# This is not true "After a candidate type is selected"
|
||||
result = reduceToBase(f.base)
|
||||
of tyRange:
|
||||
result = f.elementType
|
||||
else:
|
||||
result = f
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
import semmacrosanity
|
||||
import
|
||||
std/[strutils, tables, intsets, parseutils],
|
||||
std/[strutils, tables, parseutils],
|
||||
msgs, vmdef, vmgen, nimsets, types,
|
||||
parser, vmdeps, idents, trees, renderer, options, transf,
|
||||
gorgeimpl, lineinfos, btrees, macrocacheimpl,
|
||||
@@ -516,8 +516,6 @@ const
|
||||
errIllegalConvFromXtoY = "illegal conversion from '$1' to '$2'"
|
||||
errTooManyIterations = "interpretation requires too many iterations; " &
|
||||
"if you are sure this is not a bug in your code, compile with `--maxLoopIterationsVM:number` (current value: $1)"
|
||||
errCallDepthExceeded = "maximum call depth for the VM exceeded; " &
|
||||
"if you are sure this is not a bug in your code, compile with `--maxCallDepthVM:number` (current value: $1)"
|
||||
errFieldXNotFound = "node lacks field: "
|
||||
|
||||
|
||||
@@ -592,7 +590,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
let newPc = c.cleanUpOnReturn(tos)
|
||||
# Perform any cleanup action before returning
|
||||
if newPc < 0:
|
||||
inc(c.callDepth)
|
||||
pc = tos.comesFrom
|
||||
let retVal = regs[0]
|
||||
tos = tos.next
|
||||
@@ -1448,14 +1445,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
newFrame.slots[i] = regs[rb+i]
|
||||
if isClosure:
|
||||
newFrame.slots[rc] = TFullReg(kind: rkNode, node: regs[rb].node[1])
|
||||
if c.callDepth <= 0:
|
||||
if allowInfiniteRecursion in c.features:
|
||||
c.callDepth = c.config.maxCallDepthVM
|
||||
else:
|
||||
msgWriteln(c.config, "stack trace: (most recent call last)", {msgNoUnitSep})
|
||||
stackTraceAux(c, tos, pc)
|
||||
globalError(c.config, c.debug[pc], errCallDepthExceeded % $c.config.maxCallDepthVM)
|
||||
dec(c.callDepth)
|
||||
tos = newFrame
|
||||
updateRegsAlias
|
||||
# -1 for the following 'inc pc'
|
||||
@@ -2322,7 +2311,6 @@ proc execute(c: PCtx, start: int): PNode =
|
||||
|
||||
proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
|
||||
c.loopIterations = c.config.maxLoopIterationsVM
|
||||
c.callDepth = c.config.maxCallDepthVM
|
||||
if sym.kind in routineKinds:
|
||||
if sym.typ.paramsLen != args.len:
|
||||
result = nil
|
||||
@@ -2425,12 +2413,9 @@ proc evalConstExprAux(module: PSym; idgen: IdGenerator;
|
||||
setupGlobalCtx(module, g, idgen)
|
||||
var c = PCtx g.vm
|
||||
let oldMode = c.mode
|
||||
let oldLocals = c.locals
|
||||
c.mode = mode
|
||||
c.locals = initIntSet()
|
||||
c.cannotEval = false
|
||||
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
|
||||
c.locals = oldLocals
|
||||
if c.cannotEval:
|
||||
return errorNode(idgen, prc, n)
|
||||
if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## This module contains the type definitions for the new evaluation engine.
|
||||
## An instruction is 1-3 int32s in memory, it is a register based VM.
|
||||
|
||||
import std/[tables, strutils, intsets]
|
||||
import std/[tables, strutils]
|
||||
|
||||
import ast, idents, options, modulegraphs, lineinfos
|
||||
|
||||
@@ -201,7 +201,6 @@ type
|
||||
TSandboxFlag* = enum ## what the evaluation engine should allow
|
||||
allowCast, ## allow unsafe language feature: 'cast'
|
||||
allowInfiniteLoops ## allow endless loops
|
||||
allowInfiniteRecursion ## allow infinite recursion
|
||||
TSandboxFlags* = set[TSandboxFlag]
|
||||
|
||||
TSlotKind* = enum # We try to re-use slots in a smart way to
|
||||
@@ -258,7 +257,7 @@ type
|
||||
mode*: TEvalMode
|
||||
features*: TSandboxFlags
|
||||
traceActive*: bool
|
||||
loopIterations*, callDepth*: int
|
||||
loopIterations*: int
|
||||
comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces
|
||||
callbacks*: seq[VmCallback]
|
||||
callbackIndex*: Table[string, int]
|
||||
@@ -272,7 +271,6 @@ type
|
||||
vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC)
|
||||
procToCodePos*: Table[int, int]
|
||||
cannotEval*: bool
|
||||
locals*: IntSet
|
||||
|
||||
PStackFrame* = ref TStackFrame
|
||||
TStackFrame* {.acyclic.} = object
|
||||
@@ -296,7 +294,6 @@ proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator
|
||||
PCtx(code: @[], debug: @[],
|
||||
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
||||
prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM,
|
||||
callDepth: g.config.maxCallDepthVM,
|
||||
comesFromHeuristic: unknownLineInfo, callbacks: @[], callbackIndex: initTable[string, int](), errorFlag: "",
|
||||
cache: cache, config: g.config, graph: g, idgen: idgen)
|
||||
|
||||
@@ -304,7 +301,6 @@ proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) =
|
||||
c.module = module
|
||||
c.prc = PProc(blocks: @[])
|
||||
c.loopIterations = c.config.maxLoopIterationsVM
|
||||
c.callDepth = c.config.maxCallDepthVM
|
||||
c.idgen = idgen
|
||||
|
||||
proc reverseName(s: string): string =
|
||||
|
||||
@@ -1543,9 +1543,7 @@ proc setSlot(c: PCtx; v: PSym) =
|
||||
v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
|
||||
|
||||
template cannotEval(c: PCtx; n: PNode) =
|
||||
if c.config.cmd == cmdCheck and c.config.m.errorOutputs != {}:
|
||||
# nim check command with no error outputs doesn't need to cascade here,
|
||||
# includes `tryConstExpr` case which should not continue generating code
|
||||
if c.config.cmd == cmdCheck:
|
||||
localError(c.config, n.info, "cannot evaluate at compile time: " &
|
||||
n.renderTree)
|
||||
c.cannotEval = true
|
||||
@@ -1583,7 +1581,6 @@ proc checkCanEval(c: PCtx; n: PNode) =
|
||||
# are in the right scope:
|
||||
if sfGenSym in s.flags and c.prc.sym == nil: discard
|
||||
elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard
|
||||
elif s.kind in {skVar, skLet} and s.id in c.locals: discard
|
||||
else: cannotEval(c, n)
|
||||
elif s.kind in {skProc, skFunc, skConverter, skMethod,
|
||||
skIterator} and sfForward in s.flags:
|
||||
@@ -1976,7 +1973,7 @@ proc genVarSection(c: PCtx; n: PNode) =
|
||||
c.gen(lowerTupleUnpacking(c.graph, a, c.idgen, c.getOwner))
|
||||
elif a[0].kind == nkSym:
|
||||
let s = a[0].sym
|
||||
c.locals.incl(s.id)
|
||||
checkCanEval(c, a[0])
|
||||
if s.isGlobal:
|
||||
let runtimeAccessToCompileTime = c.mode == emRepl and
|
||||
sfCompileTime in s.flags and s.position > 0
|
||||
@@ -2041,7 +2038,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||
c.gABx(n, opcLdNull, dest, c.genType(n.typ))
|
||||
|
||||
let intType = getSysType(c.graph, n.info, tyInt)
|
||||
let seqType = n.typ.skipTypes(abstractVar+{tyStatic}-{tyTypeDesc})
|
||||
let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
|
||||
if seqType.kind == tySequence:
|
||||
var tmp = c.getTemp(intType)
|
||||
c.gABx(n, opcLdImmInt, tmp, n.len)
|
||||
|
||||
@@ -263,7 +263,7 @@ proc registerAdditionalOps*(c: PCtx) =
|
||||
wrap2si(readLines, ioop)
|
||||
systemop getCurrentExceptionMsg
|
||||
systemop getCurrentException
|
||||
registerCallback c, "stdlib.staticos.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
|
||||
registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
|
||||
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
|
||||
registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
|
||||
setResult(a, dirExists(getString(a, 0)))
|
||||
|
||||
@@ -15,6 +15,7 @@ proc dispatch(x: Base, params: ...) =
|
||||
|
||||
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
|
||||
|
||||
@@ -32,7 +33,7 @@ proc dispatch(x: Base, params: ...) =
|
||||
dispatchObject,
|
||||
newIntNode(nkIntLit, index)
|
||||
)
|
||||
getVTableCall.typ() = getSysType(g, unknownLineInfo, tyPointer)
|
||||
getVTableCall.typ() = base.typ
|
||||
var vTableCall = newNodeIT(nkCall, base.info, base.typ.returnType)
|
||||
var castNode = newTree(nkCast,
|
||||
newNodeIT(nkType, base.info, base.typ),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
=====================================================
|
||||
Nim -- a Compiler for Nim. https://nim-lang.org/
|
||||
|
||||
Copyright (C) 2006-2025 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
|
||||
|
||||
@@ -51,7 +51,7 @@ Advanced options:
|
||||
--spellSuggest:num show at most `num >= 0` spelling suggestions on typos.
|
||||
if `num` is not specified (or `auto`), return
|
||||
an implementation defined set of suggestions.
|
||||
--hints:on|off|list `on|off` enables or disables hints.
|
||||
--hints:on|off|list. `on|off` enables or disables hints.
|
||||
`list` reports which hints are selected.
|
||||
--hint:X:on|off turn specific hint X on|off. `hint:X` means `hint:X:on`,
|
||||
as with similar flags. `all` is the set of all hints
|
||||
@@ -165,7 +165,6 @@ Advanced options:
|
||||
--verbosity:0|1|2|3 set Nim's verbosity level (1 is default)
|
||||
--errorMax:N stop compilation after N errors; 0 means unlimited
|
||||
--maxLoopIterationsVM:N set max iterations for all VM loops
|
||||
--maxCallDepthVM:N set max call depth in the VM
|
||||
--experimental:$1
|
||||
enable experimental language feature
|
||||
--legacy:$2
|
||||
|
||||
@@ -249,7 +249,7 @@ 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. It is influenced by the `--nimMainPrefix:prefix` switch, too.
|
||||
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
|
||||
@@ -358,7 +358,7 @@ Memory management
|
||||
|
||||
In the previous sections, the `NimMain()` function reared its head. Since
|
||||
JavaScript already provides automatic memory management, you can freely pass
|
||||
objects between the two languages without problems. In C and derivative languages
|
||||
objects between the two languages without problems. In C and derivate languages
|
||||
you need to be careful about what you do and how you share memory. The
|
||||
previous examples only dealt with simple scalar values, but passing a Nim
|
||||
string to C, or reading back a C string in Nim already requires you to be
|
||||
|
||||
128
doc/manual.md
128
doc/manual.md
@@ -5186,22 +5186,22 @@ caught by reference. Example:
|
||||
proc fn() =
|
||||
let a = initRuntimeError("foo")
|
||||
doAssert $a.what == "foo"
|
||||
var b = ""
|
||||
var b: cstring
|
||||
try: raise initRuntimeError("foo2")
|
||||
except CStdException as e:
|
||||
doAssert e is CStdException
|
||||
b = $e.what()
|
||||
doAssert b == "foo2"
|
||||
b = e.what()
|
||||
doAssert $b == "foo2"
|
||||
|
||||
try: raise initStdException()
|
||||
except CStdException: discard
|
||||
|
||||
try: raise initRuntimeError("foo3")
|
||||
except CRuntimeError as e:
|
||||
b = $e.what()
|
||||
b = e.what()
|
||||
except CStdException:
|
||||
doAssert false
|
||||
doAssert b == "foo3"
|
||||
doAssert $b == "foo3"
|
||||
|
||||
fn()
|
||||
```
|
||||
@@ -6264,7 +6264,7 @@ The default for symbols of entity `type`, `var`, `let` and `const`
|
||||
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 overridden by marking the routine as `inject`.
|
||||
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:
|
||||
|
||||
@@ -9043,119 +9043,3 @@ This means the following compiles (for now) even though it really should not:
|
||||
inc i
|
||||
access a[i].v
|
||||
```
|
||||
|
||||
Strict definitions and `out` parameters
|
||||
=======================================
|
||||
|
||||
*every* local variable must be initialized explicitly before it can be used:
|
||||
|
||||
```nim
|
||||
proc test =
|
||||
var s: seq[string]
|
||||
s.add "abc" # invalid!
|
||||
```
|
||||
|
||||
Needs to be written as:
|
||||
|
||||
```nim
|
||||
proc test =
|
||||
var s: seq[string] = @[]
|
||||
s.add "abc" # valid!
|
||||
```
|
||||
|
||||
A control flow analysis is performed in order to prove that a variable has been written to
|
||||
before it is used. Thus the following is valid:
|
||||
|
||||
```nim
|
||||
proc test(cond: bool) =
|
||||
var s: seq[string]
|
||||
if cond:
|
||||
s = @["y"]
|
||||
else:
|
||||
s = @[]
|
||||
s.add "abc" # valid!
|
||||
```
|
||||
|
||||
In this example every path does set `s` to a value before it is used.
|
||||
|
||||
```nim
|
||||
proc test(cond: bool) =
|
||||
let s: seq[string]
|
||||
if cond:
|
||||
s = @["y"]
|
||||
else:
|
||||
s = @[]
|
||||
```
|
||||
|
||||
`let` statements are allowed to not have an initial value, but every path should set `s` to a value before it is used.
|
||||
|
||||
|
||||
`out` parameters
|
||||
----------------
|
||||
|
||||
An `out` parameter is like a `var` parameter but it must be written to before it can be used:
|
||||
|
||||
```nim
|
||||
proc myopen(f: out File; name: string): bool =
|
||||
f = default(File)
|
||||
result = open(f, name)
|
||||
```
|
||||
|
||||
While it is usually the better style to use the return type in order to return results API and ABI
|
||||
considerations might make this infeasible. Like for `var T` Nim maps `out T` to a hidden pointer.
|
||||
For example POSIX's `stat` routine can be wrapped as:
|
||||
|
||||
```nim
|
||||
proc stat*(a1: cstring, a2: out Stat): cint {.importc, header: "<sys/stat.h>".}
|
||||
```
|
||||
|
||||
When the implementation of a routine with output parameters is analysed, the compiler
|
||||
checks that every path before the (implicit or explicit) return does set every output
|
||||
parameter:
|
||||
|
||||
```nim
|
||||
proc p(x: out int; y: out string; cond: bool) =
|
||||
x = 4
|
||||
if cond:
|
||||
y = "abc"
|
||||
# error: not every path initializes 'y'
|
||||
```
|
||||
|
||||
|
||||
Out parameters and exception handling
|
||||
-------------------------------------
|
||||
|
||||
The analysis should take exceptions into account (but currently does not):
|
||||
|
||||
```nim
|
||||
proc p(x: out int; y: out string; cond: bool) =
|
||||
x = canRaise(45)
|
||||
y = "abc" # <-- error: not every path initializes 'y'
|
||||
```
|
||||
|
||||
Once the implementation takes exceptions into account it is easy enough to
|
||||
use `outParam = default(typeof(outParam))` in the beginning of the proc body.
|
||||
|
||||
Out parameters and inheritance
|
||||
------------------------------
|
||||
|
||||
It is not valid to pass an lvalue of a supertype to an `out T` parameter:
|
||||
|
||||
```nim
|
||||
type
|
||||
Superclass = object of RootObj
|
||||
a: int
|
||||
Subclass = object of Superclass
|
||||
s: string
|
||||
|
||||
proc init(x: out Superclass) =
|
||||
x = Superclass(a: 8)
|
||||
|
||||
var v: Subclass
|
||||
init v
|
||||
use v.s # the 's' field was never initialized!
|
||||
```
|
||||
|
||||
However, in the future this could be allowed and provide a better way to write object
|
||||
constructors that take inheritance into account.
|
||||
|
||||
|
||||
@@ -1919,6 +1919,136 @@ restrictions / changes:
|
||||
yet performed for ordinary slices outside of a `parallel` section.
|
||||
|
||||
|
||||
Strict definitions and `out` parameters
|
||||
=======================================
|
||||
|
||||
With `experimental: "strictDefs"` *every* local variable must be initialized explicitly before it can be used:
|
||||
|
||||
```nim
|
||||
{.experimental: "strictDefs".}
|
||||
|
||||
proc test =
|
||||
var s: seq[string]
|
||||
s.add "abc" # invalid!
|
||||
|
||||
```
|
||||
|
||||
Needs to be written as:
|
||||
|
||||
```nim
|
||||
{.experimental: "strictDefs".}
|
||||
|
||||
proc test =
|
||||
var s: seq[string] = @[]
|
||||
s.add "abc" # valid!
|
||||
|
||||
```
|
||||
|
||||
A control flow analysis is performed in order to prove that a variable has been written to
|
||||
before it is used. Thus the following is valid:
|
||||
|
||||
```nim
|
||||
{.experimental: "strictDefs".}
|
||||
|
||||
proc test(cond: bool) =
|
||||
var s: seq[string]
|
||||
if cond:
|
||||
s = @["y"]
|
||||
else:
|
||||
s = @[]
|
||||
s.add "abc" # valid!
|
||||
```
|
||||
|
||||
In this example every path does set `s` to a value before it is used.
|
||||
|
||||
```nim
|
||||
{.experimental: "strictDefs".}
|
||||
|
||||
proc test(cond: bool) =
|
||||
let s: seq[string]
|
||||
if cond:
|
||||
s = @["y"]
|
||||
else:
|
||||
s = @[]
|
||||
```
|
||||
|
||||
With `experimental: "strictDefs"`, `let` statements are allowed to not have an initial value, but every path should set `s` to a value before it is used.
|
||||
|
||||
|
||||
`out` parameters
|
||||
----------------
|
||||
|
||||
An `out` parameter is like a `var` parameter but it must be written to before it can be used:
|
||||
|
||||
```nim
|
||||
proc myopen(f: out File; name: string): bool =
|
||||
f = default(File)
|
||||
result = open(f, name)
|
||||
```
|
||||
|
||||
While it is usually the better style to use the return type in order to return results API and ABI
|
||||
considerations might make this infeasible. Like for `var T` Nim maps `out T` to a hidden pointer.
|
||||
For example POSIX's `stat` routine can be wrapped as:
|
||||
|
||||
```nim
|
||||
proc stat*(a1: cstring, a2: out Stat): cint {.importc, header: "<sys/stat.h>".}
|
||||
```
|
||||
|
||||
When the implementation of a routine with output parameters is analysed, the compiler
|
||||
checks that every path before the (implicit or explicit) return does set every output
|
||||
parameter:
|
||||
|
||||
```nim
|
||||
proc p(x: out int; y: out string; cond: bool) =
|
||||
x = 4
|
||||
if cond:
|
||||
y = "abc"
|
||||
# error: not every path initializes 'y'
|
||||
```
|
||||
|
||||
|
||||
Out parameters and exception handling
|
||||
-------------------------------------
|
||||
|
||||
The analysis should take exceptions into account (but currently does not):
|
||||
|
||||
```nim
|
||||
proc p(x: out int; y: out string; cond: bool) =
|
||||
x = canRaise(45)
|
||||
y = "abc" # <-- error: not every path initializes 'y'
|
||||
```
|
||||
|
||||
Once the implementation takes exceptions into account it is easy enough to
|
||||
use `outParam = default(typeof(outParam))` in the beginning of the proc body.
|
||||
|
||||
Out parameters and inheritance
|
||||
------------------------------
|
||||
|
||||
It is not valid to pass an lvalue of a supertype to an `out T` parameter:
|
||||
|
||||
```nim
|
||||
type
|
||||
Superclass = object of RootObj
|
||||
a: int
|
||||
Subclass = object of Superclass
|
||||
s: string
|
||||
|
||||
proc init(x: out Superclass) =
|
||||
x = Superclass(a: 8)
|
||||
|
||||
var v: Subclass
|
||||
init v
|
||||
use v.s # the 's' field was never initialized!
|
||||
```
|
||||
|
||||
However, in the future this could be allowed and provide a better way to write object
|
||||
constructors that take inheritance into account.
|
||||
|
||||
|
||||
**Note**: The implementation of "strict definitions" and "out parameters" is experimental but the concept
|
||||
is solid and it is expected that eventually this mode becomes the default in later versions.
|
||||
|
||||
|
||||
Strict case objects
|
||||
===================
|
||||
|
||||
@@ -2537,114 +2667,3 @@ proc nothing() =
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
Type-bound overloads
|
||||
====================
|
||||
|
||||
With the experimental option `--experimental:typeBoundOps`, each "root"
|
||||
nominal type (namely `object`, `enum`, `distinct`, direct `Foo = ref object`
|
||||
types as well as their generic versions) can have operations attached to it.
|
||||
Exported top-level routines declared in the same scope as a nominal type
|
||||
with a parameter having a type directly deriving from that nominal type (i.e.
|
||||
with `var`/`sink`/`typedesc` modifiers or being in a generic constraint)
|
||||
are considered "attached" to the respective nominal type.
|
||||
This applies to every parameter regardless of placement.
|
||||
|
||||
When a call to a symbol is openly overloaded and overload matching starts,
|
||||
for all arguments in the call that have already undergone type checking,
|
||||
routines with the same name attached to the root nominal type (if it exists)
|
||||
of each given argument are added as a candidate to the overload match.
|
||||
This also happens as arguments gradually get typed after every match to an overload.
|
||||
This is so that the only overloads considered out of scope are
|
||||
attached to the types of the given arguments, and that matches to
|
||||
`untyped` or missing parameters are not influenced by outside overloads.
|
||||
|
||||
If no overloads with a given name are in scope, then overload matching
|
||||
will not begin, and so type-bound overloads are not considered for that name.
|
||||
Similarly, if the only overloads with a given name require a parameter to be
|
||||
`untyped` or missing, then type-bound overloads will not be considered for
|
||||
the argument in that position.
|
||||
Generally this means that a "base" overload with a compliant signature should
|
||||
be in scope so that type-bound overloads can be used.
|
||||
|
||||
In the case of ambiguity between distinct local/imported and type-bound symbols
|
||||
in overload matching, type-bound symbols are considered as a less specific
|
||||
scope than imports.
|
||||
|
||||
An example with the `hash` interface in the standard library is as follows:
|
||||
|
||||
```nim
|
||||
# objs.nim
|
||||
import std/hashes
|
||||
|
||||
type
|
||||
Obj* = object
|
||||
x*, y*: int
|
||||
z*: string # to be ignored for equality
|
||||
|
||||
proc `==`*(a, b: Obj): bool =
|
||||
a.x == b.x and a.y == b.y
|
||||
|
||||
proc hash*(a: Obj): Hash =
|
||||
$!(hash(a.x) &! hash(a.y))
|
||||
|
||||
# here both `==` and `hash` are attached to Obj
|
||||
# 1. they are both exported
|
||||
# 2. they are in the same scope as Obj
|
||||
# 3. they have parameters with types directly deriving from Obj
|
||||
# 4. Obj is nominal
|
||||
```
|
||||
|
||||
```nim
|
||||
# main.nim
|
||||
{.experimental: "typeBoundOps".}
|
||||
from objs import Obj # objs.hash, objs.`==` not imported
|
||||
import std/tables
|
||||
# tables use `hash`, only using the overloads in `std/hashes` and
|
||||
# the ones in instantiation scope (in this case, there are none)
|
||||
|
||||
var t: Table[Obj, int]
|
||||
# because tables use `hash` and `==` in a compliant way,
|
||||
# the overloads bound to Obj are also considered, and in this case match best
|
||||
t[Obj(x: 3, y: 4, z: "debug")] = 34
|
||||
# if `hash` for all objects as in `std/hashes` was used, this would error:
|
||||
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
|
||||
```
|
||||
|
||||
Another example, this time with `$` and indirect imports:
|
||||
|
||||
```nim
|
||||
# foo.nim
|
||||
type Foo* = object
|
||||
x*, y*: int
|
||||
|
||||
proc `$`*(f: Foo): string =
|
||||
"Foo(" & $f.x & ", " & $f.y & ")"
|
||||
```
|
||||
|
||||
```nim
|
||||
# bar.nim
|
||||
import foo
|
||||
|
||||
proc makeFoo*(x, y: int): Foo =
|
||||
Foo(x: x, y: y)
|
||||
|
||||
proc useFoo*(f: Foo) =
|
||||
echo "used: ", f # directly calls `foo.$` from scope
|
||||
```
|
||||
|
||||
```nim
|
||||
# debugger.nim
|
||||
proc debug*[T](obj: T) =
|
||||
echo "debugging: ", obj # calls generic `$`
|
||||
```
|
||||
|
||||
```nim
|
||||
# main.nim
|
||||
{.experimental: "typeBoundOps".}
|
||||
import bar, debugger # `foo` not imported, so `foo.$` not in scope
|
||||
|
||||
let f = makeFoo(123, 456)
|
||||
useFoo(f) # used: Foo(123, 456)
|
||||
debug(f) # debugging: Foo(123, 456)
|
||||
```
|
||||
|
||||
@@ -770,10 +770,11 @@ div.topic {
|
||||
|
||||
div.search_results {
|
||||
background-color: var(--third-background);
|
||||
margin: 3vh 5vw;
|
||||
padding: 1em;
|
||||
border: 1px solid #4d4d4d;
|
||||
position: sticky;
|
||||
top: 1em;
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
isolation: isolate;
|
||||
max-width: calc(100vw - 6em);
|
||||
z-index: 1;
|
||||
|
||||
@@ -30,30 +30,28 @@ a working NodeJS on `PATH`.
|
||||
Commands
|
||||
========
|
||||
|
||||
========================== ==========================
|
||||
p|pat|pattern <glob> run all the tests matching the given pattern
|
||||
all run all tests inside of category folders
|
||||
c|cat|category <category> run all the tests of a certain category
|
||||
r|run <test> run single test file
|
||||
html generate testresults.html from the database
|
||||
========================== ==========================
|
||||
|
||||
|
||||
Options
|
||||
=======
|
||||
|
||||
--print print results to the console
|
||||
--verbose print commands (compiling and running tests)
|
||||
--simulate see what tests would be run but don't run them (for debugging)
|
||||
--failing only show failing/ignored tests
|
||||
--targets:"c cpp js objc" run tests for specified targets (default: c)
|
||||
--nim:path use a particular nim executable (default: $PATH/nim)
|
||||
--directory:dir Change to directory dir before reading the tests or doing anything else.
|
||||
--colors:on|off Turn messages coloring on|off.
|
||||
--backendLogging:on|off Disable or enable backend logging. By default turned on.
|
||||
--megatest:on|off Enable or disable megatest. Default is on.
|
||||
--valgrind:on|off Enable or disable valgrind support. Default is on.
|
||||
--skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored
|
||||
--print print results to the console
|
||||
--verbose print commands (compiling and running tests)
|
||||
--simulate see what tests would be run but don't run them (for debugging)
|
||||
--failing only show failing/ignored tests
|
||||
--targets:"c cpp js objc" run tests for specified targets (default: c)
|
||||
--nim:path use a particular nim executable (default: $PATH/nim)
|
||||
--directory:dir Change to directory dir before reading the tests or doing anything else.
|
||||
--colors:on|off Turn messages coloring on|off.
|
||||
--backendLogging:on|off Disable or enable backend logging. By default turned on.
|
||||
--megatest:on|off Enable or disable megatest. Default is on.
|
||||
--valgrind:on|off Enable or disable valgrind support. Default is on.
|
||||
--skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored
|
||||
|
||||
|
||||
Running a single test
|
||||
|
||||
13
koch.nim
13
koch.nim
@@ -11,7 +11,7 @@
|
||||
|
||||
const
|
||||
# examples of possible values for repos: Head, ea82b54
|
||||
NimbleStableCommit = "123f97a5e4ee9ba35720c0869e19a047c43c797e" # 0.16.4
|
||||
NimbleStableCommit = "46e2ae13eeb95619a371a74d16efc5aff30ea371" # 0.16.2
|
||||
AtlasStableCommit = "5faec3e9a33afe99a7d22377dd1b45a5391f5504"
|
||||
ChecksumsStableCommit = "bd9bf4eaea124bf8d01e08f92ac1b14c6879d8d3"
|
||||
SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
|
||||
@@ -159,12 +159,13 @@ proc bundleNimbleExe(latest: bool, args: string) =
|
||||
let commit = if latest: "HEAD" else: NimbleStableCommit
|
||||
cloneDependency(distDir, "https://github.com/nim-lang/nimble.git",
|
||||
commit = commit, allowBundled = true)
|
||||
updateSubmodules(distDir / "nimble", 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)
|
||||
const zippyTests = "dist/nimble/vendor/zippy/tests"
|
||||
if dirExists(zippyTests):
|
||||
removeDir(zippyTests)
|
||||
options = "-d:release -d:nimNimbleBootstrap --noNimblePath " & args)
|
||||
|
||||
proc bundleAtlasExe(latest: bool, args: string) =
|
||||
let commit = if latest: "HEAD" else: AtlasStableCommit
|
||||
|
||||
@@ -44,9 +44,6 @@ runnableExamples:
|
||||
assert mcCounter.value == 3
|
||||
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
CacheSeq* = distinct string
|
||||
## Compile-time sequence of `NimNode`s.
|
||||
@@ -210,7 +207,7 @@ proc hasKey*(t: CacheTable; key: string): bool =
|
||||
mcTable["foo"] = newEmptyNode()
|
||||
# Will now be true since we inserted a value
|
||||
assert mcTable.hasKey("foo")
|
||||
raiseAssert "implemented in the vmops"
|
||||
discard "Implemented in vmops"
|
||||
|
||||
proc contains*(t: CacheTable; key: string): bool {.inline.} =
|
||||
## Alias of [hasKey][hasKey(CacheTable, string)] for use with the `in` operator.
|
||||
|
||||
@@ -365,7 +365,7 @@ proc symBodyHash*(s: NimNode): string {.noSideEffect.} =
|
||||
## and owning module, but also implementation body. All procs/variables used in
|
||||
## the implementation of this symbol are hashed recursively as well, including
|
||||
## magics from system module.
|
||||
raiseAssert "implemented in the vmops"
|
||||
discard
|
||||
|
||||
proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
|
||||
## Version of `getTypeImpl` which takes a `typedesc`.
|
||||
@@ -1327,7 +1327,7 @@ proc `name=`*(someProc: NimNode; val: NimNode) =
|
||||
else: someProc[0] = val
|
||||
|
||||
proc params*(someProc: NimNode): NimNode =
|
||||
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
|
||||
if someProc.kind == nnkProcTy:
|
||||
someProc[0]
|
||||
else:
|
||||
someProc.expectRoutine
|
||||
@@ -1335,7 +1335,7 @@ proc params*(someProc: NimNode): NimNode =
|
||||
|
||||
proc `params=`* (someProc: NimNode; params: NimNode) =
|
||||
expectKind(params, nnkFormalParams)
|
||||
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
|
||||
if someProc.kind == nnkProcTy:
|
||||
someProc[0] = params
|
||||
else:
|
||||
someProc.expectRoutine
|
||||
@@ -1344,7 +1344,7 @@ proc `params=`* (someProc: NimNode; params: NimNode) =
|
||||
proc pragma*(someProc: NimNode): NimNode =
|
||||
## Get the pragma of a proc type.
|
||||
## These will be expanded.
|
||||
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
|
||||
if someProc.kind == nnkProcTy:
|
||||
result = someProc[1]
|
||||
else:
|
||||
someProc.expectRoutine
|
||||
@@ -1352,7 +1352,7 @@ proc pragma*(someProc: NimNode): NimNode =
|
||||
proc `pragma=`*(someProc: NimNode; val: NimNode) =
|
||||
## Set the pragma of a proc type.
|
||||
expectKind(val, {nnkEmpty, nnkPragma})
|
||||
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
|
||||
if someProc.kind == nnkProcTy:
|
||||
someProc[1] = val
|
||||
else:
|
||||
someProc.expectRoutine
|
||||
@@ -1440,7 +1440,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
|
||||
## it.basename.ident == ident"foo")
|
||||
## ```
|
||||
block:
|
||||
var res: NimNode = nil
|
||||
var res: NimNode
|
||||
for it in n.children:
|
||||
if cond:
|
||||
res = it
|
||||
@@ -1508,7 +1508,6 @@ proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} =
|
||||
|
||||
proc hasArgOfName*(params: NimNode; name: string): bool =
|
||||
## Search `nnkFormalParams` for an argument.
|
||||
result = false
|
||||
expectKind(params, nnkFormalParams)
|
||||
for i in 1..<params.len:
|
||||
for j in 0..<params[i].len-2:
|
||||
@@ -1576,7 +1575,6 @@ proc extractTypeImpl(n: NimNode): NimNode =
|
||||
else: error("Invalid node to retrieve type implementation of: " & $n.kind)
|
||||
|
||||
proc customPragmaNode(n: NimNode): NimNode =
|
||||
result = nil
|
||||
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr})
|
||||
let
|
||||
typ = n.getTypeInst()
|
||||
@@ -1732,7 +1730,7 @@ macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
|
||||
for i in 0 ..< args.len:
|
||||
result.add args[i]
|
||||
|
||||
proc getProjectPath*(): string = raiseAssert "implemented in the vmops"
|
||||
proc getProjectPath*(): string = discard
|
||||
## Returns the path to the currently compiling project.
|
||||
##
|
||||
## This is not to be confused with `system.currentSourcePath <system.html#currentSourcePath.t>`_
|
||||
@@ -1775,7 +1773,6 @@ proc getOffset*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
|
||||
|
||||
proc isExported*(n: NimNode): bool {.noSideEffect.} =
|
||||
## Returns whether the symbol is exported or not.
|
||||
raiseAssert "implemented in the vmops"
|
||||
|
||||
proc extractDocCommentsAndRunnables*(n: NimNode): NimNode =
|
||||
## returns a `nnkStmtList` containing the top-level doc comments and
|
||||
|
||||
@@ -24,7 +24,7 @@ type
|
||||
proc initRLock*(lock: var RLock) {.inline.} =
|
||||
## Initializes the given lock.
|
||||
when defined(posix):
|
||||
var a: SysLockAttr = default(SysLockAttr)
|
||||
var a: SysLockAttr
|
||||
initSysLockAttr(a)
|
||||
setSysLockType(a, SysLockType_Reentrant)
|
||||
initSysLock(lock, a.addr)
|
||||
|
||||
@@ -145,7 +145,7 @@ proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
|
||||
of 2: d = int(cast[ptr uint16](a +% n.offset)[])
|
||||
of 4: d = int(cast[ptr uint32](a +% n.offset)[])
|
||||
of 8: d = int(cast[ptr uint64](a +% n.offset)[])
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert(false)
|
||||
return d
|
||||
|
||||
proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
@@ -158,7 +158,7 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
result = n.sons[n.len]
|
||||
|
||||
proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
|
||||
result = Any(value: value)
|
||||
result.value = value
|
||||
result.rawType = rawType
|
||||
|
||||
proc toAny*[T](x: var T): Any {.inline.} =
|
||||
@@ -169,7 +169,7 @@ proc toAny*[T](x: var T): Any {.inline.} =
|
||||
|
||||
proc kind*(x: Any): AnyKind {.inline.} =
|
||||
## Gets the type kind.
|
||||
result = cast[AnyKind](ord(x.rawType.kind))
|
||||
result = AnyKind(ord(x.rawType.kind))
|
||||
|
||||
proc size*(x: Any): int {.inline.} =
|
||||
## Returns the size of `x`'s type.
|
||||
@@ -178,16 +178,12 @@ proc size*(x: Any): int {.inline.} =
|
||||
proc baseTypeKind*(x: Any): AnyKind {.inline.} =
|
||||
## Gets the base type's kind. If `x` has no base type, `akNone` is returned.
|
||||
if x.rawType.base != nil:
|
||||
result = cast[AnyKind](ord(x.rawType.base.kind))
|
||||
else:
|
||||
result = akNone
|
||||
result = AnyKind(ord(x.rawType.base.kind))
|
||||
|
||||
proc baseTypeSize*(x: Any): int {.inline.} =
|
||||
## Returns the size of `x`'s base type. If `x` has no base type, 0 is returned.
|
||||
if x.rawType.base != nil:
|
||||
result = x.rawType.base.size
|
||||
else:
|
||||
result = 0
|
||||
|
||||
proc invokeNew*(x: Any) =
|
||||
## Performs `new(x)`. `x` needs to represent a `ref`.
|
||||
@@ -267,7 +263,7 @@ proc `[]`*(x: Any, i: int): Any =
|
||||
if i >=% cast[PGenSeq](s).len:
|
||||
raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
|
||||
return newAny(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), x.rawType.base)
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert false
|
||||
|
||||
proc `[]=`*(x: Any, i: int, y: Any) =
|
||||
## Accessor for an any `x` that represents an array or a sequence.
|
||||
@@ -296,7 +292,7 @@ proc `[]=`*(x: Any, i: int, y: Any) =
|
||||
raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
|
||||
assert y.rawType == x.rawType.base
|
||||
genericAssign(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), y.value, y.rawType)
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert false
|
||||
|
||||
proc len*(x: Any): int =
|
||||
## `len` for an any `x` that represents an array or a sequence.
|
||||
@@ -312,13 +308,14 @@ proc len*(x: Any): int =
|
||||
result = 0
|
||||
else:
|
||||
result = pgenSeq.len
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert false
|
||||
|
||||
|
||||
proc base*(x: Any): Any =
|
||||
## Returns the base type of `x` (useful for inherited object types).
|
||||
result = Any(value: x.value)
|
||||
result.rawType = x.rawType.base
|
||||
result.value = x.value
|
||||
|
||||
|
||||
proc isNil*(x: Any): bool =
|
||||
## `isNil` for an `x` that represents a cstring, proc or
|
||||
@@ -370,7 +367,7 @@ iterator fields*(x: Any): tuple[name: string, any: Any] =
|
||||
# XXX BUG: does not work yet, however is questionable anyway
|
||||
when false:
|
||||
if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
|
||||
var ret: seq[tuple[name: cstring, any: Any]] = @[]
|
||||
var ret: seq[tuple[name: cstring, any: Any]]
|
||||
if t.kind == tyObject:
|
||||
while true:
|
||||
fieldsAux(p, t.node, ret)
|
||||
@@ -382,9 +379,8 @@ iterator fields*(x: Any): tuple[name: string, any: Any] =
|
||||
yield ($name, any)
|
||||
|
||||
proc getFieldNode(p: pointer, n: ptr TNimNode, name: cstring): ptr TNimNode =
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkNone: raiseAssert "unreachable"
|
||||
of nkNone: assert(false)
|
||||
of nkSlot:
|
||||
if cmpNimIdentifier(n.name, name) == 0:
|
||||
result = n
|
||||
@@ -422,7 +418,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
|
||||
assert x.rawType.kind in {tyTuple, tyObject}
|
||||
let n = getFieldNode(x.value, t.node, fieldName)
|
||||
if n != nil:
|
||||
result = Any(value: x.value +!! n.offset)
|
||||
result.value = x.value +!! n.offset
|
||||
result.rawType = n.typ
|
||||
elif x.rawType.kind == tyObject and x.rawType.base != nil:
|
||||
return `[]`(newAny(x.value, x.rawType.base), fieldName)
|
||||
@@ -432,7 +428,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
|
||||
proc `[]`*(x: Any): Any =
|
||||
## Dereference operator for `Any`. `x` needs to represent a ptr or a ref.
|
||||
assert x.rawType.kind in {tyRef, tyPtr}
|
||||
result = Any(value: cast[ppointer](x.value)[])
|
||||
result.value = cast[ppointer](x.value)[]
|
||||
result.rawType = x.rawType.base
|
||||
|
||||
proc `[]=`*(x, y: Any) =
|
||||
@@ -485,12 +481,12 @@ proc getBiggestInt*(x: Any): BiggestInt =
|
||||
of 2: result = int64(cast[ptr uint16](x.value)[])
|
||||
of 4: result = BiggestInt(cast[ptr int32](x.value)[])
|
||||
of 8: result = BiggestInt(cast[ptr int64](x.value)[])
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert false
|
||||
of tyUInt: result = BiggestInt(cast[ptr uint](x.value)[])
|
||||
of tyUInt8: result = BiggestInt(cast[ptr uint8](x.value)[])
|
||||
of tyUInt16: result = BiggestInt(cast[ptr uint16](x.value)[])
|
||||
of tyUInt32: result = BiggestInt(cast[ptr uint32](x.value)[])
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert false
|
||||
|
||||
proc setBiggestInt*(x: Any, y: BiggestInt) =
|
||||
## Sets the integer value of `x`. `x` needs to represent
|
||||
@@ -552,7 +548,7 @@ proc getBiggestUint*(x: Any): uint64 =
|
||||
of tyUInt16: result = uint64(cast[ptr uint16](x.value)[])
|
||||
of tyUInt32: result = uint64(cast[ptr uint32](x.value)[])
|
||||
of tyUInt64: result = uint64(cast[ptr uint64](x.value)[])
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert false
|
||||
|
||||
proc setBiggestUint*(x: Any; y: uint64) =
|
||||
## Sets the unsigned integer value of `x`. `x` needs to represent an
|
||||
@@ -564,7 +560,7 @@ proc setBiggestUint*(x: Any; y: uint64) =
|
||||
of tyUInt16: cast[ptr uint16](x.value)[] = uint16(y)
|
||||
of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
|
||||
of tyUInt64: cast[ptr uint64](x.value)[] = uint64(y)
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert false
|
||||
|
||||
proc getChar*(x: Any): char =
|
||||
## Retrieves the `char` value out of `x`. `x` needs to represent a `char`.
|
||||
@@ -581,8 +577,8 @@ proc getBool*(x: Any): bool =
|
||||
proc skipRange*(x: Any): Any =
|
||||
## Skips the range information of `x`.
|
||||
assert x.rawType.kind == tyRange
|
||||
result = Any(value: x.value)
|
||||
result.rawType = x.rawType.base
|
||||
result.value = x.value
|
||||
|
||||
proc getEnumOrdinal*(x: Any, name: string): int =
|
||||
## Gets the enum field ordinal from `name`. `x` needs to represent an enum
|
||||
@@ -644,7 +640,7 @@ proc getBiggestFloat*(x: Any): BiggestFloat =
|
||||
of tyFloat: result = BiggestFloat(cast[ptr float](x.value)[])
|
||||
of tyFloat32: result = BiggestFloat(cast[ptr float32](x.value)[])
|
||||
of tyFloat64: result = BiggestFloat(cast[ptr float64](x.value)[])
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert false
|
||||
|
||||
proc setBiggestFloat*(x: Any, y: BiggestFloat) =
|
||||
## Sets the float value of `x`. `x` needs to represent
|
||||
@@ -653,7 +649,7 @@ proc setBiggestFloat*(x: Any, y: BiggestFloat) =
|
||||
of tyFloat: cast[ptr float](x.value)[] = y
|
||||
of tyFloat32: cast[ptr float32](x.value)[] = y.float32
|
||||
of tyFloat64: cast[ptr float64](x.value)[] = y
|
||||
else: raiseAssert "unreachable"
|
||||
else: assert false
|
||||
|
||||
proc getString*(x: Any): string =
|
||||
## Retrieves the `string` value out of `x`. `x` needs to represent a `string`.
|
||||
@@ -686,7 +682,7 @@ iterator elements*(x: Any): int =
|
||||
let typ = x.rawType
|
||||
let p = x.value
|
||||
# "typ.slots.len" field is for sets the "first" field
|
||||
var u: int64 = 0'i64
|
||||
var u: int64
|
||||
case typ.size
|
||||
of 1: u = int64(cast[ptr uint8](p)[])
|
||||
of 2: u = int64(cast[ptr uint16](p)[])
|
||||
|
||||
@@ -82,7 +82,7 @@ proc diffCodes(aText: string; h: var Table[string, int]): DiffData =
|
||||
## `trimSpace` ignore leading and trailing space characters
|
||||
## Returns a array of integers.
|
||||
var lastUsedCode = h.len
|
||||
result = DiffData(data: newSeq[int]())
|
||||
result.data = newSeq[int]()
|
||||
for s in aText.splitLines:
|
||||
if h.contains s:
|
||||
result.data.add h[s]
|
||||
@@ -123,7 +123,7 @@ proc sms(dataA: var DiffData; lowerA, upperA: int; dataB: DiffData; lowerB, uppe
|
||||
## `downVector` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.
|
||||
## `upVector` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.
|
||||
## Returns a MiddleSnakeData record containing x,y and u,v.
|
||||
result = Smsrd()
|
||||
|
||||
let max = dataA.len + dataB.len + 1
|
||||
|
||||
let downK = lowerA - lowerB # the k-line to start the forward search
|
||||
@@ -250,7 +250,6 @@ proc lcs(dataA: var DiffData; lowerA, upperA: int; dataB: var DiffData; lowerB,
|
||||
proc createDiffs(dataA, dataB: DiffData): seq[Item] =
|
||||
## Scan the tables of which lines are inserted and deleted,
|
||||
## producing an edit script in forward order.
|
||||
result = @[]
|
||||
var startA = 0
|
||||
var startB = 0
|
||||
var lineA = 0
|
||||
|
||||
@@ -74,14 +74,7 @@ when defined(nimPreviewSlimSystem):
|
||||
export options
|
||||
|
||||
type
|
||||
RegexDesc* = object
|
||||
pattern*: string
|
||||
pcreObj: ptr pcre.Pcre ## not nil
|
||||
pcreExtra: ptr pcre.ExtraData ## nil
|
||||
|
||||
captureNameToId: Table[string, int]
|
||||
|
||||
Regex* = ref RegexDesc
|
||||
Regex* = ref object
|
||||
## Represents the pattern that things are matched against, constructed with
|
||||
## `re(string)`. Examples: `re"foo"`, `re(r"(*ANYCRLF)(?x)foo #
|
||||
## comment".`
|
||||
@@ -152,6 +145,11 @@ type
|
||||
## `DOLLAR_ENDONLY`, `FIRSTLINE`, `NO_AUTO_CAPTURE`,
|
||||
## `JAVASCRIPT_COMPAT`, `U`, `NO_STUDY`. In other PCRE wrappers, you
|
||||
## will need to pass these as separate flags to PCRE.
|
||||
pattern*: string
|
||||
pcreObj: ptr pcre.Pcre ## not nil
|
||||
pcreExtra: ptr pcre.ExtraData ## nil
|
||||
|
||||
captureNameToId: Table[string, int]
|
||||
|
||||
RegexMatch* = object
|
||||
## Usually seen as Option[RegexMatch], it represents the result of an
|
||||
@@ -218,31 +216,14 @@ type
|
||||
## for whatever reason. The message contains the error
|
||||
## code.
|
||||
|
||||
when defined(gcDestructors):
|
||||
when defined(nimAllowNonVarDestructor) and defined(nimPreviewNonVarDestructor):
|
||||
proc `=destroy`(pattern: RegexDesc) =
|
||||
`=destroy`(pattern.pattern)
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
`=destroy`(pattern.captureNameToId)
|
||||
else:
|
||||
proc `=destroy`(pattern: var RegexDesc) =
|
||||
`=destroy`(pattern.pattern)
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
`=destroy`(pattern.captureNameToId)
|
||||
else:
|
||||
proc destroyRegex(pattern: Regex) =
|
||||
`=destroy`(pattern.pattern)
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
`=destroy`(pattern.captureNameToId)
|
||||
proc destroyRegex(pattern: Regex) =
|
||||
`=destroy`(pattern.pattern)
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
`=destroy`(pattern.captureNameToId)
|
||||
|
||||
proc getinfo[T](pattern: Regex, opt: cint): T =
|
||||
result = default(T)
|
||||
let retcode = pcre.fullinfo(pattern.pcreObj, pattern.pcreExtra, opt, addr result)
|
||||
|
||||
if retcode < 0:
|
||||
@@ -270,14 +251,11 @@ proc getNameToNumberTable(pattern: Regex): Table[string, int] =
|
||||
result[name] = num
|
||||
|
||||
proc initRegex(pattern: string, flags: int, study = true): Regex =
|
||||
when defined(gcDestructors):
|
||||
result = Regex()
|
||||
else:
|
||||
new(result, destroyRegex)
|
||||
new(result, destroyRegex)
|
||||
result.pattern = pattern
|
||||
|
||||
var errorMsg: cstring = ""
|
||||
var errOffset: cint = 0
|
||||
var errorMsg: cstring
|
||||
var errOffset: cint
|
||||
|
||||
result.pcreObj = pcre.compile(cstring(pattern),
|
||||
# better hope int is at least 4 bytes..
|
||||
@@ -289,7 +267,7 @@ proc initRegex(pattern: string, flags: int, study = true): Regex =
|
||||
|
||||
if study:
|
||||
var options: cint = 0
|
||||
var hasJit: cint = cint(0)
|
||||
var hasJit: cint
|
||||
if pcre.config(pcre.CONFIG_JIT, addr hasJit) == 0:
|
||||
if hasJit == 1'i32:
|
||||
options = pcre.STUDY_JIT_COMPILE
|
||||
@@ -314,7 +292,7 @@ proc matchesCrLf(pattern: Regex): bool =
|
||||
return true
|
||||
|
||||
# get flags from build config
|
||||
var confFlags: cint = cint(0)
|
||||
var confFlags: cint
|
||||
if pcre.config(pcre.CONFIG_NEWLINE, addr confFlags) != 0:
|
||||
assert(false, "CONFIG_NEWLINE apparently got screwed up")
|
||||
|
||||
@@ -574,7 +552,7 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R
|
||||
pcre.UTF8) > 0u32
|
||||
let strlen = if endpos == int.high: str.len else: endpos+1
|
||||
var offset = start
|
||||
var match: Option[RegexMatch] = default(Option[RegexMatch])
|
||||
var match: Option[RegexMatch]
|
||||
var neverMatched = true
|
||||
|
||||
while true:
|
||||
@@ -763,7 +741,7 @@ proc escapeRe*(str: string): string {.gcsafe.} =
|
||||
const SpecialCharMatcher = {'\\', '+', '*', '?', '[', '^', ']', '$', '(',
|
||||
')', '{', '}', '=', '!', '<', '>', '|', ':',
|
||||
'-'}
|
||||
result = ""
|
||||
|
||||
for c in items(str):
|
||||
case c
|
||||
of SpecialCharMatcher:
|
||||
|
||||
@@ -466,7 +466,7 @@ template `=~` *(s: string, pattern: Regex): untyped =
|
||||
doAssert parse(" # comment ... ") == """("# comment ... ",)"""
|
||||
bind MaxSubpatterns
|
||||
when not declaredInScope(matches):
|
||||
var matches {.inject.}: array[MaxSubpatterns, string] = default(array[MaxSubpatterns, string])
|
||||
var matches {.inject.}: array[MaxSubpatterns, string]
|
||||
match(s, pattern, matches)
|
||||
|
||||
# ------------------------- more string handling ------------------------------
|
||||
|
||||
@@ -1490,9 +1490,7 @@ proc clearInterval*(i: Interval) {.importc, nodecl.}
|
||||
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
|
||||
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
|
||||
proc dispatchEvent*(et: EventTarget, ev: Event)
|
||||
proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
|
||||
proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
|
||||
|
||||
proc removeEventListener*(et: EventTarget; ev: cstring; cb: proc(ev: Event))
|
||||
|
||||
# Window "methods"
|
||||
proc alert*(w: Window, msg: cstring)
|
||||
|
||||
@@ -15,7 +15,6 @@ __TINYC__
|
||||
__clang__
|
||||
__AVR__
|
||||
__arm__
|
||||
__riscv
|
||||
__EMSCRIPTEN__
|
||||
*/
|
||||
|
||||
@@ -473,8 +472,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(_MSC_VER) || defined(__TINYC__) || \
|
||||
(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)) // C99
|
||||
#elif defined(__GNUC__) || defined(_MSC_VER)
|
||||
# define SEQ_DECL_SIZE /* empty is correct! */
|
||||
#else
|
||||
# define SEQ_DECL_SIZE 1000000
|
||||
@@ -581,8 +579,8 @@ 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
|
||||
#if (defined(__arm__) || defined(__riscv)) && defined(__GNUC__)
|
||||
/* arm-none-eabi-gcc and riscv32-unknown-elf-gcc targets define int32_t as long int */
|
||||
#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)
|
||||
|
||||
@@ -301,8 +301,6 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) =
|
||||
g.kind = nimGetKeyword(id)
|
||||
elif isKeyword(keywords, id) >= 0:
|
||||
g.kind = gtKeyword
|
||||
else:
|
||||
g.kind = gtIdentifier
|
||||
of '0':
|
||||
inc(pos)
|
||||
case g.buf[pos]
|
||||
|
||||
@@ -503,7 +503,7 @@ proc pthread_spin_unlock*(a1: ptr Pthread_spinlock): cint {.
|
||||
proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
|
||||
|
||||
|
||||
proc exitnow*(code: cint) {.importc: "_exit", header: "<unistd.h>", noreturn.}
|
||||
proc exitnow*(code: int) {.importc: "_exit", header: "<unistd.h>".}
|
||||
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
|
||||
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
|
||||
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
|
||||
|
||||
@@ -143,7 +143,6 @@ proc reversed*[T](a: openArray[T]): seq[T] {.inline.} =
|
||||
runnableExamples:
|
||||
assert [10, 11, 12].reversed == @[12, 11, 10]
|
||||
assert seq[string].default.reversed == @[]
|
||||
result = @[]
|
||||
let n = a.len
|
||||
result.setLen(n)
|
||||
for i in 0..<n: result[i] = a[n - (i + 1)]
|
||||
|
||||
@@ -1300,8 +1300,7 @@ else:
|
||||
# `rwlist` associated with file descriptor MUST BE emptied before
|
||||
# dispatching callback (See https://github.com/nim-lang/Nim/issues/5128),
|
||||
# or it can be possible to fall into endless cycle.
|
||||
result = (0, 0)
|
||||
var curList: seq[Callback] = @[]
|
||||
var curList: seq[Callback]
|
||||
|
||||
let selector = getGlobalDispatcher().selector
|
||||
withData(selector, fd.int, fdData):
|
||||
@@ -1353,7 +1352,7 @@ else:
|
||||
# {Event.Timer, Event.Signal, Event.Process, Event.Vnode}.
|
||||
# There can be only one callback registered with one descriptor,
|
||||
# so there is no need to iterate over list.
|
||||
var curList: seq[Callback] = @[]
|
||||
var curList: seq[Callback]
|
||||
|
||||
withData(p.selector, fd.int, adata) do:
|
||||
curList = move adata.readList
|
||||
@@ -1551,7 +1550,7 @@ else:
|
||||
var retFuture = newFuture[void]("sendTo")
|
||||
|
||||
# we will preserve address in our stack
|
||||
var staddr {.noinit.} : array[128, char] # SOCKADDR_STORAGE size is 128 bytes
|
||||
var staddr: array[128, char] # SOCKADDR_STORAGE size is 128 bytes
|
||||
var stalen = saddrLen
|
||||
zeroMem(addr(staddr[0]), 128)
|
||||
copyMem(addr(staddr[0]), saddr, saddrLen)
|
||||
@@ -1605,7 +1604,7 @@ else:
|
||||
client: AsyncFD]]("acceptAddr")
|
||||
proc cb(sock: AsyncFD): bool {.gcsafe.} =
|
||||
result = true
|
||||
var sockAddress: Sockaddr_storage = default(Sockaddr_storage)
|
||||
var sockAddress: Sockaddr_storage
|
||||
var addrLen = sizeof(sockAddress).SockLen
|
||||
var client =
|
||||
when declared(accept4):
|
||||
@@ -1697,12 +1696,12 @@ proc drain*(timeout = 500) =
|
||||
## Waits for completion of **all** events and processes them. Raises `ValueError`
|
||||
## if there are no pending operations. In contrast to `poll` this
|
||||
## processes as many events as are available until the timeout has elapsed.
|
||||
var elapsed = 0
|
||||
let start = getMonoTime()
|
||||
var curTimeout = timeout
|
||||
let start = now()
|
||||
while hasPendingOperations():
|
||||
discard runOnce(timeout - elapsed)
|
||||
elapsed = int (getMonoTime() - start).inMilliseconds
|
||||
if elapsed >= timeout:
|
||||
discard runOnce(curTimeout)
|
||||
curTimeout -= (now() - start).inMilliseconds.int
|
||||
if curTimeout < 0:
|
||||
break
|
||||
|
||||
proc poll*(timeout = 500) =
|
||||
@@ -2046,7 +2045,7 @@ when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or
|
||||
elif defined(zephyr) or defined(freertos):
|
||||
result = FD_MAX
|
||||
else:
|
||||
var fdLim: RLimit = default(RLimit)
|
||||
var fdLim: RLimit
|
||||
if getrlimit(RLIMIT_NOFILE, fdLim) < 0:
|
||||
raiseOSError(osLastError())
|
||||
result = int(fdLim.rlim_cur) - 1
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import std/[os, sets, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits]
|
||||
import std/[os, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits]
|
||||
|
||||
import system/stacktraces
|
||||
|
||||
@@ -293,47 +293,57 @@ template getFilenameProcname(entry: StackTraceEntry): (string, string) =
|
||||
else:
|
||||
($entry.filename, $entry.procname)
|
||||
|
||||
proc format(entry: StackTraceEntry): string =
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
let left = "$#($#)" % [filename, $entry.line]
|
||||
result = spaces(2) & "$# $#\n" % [left, procname]
|
||||
proc getHint(entry: StackTraceEntry): string =
|
||||
## We try to provide some hints about stack trace entries that the user
|
||||
## may not be familiar with, in particular calls inside the stdlib.
|
||||
|
||||
proc isInternal(entry: StackTraceEntry): bool =
|
||||
# --excessiveStackTrace:off
|
||||
const internals = [
|
||||
"asyncdispatch.nim",
|
||||
"asyncfutures.nim",
|
||||
"threadimpl.nim", # XXX ?
|
||||
]
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
for line in internals:
|
||||
if filename.endsWith line:
|
||||
return true
|
||||
return false
|
||||
|
||||
result = ""
|
||||
if procname == "processPendingCallbacks":
|
||||
if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
|
||||
return "Executes pending callbacks"
|
||||
elif procname == "poll":
|
||||
if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
|
||||
return "Processes asynchronous completion events"
|
||||
|
||||
if procname.endsWith(NimAsyncContinueSuffix):
|
||||
if cmpIgnoreStyle(filename, "asyncmacro.nim") == 0:
|
||||
return "Resumes an async procedure"
|
||||
|
||||
proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string =
|
||||
result = ""
|
||||
when defined(nimStackTraceOverride):
|
||||
let entries = addDebuggingInfo(stackTraceEntries)
|
||||
else:
|
||||
let entries = stackTraceEntries
|
||||
var seenEntries = initHashSet[StackTraceEntry]()
|
||||
let L = entries.len-1
|
||||
var i = L
|
||||
var j = 0
|
||||
while i >= 0:
|
||||
if entries[i].line == reraisedFromBegin or i == 0:
|
||||
j = i + int(i != 0)
|
||||
while j < L:
|
||||
if entries[j].line == reraisedFromBegin:
|
||||
break
|
||||
if entries[j].line >= 0 and not isInternal(entries[j]):
|
||||
# this skips recursive calls sadly
|
||||
if entries[j] notin seenEntries:
|
||||
result.add format(entries[j])
|
||||
seenEntries.incl entries[j]
|
||||
inc j
|
||||
dec i
|
||||
|
||||
result = ""
|
||||
# Find longest filename & line number combo for alignment purposes.
|
||||
var longestLeft = 0
|
||||
for entry in entries:
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
|
||||
if procname == "": continue
|
||||
|
||||
let leftLen = filename.len + len($entry.line)
|
||||
if leftLen > longestLeft:
|
||||
longestLeft = leftLen
|
||||
|
||||
# Format the entries.
|
||||
for entry in entries:
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
|
||||
if procname == "" and entry.line == reraisedFromBegin:
|
||||
break
|
||||
|
||||
let left = "$#($#)" % [filename, $entry.line]
|
||||
result.add((spaces(2) & "$# $#\n") % [
|
||||
left,
|
||||
procname
|
||||
])
|
||||
let hint = getHint(entry)
|
||||
if hint.len > 0:
|
||||
result.add(spaces(4) & "## " & hint & "\n")
|
||||
|
||||
proc injectStacktrace[T](future: Future[T]) =
|
||||
when not defined(release):
|
||||
@@ -346,6 +356,7 @@ proc injectStacktrace[T](future: Future[T]) =
|
||||
let start = exceptionMsg.find(header)
|
||||
exceptionMsg = exceptionMsg[0..<start]
|
||||
|
||||
|
||||
var newMsg = exceptionMsg & header
|
||||
|
||||
let entries = getStackTraceEntries(future.error)
|
||||
|
||||
@@ -146,7 +146,6 @@ proc respondError(req: Request, code: HttpCode): Future[void] =
|
||||
result = req.client.send(msg)
|
||||
|
||||
proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
|
||||
result = default(tuple[orig: string, major, minor: int])
|
||||
var i = protocol.skipIgnoreCase("HTTP/")
|
||||
if i != 5:
|
||||
raise newException(ValueError, "Invalid request protocol. Got: " &
|
||||
|
||||
@@ -22,39 +22,36 @@ proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimN
|
||||
result = newCall(theProc, args)
|
||||
result.copyLineInfo(fromNode)
|
||||
|
||||
type
|
||||
ClosureIt[T] = iterator(f: Future[T]): owned(FutureBase)
|
||||
|
||||
template createCb(futTyp, strName, identName, futureVarCompletions: untyped) =
|
||||
template createCb(retFutureSym, iteratorNameSym,
|
||||
strName, identName, futureVarCompletions: untyped) =
|
||||
bind finished
|
||||
{.push stackTrace: off.}
|
||||
proc identName(fut: Future[futTyp], it: ClosureIt[futTyp]) {.effectsOf: it.} =
|
||||
var nameIterVar = iteratorNameSym
|
||||
proc identName {.closure, stackTrace: off.} =
|
||||
try:
|
||||
if not it.finished:
|
||||
var next = it(fut)
|
||||
if not nameIterVar.finished:
|
||||
var next = nameIterVar()
|
||||
# Continue while the yielded future is already finished.
|
||||
while (not next.isNil) and next.finished:
|
||||
next = it(fut)
|
||||
if it.finished:
|
||||
next = nameIterVar()
|
||||
if nameIterVar.finished:
|
||||
break
|
||||
|
||||
if next == nil:
|
||||
if not fut.finished:
|
||||
if not retFutureSym.finished:
|
||||
let msg = "Async procedure ($1) yielded `nil`, are you await'ing a `nil` Future?"
|
||||
raise newException(AssertionDefect, msg % strName)
|
||||
else:
|
||||
{.gcsafe.}:
|
||||
next.addCallback(cast[proc() {.closure, gcsafe.}](proc =
|
||||
identName(fut, it)))
|
||||
next.addCallback cast[proc() {.closure, gcsafe.}](identName)
|
||||
except:
|
||||
futureVarCompletions
|
||||
if fut.finished:
|
||||
if retFutureSym.finished:
|
||||
# Take a look at tasyncexceptions for the bug which this fixes.
|
||||
# That test explains it better than I can here.
|
||||
raise
|
||||
else:
|
||||
fut.fail(getCurrentException())
|
||||
{.pop.}
|
||||
retFutureSym.fail(getCurrentException())
|
||||
identName()
|
||||
|
||||
proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode): NimNode =
|
||||
result = newNimNode(nnkStmtList, fromNode)
|
||||
@@ -71,7 +68,7 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode
|
||||
)
|
||||
)
|
||||
|
||||
proc processBody(ctx: Context; node, needsCompletionSym, retFutParamSym: NimNode, futureVarIdents: seq[NimNode]): NimNode =
|
||||
proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode =
|
||||
result = node
|
||||
case node.kind
|
||||
of nnkReturnStmt:
|
||||
@@ -83,14 +80,14 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutParamSym: NimNode
|
||||
ctx.hasRet = true
|
||||
if node[0].kind == nnkEmpty:
|
||||
if ctx.inTry == 0:
|
||||
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutParamSym, newIdentNode("result"))
|
||||
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, newIdentNode("result"))
|
||||
else:
|
||||
result.add newAssignment(needsCompletionSym, newLit(true))
|
||||
else:
|
||||
let x = processBody(ctx, node[0], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
let x = processBody(ctx, node[0], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
if x.kind == nnkYieldStmt: result.add x
|
||||
elif ctx.inTry == 0:
|
||||
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutParamSym, x)
|
||||
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, x)
|
||||
else:
|
||||
result.add newAssignment(newIdentNode("result"), x)
|
||||
result.add newAssignment(needsCompletionSym, newLit(true))
|
||||
@@ -103,10 +100,10 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutParamSym: NimNode
|
||||
of nnkTryStmt:
|
||||
if result[^1].kind == nnkFinally:
|
||||
inc ctx.inTry
|
||||
result[0] = processBody(ctx, result[0], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
result[0] = processBody(ctx, result[0], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
dec ctx.inTry
|
||||
for i in 1 ..< result.len:
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
if ctx.inTry == 0 and ctx.hasRet:
|
||||
let finallyNode = copyNimNode(result[^1])
|
||||
let stmtNode = newNimNode(nnkStmtList)
|
||||
@@ -114,7 +111,7 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutParamSym: NimNode
|
||||
stmtNode.add child
|
||||
stmtNode.add newIfStmt(
|
||||
( needsCompletionSym,
|
||||
newCallWithLineInfo(node, newIdentNode("complete"), retFutParamSym,
|
||||
newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym,
|
||||
newIdentNode("result")
|
||||
)
|
||||
)
|
||||
@@ -123,10 +120,10 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutParamSym: NimNode
|
||||
result[^1] = finallyNode
|
||||
else:
|
||||
for i in 0 ..< result.len:
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
else:
|
||||
for i in 0 ..< result.len:
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
|
||||
# echo result.repr
|
||||
|
||||
@@ -170,8 +167,7 @@ template await*[T](f: Future[T]): auto {.used.} =
|
||||
when compiles(yieldFuture):
|
||||
var internalTmpFuture: FutureBase = f
|
||||
yield internalTmpFuture
|
||||
{.line: instantiationInfo(fullPaths = true).}:
|
||||
(cast[typeof(f)](internalTmpFuture)).read()
|
||||
(cast[typeof(f)](internalTmpFuture)).read()
|
||||
else:
|
||||
macro errorAsync(futureError: Future[T]) =
|
||||
error(
|
||||
@@ -222,7 +218,6 @@ proc asyncSingleProc(prc: NimNode): NimNode =
|
||||
elif returnType.kind == nnkEmpty:
|
||||
baseType = returnType
|
||||
else:
|
||||
baseType = nil
|
||||
verifyReturnType(repr(returnType), returnType)
|
||||
|
||||
let futureVarIdents = getFutureVarIdents(prc.params)
|
||||
@@ -233,21 +228,29 @@ proc asyncSingleProc(prc: NimNode): NimNode =
|
||||
# transformation even more complex.
|
||||
let body2 = extractDocCommentsAndRunnables(prc.body)
|
||||
|
||||
# -> var retFuture = newFuture[T]()
|
||||
var retFutureSym = genSym(nskVar, "retFuture")
|
||||
var subRetType =
|
||||
if returnType.kind == nnkEmpty: newIdentNode("void")
|
||||
else: baseType
|
||||
let retFutParamSym = genSym(nskParam, "retFutParamSym")
|
||||
outerProcBody.add(
|
||||
newVarStmt(retFutureSym,
|
||||
newCall(
|
||||
newNimNode(nnkBracketExpr, prc.body).add(
|
||||
newIdentNode("newFuture"),
|
||||
subRetType),
|
||||
newLit(prcName)))) # Get type from return type of this proc
|
||||
|
||||
# -> iterator nameIter(retFutParam: Future[T]): FutureBase {.closure.} =
|
||||
# -> iterator nameIter(): FutureBase {.closure.} =
|
||||
# -> {.push warning[resultshadowed]: off.}
|
||||
# -> var result: T
|
||||
# -> {.pop.}
|
||||
# -> <proc_body>
|
||||
# -> complete(retFutParam, result)
|
||||
# -> complete(retFuture, result)
|
||||
var iteratorNameSym = genSym(nskIterator, $prcName & " (Async)")
|
||||
var needsCompletionSym = genSym(nskVar, "needsCompletion")
|
||||
var ctx = Context()
|
||||
var procBody = processBody(ctx, prc.body, needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
var procBody = processBody(ctx, prc.body, needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
# don't do anything with forward bodies (empty)
|
||||
if procBody.kind != nnkEmpty:
|
||||
# fix #13899, defer should not escape its original scope
|
||||
@@ -264,18 +267,16 @@ proc asyncSingleProc(prc: NimNode): NimNode =
|
||||
procBody.insert(0): quote do:
|
||||
{.push warning[resultshadowed]: off.}
|
||||
when `subRetType` isnot void:
|
||||
var `resultIdent`: `subRetType` = default(`subRetType`)
|
||||
var `resultIdent`: `subRetType`
|
||||
else:
|
||||
var `resultIdent`: Future[void] = nil
|
||||
var `resultIdent`: Future[void]
|
||||
{.pop.}
|
||||
|
||||
var `needsCompletionSym` = false
|
||||
procBody.add quote do:
|
||||
complete(`retFutParamSym`, `resultIdent`)
|
||||
complete(`retFutureSym`, `resultIdent`)
|
||||
|
||||
var retFutureTyp = newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(subRetType)
|
||||
var retFutureParam = newNimNode(nnkIdentDefs, prc).add(retFutParamSym).add(retFutureTyp).add(newEmptyNode())
|
||||
var closureIterator = newProc(iteratorNameSym, [quote do: owned(FutureBase), retFutureParam],
|
||||
var closureIterator = newProc(iteratorNameSym, [quote do: owned(FutureBase)],
|
||||
procBody, nnkIteratorDef)
|
||||
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom = prc.body)
|
||||
closureIterator.addPragma(newIdentNode("closure"))
|
||||
@@ -285,31 +286,17 @@ proc asyncSingleProc(prc: NimNode): NimNode =
|
||||
closureIterator.addPragma(newIdentNode("gcsafe"))
|
||||
outerProcBody.add(closureIterator)
|
||||
|
||||
# -> createCb()
|
||||
# -> createCb(retFuture)
|
||||
# NOTE: The NimAsyncContinueSuffix is checked for in asyncfutures.nim to produce
|
||||
# friendlier stack traces:
|
||||
var cbName = genSym(nskProc, prcName & NimAsyncContinueSuffix)
|
||||
var procCb = getAst createCb(
|
||||
subRetType,
|
||||
newStrLitNode(prcName),
|
||||
cbName,
|
||||
createFutureVarCompletions(futureVarIdents, nil)
|
||||
)
|
||||
var procCb = getAst createCb(retFutureSym, iteratorNameSym,
|
||||
newStrLitNode(prcName),
|
||||
cbName,
|
||||
createFutureVarCompletions(futureVarIdents, nil)
|
||||
)
|
||||
outerProcBody.add procCb
|
||||
|
||||
# -> var retFuture = newFuture[T]()
|
||||
let retFutureSym = genSym(nskVar, "retFuture")
|
||||
outerProcBody.add(
|
||||
newVarStmt(retFutureSym,
|
||||
newCall(
|
||||
newNimNode(nnkBracketExpr, prc.body).add(
|
||||
newIdentNode("newFuture"),
|
||||
subRetType),
|
||||
newLit(prcName)))) # Get type from return type of this proc
|
||||
|
||||
# -> cb(retFuture, nameIter)
|
||||
outerProcBody.add newCall(cbName, retFutureSym, iteratorNameSym)
|
||||
|
||||
# -> return retFuture
|
||||
outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym)
|
||||
|
||||
|
||||
@@ -850,7 +850,7 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string,
|
||||
var
|
||||
it = aiList
|
||||
success = false
|
||||
lastException: ref Exception = nil
|
||||
lastException: ref Exception
|
||||
|
||||
while it != nil:
|
||||
let fut = sendTo(socket.fd.AsyncFD, cstring(data), len(data), it.ai_addr,
|
||||
@@ -915,16 +915,16 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int,
|
||||
"Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead")
|
||||
assert(not socket.closed, "Cannot `recvFrom` on a closed socket")
|
||||
assert(size == len(data.mget()),
|
||||
"`data` was not initialized correctly. `size` != `len(data.mget())`")
|
||||
"`date` was not initialized correctly. `size` != `len(data.mget())`")
|
||||
assert(46 == len(address.mget()),
|
||||
"`address` was not initialized correctly. 46 != `len(address.mget())`")
|
||||
|
||||
case socket.domain
|
||||
of AF_INET6:
|
||||
var sAddr: Sockaddr_in6 = default(Sockaddr_in6)
|
||||
var sAddr: Sockaddr_in6
|
||||
adaptRecvFromToDomain(AF_INET6)
|
||||
of AF_INET:
|
||||
var sAddr: Sockaddr_in = default(Sockaddr_in)
|
||||
var sAddr: Sockaddr_in
|
||||
adaptRecvFromToDomain(AF_INET)
|
||||
else:
|
||||
raise newException(ValueError, "Unknown socket address family")
|
||||
|
||||
@@ -151,12 +151,10 @@ proc encode*[T: byte|char](s: openArray[T], safe = false): string =
|
||||
assert encode(['n', 'i', 'm']) == "bmlt"
|
||||
assert encode(@['n', 'i', 'm']) == "bmlt"
|
||||
assert encode([1'u8, 2, 3, 4, 5]) == "AQIDBAU="
|
||||
result = ""
|
||||
encodeImpl()
|
||||
|
||||
proc encode*[T: SomeInteger and not byte](s: openArray[T], safe = false): string
|
||||
{.deprecated: "use `byte` or `char` instead".} =
|
||||
result = ""
|
||||
encodeImpl()
|
||||
|
||||
proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n",
|
||||
@@ -189,7 +187,7 @@ proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n",
|
||||
if e.len <= lineLen or newLine.len == 0:
|
||||
return e
|
||||
result = newString(e.len + newLine.len * ((e.len div lineLen) - int(e.len mod lineLen == 0)))
|
||||
var i, j, k, b: int = 0
|
||||
var i, j, k, b: int
|
||||
let nd = e.len - lineLen
|
||||
while j < nd:
|
||||
cpy(i + lineLen, e, j)
|
||||
@@ -199,7 +197,6 @@ proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n",
|
||||
|
||||
proc initDecodeTable*(): array[256, char] =
|
||||
# computes a decode table at compile time
|
||||
result = default(array[256, char])
|
||||
for i in 0 ..< 256:
|
||||
let ch = char(i)
|
||||
var code = invalidChar
|
||||
@@ -222,7 +219,6 @@ proc decode*(s: string): string =
|
||||
runnableExamples:
|
||||
assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
|
||||
assert decode(" SGVsbG8gV29ybGQ=") == "Hello World"
|
||||
result = ""
|
||||
if s.len == 0: return
|
||||
|
||||
proc decodeSize(size: int): int =
|
||||
|
||||
@@ -83,8 +83,6 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string =
|
||||
else:
|
||||
if methodNone notin allowedMethods:
|
||||
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
|
||||
else:
|
||||
result = ""
|
||||
|
||||
iterator decodeData*(data: string): tuple[key, value: string] =
|
||||
## Reads and decodes CGI data and yields the (name, value) pairs the
|
||||
|
||||
@@ -67,7 +67,6 @@ func len*[T](c: CritBitTree[T]): int {.inline.} =
|
||||
result = c.count
|
||||
|
||||
proc rawGet[T](c: CritBitTree[T], key: string): Node[T] =
|
||||
result = nil
|
||||
var it = c.root
|
||||
while it != nil:
|
||||
if not it.isLeaf:
|
||||
@@ -146,7 +145,7 @@ func exclImpl[T](c: var CritBitTree[T], key: string): int =
|
||||
var whereq: ptr Node[T] = nil
|
||||
if p == nil: return c.count
|
||||
var dir = 0
|
||||
var q: Node[T] = nil
|
||||
var q: Node[T]
|
||||
while not p.isLeaf:
|
||||
whereq = wherep
|
||||
q = p
|
||||
@@ -395,7 +394,6 @@ iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
|
||||
for x in leaves(c.root): yield (x.key, x.val)
|
||||
|
||||
proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
|
||||
result = nil
|
||||
var p = c.root
|
||||
var top = p
|
||||
if p != nil:
|
||||
@@ -535,5 +533,5 @@ proc toCritBitTree*(items: sink openArray[string]): CritBitTree[void] {.since: (
|
||||
## Creates a new `CritBitTree` that contains the given `items`.
|
||||
runnableExamples:
|
||||
doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void]
|
||||
result = default(CritBitTree[void])
|
||||
|
||||
for item in items: result.incl item
|
||||
|
||||
@@ -92,7 +92,6 @@ proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] =
|
||||
##
|
||||
## **See also:**
|
||||
## * `toDeque proc <#toDeque,openArray[T]>`_
|
||||
result = Deque[T]()
|
||||
result.initImpl(initialSize)
|
||||
|
||||
func len*[T](deq: Deque[T]): int {.inline.} =
|
||||
@@ -298,7 +297,7 @@ proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} =
|
||||
let a = toDeque([7, 8, 9])
|
||||
assert len(a) == 3
|
||||
assert $a == "[7, 8, 9]"
|
||||
result = Deque[T]()
|
||||
|
||||
result.initImpl(x.len)
|
||||
for item in items(x):
|
||||
result.addLast(item)
|
||||
|
||||
@@ -157,7 +157,7 @@ proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} =
|
||||
assert heap[0] == 8
|
||||
|
||||
# see https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap
|
||||
result = HeapQueue[T](data: @x)
|
||||
result.data = @x
|
||||
for i in countdown(x.len div 2 - 1, 0):
|
||||
siftdown(result, i)
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
|
||||
runnableExamples:
|
||||
let a = initSinglyLinkedList[int]()
|
||||
|
||||
result = default(SinglyLinkedList[T])
|
||||
discard
|
||||
|
||||
proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
|
||||
## Creates a new doubly linked list that is empty.
|
||||
@@ -125,7 +125,7 @@ proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
|
||||
runnableExamples:
|
||||
let a = initDoublyLinkedList[int]()
|
||||
|
||||
result = default(DoublyLinkedList[T])
|
||||
discard
|
||||
|
||||
proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
|
||||
## Creates a new singly linked ring that is empty.
|
||||
@@ -135,7 +135,7 @@ proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
|
||||
runnableExamples:
|
||||
let a = initSinglyLinkedRing[int]()
|
||||
|
||||
result = default(SinglyLinkedRing[T])
|
||||
discard
|
||||
|
||||
proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
|
||||
## Creates a new doubly linked ring that is empty.
|
||||
@@ -145,7 +145,7 @@ proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
|
||||
runnableExamples:
|
||||
let a = initDoublyLinkedRing[int]()
|
||||
|
||||
result = default(DoublyLinkedRing[T])
|
||||
discard
|
||||
|
||||
proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
|
||||
## Creates a new doubly linked node with the given `value`.
|
||||
@@ -319,10 +319,6 @@ proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
|
||||
let a = [9, 8].toSinglyLinkedList
|
||||
assert a.find(9).value == 9
|
||||
assert a.find(1) == nil
|
||||
when typeof(nodes(L)) is SinglyLinkedNode[T]:
|
||||
result = SinglyLinkedNode[T](nil)
|
||||
else:
|
||||
result = DoublyLinkedNode[T](nil)
|
||||
|
||||
for x in nodes(L):
|
||||
if x.value == value: return x
|
||||
|
||||
@@ -167,7 +167,7 @@ func count*[T](s: openArray[T], x: T): int =
|
||||
assert count(a, 2) == 4
|
||||
assert count(a, 99) == 0
|
||||
assert count(b, 'r') == 2
|
||||
result = 0
|
||||
|
||||
for itm in items(s):
|
||||
if itm == x:
|
||||
inc result
|
||||
@@ -244,7 +244,7 @@ func minIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
|
||||
assert minIndex(b) == 3
|
||||
assert minIndex(c) == 1
|
||||
assert minIndex(d) == 2
|
||||
result = 0
|
||||
|
||||
for i in 1..high(s):
|
||||
if s[i] < s[result]: result = i
|
||||
|
||||
@@ -261,7 +261,7 @@ func maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
|
||||
assert maxIndex(b) == 0
|
||||
assert maxIndex(c) == 2
|
||||
assert maxIndex(d) == 0
|
||||
result = 0
|
||||
|
||||
for i in 1..high(s):
|
||||
if s[i] > s[result]: result = i
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ proc enlarge[A](s: var HashSet[A]) =
|
||||
template inclImpl() {.dirty.} =
|
||||
if s.data.len == 0:
|
||||
initImpl(s, defaultInitialSize)
|
||||
var hc: Hash = default(Hash)
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
if index < 0:
|
||||
if mustRehash(s):
|
||||
|
||||
@@ -670,7 +670,7 @@ proc initOrderedSet*[A](initialSize = defaultInitialSize): OrderedSet[A] =
|
||||
var a = initOrderedSet[int]()
|
||||
a.incl(3)
|
||||
assert len(a) == 1
|
||||
result = OrderedSet[A]()
|
||||
|
||||
result.init(initialSize)
|
||||
|
||||
proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
|
||||
|
||||
@@ -139,15 +139,12 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A,
|
||||
proc mget*[A, B](t: var SharedTable[A, B], key: A): var B =
|
||||
## Retrieves the value at `t[key]`. The value can be modified.
|
||||
## If `key` is not in `t`, the `KeyError` exception is raised.
|
||||
acquire(t.lock)
|
||||
var hc: Hash = Hash(0)
|
||||
var index = rawGet(t, key, hc)
|
||||
let hasKey = index >= 0
|
||||
if hasKey:
|
||||
result = t.data[index].val
|
||||
release(t.lock)
|
||||
else:
|
||||
release(t.lock)
|
||||
withLock t:
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
let hasKey = index >= 0
|
||||
if hasKey: result = t.data[index].val
|
||||
if not hasKey:
|
||||
when compiles($key):
|
||||
raise newException(KeyError, "key not found: " & $key)
|
||||
else:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user