mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 19:03:42 +00:00
Compare commits
53 Commits
pr_build_t
...
v2.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78983f1876 | ||
|
|
0e1df88f7e | ||
|
|
a5f46a72ba | ||
|
|
4974baf7fa | ||
|
|
b82ff5a87b | ||
|
|
2a48182288 | ||
|
|
febc58e036 | ||
|
|
b0e6d28782 | ||
|
|
7974a2208c | ||
|
|
7cbe031909 | ||
|
|
4f5c0efaf2 | ||
|
|
821d0806fe | ||
|
|
2cdc0e913f | ||
|
|
dc3ffb6a71 | ||
|
|
56a3dd57fb | ||
|
|
7cccf36d7b | ||
|
|
d4027f25c4 | ||
|
|
75b9d66582 | ||
|
|
c21bf7f41b | ||
|
|
62a5bb4d0a | ||
|
|
fd379c2f94 | ||
|
|
1bd5a4a99e | ||
|
|
a27542195c | ||
|
|
69b2a6effc | ||
|
|
6d6489a9ab | ||
|
|
3b85c1a2e9 | ||
|
|
b9de2bb4f3 | ||
|
|
6f6e34ebb0 | ||
|
|
a55c15c651 | ||
|
|
7da2ffb751 | ||
|
|
5c843d3d60 | ||
|
|
a1777200c1 | ||
|
|
d51d88700b | ||
|
|
37dba853c9 | ||
|
|
755307be61 | ||
|
|
05a7a48a2b | ||
|
|
84f5060e94 | ||
|
|
ff005ad7dc | ||
|
|
6cc50ec316 | ||
|
|
58cf62451d | ||
|
|
00ac961ab1 | ||
|
|
0c3573e4a0 | ||
|
|
79b17b7c05 | ||
|
|
1660ddf98a | ||
|
|
c759d7abd1 | ||
|
|
04ccd2f4f0 | ||
|
|
680a13a142 | ||
|
|
21a161a535 | ||
|
|
1fbb67ffe9 | ||
|
|
b5f2eafed1 | ||
|
|
fe55dcb2be | ||
|
|
651fdbe586 | ||
|
|
d0dc4ac22f |
179
changelog.md
179
changelog.md
@@ -1,195 +1,18 @@
|
||||
# v2.2.0 - yyyy-mm-dd
|
||||
# v2.x.x - yyyy-mm-dd
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to be in the same module where their type has been defined.
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
|
||||
```nim
|
||||
var a, b: int
|
||||
(a, b) = (1, 2, 3, 4)
|
||||
```
|
||||
will no longer compile.
|
||||
- `internalNew` is removed from system, use `new` instead.
|
||||
|
||||
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
|
||||
|
||||
- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behavior.
|
||||
|
||||
- JS backend now supports closure iterators.
|
||||
|
||||
- `owner` in `std/macros` is deprecated.
|
||||
|
||||
- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
|
||||
Previously; in templates they would error immediately at the template definition,
|
||||
and in generic procs a type symbol would arbitrarily be captured, losing the
|
||||
information of the other symbols. This means that generic code can now give
|
||||
errors for ambiguous type symbols, and macros operating on generic proc AST
|
||||
may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
|
||||
|
||||
- Partial generic instantiation of routines is no longer allowed. Previously
|
||||
it compiled in niche situations due to bugs in the compiler.
|
||||
|
||||
```nim
|
||||
proc foo[T, U](x: T, y: U) = echo (x, y)
|
||||
proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
|
||||
|
||||
proc bar[T]() =
|
||||
foo[float](1, "abc")
|
||||
|
||||
bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
|
||||
```
|
||||
|
||||
- `const` values now open a new scope for each constant, meaning symbols
|
||||
declared in them can no longer be used outside or in the value of
|
||||
other constants.
|
||||
|
||||
```nim
|
||||
const foo = (var a = 1; a)
|
||||
const bar = a # error
|
||||
let baz = a # error
|
||||
```
|
||||
- The following POSIX wrappers have had their types changed from signed to
|
||||
unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
|
||||
- `Gid` (was `int32`, is now `uint32`)
|
||||
- `Uid` (was `int32`, is now `uint32`)
|
||||
- `Dev` (was `int32`, is now `uint32` on FreeBSD)
|
||||
- `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
|
||||
- `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
|
||||
(were `int32`, are now `uint32`)
|
||||
- `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- Changed `std/osfiles.copyFile` to allow to specify `bufferSize` instead of a hardcoded one.
|
||||
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
|
||||
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to system, which doesn't initialize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
|
||||
convert a value with a range type to its base type.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
|
||||
on Google's Farm Hash) which is also often faster than the present one. Define
|
||||
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
|
||||
produces the old values. This may impact your automated tests if they depend
|
||||
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
|
||||
is often an easy workaround.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
|
||||
|
||||
[//]: # "Removals:"
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
|
||||
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
|
||||
- `member` can be used to attach a procedure to a C++ type.
|
||||
- C++ `constructor` now reuses `result` instead creating `this`.
|
||||
|
||||
- Tuple unpacking changes:
|
||||
- Tuple unpacking assignment now supports using underscores to discard values.
|
||||
```nim
|
||||
var a, c: int
|
||||
(a, _, c) = (1, 2, 3)
|
||||
```
|
||||
- Tuple unpacking variable declarations now support type annotations, but
|
||||
only for the entire tuple.
|
||||
```nim
|
||||
let (a, b): (int, int) = (1, 2)
|
||||
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
|
||||
```
|
||||
|
||||
- The experimental option `--experimental:openSym` has been added to allow
|
||||
captured symbols in generic routine and template bodies respectively to be
|
||||
replaced by symbols injected locally by templates/macros at instantiation
|
||||
time. `bind` may be used to keep the captured symbols over the injected ones
|
||||
regardless of enabling the option, but other methods like renaming the
|
||||
captured symbols should be used instead so that the code is not affected by
|
||||
context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym`, or `genericsOpenSym` and `templateOpenSym` for only the respective
|
||||
routines, needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".} # or {.experimental: "genericsOpenSym".} for just generic procs
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
# {.experimental: "templateOpenSym".} would be needed here if genericsOpenSym was used
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert barTempl() == "injected" # previously it would be "captured"
|
||||
|
||||
template bazTempl(): string =
|
||||
bind value
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert bazTempl() == "captured"
|
||||
```
|
||||
|
||||
This option also generates a new node kind `nnkOpenSym` which contains
|
||||
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
|
||||
modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.
|
||||
|
||||
|
||||
@@ -1,12 +1,248 @@
|
||||
# v2.2.0 - 2023-mm-dd
|
||||
# v2.2.0 - 2024-10-02
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` is out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backward compatibility.
|
||||
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
|
||||
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to the same module where their type has been defined.
|
||||
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
|
||||
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
|
||||
```nim
|
||||
var a, b: int
|
||||
(a, b) = (1, 2, 3, 4)
|
||||
```
|
||||
will no longer compile.
|
||||
|
||||
- `internalNew` is removed from the `system` module, use `new` instead.
|
||||
|
||||
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
|
||||
|
||||
- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors.
|
||||
|
||||
- JS backend now supports closure iterators.
|
||||
|
||||
- `owner` in `std/macros` is deprecated.
|
||||
|
||||
- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
|
||||
Previously; in templates they would error immediately at the template definition,
|
||||
and in generic procs a type symbol would arbitrarily be captured, losing the
|
||||
information of the other symbols. This means that generic code can now give
|
||||
errors for ambiguous type symbols, and macros operating on generic proc AST
|
||||
may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
|
||||
|
||||
- Partial generic instantiation of routines is no longer allowed. Previously
|
||||
it compiled in niche situations due to bugs in the compiler.
|
||||
|
||||
```nim
|
||||
proc foo[T, U](x: T, y: U) = echo (x, y)
|
||||
proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
|
||||
|
||||
proc bar[T]() =
|
||||
foo[float](1, "abc")
|
||||
|
||||
bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
|
||||
```
|
||||
|
||||
- `const` values now open a new scope for each constant, meaning symbols
|
||||
declared in them can no longer be used outside or in the value of
|
||||
other constants.
|
||||
|
||||
```nim
|
||||
const foo = (var a = 1; a)
|
||||
const bar = a # error
|
||||
let baz = a # error
|
||||
```
|
||||
|
||||
- The following POSIX wrappers have had their types changed from signed to
|
||||
unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
|
||||
- `Gid` (was `int32`, is now `uint32`)
|
||||
- `Uid` (was `int32`, is now `uint32`)
|
||||
- `Dev` (was `int32`, is now `uint32` on FreeBSD)
|
||||
- `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
|
||||
- `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
|
||||
(were `int32`, are now `uint32`)
|
||||
- `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
|
||||
|
||||
- The `Atomic[T]` type on C++ now uses C11 primitives by default instead of
|
||||
`std::atomic`. To use `std::atomic` instead, `-d:nimUseCppAtomics` can be defined.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
|
||||
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
|
||||
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- Added `newStringUninit` to the `system` module, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to the `system` module, which doesn't initialize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
|
||||
convert a value with a range type to its base type.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
|
||||
on Google's Farm Hash) which is also often faster than the present one. Define
|
||||
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
|
||||
produces the old values. This may impact your automated tests if they depend
|
||||
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
|
||||
is often an easy workaround.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
|
||||
|
||||
[//]: # "Removals:"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
|
||||
|
||||
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
|
||||
|
||||
- `member` can be used to attach a procedure to a C++ type.
|
||||
|
||||
- C++ `constructor` now reuses `result` instead creating `this`.
|
||||
|
||||
- Tuple unpacking changes:
|
||||
- Tuple unpacking assignment now supports using underscores to discard values.
|
||||
```nim
|
||||
var a, c: int
|
||||
(a, _, c) = (1, 2, 3)
|
||||
```
|
||||
- Tuple unpacking variable declarations now support type annotations, but
|
||||
only for the entire tuple.
|
||||
```nim
|
||||
let (a, b): (int, int) = (1, 2)
|
||||
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
|
||||
```
|
||||
|
||||
- The experimental option `--experimental:openSym` has been added to allow
|
||||
captured symbols in generic routine and template bodies respectively to be
|
||||
replaced by symbols injected locally by templates/macros at instantiation
|
||||
time. `bind` may be used to keep the captured symbols over the injected ones
|
||||
regardless of enabling the option, but other methods like renaming the
|
||||
captured symbols should be used instead so that the code is not affected by
|
||||
context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert barTempl() == "injected" # previously it would be "captured"
|
||||
|
||||
template bazTempl(): string =
|
||||
bind value
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert bazTempl() == "captured"
|
||||
```
|
||||
|
||||
This option also generates a new node kind `nnkOpenSym` which contains
|
||||
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
|
||||
modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
Another experimental switch `genericsOpenSym` exists that enables this behavior
|
||||
at instantiation time, meaning templates etc can enable it specifically when
|
||||
they are being called. However this does not generate `nnkOpenSym` nodes
|
||||
(unless the other switch is enabled) and so doesn't reflect the regular
|
||||
behavior of the switch.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
echo bar[int]() # "injected"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
var res: string
|
||||
foo(123):
|
||||
res = value
|
||||
res
|
||||
assert barTempl() == "injected"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# v1.xx.x - yyyy-mm-dd
|
||||
# v2.xx.x - yyyy-mm-dd
|
||||
|
||||
This is an example file.
|
||||
The changes should go to changelog.md!
|
||||
|
||||
@@ -1057,8 +1057,11 @@ proc getDeclPragma*(n: PNode): PNode =
|
||||
|
||||
proc extractPragma*(s: PSym): PNode =
|
||||
## gets the pragma node of routine/type/var/let/const symbol `s`
|
||||
if s.kind in routineKinds:
|
||||
result = s.ast[pragmasPos]
|
||||
if s.kind in routineKinds: # bug #24167
|
||||
if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
|
||||
result = s.ast[pragmasPos]
|
||||
else:
|
||||
result = nil
|
||||
elif s.kind in {skType, skVar, skLet, skConst}:
|
||||
if s.ast != nil and s.ast.len > 0:
|
||||
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
|
||||
|
||||
121
compiler/cbuilder.nim
Normal file
121
compiler/cbuilder.nim
Normal file
@@ -0,0 +1,121 @@
|
||||
type
|
||||
Snippet = string
|
||||
Builder = string
|
||||
|
||||
template newBuilder(s: string): Builder =
|
||||
s
|
||||
|
||||
proc addField(obj: var Builder; name, typ: Snippet) =
|
||||
obj.add('\t')
|
||||
obj.add(typ)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool; initializer: Snippet) =
|
||||
obj.add('\t')
|
||||
if field.alignment > 0:
|
||||
obj.add("NIM_ALIGN(")
|
||||
obj.addInt(field.alignment)
|
||||
obj.add(") ")
|
||||
obj.add(typ)
|
||||
if sfNoalias in field.flags:
|
||||
obj.add(" NIM_NOALIAS")
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if field.bitsize != 0:
|
||||
obj.add(":")
|
||||
obj.addInt(field.bitsize)
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc structOrUnion(t: PType): Snippet =
|
||||
let t = t.skipTypes({tyAlias, tySink})
|
||||
if tfUnion in t.flags: "union"
|
||||
else: "struct"
|
||||
|
||||
proc ptrType(t: Snippet): Snippet =
|
||||
t & "*"
|
||||
|
||||
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: string; body: typed) =
|
||||
if tfPacked in typ.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add(structOrUnion(typ))
|
||||
obj.add(" __attribute__((__packed__))")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\n")
|
||||
obj.add(structOrUnion(typ))
|
||||
else:
|
||||
obj.add(structOrUnion(typ))
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
type BaseClassKind = enum
|
||||
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
|
||||
var baseKind = bcNone
|
||||
if typ.kind == tyObject:
|
||||
if typ.baseClass == nil:
|
||||
if lacksMTypeField(typ):
|
||||
baseKind = bcNone
|
||||
elif optTinyRtti in m.config.globalOptions:
|
||||
baseKind = bcNoneTinyRtti
|
||||
else:
|
||||
baseKind = bcNoneRtti
|
||||
elif m.compileToCpp:
|
||||
baseKind = bcCppInherit
|
||||
else:
|
||||
baseKind = bcSupField
|
||||
if baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
let currLen = obj.len
|
||||
case baseKind
|
||||
of bcNone:
|
||||
# rest of the options add a field or don't need it due to inheritance,
|
||||
# we need to add the dummy field for uncheckedarray ahead of time
|
||||
# so that it remains trailing
|
||||
if typ.itemId notin m.g.graph.memberProcsPerType and
|
||||
typ.n != nil and typ.n.len == 1 and typ.n[0].kind == nkSym and
|
||||
typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
|
||||
# only consists of flexible array field, add *initial* dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
of bcCppInherit: discard
|
||||
of bcNoneRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
|
||||
of bcNoneTinyRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
|
||||
of bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
body
|
||||
if baseKind == bcNone and currLen == obj.len and typ.itemId notin m.g.graph.memberProcsPerType:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
obj.add("};\n")
|
||||
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
|
||||
## adds a field with a `struct { ... }` type, building it according to `body`
|
||||
obj.add('\t')
|
||||
if tfPacked in parentTyp.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add("struct __attribute__((__packed__)) {\n")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\nstruct {")
|
||||
else:
|
||||
obj.add("struct {\n")
|
||||
body
|
||||
obj.add("} ")
|
||||
obj.add(fieldName)
|
||||
obj.add(";\n")
|
||||
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addAnonUnion(obj: var Builder; body: typed) =
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("};\n")
|
||||
@@ -376,11 +376,6 @@ proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope =
|
||||
result = getSimpleTypeDesc(m, typ)
|
||||
if result == "": result = cacheGetType(m.typeCache, sig)
|
||||
|
||||
proc structOrUnion(t: PType): Rope =
|
||||
let t = t.skipTypes({tyAlias, tySink})
|
||||
if tfUnion in t.flags: "union"
|
||||
else: "struct"
|
||||
|
||||
proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) =
|
||||
if m.compileToCpp:
|
||||
m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
|
||||
@@ -698,7 +693,7 @@ proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool)
|
||||
|
||||
proc genRecordFieldsAux(m: BModule; n: PNode,
|
||||
rectype: PType,
|
||||
check: var IntSet; result: var Rope; unionPrefix = "") =
|
||||
check: var IntSet; result: var Builder; unionPrefix = "") =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
@@ -715,64 +710,51 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
|
||||
let k = lastSon(n[i])
|
||||
if k.kind != nkSym:
|
||||
let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i
|
||||
var a = newRopeAppender()
|
||||
var a = newBuilder("")
|
||||
genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".")
|
||||
if a != "":
|
||||
if tfPacked notin rectype.flags:
|
||||
unionBody.add("struct {")
|
||||
else:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
unionBody.add("struct __attribute__((__packed__)){")
|
||||
else:
|
||||
unionBody.addf("#pragma pack(push, 1)$nstruct{", [])
|
||||
unionBody.add(a)
|
||||
unionBody.addf("} $1;$n", [structName])
|
||||
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
unionBody.addf("#pragma pack(pop)$n", [])
|
||||
if a.len != 0:
|
||||
unionBody.addFieldWithStructType(m, rectype, structName):
|
||||
unionBody.add(a)
|
||||
else:
|
||||
genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix)
|
||||
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
|
||||
if unionBody != "":
|
||||
result.addf("union{\n$1};$n", [unionBody])
|
||||
if unionBody.len != 0:
|
||||
result.addAnonUnion:
|
||||
result.add(unionBody)
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
#assert(field.ast == nil)
|
||||
let sname = mangleRecFieldName(m, field)
|
||||
fillLoc(field.loc, locField, n, unionPrefix & sname, OnUnknown)
|
||||
if field.alignment > 0:
|
||||
result.addf "NIM_ALIGN($1) ", [rope(field.alignment)]
|
||||
# for importcpp'ed objects, we only need to set field.loc, but don't
|
||||
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
|
||||
# with heavily templatized C++ code:
|
||||
if not isImportedCppType(rectype):
|
||||
let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: ""
|
||||
|
||||
let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
|
||||
var typ: Rope = ""
|
||||
var isFlexArray = false
|
||||
var initializer = ""
|
||||
if fieldType.kind == tyUncheckedArray:
|
||||
result.addf("\t$1 $2[SEQ_DECL_SIZE];$n",
|
||||
[getTypeDescAux(m, fieldType.elemType, check, dkField), sname])
|
||||
typ = getTypeDescAux(m, fieldType.elemType, check, dkField)
|
||||
isFlexArray = true
|
||||
elif fieldType.kind == tySequence:
|
||||
# we need to use a weak dependency here for trecursive_table.
|
||||
result.addf("\t$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, dkField), sname, noAlias])
|
||||
elif field.bitsize != 0:
|
||||
result.addf("\t$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, rope($field.bitsize), noAlias])
|
||||
typ = getTypeDescWeak(m, field.loc.t, check, dkField)
|
||||
else:
|
||||
typ = getTypeDescAux(m, field.loc.t, check, dkField)
|
||||
# don't use fieldType here because we need the
|
||||
# tyGenericInst for C++ template support
|
||||
let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags)
|
||||
if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)):
|
||||
var didGenTemp = false
|
||||
var initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
|
||||
result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer])
|
||||
else:
|
||||
result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias])
|
||||
initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
|
||||
result.addField(field, sname, typ, isFlexArray, initializer)
|
||||
else: internalError(m.config, n.info, "genRecordFieldsAux()")
|
||||
|
||||
proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false)
|
||||
|
||||
proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope =
|
||||
result = newRopeAppender()
|
||||
proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) =
|
||||
genRecordFieldsAux(m, typ.n, typ, check, result)
|
||||
if typ.itemId in m.g.graph.memberProcsPerType:
|
||||
let procs = m.g.graph.memberProcsPerType[typ.itemId]
|
||||
@@ -794,88 +776,34 @@ proc fillObjectFields*(m: BModule; typ: PType) =
|
||||
# sometimes generic objects are not consistently merged. We patch over
|
||||
# this fact here.
|
||||
var check = initIntSet()
|
||||
discard getRecordFields(m, typ, check)
|
||||
var ignored = newBuilder("")
|
||||
addRecordFields(ignored, m, typ, check)
|
||||
|
||||
proc mangleDynLibProc(sym: PSym): Rope
|
||||
|
||||
proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope,
|
||||
check: var IntSet, hasField:var bool): Rope =
|
||||
result = ""
|
||||
if typ.kind == tyObject:
|
||||
if typ.baseClass == nil:
|
||||
if lacksMTypeField(typ):
|
||||
appcg(m, result, " {$n", [])
|
||||
else:
|
||||
if optTinyRtti in m.config.globalOptions:
|
||||
appcg(m, result, " {$n#TNimTypeV2* m_type;$n", [])
|
||||
else:
|
||||
appcg(m, result, " {$n#TNimType* m_type;$n", [])
|
||||
hasField = true
|
||||
elif m.compileToCpp:
|
||||
appcg(m, result, " : public $1 {$n", [baseType])
|
||||
if typ.isException and m.config.exc == excCpp:
|
||||
when false:
|
||||
appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions
|
||||
if typ.sym.magic == mException:
|
||||
# Add cleanup destructor to Exception base class
|
||||
appcg(m, result, "~$1();$n", [name])
|
||||
# define it out of the class body and into the procs section so we don't have to
|
||||
# artificially forward-declare popCurrentExceptionEx (very VERY troublesome for HCR)
|
||||
appcg(m, cfsProcs, "inline $1::~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name])
|
||||
hasField = true
|
||||
else:
|
||||
appcg(m, result, " {$n $1 Sup;$n", [baseType])
|
||||
hasField = true
|
||||
else:
|
||||
result.addf(" {$n", [name])
|
||||
|
||||
proc getRecordDesc(m: BModule; typ: PType, name: Rope,
|
||||
check: var IntSet): Rope =
|
||||
# declare the record:
|
||||
var hasField = false
|
||||
var structOrUnion: string
|
||||
if tfPacked in typ.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
structOrUnion = structOrUnion(typ) & " __attribute__((__packed__))"
|
||||
else:
|
||||
structOrUnion = "#pragma pack(push, 1)\L" & structOrUnion(typ)
|
||||
else:
|
||||
structOrUnion = structOrUnion(typ)
|
||||
var baseType: string = ""
|
||||
if typ.baseClass != nil:
|
||||
baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField)
|
||||
if typ.sym == nil or sfCodegenDecl notin typ.sym.flags:
|
||||
result = structOrUnion & " " & name
|
||||
result.add(getRecordDescAux(m, typ, name, baseType, check, hasField))
|
||||
let desc = getRecordFields(m, typ, check)
|
||||
if not hasField and typ.itemId notin m.g.graph.memberProcsPerType:
|
||||
if desc == "":
|
||||
result.add("\tchar dummy;\n")
|
||||
elif typ.n.len == 1 and typ.n[0].kind == nkSym:
|
||||
let field = typ.n[0].sym
|
||||
let fieldType = field.typ.skipTypes(abstractInst)
|
||||
if fieldType.kind == tyUncheckedArray:
|
||||
result.add("\tchar dummy;\n")
|
||||
result.add(desc)
|
||||
else:
|
||||
result.add(desc)
|
||||
result.add("};\L")
|
||||
result = newBuilder("")
|
||||
result.addStruct(m, typ, name, baseType):
|
||||
result.addRecordFields(m, typ, check)
|
||||
else:
|
||||
let desc = getRecordFields(m, typ, check)
|
||||
var desc = newBuilder("")
|
||||
desc.addRecordFields(m, typ, check)
|
||||
result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType])
|
||||
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add "#pragma pack(pop)\L"
|
||||
|
||||
proc getTupleDesc(m: BModule; typ: PType, name: Rope,
|
||||
check: var IntSet): Rope =
|
||||
result = "$1 $2 {$n" % [structOrUnion(typ), name]
|
||||
var desc: Rope = ""
|
||||
for i, a in typ.ikids:
|
||||
desc.addf("$1 Field$2;$n",
|
||||
[getTypeDescAux(m, a, check, dkField), rope(i)])
|
||||
if desc == "": result.add("char dummy;\L")
|
||||
else: result.add(desc)
|
||||
result.add("};\L")
|
||||
result = newBuilder("")
|
||||
result.addStruct(m, typ, name, ""):
|
||||
for i, a in typ.ikids:
|
||||
result.addField(
|
||||
name = "Field" & $i,
|
||||
typ = getTypeDescAux(m, a, check, dkField))
|
||||
|
||||
proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
|
||||
# A helper proc for handling cppimport patterns, involving numeric
|
||||
@@ -932,12 +860,11 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
|
||||
let sig = hashType(origTyp, m.config)
|
||||
|
||||
result = "" # todo move `result = getTypePre(m, t, sig)` here ?
|
||||
result = getTypePre(m, t, sig)
|
||||
defer: # defer is the simplest in this case
|
||||
if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
|
||||
addAbiCheck(m, t, result)
|
||||
|
||||
result = getTypePre(m, t, sig)
|
||||
if result != "" and t.kind != tyOpenArray:
|
||||
excl(check, t.id)
|
||||
if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst:
|
||||
|
||||
@@ -373,6 +373,7 @@ proc dataField(p: BProc): Rope =
|
||||
|
||||
proc genProcPrototype(m: BModule, sym: PSym)
|
||||
|
||||
include cbuilder
|
||||
include ccgliterals
|
||||
include ccgtypes
|
||||
|
||||
@@ -783,15 +784,11 @@ $1define nimfr_(proc, file) \
|
||||
TFrame FR_; \
|
||||
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_);
|
||||
|
||||
$1define nimfrs_(proc, file, slots, length) \
|
||||
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename;NI len;VarSlot s[slots];} FR_; \
|
||||
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_);
|
||||
$1define nimln_(n) \
|
||||
FR_.line = n;
|
||||
|
||||
$1define nimln_(n) \
|
||||
FR_.line = n;
|
||||
|
||||
$1define nimlf_(n, file) \
|
||||
FR_.line = n; FR_.filename = file;
|
||||
$1define nimlf_(n, file) \
|
||||
FR_.line = n; FR_.filename = file;
|
||||
|
||||
"""
|
||||
if p.module.s[cfsFrameDefines].len == 0:
|
||||
|
||||
@@ -167,4 +167,5 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
|
||||
defineSymbol("nimHasVtables")
|
||||
defineSymbol("nimHasGenericsOpenSym2")
|
||||
defineSymbol("nimHasGenericsOpenSym3")
|
||||
defineSymbol("nimHasJsNoLambdaLifting")
|
||||
|
||||
@@ -1000,10 +1000,11 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
|
||||
# works out of the box with `hashMainCompilationParams`.
|
||||
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
|
||||
|
||||
const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
|
||||
const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
|
||||
type BuildCache = object
|
||||
cacheVersion: string
|
||||
outputFile: string
|
||||
outputLastModificationTime: string
|
||||
compile: seq[(string, string)]
|
||||
link: seq[string]
|
||||
linkcmd: string
|
||||
@@ -1047,6 +1048,8 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
|
||||
bcache.depfiles.add (path, $secureHashFile(path))
|
||||
|
||||
bcache.nimexe = hashNimExe()
|
||||
if fileExists(bcache.outputFile):
|
||||
bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
|
||||
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
|
||||
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
|
||||
|
||||
@@ -1067,6 +1070,8 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
|
||||
# xxx optimize by returning false if stdin input was the same
|
||||
for (file, hash) in bcache.depfiles:
|
||||
if $secureHashFile(file) != hash: return true
|
||||
if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
|
||||
return true
|
||||
|
||||
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
var bcache: BuildCache = default(BuildCache)
|
||||
@@ -1083,7 +1088,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
"jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
|
||||
[outputCurrent, output, jsonFile.string])
|
||||
var cmds: TStringSeq = default(TStringSeq)
|
||||
var prettyCmds: TStringSeq= default(TStringSeq)
|
||||
var prettyCmds: TStringSeq = default(TStringSeq)
|
||||
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
|
||||
for (name, cmd) in bcache.compile:
|
||||
cmds.add cmd
|
||||
|
||||
@@ -246,7 +246,8 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
|
||||
result = createModuleAliasImpl(realModule.name)
|
||||
if importHidden:
|
||||
result.options.incl optImportHidden
|
||||
c.unusedImports.add((result, n.info))
|
||||
let moduleIdent = if n.kind == nkInfix: n[^1] else: n
|
||||
c.unusedImports.add((result, moduleIdent.info))
|
||||
c.importModuleMap[result.id] = realModule.id
|
||||
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
|
||||
|
||||
|
||||
@@ -317,8 +317,9 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
|
||||
]#
|
||||
result = dest.kind != nkSym
|
||||
|
||||
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc:
|
||||
proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags:
|
||||
# add cyclic flag, but not to sink calls, which IsExplicitSink generates
|
||||
let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if cyclicType(c.graph, t):
|
||||
result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
|
||||
@@ -464,7 +465,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
var newCall = newTreeIT(nkCall, src.info, src.typ,
|
||||
newSymNode(op),
|
||||
src)
|
||||
c.finishCopy(newCall, n, isFromSink = true)
|
||||
c.finishCopy(newCall, n, {}, isFromSink = true)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
newCall
|
||||
@@ -473,7 +474,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
c.finishCopy(m, n, {}, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
message(c.graph.config, n.info, hintPerformance,
|
||||
@@ -761,7 +762,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||
let tmp = c.getTemp(s, n[0].typ, n.info)
|
||||
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
|
||||
m.add p(n[0], c, s, normal)
|
||||
c.finishCopy(m, n[0], isFromSink = false)
|
||||
c.finishCopy(m, n[0], {}, isFromSink = false)
|
||||
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
|
||||
var toDisarm = n[0]
|
||||
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
|
||||
@@ -1173,7 +1174,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
@@ -1181,7 +1182,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
else:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
@@ -1202,7 +1203,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
@@ -1222,7 +1223,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
|
||||
when false:
|
||||
proc computeUninit(c: var Con) =
|
||||
|
||||
@@ -224,10 +224,16 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
|
||||
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
if t.baseClass != nil:
|
||||
let obj = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
obj.add newNodeI(nkEmpty, c.info)
|
||||
obj.add x
|
||||
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, obj, y)
|
||||
let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
dest.add newNodeI(nkEmpty, c.info)
|
||||
dest.add x
|
||||
var src = y
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
src.add newNodeI(nkEmpty, c.info)
|
||||
src.add y
|
||||
|
||||
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
|
||||
@@ -685,10 +685,12 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
|
||||
if m != nil and m.kind == skModule:
|
||||
var ident: PIdent = nil
|
||||
if n[1].kind == nkIdent:
|
||||
ident = n[1].ident
|
||||
elif n[1].kind == nkAccQuoted:
|
||||
if n[1].kind == nkAccQuoted:
|
||||
ident = considerQuotedIdent(c, n[1])
|
||||
else:
|
||||
# this includes sym and symchoice nodes, but since we are looking in
|
||||
# a module, it shouldn't matter what was captured
|
||||
ident = n[1].getPIdent
|
||||
if ident != nil:
|
||||
if m == c.module:
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
|
||||
@@ -629,7 +629,7 @@ proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "
|
||||
message(conf, info, warnDeprecated, msg)
|
||||
|
||||
proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
|
||||
if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
|
||||
if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return
|
||||
writeContext(conf, info)
|
||||
liMessage(conf, info, errInternal, errMsg, doAbort, info2)
|
||||
|
||||
|
||||
@@ -226,8 +226,8 @@ type
|
||||
strictCaseObjects,
|
||||
inferGenericTypes,
|
||||
openSym, # remove nfDisabledOpenSym when this is default
|
||||
# separated alternatives to above:
|
||||
genericsOpenSym, templateOpenSym,
|
||||
# alternative to above:
|
||||
genericsOpenSym
|
||||
vtables
|
||||
|
||||
LegacyFeature* = enum
|
||||
|
||||
@@ -1401,7 +1401,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode =
|
||||
result = primarySuffix(p, result, baseInd, mode)
|
||||
|
||||
proc binaryNot(p: var Parser; a: PNode): PNode =
|
||||
if p.tok.tokType == tkNot:
|
||||
if p.tok.tokType == tkNot and p.tok.indent < 0:
|
||||
let notOpr = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
optInd(p, notOpr)
|
||||
|
||||
@@ -800,13 +800,14 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
|
||||
proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode =
|
||||
var callNode: PNode
|
||||
|
||||
if n.kind in {nkIdent, nkSym}:
|
||||
case n.kind
|
||||
of nkIdentKinds:
|
||||
# pragma -> pragma()
|
||||
callNode = newTree(nkCall, n)
|
||||
elif n.kind == nkExprColonExpr:
|
||||
of nkExprColonExpr:
|
||||
# pragma: arg -> pragma(arg)
|
||||
callNode = newTree(nkCall, n[0], n[1])
|
||||
elif n.kind in nkPragmaCallKinds:
|
||||
of nkPragmaCallKinds - {nkExprColonExpr}:
|
||||
callNode = n
|
||||
else:
|
||||
invalidPragma(c, n)
|
||||
@@ -1343,6 +1344,16 @@ proc mergePragmas(n, pragmas: PNode) =
|
||||
else:
|
||||
for p in pragmas: n[pragmasPos].add p
|
||||
|
||||
proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) =
|
||||
if n[pragmasPos].kind == nkEmpty:
|
||||
n[pragmasPos] = newNodeI(nkPragma, n.info)
|
||||
for p in pragmas:
|
||||
let prag = whichPragma(p)
|
||||
if prag in validPragmas:
|
||||
let copy = copyTree(p)
|
||||
overwriteLineInfo copy, n.info
|
||||
n[pragmasPos].add copy
|
||||
|
||||
proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
|
||||
validPragmas: TSpecialWords) =
|
||||
if sym != nil and sym.kind != skModule:
|
||||
@@ -1356,7 +1367,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
|
||||
internalError(c.config, info, "implicitPragmas")
|
||||
inc i
|
||||
popInfoContext(c.config)
|
||||
if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o)
|
||||
if sym.kind in routineKinds and sym.ast != nil:
|
||||
mergeValidPragmas(sym.ast, o, validPragmas)
|
||||
|
||||
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
|
||||
localError(c.config, info, ".dynlib requires .exportc")
|
||||
|
||||
@@ -442,6 +442,11 @@ proc atom(g: TSrcGen; n: PNode): string =
|
||||
result = $n.floatVal & "\'f64"
|
||||
else:
|
||||
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
|
||||
of nkFloat128Lit:
|
||||
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
|
||||
result = $n.floatVal & "\'f128"
|
||||
else:
|
||||
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128"
|
||||
of nkNilLit: result = "nil"
|
||||
of nkType:
|
||||
if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
|
||||
|
||||
@@ -246,10 +246,18 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
candidates.add(getProcHeader(c.config, err.sym, prefer))
|
||||
candidates.addDeclaredLocMaybe(c.config, err.sym)
|
||||
candidates.add("\n")
|
||||
let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
|
||||
const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam}
|
||||
let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches
|
||||
var argList = n
|
||||
if isGenericMismatch and n[0].kind == nkBracketExpr:
|
||||
argList = n[0]
|
||||
let nArg =
|
||||
if err.firstMismatch.arg < argList.len:
|
||||
argList[err.firstMismatch.arg]
|
||||
else:
|
||||
nil
|
||||
let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
|
||||
if n.len > 1:
|
||||
const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam}
|
||||
if verboseTypeMismatch notin c.config.legacyFeatures:
|
||||
case err.firstMismatch.kind
|
||||
of kUnknownNamedParam:
|
||||
@@ -309,7 +317,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
var wanted = err.firstMismatch.formal.typ
|
||||
if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints:
|
||||
wanted = wanted.genericConstraint
|
||||
let got = arg.typ
|
||||
let got = arg.typ.skipTypes({tyTypeDesc})
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
doAssert wanted != nil
|
||||
doAssert got != nil
|
||||
@@ -350,17 +358,9 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
of kMissingGenericParam:
|
||||
candidates.add("\n missing generic parameter: " & nameParam)
|
||||
of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded:
|
||||
var arg: PNode = nArg
|
||||
let genericMismatch = err.firstMismatch.kind == kGenericParamTypeMismatch
|
||||
if genericMismatch:
|
||||
let pos = err.firstMismatch.arg
|
||||
doAssert n[0].kind == nkBracketExpr and pos < n[0].len
|
||||
arg = n[0][pos]
|
||||
else:
|
||||
arg = nArg
|
||||
doAssert arg != nil
|
||||
doAssert nArg != nil
|
||||
var wanted = err.firstMismatch.formal.typ
|
||||
if genericMismatch and wanted.kind == tyGenericParam and
|
||||
if isGenericMismatch and wanted.kind == tyGenericParam and
|
||||
wanted.genericParamHasConstraints:
|
||||
wanted = wanted.genericConstraint
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
@@ -368,16 +368,17 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
|
||||
candidates.add "\n but expression '"
|
||||
if err.firstMismatch.kind == kVarNeeded:
|
||||
candidates.add renderNotLValue(arg)
|
||||
candidates.add renderNotLValue(nArg)
|
||||
candidates.add "' is immutable, not 'var'"
|
||||
else:
|
||||
candidates.add renderTree(arg)
|
||||
candidates.add renderTree(nArg)
|
||||
candidates.add "' is of type: "
|
||||
let got = arg.typ
|
||||
var got = nArg.typ
|
||||
if isGenericMismatch: got = got.skipTypes({tyTypeDesc})
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, got)
|
||||
if arg.kind in nkSymChoices:
|
||||
if nArg.kind in nkSymChoices:
|
||||
candidates.add "\n"
|
||||
candidates.add ambiguousIdentifierMsg(arg, indent = 2)
|
||||
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
|
||||
doAssert wanted != nil
|
||||
if got != nil:
|
||||
if got.kind == tyProc and wanted.kind == tyProc:
|
||||
|
||||
@@ -75,6 +75,7 @@ type
|
||||
# overload resolution.
|
||||
efTypeAllowed # typeAllowed will be called after
|
||||
efWantNoDefaults
|
||||
efIgnoreDefaults # var statements without initialization
|
||||
efAllowSymChoice # symchoice node should not be resolved
|
||||
|
||||
TExprFlags* = set[TExprFlag]
|
||||
|
||||
@@ -187,6 +187,7 @@ proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType,
|
||||
break
|
||||
o = o.owner
|
||||
# nothing found
|
||||
n.flags.excl nfDisabledOpenSym
|
||||
if not warnDisabled and isSym:
|
||||
result = semExpr(c, n, flags, expectedType)
|
||||
else:
|
||||
@@ -197,7 +198,9 @@ proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType,
|
||||
|
||||
proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
if n.kind == nkOpenSymChoice:
|
||||
result = semOpenSym(c, n, flags, expectedType, warnDisabled = nfDisabledOpenSym in n.flags)
|
||||
result = semOpenSym(c, n, flags, expectedType,
|
||||
warnDisabled = nfDisabledOpenSym in n.flags and
|
||||
genericsOpenSym notin c.features)
|
||||
if result != nil:
|
||||
return
|
||||
result = n
|
||||
@@ -887,6 +890,9 @@ proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
|
||||
|
||||
proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) =
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
if n[0].typ == nil:
|
||||
# n[0] might be erroring node in nimsuggest
|
||||
return
|
||||
const
|
||||
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
|
||||
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
|
||||
@@ -3293,8 +3299,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
of nkSym:
|
||||
let s = n.sym
|
||||
if nfDisabledOpenSym in n.flags:
|
||||
let res = semOpenSym(c, n, flags, expectedType, warnDisabled = true)
|
||||
assert res == nil
|
||||
let override = genericsOpenSym in c.features
|
||||
let res = semOpenSym(c, n, flags, expectedType,
|
||||
warnDisabled = not override)
|
||||
if res != nil:
|
||||
assert override
|
||||
return res
|
||||
# because of the changed symbol binding, this does not mean that we
|
||||
# don't have to check the symbol for semantics here again!
|
||||
result = semSym(c, n, s, flags)
|
||||
@@ -3307,7 +3317,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
of nkNilLit:
|
||||
if result.typ == nil:
|
||||
result.typ = getNilType(c)
|
||||
if expectedType != nil:
|
||||
if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}:
|
||||
var m = newCandidate(c, result.typ)
|
||||
if typeRel(m, expectedType, result.typ) >= isSubtype:
|
||||
result.typ = expectedType
|
||||
|
||||
@@ -74,7 +74,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
else:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
if canOpenSym(s):
|
||||
if {openSym, genericsOpenSym} * c.features != {}:
|
||||
if openSym in c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
@@ -112,7 +112,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
# we are in a generic context and `prepareNode` will be called
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if {openSym, genericsOpenSym} * c.features != {}:
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
@@ -122,7 +122,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if {openSym, genericsOpenSym} * c.features != {}:
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
@@ -141,7 +141,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
return
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if {openSym, genericsOpenSym} * c.features != {}:
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
@@ -153,7 +153,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
# we are in a generic context and `prepareNode` will be called
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if {openSym, genericsOpenSym} * c.features != {}:
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
@@ -164,7 +164,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if {openSym, genericsOpenSym} * c.features != {}:
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
|
||||
@@ -254,6 +254,8 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
|
||||
|
||||
let needsStaticSkipping = resulti.kind == tyFromExpr
|
||||
let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags
|
||||
if resulti.kind == tyFromExpr:
|
||||
resulti.flags.incl tfNonConstExpr
|
||||
result[i] = replaceTypeVarsT(cl, resulti)
|
||||
if needsStaticSkipping:
|
||||
result[i] = result[i].skipTypes({tyStatic})
|
||||
|
||||
@@ -50,8 +50,8 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
m = mode.intVal
|
||||
result = newNodeI(nkTypeOfExpr, n.info)
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
dec c.inTypeofContext
|
||||
result.add typExpr
|
||||
if typExpr.typ.kind == tyFromExpr:
|
||||
typExpr.typ.flags.incl tfNonConstExpr
|
||||
|
||||
@@ -387,10 +387,13 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
if e != nil:
|
||||
result.status = initFull
|
||||
elif field.ast != nil:
|
||||
result.status = initUnknown
|
||||
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
|
||||
if efIgnoreDefaults notin flags:
|
||||
result.status = initUnknown
|
||||
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
|
||||
else:
|
||||
result.status = initNone
|
||||
else:
|
||||
if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass
|
||||
if {efWantNoDefaults, efIgnoreDefaults} * flags == {}: # cannot compute defaults at the typeRightPass
|
||||
let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault)
|
||||
if defaultExpr != nil:
|
||||
result.status = initUnknown
|
||||
@@ -443,7 +446,7 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
|
||||
assert objType != nil
|
||||
if objType.kind == tyObject:
|
||||
var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
|
||||
let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
|
||||
let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults})
|
||||
if constrCtx.missingFields.len > 0:
|
||||
localError(c.config, info,
|
||||
"The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)])
|
||||
|
||||
@@ -1210,7 +1210,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
|
||||
tracked.owner.flags.incl sfInjectDestructors
|
||||
# bug #15038: ensure consistency
|
||||
if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ
|
||||
if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ = n.sym.typ
|
||||
of nkHiddenAddr, nkAddr:
|
||||
if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym) and
|
||||
n.typ.kind notin {tyVar, tyLent}:
|
||||
|
||||
@@ -233,7 +233,7 @@ proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bo
|
||||
of OverloadableSyms:
|
||||
result = symChoice(c.c, n, s, scOpen, isField)
|
||||
if not isField and result.kind in {nkSym, nkOpenSymChoice}:
|
||||
if {openSym, templateOpenSym} * c.c.features != {}:
|
||||
if openSym in c.c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
@@ -246,7 +246,7 @@ proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bo
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
|
||||
if not isField and s.owner != c.owner:
|
||||
if {openSym, templateOpenSym} * c.c.features != {}:
|
||||
if openSym in c.c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
@@ -264,7 +264,7 @@ proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bo
|
||||
if not isField and not (s.owner == c.owner and
|
||||
s.typ != nil and s.typ.kind == tyGenericParam) and
|
||||
result.kind in {nkSym, nkOpenSymChoice}:
|
||||
if {openSym, templateOpenSym} * c.c.features != {}:
|
||||
if openSym in c.c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
@@ -277,7 +277,7 @@ proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bo
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if not isField:
|
||||
if {openSym, templateOpenSym} * c.c.features != {}:
|
||||
if openSym in c.c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
@@ -693,6 +693,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
pushOwner(c, s)
|
||||
openScope(c)
|
||||
n[namePos] = newSymNode(s)
|
||||
s.ast = n # for implicitPragmas to use
|
||||
pragmaCallable(c, s, n, templatePragmas)
|
||||
implicitPragmas(c, s, n.info, templatePragmas)
|
||||
|
||||
@@ -763,11 +764,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
closeScope(c)
|
||||
popOwner(c)
|
||||
|
||||
# set the symbol AST after pragmas, at least. This stops pragma that have
|
||||
# been pushed (implicit) to be explicitly added to the template definition
|
||||
# and misapplied to the body. see #18113
|
||||
s.ast = n
|
||||
|
||||
if sfCustomPragma in s.flags:
|
||||
if n[bodyPos].kind != nkEmpty:
|
||||
localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)
|
||||
|
||||
@@ -586,9 +586,14 @@ proc semBranchRange(c: PContext, n, a, b: PNode, covered: var Int128): PNode =
|
||||
let bc = semConstExpr(c, b)
|
||||
if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}:
|
||||
localError(c.config, b.info, "range of string is invalid")
|
||||
let at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
|
||||
let bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
|
||||
|
||||
var at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
|
||||
var bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
|
||||
# the calls to fitNode may introduce calls to converters
|
||||
# mirrored with semCaseBranch for single elements
|
||||
if at.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
|
||||
at = semConstExpr(c, at)
|
||||
if bt.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
|
||||
bt = semConstExpr(c, bt)
|
||||
result = newNodeI(nkRange, a.info)
|
||||
result.add(at)
|
||||
result.add(bt)
|
||||
@@ -619,6 +624,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
|
||||
var b = branch[i]
|
||||
if b.kind == nkRange:
|
||||
branch[i] = b
|
||||
# same check as in semBranchRange for exhaustiveness
|
||||
covered = covered + getOrdValue(b[1]) + 1 - getOrdValue(b[0])
|
||||
elif isRange(b):
|
||||
branch[i] = semCaseBranchRange(c, n, b, covered)
|
||||
else:
|
||||
@@ -634,8 +641,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
var tmp = fitNode(c, n[0].typ, r, r.info)
|
||||
# the call to fitNode may introduce a call to a converter
|
||||
if tmp.kind == nkHiddenCallConv or
|
||||
(tmp.kind == nkHiddenStdConv and n[0].typ.kind == tyCstring):
|
||||
# mirrored with semBranchRange
|
||||
if tmp.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
|
||||
tmp = semConstExpr(c, tmp)
|
||||
branch[i] = skipConv(tmp)
|
||||
inc(covered)
|
||||
@@ -1864,8 +1871,8 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
|
||||
proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
|
||||
openScope(c)
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let t = semExprWithType(c, n, {efInTypeof})
|
||||
dec c.inTypeofContext
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
result = t.typ
|
||||
@@ -1882,8 +1889,8 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
else:
|
||||
m = mode.intVal
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
dec c.inTypeofContext
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
result = t.typ
|
||||
@@ -2160,7 +2167,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if s.kind != skError: localError(c.config, n.info, errTypeExpected)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
|
||||
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
|
||||
internalAssert c.config, s.typ.base.kind != tyNone
|
||||
result = s.typ.base
|
||||
elif prev == nil:
|
||||
result = s.typ
|
||||
@@ -2184,7 +2191,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if s.kind == skType:
|
||||
s.typ
|
||||
else:
|
||||
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
|
||||
internalAssert c.config, s.typ.base.kind != tyNone
|
||||
s.typ.base
|
||||
let alias = maybeAliasType(c, t, prev)
|
||||
if alias != nil:
|
||||
|
||||
@@ -1125,9 +1125,21 @@ proc inferStaticsInRange(c: var TCandidate,
|
||||
doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete))
|
||||
|
||||
template subtypeCheck() =
|
||||
if result <= isSubrange and f.last.skipTypes(abstractInst).kind in {
|
||||
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
|
||||
case result
|
||||
of isIntConv:
|
||||
result = isNone
|
||||
of isSubrange:
|
||||
discard # XXX should be isNone with preview define, warnings
|
||||
of isConvertible:
|
||||
if f.last.skipTypes(abstractInst).kind != tyOpenArray:
|
||||
# exclude var openarray which compiler supports
|
||||
result = isNone
|
||||
of isSubtype:
|
||||
if f.last.skipTypes(abstractInst).kind in {
|
||||
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
|
||||
# compiler can't handle subtype conversions with pointer indirection
|
||||
result = isNone
|
||||
else: discard
|
||||
|
||||
proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
|
||||
# this proc is always called for a pair of matching types
|
||||
@@ -1279,6 +1291,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if prev == nil: body
|
||||
else: return typeRel(c, prev, a, flags)
|
||||
|
||||
if c.c.inGenericContext > 0 and not c.isNoCall and
|
||||
(tfUnresolved in a.flags or a.kind in tyTypeClasses):
|
||||
# cheap check for unresolved arg, not nested
|
||||
return isNone
|
||||
|
||||
case a.kind
|
||||
of tyOr:
|
||||
# XXX: deal with the current dual meaning of tyGenericParam
|
||||
@@ -1523,7 +1540,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
reduceToBase(a)
|
||||
if effectiveArgType.kind == tyObject:
|
||||
if sameObjectTypes(f, effectiveArgType):
|
||||
c.inheritancePenalty = 0
|
||||
c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
|
||||
result = isEqual
|
||||
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
|
||||
elif trIsOutParam notin flags:
|
||||
@@ -1995,7 +2012,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
var r = tryResolvingStaticExpr(c, f.n)
|
||||
if r == nil: r = f.n
|
||||
if not exprStructuralEquivalent(r, aOrig.n) and
|
||||
not (aOrig.n.kind == nkIntLit and
|
||||
not (aOrig.n != nil and aOrig.n.kind == nkIntLit and
|
||||
inferStaticParam(c, r, aOrig.n.intVal)):
|
||||
result = isNone
|
||||
elif f.base.kind == tyGenericParam:
|
||||
@@ -2096,15 +2113,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
# not resolved
|
||||
result = isNone
|
||||
of tyTypeDesc:
|
||||
result = typeRel(c, a, reevaluated.base, flags)
|
||||
result = typeRel(c, reevaluated.base, a, flags)
|
||||
of tyStatic:
|
||||
result = typeRel(c, a, reevaluated.base, flags)
|
||||
result = typeRel(c, reevaluated.base, a, flags)
|
||||
if result != isNone and reevaluated.n != nil:
|
||||
if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
|
||||
result = isNone
|
||||
else:
|
||||
# bug #14136: other types are just like 'tyStatic' here:
|
||||
result = typeRel(c, a, reevaluated, flags)
|
||||
result = typeRel(c, reevaluated, a, flags)
|
||||
if result != isNone and reevaluated.n != nil:
|
||||
if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
|
||||
result = isNone
|
||||
|
||||
@@ -511,7 +511,12 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
|
||||
if n[0].kind in kinds and
|
||||
not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and
|
||||
n[0][0].typ.skipTypes(abstractVar).kind == tyTuple
|
||||
): # elimination is harmful to `for tuple unpack` because of newTupleAccess
|
||||
) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and
|
||||
n.typ.kind == tyVar and
|
||||
n.typ.skipTypes(abstractVar).kind == tyOpenArray and
|
||||
n[0][0].typ.skipTypes(abstractVar).kind == tyString)
|
||||
: # elimination is harmful to `for tuple unpack` because of newTupleAccess
|
||||
# it is also harmful to openArrayLoc (var openArray) for strings
|
||||
# addr ( deref ( x )) --> x
|
||||
result = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
|
||||
@@ -1250,18 +1250,18 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
b = skipTypes(b.last, aliasSkipSet)
|
||||
assert(a != nil)
|
||||
assert(b != nil)
|
||||
if a.kind != b.kind:
|
||||
case c.cmp
|
||||
of dcEq: return false
|
||||
of dcEqIgnoreDistinct:
|
||||
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
|
||||
a = a.skipTypes(distinctSkipSet)
|
||||
b = b.skipTypes(distinctSkipSet)
|
||||
if a.kind != b.kind: return false
|
||||
of dcEqOrDistinctOf:
|
||||
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
|
||||
a = a.skipTypes(distinctSkipSet)
|
||||
if a.kind != b.kind: return false
|
||||
case c.cmp
|
||||
of dcEq:
|
||||
if a.kind != b.kind: return false
|
||||
of dcEqIgnoreDistinct:
|
||||
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
|
||||
a = a.skipTypes(distinctSkipSet)
|
||||
b = b.skipTypes(distinctSkipSet)
|
||||
if a.kind != b.kind: return false
|
||||
of dcEqOrDistinctOf:
|
||||
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
|
||||
a = a.skipTypes(distinctSkipSet)
|
||||
if a.kind != b.kind: return false
|
||||
|
||||
#[
|
||||
The following code should not run in the case either side is an generic alias,
|
||||
@@ -1269,7 +1269,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
objects ie `type A[T] = SomeObject`
|
||||
]#
|
||||
# this is required by tunique_type but makes no sense really:
|
||||
if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
|
||||
if c.cmp == dcEq and x.kind == tyGenericInst and
|
||||
IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
|
||||
let
|
||||
lhs = x.skipGenericAlias
|
||||
rhs = y.skipGenericAlias
|
||||
|
||||
@@ -609,7 +609,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcYldVal: assert false
|
||||
of opcAsgnInt:
|
||||
decodeB(rkInt)
|
||||
regs[ra].intVal = regs[rb].intVal
|
||||
if regs[rb].kind == rkInt:
|
||||
regs[ra].intVal = regs[rb].intVal
|
||||
else:
|
||||
stackTrace(c, tos, pc, "opcAsgnInt: got " & $regs[rb].kind)
|
||||
of opcAsgnFloat:
|
||||
decodeB(rkFloat)
|
||||
regs[ra].floatVal = regs[rb].floatVal
|
||||
@@ -676,16 +679,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
else:
|
||||
assert regs[rb].kind == rkNode
|
||||
let nb = regs[rb].node
|
||||
case nb.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
ensureKind(rkInt)
|
||||
regs[ra].intVal = nb.intVal
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
ensureKind(rkFloat)
|
||||
regs[ra].floatVal = nb.floatVal
|
||||
if nb == nil:
|
||||
stackTrace(c, tos, pc, errNilAccess)
|
||||
else:
|
||||
ensureKind(rkNode)
|
||||
regs[ra].node = nb
|
||||
case nb.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
ensureKind(rkInt)
|
||||
regs[ra].intVal = nb.intVal
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
ensureKind(rkFloat)
|
||||
regs[ra].floatVal = nb.floatVal
|
||||
else:
|
||||
ensureKind(rkNode)
|
||||
regs[ra].node = nb
|
||||
of opcSlice:
|
||||
# A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits
|
||||
# nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken.
|
||||
@@ -850,25 +856,30 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcLdObj:
|
||||
# a = b.c
|
||||
decodeBC(rkNode)
|
||||
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
|
||||
case src.kind
|
||||
of nkEmpty..nkNilLit:
|
||||
# for nkPtrLit, this could be supported in the future, use something like:
|
||||
# derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
|
||||
# where we compute the offset in bytes for field rc
|
||||
stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
|
||||
of nkObjConstr:
|
||||
let n = src[rc + 1].skipColon
|
||||
regs[ra].node = n
|
||||
of nkTupleConstr:
|
||||
let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
|
||||
src[rc]
|
||||
else:
|
||||
src[rc].skipColon
|
||||
regs[ra].node = n
|
||||
if rb >= regs.len or regs[rb].kind == rkNone or
|
||||
(regs[rb].kind == rkNode and regs[rb].node == nil) or
|
||||
(regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil):
|
||||
stackTrace(c, tos, pc, errNilAccess)
|
||||
else:
|
||||
let n = src[rc]
|
||||
regs[ra].node = n
|
||||
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
|
||||
case src.kind
|
||||
of nkEmpty..nkNilLit:
|
||||
# for nkPtrLit, this could be supported in the future, use something like:
|
||||
# derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
|
||||
# where we compute the offset in bytes for field rc
|
||||
stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
|
||||
of nkObjConstr:
|
||||
let n = src[rc + 1].skipColon
|
||||
regs[ra].node = n
|
||||
of nkTupleConstr:
|
||||
let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
|
||||
src[rc]
|
||||
else:
|
||||
src[rc].skipColon
|
||||
regs[ra].node = n
|
||||
else:
|
||||
let n = src[rc]
|
||||
regs[ra].node = n
|
||||
of opcLdObjAddr:
|
||||
# a = addr(b.c)
|
||||
decodeBC(rkNodeAddr)
|
||||
|
||||
@@ -237,7 +237,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
|
||||
of tyProc:
|
||||
if inst:
|
||||
result = newNodeX(nkProcTy)
|
||||
result = newNodeX(if tfIterator in t.flags: nkIteratorTy else: nkProcTy)
|
||||
var fp = newNodeX(nkFormalParams)
|
||||
if t.returnType == nil:
|
||||
fp.add newNodeI(nkEmpty, info)
|
||||
@@ -246,8 +246,15 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
for i in FirstParamAt..<t.kidsLen:
|
||||
fp.add newIdentDefs(t.n[i], t[i])
|
||||
result.add fp
|
||||
result.add if t.n[0].len > 0: t.n[0][pragmasEffects].copyTree
|
||||
else: newNodeI(nkEmpty, info)
|
||||
var prag =
|
||||
if t.n[0].len > 0:
|
||||
t.n[0][pragmasEffects].copyTree
|
||||
else:
|
||||
newNodeI(nkEmpty, info)
|
||||
if t.callConv != ccClosure or tfExplicitCallConv in t.flags:
|
||||
if prag.kind == nkEmpty: prag = newNodeI(nkPragma, info)
|
||||
prag.add newIdentNode(getIdent(cache, $t.callConv), info)
|
||||
result.add prag
|
||||
else:
|
||||
result = mapTypeToBracket("proc", mNone, t, info)
|
||||
of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
|
||||
|
||||
@@ -53,6 +53,7 @@ type
|
||||
gfNode # Affects how variables are loaded - always loads as rkNode
|
||||
gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr
|
||||
gfIsParam # do not deepcopy parameters, they are immutable
|
||||
gfIsSinkParam # deepcopy sink parameters
|
||||
TGenFlags = set[TGenFlag]
|
||||
|
||||
proc debugInfo(c: PCtx; info: TLineInfo): string =
|
||||
@@ -245,7 +246,7 @@ proc getTemp(cc: PCtx; tt: PType): TRegister =
|
||||
|
||||
proc freeTemp(c: PCtx; r: TRegister) =
|
||||
let c = c.prc
|
||||
if c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}:
|
||||
if r < c.regInfo.len and c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}:
|
||||
# this seems to cause https://github.com/nim-lang/Nim/issues/10647
|
||||
c.regInfo[r].inUse = false
|
||||
|
||||
@@ -357,12 +358,13 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
|
||||
#if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}:
|
||||
if i != dest:
|
||||
when not defined(release):
|
||||
if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
|
||||
slotTempInt,
|
||||
slotTempFloat,
|
||||
slotTempStr,
|
||||
slotTempComplex}:
|
||||
raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind
|
||||
if c.config.cmd != cmdCheck:
|
||||
if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
|
||||
slotTempInt,
|
||||
slotTempFloat,
|
||||
slotTempStr,
|
||||
slotTempComplex}:
|
||||
raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind
|
||||
c.prc.regInfo[i] = (inUse: false, kind: slotEmpty)
|
||||
|
||||
c.clearDest(n, dest)
|
||||
@@ -619,10 +621,17 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
|
||||
let fntyp = skipTypes(n[0].typ, abstractInst)
|
||||
for i in 0..<n.len:
|
||||
var r: TRegister = x+i
|
||||
c.gen(n[i], r, {gfIsParam})
|
||||
if i >= fntyp.signatureLen:
|
||||
c.gen(n[i], r, {gfIsParam})
|
||||
internalAssert c.config, tfVarargs in fntyp.flags
|
||||
c.gABx(n, opcSetType, r, c.genType(n[i].typ))
|
||||
else:
|
||||
if fntyp[i] != nil and fntyp[i].kind == tySink and
|
||||
fntyp[i].skipTypes({tySink}).kind in {tyObject, tyString, tySequence}:
|
||||
c.gen(n[i], r, {gfIsSinkParam})
|
||||
else:
|
||||
c.gen(n[i], r, {gfIsParam})
|
||||
|
||||
if dest < 0:
|
||||
c.gABC(n, opcIndCall, 0, x, n.len)
|
||||
else:
|
||||
@@ -696,6 +705,9 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
||||
let dest = c.genx(le, {gfNodeAddr})
|
||||
c.gABC(le, opcWrDeref, dest, 0, value)
|
||||
c.freeTemp(dest)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
if sameBackendType(le.typ, le[1].typ):
|
||||
genAsgnPatch(c, le[1], value)
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -868,7 +880,7 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||
genBinaryABC(c, n, dest, opc)
|
||||
c.genNarrow(n, dest)
|
||||
|
||||
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
|
||||
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest, flags: TGenFlags = {}; opc=opcConv) =
|
||||
let t2 = n.typ.skipTypes({tyDistinct})
|
||||
let targ2 = arg.typ.skipTypes({tyDistinct})
|
||||
|
||||
@@ -882,7 +894,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
|
||||
result = false
|
||||
|
||||
if implicitConv():
|
||||
gen(c, arg, dest)
|
||||
gen(c, arg, dest, flags)
|
||||
return
|
||||
|
||||
let tmp = c.genx(arg)
|
||||
@@ -1050,7 +1062,7 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode =
|
||||
else:
|
||||
(if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex)
|
||||
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) =
|
||||
case m
|
||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||
of mOr: c.genAndOr(n, opcTJmp, dest)
|
||||
@@ -1189,7 +1201,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(size*8))
|
||||
of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr:
|
||||
genConv(c, n, n[1], dest)
|
||||
genConv(c, n, n[1], dest, flags)
|
||||
of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
|
||||
of mEqCString: genBinaryABC(c, n, dest, opcEqCString)
|
||||
of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
|
||||
@@ -1529,7 +1541,11 @@ proc setSlot(c: PCtx; v: PSym) =
|
||||
if v.position == 0:
|
||||
v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
|
||||
|
||||
proc cannotEval(c: PCtx; n: PNode) {.noinline.} =
|
||||
template cannotEval(c: PCtx; n: PNode) =
|
||||
if c.config.cmd == cmdCheck:
|
||||
localError(c.config, n.info, "cannot evaluate at compile time: " &
|
||||
n.renderTree)
|
||||
return
|
||||
globalError(c.config, n.info, "cannot evaluate at compile time: " &
|
||||
n.renderTree)
|
||||
|
||||
@@ -1652,6 +1668,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||
c.freeTemp(cc)
|
||||
else:
|
||||
gen(c, ri, dest)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
if sameBackendType(le.typ, le[1].typ):
|
||||
genAsgn(c, le[1], ri, requiresCopy)
|
||||
else:
|
||||
let dest = c.genx(le, {gfNodeAddr})
|
||||
genAsgn(c, dest, ri, requiresCopy)
|
||||
@@ -1729,6 +1748,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
c.gABx(n, opcLdGlobalAddr, dest, s.position)
|
||||
elif isImportcVar:
|
||||
c.gABx(n, opcLdGlobalDerefFFI, dest, s.position)
|
||||
elif gfIsSinkParam in flags:
|
||||
genAsgn(c, dest, n, requiresCopy = true)
|
||||
elif fitsRegister(s.typ) and gfNode notin flags:
|
||||
var cc = c.getTemp(n.typ)
|
||||
c.gABx(n, opcLdGlobal, cc, s.position)
|
||||
@@ -1742,7 +1763,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
s.kind in {skParam, skResult}):
|
||||
if dest < 0:
|
||||
dest = s.position + ord(s.kind == skParam)
|
||||
internalAssert(c.config, c.prc.regInfo[dest].kind < slotSomeTemp)
|
||||
internalAssert(c.config, c.prc.regInfo.len > dest and c.prc.regInfo[dest].kind < slotSomeTemp)
|
||||
else:
|
||||
# we need to generate an assignment:
|
||||
let requiresCopy = c.prc.regInfo[dest].kind >= slotSomeTemp and
|
||||
@@ -2164,7 +2185,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
if n[0].kind == nkSym:
|
||||
let s = n[0].sym
|
||||
if s.magic != mNone:
|
||||
genMagic(c, n, dest, s.magic)
|
||||
genMagic(c, n, dest, flags, s.magic)
|
||||
elif s.kind == skMethod:
|
||||
localError(c.config, n.info, "cannot call method " & s.name.s &
|
||||
" at compile time")
|
||||
@@ -2221,11 +2242,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
unused(c, n, dest)
|
||||
gen(c, n[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
genConv(c, n, n[1], dest)
|
||||
genConv(c, n, n[1], dest, flags)
|
||||
of nkObjDownConv:
|
||||
genConv(c, n, n[0], dest)
|
||||
genConv(c, n, n[0], dest, flags)
|
||||
of nkObjUpConv:
|
||||
genConv(c, n, n[0], dest)
|
||||
genConv(c, n, n[0], dest, flags)
|
||||
of nkVarSection, nkLetSection:
|
||||
unused(c, n, dest)
|
||||
genVarSection(c, n)
|
||||
@@ -2235,7 +2256,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
genLit(c, newSymNode(n[namePos].sym), dest)
|
||||
of nkChckRangeF, nkChckRange64, nkChckRange:
|
||||
if skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64}:
|
||||
genConv(c, n, n[0], dest)
|
||||
genConv(c, n, n[0], dest, flags)
|
||||
else:
|
||||
let
|
||||
tmp0 = c.genx(n[0])
|
||||
@@ -2261,7 +2282,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest)
|
||||
of nkCast:
|
||||
if allowCast in c.features:
|
||||
genConv(c, n, n[1], dest, opcCast)
|
||||
genConv(c, n, n[1], dest, flags, opcCast)
|
||||
else:
|
||||
genCastIntFloat(c, n, dest)
|
||||
of nkTypeOfExpr:
|
||||
|
||||
@@ -2533,8 +2533,7 @@ renaming the captured symbols should be used instead so that the code is not
|
||||
affected by context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym`, or `genericsOpenSym` and `templateOpenSym` for only the respective
|
||||
routines, needs to be enabled; and a warning is given in the case where an
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
@@ -2555,7 +2554,7 @@ template oldTempl(): string =
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".} # or {.experimental: "genericsOpenSym".} for just generic procs
|
||||
{.experimental: "openSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
@@ -2568,8 +2567,6 @@ proc baz[T](): string =
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
# {.experimental: "templateOpenSym".} would be needed here if genericsOpenSym was used
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
@@ -2590,6 +2587,34 @@ modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
Another experimental switch `genericsOpenSym` exists that enables this behavior
|
||||
at instantiation time, meaning templates etc can enable it specifically when
|
||||
they are being called. However this does not generate `nnkOpenSym` nodes
|
||||
(unless the other switch is enabled) and so doesn't reflect the regular
|
||||
behavior of the switch.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
echo bar[int]() # "injected"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
var res: string
|
||||
foo(123):
|
||||
res = value
|
||||
res
|
||||
assert barTempl() == "injected"
|
||||
```
|
||||
|
||||
|
||||
VTable for methods
|
||||
==================
|
||||
|
||||
37
doc/nims.md
37
doc/nims.md
@@ -61,43 +61,44 @@ Standard library modules
|
||||
|
||||
At least the following standard library modules are available:
|
||||
|
||||
* [macros](macros.html)
|
||||
* [os](os.html)
|
||||
* [strutils](strutils.html)
|
||||
* [math](math.html)
|
||||
* [distros](distros.html)
|
||||
* [sugar](sugar.html)
|
||||
* [algorithm](algorithm.html)
|
||||
* [base64](base64.html)
|
||||
* [bitops](bitops.html)
|
||||
* [chains](chains.html)
|
||||
* [colors](colors.html)
|
||||
* [complex](complex.html)
|
||||
* [distros](distros.html)
|
||||
* [std/editdistance](editdistance.html)
|
||||
* [htmlgen](htmlgen.html)
|
||||
* [htmlparser](htmlparser.html)
|
||||
* [httpcore](httpcore.html)
|
||||
* [json](json.html)
|
||||
* [lenientops](lenientops.html)
|
||||
* [macros](macros.html)
|
||||
* [math](math.html)
|
||||
* [options](options.html)
|
||||
* [os](os.html)
|
||||
* [parsecfg](parsecfg.html)
|
||||
* [parsecsv](parsecsv.html)
|
||||
* [parsejson](parsejson.html)
|
||||
* [parsesql](parsesql.html)
|
||||
* [parseutils](parseutils.html)
|
||||
* [punycode](punycode.html)
|
||||
* [random](random.html)
|
||||
* [ropes](ropes.html)
|
||||
* [std/setutils](setutils.html)
|
||||
* [stats](stats.html)
|
||||
* [strformat](strformat.html)
|
||||
* [strmisc](strmisc.html)
|
||||
* [strscans](strscans.html)
|
||||
* [unicode](unicode.html)
|
||||
* [uri](uri.html)
|
||||
* [std/editdistance](editdistance.html)
|
||||
* [std/wordwrap](wordwrap.html)
|
||||
* [parsecsv](parsecsv.html)
|
||||
* [parsecfg](parsecfg.html)
|
||||
* [parsesql](parsesql.html)
|
||||
* [xmlparser](xmlparser.html)
|
||||
* [htmlparser](htmlparser.html)
|
||||
* [ropes](ropes.html)
|
||||
* [json](json.html)
|
||||
* [parsejson](parsejson.html)
|
||||
* [strtabs](strtabs.html)
|
||||
* [strutils](strutils.html)
|
||||
* [sugar](sugar.html)
|
||||
* [unicode](unicode.html)
|
||||
* [unidecode](unidecode.html)
|
||||
* [uri](uri.html)
|
||||
* [std/wordwrap](wordwrap.html)
|
||||
* [xmlparser](xmlparser.html)
|
||||
|
||||
In addition to the standard Nim syntax ([system](system.html) module),
|
||||
NimScripts support the procs and templates defined in the
|
||||
|
||||
13
koch.nim
13
koch.nim
@@ -1,12 +1,12 @@
|
||||
#
|
||||
#
|
||||
# Maintenance program for Nim
|
||||
# (c) Copyright 2017 Andreas Rumpf
|
||||
# (c) Copyright 2024 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
# See doc/koch.txt for documentation.
|
||||
# See doc/koch.md for documentation.
|
||||
#
|
||||
|
||||
const
|
||||
@@ -52,7 +52,7 @@ const
|
||||
+-----------------------------------------------------------------+
|
||||
| Maintenance program for Nim |
|
||||
| Version $1|
|
||||
| (c) 2017 Andreas Rumpf |
|
||||
| (c) 2024 Andreas Rumpf |
|
||||
+-----------------------------------------------------------------+
|
||||
Build time: $2, $3
|
||||
|
||||
@@ -77,6 +77,7 @@ Possible Commands:
|
||||
doesn't require network connectivity
|
||||
nimble builds the Nimble tool
|
||||
atlas builds the Atlas tool
|
||||
checksums installs the checksums dependency
|
||||
fusion installs fusion via Nimble
|
||||
|
||||
Boot options:
|
||||
@@ -344,8 +345,7 @@ proc boot(args: string, skipIntegrityCheck: bool) =
|
||||
let smartNimcache = (if "release" in args or "danger" in args: "nimcache/r_" else: "nimcache/d_") &
|
||||
hostOS & "_" & hostCPU
|
||||
|
||||
if not dirExists("dist/checksums"):
|
||||
bundleChecksums(false)
|
||||
bundleChecksums(false)
|
||||
|
||||
let usingLibFFI = "nimHasLibFFI" in args
|
||||
if usingLibFFI and not dirExists("dist/libffi"):
|
||||
@@ -508,8 +508,7 @@ proc temp(args: string) =
|
||||
result[1].add " " & quoteShell(args[i])
|
||||
inc i
|
||||
|
||||
if not dirExists("dist/checksums"):
|
||||
bundleChecksums(false)
|
||||
bundleChecksums(false)
|
||||
|
||||
let d = getAppDir()
|
||||
let output = d / "compiler" / "nim".exe
|
||||
|
||||
@@ -161,16 +161,6 @@ proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
|
||||
result.value = value
|
||||
result.rawType = rawType
|
||||
|
||||
when declared(system.VarSlot):
|
||||
proc toAny*(x: VarSlot): Any {.inline.} =
|
||||
## Constructs an `Any` object from a variable slot `x`.
|
||||
## This captures `x`'s address, so `x` can be modified with its
|
||||
## `Any` wrapper! The caller needs to ensure that the wrapper
|
||||
## **does not** live longer than `x`!
|
||||
## This is provided for easier reflection capabilities of a debugger.
|
||||
result.value = x.address
|
||||
result.rawType = x.typ
|
||||
|
||||
proc toAny*[T](x: var T): Any {.inline.} =
|
||||
## Constructs an `Any` object from `x`. This captures `x`'s address, so
|
||||
## `x` can be modified with its `Any` wrapper! The caller needs to ensure
|
||||
|
||||
@@ -1526,7 +1526,7 @@ proc parseMarkdownCodeblockFields(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
else:
|
||||
result = newRstNode(rnFieldList)
|
||||
while currentTok(p).kind != tkIndent:
|
||||
while currentTok(p).kind notin {tkIndent, tkEof}:
|
||||
if currentTok(p).kind == tkWhite:
|
||||
inc p.idx
|
||||
else:
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
## Types and operations for atomic operations and lockless algorithms.
|
||||
##
|
||||
## Unstable API.
|
||||
##
|
||||
## By default, C++ uses C11 atomic primitives. To use C++ `std::atomic`,
|
||||
## `-d:nimUseCppAtomics` can be defined.
|
||||
|
||||
runnableExamples:
|
||||
# Atomic
|
||||
@@ -50,8 +53,7 @@ runnableExamples:
|
||||
flag.clear(moRelaxed)
|
||||
assert not flag.testAndSet
|
||||
|
||||
|
||||
when defined(cpp) or defined(nimdoc):
|
||||
when (defined(cpp) and defined(nimUseCppAtomics)) or defined(nimdoc):
|
||||
# For the C++ backend, types and operations map directly to C++11 atomics.
|
||||
|
||||
{.push, header: "<atomic>".}
|
||||
@@ -274,10 +276,17 @@ else:
|
||||
cast[T](interlockedXor(addr(location.value), cast[nonAtomicType(T)](value)))
|
||||
|
||||
else:
|
||||
{.push, header: "<stdatomic.h>".}
|
||||
when defined(cpp):
|
||||
{.push, header: "<atomic>".}
|
||||
template maybeWrapStd(x: string): string =
|
||||
"std::" & x
|
||||
else:
|
||||
{.push, header: "<stdatomic.h>".}
|
||||
template maybeWrapStd(x: string): string =
|
||||
x
|
||||
|
||||
type
|
||||
MemoryOrder* {.importc: "memory_order".} = enum
|
||||
MemoryOrder* {.importc: "memory_order".maybeWrapStd.} = enum
|
||||
moRelaxed
|
||||
moConsume
|
||||
moAcquire
|
||||
@@ -285,16 +294,25 @@ else:
|
||||
moAcquireRelease
|
||||
moSequentiallyConsistent
|
||||
|
||||
type
|
||||
# Atomic*[T] {.importcpp: "_Atomic('0)".} = object
|
||||
when defined(cpp):
|
||||
type
|
||||
# Atomic*[T] {.importcpp: "_Atomic('0)".} = object
|
||||
|
||||
AtomicInt8 {.importc: "_Atomic NI8".} = int8
|
||||
AtomicInt16 {.importc: "_Atomic NI16".} = int16
|
||||
AtomicInt32 {.importc: "_Atomic NI32".} = int32
|
||||
AtomicInt64 {.importc: "_Atomic NI64".} = int64
|
||||
AtomicInt8 {.importc: "std::atomic<NI8>".} = int8
|
||||
AtomicInt16 {.importc: "std::atomic<NI16>".} = int16
|
||||
AtomicInt32 {.importc: "std::atomic<NI32>".} = int32
|
||||
AtomicInt64 {.importc: "std::atomic<NI64>".} = int64
|
||||
else:
|
||||
type
|
||||
# Atomic*[T] {.importcpp: "_Atomic('0)".} = object
|
||||
|
||||
AtomicInt8 {.importc: "_Atomic NI8".} = int8
|
||||
AtomicInt16 {.importc: "_Atomic NI16".} = int16
|
||||
AtomicInt32 {.importc: "_Atomic NI32".} = int32
|
||||
AtomicInt64 {.importc: "_Atomic NI64".} = int64
|
||||
|
||||
type
|
||||
AtomicFlag* {.importc: "atomic_flag", size: 1.} = object
|
||||
AtomicFlag* {.importc: "atomic_flag".maybeWrapStd, size: 1.} = object
|
||||
|
||||
Atomic*[T] = object
|
||||
when T is Trivial:
|
||||
@@ -308,27 +326,27 @@ else:
|
||||
guard: AtomicFlag
|
||||
|
||||
#proc init*[T](location: var Atomic[T]; value: T): T {.importcpp: "atomic_init(@)".}
|
||||
proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc.}
|
||||
proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc.}
|
||||
proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
|
||||
proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
|
||||
proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc: "atomic_load_explicit".maybeWrapStd.}
|
||||
proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_store_explicit".maybeWrapStd.}
|
||||
proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_exchange_explicit".maybeWrapStd.}
|
||||
proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_strong_explicit".maybeWrapStd.}
|
||||
proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_weak_explicit".maybeWrapStd.}
|
||||
|
||||
# Numerical operations
|
||||
proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
|
||||
proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_add_explicit".maybeWrapStd.}
|
||||
proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_sub_explicit".maybeWrapStd.}
|
||||
proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_and_explicit".maybeWrapStd.}
|
||||
proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_or_explicit".maybeWrapStd.}
|
||||
proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_xor_explicit".maybeWrapStd.}
|
||||
|
||||
# Flag operations
|
||||
# var ATOMIC_FLAG_INIT {.importc, nodecl.}: AtomicFlag
|
||||
# proc init*(location: var AtomicFlag) {.inline.} = location = ATOMIC_FLAG_INIT
|
||||
proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".}
|
||||
proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".}
|
||||
proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".maybeWrapStd.}
|
||||
proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".maybeWrapStd.}
|
||||
|
||||
proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".}
|
||||
proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".}
|
||||
proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".maybeWrapStd.}
|
||||
proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".maybeWrapStd.}
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
||||
@@ -839,6 +839,7 @@ proc addHandler*(handler: Logger) =
|
||||
## each of those threads.
|
||||
##
|
||||
## See also:
|
||||
## * `removeHandler proc`_
|
||||
## * `getHandlers proc<#getHandlers>`_
|
||||
runnableExamples:
|
||||
var logger = newConsoleLogger()
|
||||
@@ -846,6 +847,16 @@ proc addHandler*(handler: Logger) =
|
||||
doAssert logger in getHandlers()
|
||||
handlers.add(handler)
|
||||
|
||||
proc removeHandler*(handler: Logger) =
|
||||
## Removes a logger from the list of registered handlers.
|
||||
##
|
||||
## Note that for n times a logger is registered, n calls to this proc
|
||||
## are required to remove that logger.
|
||||
for i, hnd in handlers:
|
||||
if hnd == handler:
|
||||
handlers.delete(i)
|
||||
return
|
||||
|
||||
proc getHandlers*(): seq[Logger] =
|
||||
## Returns a list of all the registered handlers.
|
||||
##
|
||||
|
||||
@@ -97,6 +97,8 @@ type
|
||||
length*: int
|
||||
addrList*: seq[string]
|
||||
|
||||
const IPPROTO_NONE* = IPPROTO_IP ## Use this if your socket type requires a protocol value of zero (e.g. Unix sockets).
|
||||
|
||||
when useWinVersion:
|
||||
let
|
||||
osInvalidSocket* = winlean.INVALID_SOCKET
|
||||
|
||||
@@ -97,7 +97,7 @@ import std/nativesockets
|
||||
import std/[os, strutils, times, sets, options, monotimes]
|
||||
import std/ssl_config
|
||||
export nativesockets.Port, nativesockets.`$`, nativesockets.`==`
|
||||
export Domain, SockType, Protocol
|
||||
export Domain, SockType, Protocol, IPPROTO_NONE
|
||||
|
||||
const useWinVersion = defined(windows) or defined(nimdoc)
|
||||
const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or
|
||||
|
||||
@@ -446,13 +446,17 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1",
|
||||
else:
|
||||
discard existsOrCreateDir(p)
|
||||
|
||||
proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
proc copyDir*(source, dest: string, skipSpecial = false) {.rtl, extern: "nos$1",
|
||||
tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} =
|
||||
## Copies a directory from `source` to `dest`.
|
||||
##
|
||||
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
|
||||
## are skipped.
|
||||
##
|
||||
## If `skipSpecial` is true, then (besides all directories) only *regular*
|
||||
## files (**without** special "file" objects like FIFOs, device files,
|
||||
## etc) will be copied on Unix.
|
||||
##
|
||||
## If this fails, `OSError` is raised.
|
||||
##
|
||||
## On the Windows platform this proc will copy the attributes from
|
||||
@@ -472,16 +476,17 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
## * `createDir proc`_
|
||||
## * `moveDir proc`_
|
||||
createDir(dest)
|
||||
for kind, path in walkDir(source):
|
||||
for kind, path in walkDir(source, skipSpecial = skipSpecial):
|
||||
var noSource = splitPath(path).tail
|
||||
if kind == pcDir:
|
||||
copyDir(path, dest / noSource)
|
||||
copyDir(path, dest / noSource, skipSpecial = skipSpecial)
|
||||
else:
|
||||
copyFile(path, dest / noSource, {cfSymlinkAsIs})
|
||||
|
||||
|
||||
proc copyDirWithPermissions*(source, dest: string,
|
||||
ignorePermissionErrors = true)
|
||||
ignorePermissionErrors = true,
|
||||
skipSpecial = false)
|
||||
{.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect],
|
||||
benign, noWeirdTarget.} =
|
||||
## Copies a directory from `source` to `dest` preserving file permissions.
|
||||
@@ -489,6 +494,10 @@ proc copyDirWithPermissions*(source, dest: string,
|
||||
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
|
||||
## are skipped.
|
||||
##
|
||||
## If `skipSpecial` is true, then (besides all directories) only *regular*
|
||||
## files (**without** special "file" objects like FIFOs, device files,
|
||||
## etc) will be copied on Unix.
|
||||
##
|
||||
## If this fails, `OSError` is raised. This is a wrapper proc around
|
||||
## `copyDir`_ and `copyFileWithPermissions`_ procs
|
||||
## on non-Windows platforms.
|
||||
@@ -518,10 +527,10 @@ proc copyDirWithPermissions*(source, dest: string,
|
||||
except:
|
||||
if not ignorePermissionErrors:
|
||||
raise
|
||||
for kind, path in walkDir(source):
|
||||
for kind, path in walkDir(source, skipSpecial = skipSpecial):
|
||||
var noSource = splitPath(path).tail
|
||||
if kind == pcDir:
|
||||
copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors)
|
||||
copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors, skipSpecial = skipSpecial)
|
||||
else:
|
||||
copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors, {cfSymlinkAsIs})
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 1
|
||||
else:
|
||||
# generic version of copyFile which works for any platform:
|
||||
var d, s: File
|
||||
if not open(s, source):raiseOSError(osLastError(), source)
|
||||
if not open(s, source): raiseOSError(osLastError(), source)
|
||||
if not open(d, dest, fmWrite):
|
||||
close(s)
|
||||
raiseOSError(osLastError(), dest)
|
||||
|
||||
@@ -2085,7 +2085,8 @@ when notJSnotNims:
|
||||
proc cmpMem(a, b: pointer, size: Natural): int =
|
||||
nimCmpMem(a, b, size).int
|
||||
|
||||
when not defined(js):
|
||||
when not defined(js) or defined(nimscript):
|
||||
# nimscript can be defined if config file for js compilation
|
||||
proc cmp(x, y: string): int =
|
||||
when nimvm:
|
||||
if x < y: result = -1
|
||||
@@ -2365,7 +2366,8 @@ proc finished*[T: iterator {.closure.}](x: T): bool {.noSideEffect, inline, magi
|
||||
from std/private/digitsutils import addInt
|
||||
export addInt
|
||||
|
||||
when defined(js):
|
||||
when defined(js) and not defined(nimscript):
|
||||
# nimscript can be defined if config file for js compilation
|
||||
include "system/jssys"
|
||||
include "system/reprjs"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
proc succ*[T: Ordinal](x: T, y: int = 1): T {.magic: "Succ", noSideEffect.} =
|
||||
proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} =
|
||||
## Returns the `y`-th successor (default: 1) of the value `x`.
|
||||
##
|
||||
## If such a value does not exist, `OverflowDefect` is raised
|
||||
@@ -7,7 +7,7 @@ proc succ*[T: Ordinal](x: T, y: int = 1): T {.magic: "Succ", noSideEffect.} =
|
||||
assert succ(5) == 6
|
||||
assert succ(5, 3) == 8
|
||||
|
||||
proc pred*[T: Ordinal](x: T, y: int = 1): T {.magic: "Pred", noSideEffect.} =
|
||||
proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} =
|
||||
## Returns the `y`-th predecessor (default: 1) of the value `x`.
|
||||
##
|
||||
## If such a value does not exist, `OverflowDefect` is raised
|
||||
@@ -16,7 +16,7 @@ proc pred*[T: Ordinal](x: T, y: int = 1): T {.magic: "Pred", noSideEffect.} =
|
||||
assert pred(5) == 4
|
||||
assert pred(5, 3) == 2
|
||||
|
||||
proc inc*[T: Ordinal](x: var T, y: int = 1) {.magic: "Inc", noSideEffect.} =
|
||||
proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} =
|
||||
## Increments the ordinal `x` by `y`.
|
||||
##
|
||||
## If such a value does not exist, `OverflowDefect` is raised or a compile
|
||||
@@ -28,7 +28,7 @@ proc inc*[T: Ordinal](x: var T, y: int = 1) {.magic: "Inc", noSideEffect.} =
|
||||
inc(i, 3)
|
||||
assert i == 6
|
||||
|
||||
proc dec*[T: Ordinal](x: var T, y: int = 1) {.magic: "Dec", noSideEffect.} =
|
||||
proc dec*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Dec", noSideEffect.} =
|
||||
## Decrements the ordinal `x` by `y`.
|
||||
##
|
||||
## If such a value does not exist, `OverflowDefect` is raised or a compile
|
||||
|
||||
@@ -6,11 +6,11 @@ const
|
||||
## ```
|
||||
# see also std/private/since
|
||||
|
||||
NimMinor* {.intdefine.}: int = 1
|
||||
NimMinor* {.intdefine.}: int = 2
|
||||
## is the minor number of Nim's version.
|
||||
## Odd for devel, even for releases.
|
||||
|
||||
NimPatch* {.intdefine.}: int = 99
|
||||
NimPatch* {.intdefine.}: int = 0
|
||||
## is the patch number of Nim's version.
|
||||
## Odd for devel, even for releases.
|
||||
|
||||
|
||||
20
nimsuggest/tests/tarrowcrash.nim
Normal file
20
nimsuggest/tests/tarrowcrash.nim
Normal file
@@ -0,0 +1,20 @@
|
||||
# issue #24179
|
||||
|
||||
import sugar
|
||||
|
||||
type
|
||||
Parser[T] = object
|
||||
|
||||
proc eatWhile[T](p: Parser[T], predicate: T -> bool): seq[T] =
|
||||
return @[]
|
||||
|
||||
proc skipWs(p: Parser[char]) =
|
||||
discard p.eatWhile((c: char) => c == 'a')
|
||||
#[!]#
|
||||
|
||||
discard """
|
||||
$nimsuggest --tester $file
|
||||
>chk $1
|
||||
chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tarrowcrash.nim [Processing]";;0
|
||||
chk;;skUnknown;;;;Hint;;$file;;11;;5;;"\'skipWs\' is declared but not used [XDeclaredButNotUsed]";;0
|
||||
"""
|
||||
@@ -171,3 +171,16 @@ block: # bug #23858
|
||||
return Object()
|
||||
discard fn()
|
||||
doAssert x == 1
|
||||
|
||||
block: # bug #24147
|
||||
type
|
||||
O = object of RootObj
|
||||
val: string
|
||||
OO = object of O
|
||||
|
||||
proc `=copy`(dest: var O, src: O) =
|
||||
dest.val = src.val
|
||||
|
||||
let oo = OO(val: "hello world")
|
||||
var ooCopy : OO
|
||||
`=copy`(ooCopy, oo)
|
||||
|
||||
@@ -820,3 +820,17 @@ block: # bug #23973
|
||||
doAssert t == a
|
||||
|
||||
n()
|
||||
|
||||
block: # bug #24141
|
||||
func reverse(s: var openArray[char]) =
|
||||
s[0] = 'f'
|
||||
|
||||
func rev(s: var string) =
|
||||
s.reverse
|
||||
|
||||
proc main =
|
||||
var abc = "abc"
|
||||
abc.rev
|
||||
doAssert abc == "fbc"
|
||||
|
||||
main()
|
||||
|
||||
@@ -41,6 +41,11 @@ block t8333:
|
||||
case 0
|
||||
of 'a': echo 0
|
||||
else: echo 1
|
||||
block: # issue #11422
|
||||
var c: int = 5
|
||||
case c
|
||||
of 'a' .. 'c': discard
|
||||
else: discard
|
||||
|
||||
|
||||
block emptyset_when:
|
||||
|
||||
7
tests/casestmt/trangeexhaustiveness.nim
Normal file
7
tests/casestmt/trangeexhaustiveness.nim
Normal file
@@ -0,0 +1,7 @@
|
||||
block: # issue #22661
|
||||
template foo(a: typed) =
|
||||
a
|
||||
|
||||
foo:
|
||||
case false
|
||||
of false..true: discard
|
||||
@@ -22,3 +22,49 @@ block: # bug #23902
|
||||
|
||||
proc foo(a: sink string) =
|
||||
var x = (a, a)
|
||||
|
||||
block: # bug #24175
|
||||
block:
|
||||
func mutate(o: sink string): string =
|
||||
o[1] = '1'
|
||||
result = o
|
||||
|
||||
static:
|
||||
let s = "999"
|
||||
let m = mutate(s)
|
||||
doAssert s == "999"
|
||||
doAssert m == "919"
|
||||
|
||||
func foo() =
|
||||
let s = "999"
|
||||
let m = mutate(s)
|
||||
doAssert s == "999"
|
||||
doAssert m == "919"
|
||||
|
||||
static:
|
||||
foo()
|
||||
foo()
|
||||
|
||||
block:
|
||||
type O = object
|
||||
a: int
|
||||
|
||||
func mutate(o: sink O): O =
|
||||
o.a += 1
|
||||
o
|
||||
|
||||
static:
|
||||
let x = O(a: 1)
|
||||
let y = mutate(x)
|
||||
doAssert x.a == 1
|
||||
doAssert y.a == 2
|
||||
|
||||
proc foo() =
|
||||
let x = O(a: 1)
|
||||
let y = mutate(x)
|
||||
doAssert x.a == 1
|
||||
doAssert y.a == 2
|
||||
|
||||
static:
|
||||
foo()
|
||||
foo()
|
||||
|
||||
21
tests/distinct/tcomplexaddressableconv.nim
Normal file
21
tests/distinct/tcomplexaddressableconv.nim
Normal file
@@ -0,0 +1,21 @@
|
||||
# issue #22523
|
||||
|
||||
from std/typetraits import distinctBase
|
||||
|
||||
type
|
||||
V[p: static int] = distinct int
|
||||
D[p: static int] = distinct int
|
||||
T = V[1]
|
||||
|
||||
proc f(y: var T) = discard
|
||||
|
||||
var a: D[0]
|
||||
|
||||
static:
|
||||
doAssert distinctBase(T) is distinctBase(D[0])
|
||||
doAssert distinctBase(T) is int
|
||||
doAssert distinctBase(D[0]) is int
|
||||
doAssert T(a) is T
|
||||
|
||||
f(cast[ptr T](addr a)[])
|
||||
f(T(a))
|
||||
@@ -1,9 +1,9 @@
|
||||
discard """
|
||||
errormsg: "for a 'var' type a variable needs to be passed; but 'uint16(x)' is immutable"
|
||||
errormsg: "type mismatch: got <uint8>"
|
||||
"""
|
||||
|
||||
proc toUInt16(x: var uint16) =
|
||||
discard
|
||||
|
||||
var x = uint8(1)
|
||||
toUInt16 x
|
||||
toUInt16 x
|
||||
|
||||
13
tests/errmsgs/tgenericmismatchsegfault.nim
Normal file
13
tests/errmsgs/tgenericmismatchsegfault.nim
Normal file
@@ -0,0 +1,13 @@
|
||||
discard """
|
||||
matrix: "-d:testsConciseTypeMismatch"
|
||||
"""
|
||||
|
||||
template v[T](c: SomeOrdinal): T = T(c)
|
||||
discard v[int, char]('A') #[tt.Error
|
||||
^ type mismatch
|
||||
Expression: v[int, char]('A')
|
||||
[1] 'A': char
|
||||
|
||||
Expected one of (first mismatch at [position]):
|
||||
[2] template v[T](c: SomeOrdinal): T
|
||||
generic parameter mismatch, expected SomeOrdinal but got 'char' of type: char]#
|
||||
10
tests/errmsgs/tgenericmismatchsegfault_legacy.nim
Normal file
10
tests/errmsgs/tgenericmismatchsegfault_legacy.nim
Normal file
@@ -0,0 +1,10 @@
|
||||
template v[T](c: SomeOrdinal): T = T(c)
|
||||
discard v[int, char]('A') #[tt.Error
|
||||
^ type mismatch: got <char>
|
||||
but expected one of:
|
||||
template v[T](c: SomeOrdinal): T
|
||||
first type mismatch at position: 2 in generic parameters
|
||||
required type for SomeOrdinal: SomeOrdinal
|
||||
but expression 'char' is of type: char
|
||||
|
||||
expression: v[int, char]('A')]#
|
||||
@@ -9,7 +9,7 @@ tundeclared_routine.nim(29, 28) Error: invalid pragma: myPragma
|
||||
tundeclared_routine.nim(36, 13) Error: undeclared field: 'bar3' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(33, 8)]
|
||||
found tundeclared_routine.bar3() [iterator declared in tundeclared_routine.nim(35, 12)]
|
||||
tundeclared_routine.nim(41, 13) Error: undeclared field: 'bar4' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(39, 8)]
|
||||
tundeclared_routine.nim(44, 15) Error: attempting to call routine: 'bad5'
|
||||
tundeclared_routine.nim(44, 11) Error: undeclared identifier: 'bad5'
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Expression: newImage[string](320, 200)
|
||||
|
||||
Expected one of (first mismatch at [position]):
|
||||
[1] proc newImage[T: int32 | int64](w, h: int): ref Image[T]
|
||||
generic parameter mismatch, expected int32 or int64 but got 'string' of type: typedesc[string]
|
||||
generic parameter mismatch, expected int32 or int64 but got 'string' of type: string
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ but expected one of:
|
||||
proc newImage[T: int32 | int64](w, h: int): ref Image[T]
|
||||
first type mismatch at position: 1 in generic parameters
|
||||
required type for T: int32 or int64
|
||||
but expression 'string' is of type: typedesc[string]
|
||||
but expression 'string' is of type: string
|
||||
|
||||
expression: newImage[string](320, 200)
|
||||
'''
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{.experimental: "genericsOpenSym".}
|
||||
{.experimental: "openSym".}
|
||||
|
||||
import mopensymimport1
|
||||
|
||||
|
||||
26
tests/generics/tbadcache.nim
Normal file
26
tests/generics/tbadcache.nim
Normal file
@@ -0,0 +1,26 @@
|
||||
# issue #16128
|
||||
|
||||
import std/[tables, hashes]
|
||||
|
||||
type
|
||||
NodeId*[L] = object
|
||||
isSource: bool
|
||||
index: Table[NodeId[L], seq[NodeId[L]]]
|
||||
|
||||
func hash*[L](id: NodeId[L]): Hash = discard
|
||||
func `==`[L](a, b: NodeId[L]): bool = discard
|
||||
|
||||
proc makeIndex*[T, L](tree: T) =
|
||||
var parent = NodeId[L]()
|
||||
var tmp: Table[NodeId[L], seq[NodeId[L]]]
|
||||
tmp[parent] = @[parent]
|
||||
|
||||
proc simpleTreeDiff*[T, L](source, target: T) =
|
||||
# Swapping these two lines makes error disappear
|
||||
var m: Table[NodeId[L], NodeId[L]]
|
||||
makeIndex[T, L](target)
|
||||
|
||||
var tmp: Table[string, seq[string]] # removing this forward declaration also removes error
|
||||
|
||||
proc diff(x1, x2: string): auto =
|
||||
simpleTreeDiff[int, string](12, 12)
|
||||
@@ -44,3 +44,15 @@ block: # constant condition after dynamic one
|
||||
doAssert y.a is int
|
||||
var z: Foo[float]
|
||||
doAssert z.a is string
|
||||
|
||||
block: # issue #4774, but not with threads
|
||||
const hasThreadSupport = not defined(js)
|
||||
when hasThreadSupport:
|
||||
type Channel[T] = object
|
||||
value: T
|
||||
type
|
||||
SomeObj[T] = object
|
||||
when hasThreadSupport:
|
||||
channel: ptr Channel[T]
|
||||
var x: SomeObj[int]
|
||||
doAssert compiles(x.channel) == hasThreadSupport
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{.experimental: "genericsOpenSym".}
|
||||
{.experimental: "openSym".}
|
||||
|
||||
block: # issue #22605, normal call syntax
|
||||
const error = "bad"
|
||||
|
||||
@@ -451,3 +451,67 @@ block: # real version of above
|
||||
proc foo[T](x: T, a = Opt.none(int)) = discard
|
||||
foo(1, a = Opt.none(int))
|
||||
foo(1)
|
||||
|
||||
block: # issue #20880
|
||||
type
|
||||
Child[n: static int] = object
|
||||
data: array[n, int]
|
||||
Parent[n: static int] = object
|
||||
child: Child[3*n]
|
||||
const n = 3
|
||||
doAssert $(typeof Parent[n*3]()) == "Parent[9]"
|
||||
doAssert $(typeof Parent[1]().child) == "Child[3]"
|
||||
doAssert Parent[1]().child.data.len == 3
|
||||
|
||||
{.experimental: "dynamicBindSym".}
|
||||
block: # issue #16774
|
||||
type SecretWord = distinct uint64
|
||||
const WordBitWidth = 8 * sizeof(uint64)
|
||||
func wordsRequired(bits: int): int {.compileTime.} =
|
||||
## Compute the number of limbs required
|
||||
# from the **announced** bit length
|
||||
(bits + WordBitWidth - 1) div WordBitWidth
|
||||
type
|
||||
Curve = enum BLS12_381
|
||||
BigInt[bits: static int] = object
|
||||
limbs: array[bits.wordsRequired, SecretWord]
|
||||
const BLS12_381_Modulus = default(BigInt[381])
|
||||
macro Mod(C: static Curve): untyped =
|
||||
## Get the Modulus associated to a curve
|
||||
result = bindSym($C & "_Modulus")
|
||||
macro getCurveBitwidth(C: static Curve): untyped =
|
||||
result = nnkDotExpr.newTree(
|
||||
getAST(Mod(C)),
|
||||
ident"bits"
|
||||
)
|
||||
type Fp[C: static Curve] = object
|
||||
## Finite Fields / Modular arithmetic
|
||||
## modulo the curve modulus
|
||||
mres: BigInt[getCurveBitwidth(C)]
|
||||
var x: Fp[BLS12_381]
|
||||
doAssert x.mres.limbs.len == wordsRequired(getCurveBitWidth(BLS12_381))
|
||||
# minimized, as if we haven't tested it already:
|
||||
macro makeIntLit(c: static int): untyped =
|
||||
result = newLit(c)
|
||||
type Test[T: static int] = object
|
||||
myArray: array[makeIntLit(T), int]
|
||||
var y: Test[2]
|
||||
doAssert y.myArray.len == 2
|
||||
var z: Test[4]
|
||||
doAssert z.myArray.len == 4
|
||||
|
||||
block: # issue #16175
|
||||
type
|
||||
Thing[D: static uint] = object
|
||||
when D == 0:
|
||||
kid: char
|
||||
else:
|
||||
kid: Thing[D-1]
|
||||
var t2 = Thing[3]()
|
||||
doAssert t2.kid is Thing[2.uint]
|
||||
doAssert t2.kid.kid is Thing[1.uint]
|
||||
doAssert t2.kid.kid.kid is Thing[0.uint]
|
||||
doAssert t2.kid.kid.kid.kid is char
|
||||
var s = Thing[1]()
|
||||
doAssert s.kid is Thing[0.uint]
|
||||
doAssert s.kid.kid is char
|
||||
|
||||
@@ -32,3 +32,9 @@ block t4175:
|
||||
const j = 0u - 1u
|
||||
doAssert i == j
|
||||
doAssert j + 1u == 0u
|
||||
|
||||
block: # https://forum.nim-lang.org/t/12465#76998
|
||||
var a: int = 1
|
||||
var x: uint8 = 1
|
||||
a.inc(x) # Error: type mismatch
|
||||
doAssert a == 2
|
||||
|
||||
16
tests/int/twrongexplicitvarconv.nim
Normal file
16
tests/int/twrongexplicitvarconv.nim
Normal file
@@ -0,0 +1,16 @@
|
||||
discard """
|
||||
action: reject
|
||||
nimout: '''
|
||||
but expression 'int(a)' is immutable, not 'var'
|
||||
'''
|
||||
"""
|
||||
|
||||
proc `++`(n: var int) =
|
||||
n += 1
|
||||
|
||||
var a: int32 = 15
|
||||
|
||||
++int(a) #[tt.Error
|
||||
^ type mismatch: got <int>]#
|
||||
|
||||
echo a
|
||||
9
tests/int/twrongvarconv.nim
Normal file
9
tests/int/twrongvarconv.nim
Normal file
@@ -0,0 +1,9 @@
|
||||
proc `++`(n: var int) =
|
||||
n += 1
|
||||
|
||||
var a: int32 = 15
|
||||
|
||||
++a #[tt.Error
|
||||
^ type mismatch: got <int32>]#
|
||||
|
||||
echo a
|
||||
1
tests/js/tjsnimscombined.nim
Normal file
1
tests/js/tjsnimscombined.nim
Normal file
@@ -0,0 +1 @@
|
||||
import std/jsffi
|
||||
1
tests/js/tjsnimscombined.nims
Normal file
1
tests/js/tjsnimscombined.nims
Normal file
@@ -0,0 +1 @@
|
||||
# test the condition where both `js` and `nimscript` are defined (nimscript receives priority)
|
||||
21
tests/lent/tvm.nim
Normal file
21
tests/lent/tvm.nim
Normal file
@@ -0,0 +1,21 @@
|
||||
block: # issue #17527
|
||||
iterator items2[IX, T](a: array[IX, T]): lent T {.inline.} =
|
||||
var i = low(IX)
|
||||
if i <= high(IX):
|
||||
while true:
|
||||
yield a[i]
|
||||
if i >= high(IX): break
|
||||
inc(i)
|
||||
|
||||
proc main() =
|
||||
var s: seq[string] = @[]
|
||||
for i in 0..<3:
|
||||
for (key, val) in items2([("any", "bar")]):
|
||||
s.add $(i, key, val)
|
||||
doAssert s == @[
|
||||
"(0, \"any\", \"bar\")",
|
||||
"(1, \"any\", \"bar\")",
|
||||
"(2, \"any\", \"bar\")"
|
||||
]
|
||||
|
||||
static: main()
|
||||
2
tests/lookups/mdisambsym1.nim
Normal file
2
tests/lookups/mdisambsym1.nim
Normal file
@@ -0,0 +1,2 @@
|
||||
proc count*(s: string): int =
|
||||
s.len
|
||||
1
tests/lookups/mdisambsym2.nim
Normal file
1
tests/lookups/mdisambsym2.nim
Normal file
@@ -0,0 +1 @@
|
||||
var count*: int = 10
|
||||
1
tests/lookups/mdisambsym3.nim
Normal file
1
tests/lookups/mdisambsym3.nim
Normal file
@@ -0,0 +1 @@
|
||||
const count* = 3.142
|
||||
10
tests/lookups/mmacroamb.nim
Normal file
10
tests/lookups/mmacroamb.nim
Normal file
@@ -0,0 +1,10 @@
|
||||
# issue #12732
|
||||
|
||||
import std/macros
|
||||
const getPrivate3_tmp* = 0
|
||||
const foobar1* = 0 # comment this or make private and it'll compile fine
|
||||
macro foobar4*(): untyped =
|
||||
newLit "abc"
|
||||
template currentPkgDir2*: string = foobar4()
|
||||
macro currentPkgDir2*(dir: string): untyped =
|
||||
newLit "abc2"
|
||||
8
tests/lookups/tdisambsym.nim
Normal file
8
tests/lookups/tdisambsym.nim
Normal file
@@ -0,0 +1,8 @@
|
||||
# issue #15247
|
||||
|
||||
import mdisambsym1, mdisambsym2, mdisambsym3
|
||||
|
||||
proc twice(n: int): int =
|
||||
n*2
|
||||
|
||||
doAssert twice(count) == 20
|
||||
5
tests/lookups/tmacroamb.nim
Normal file
5
tests/lookups/tmacroamb.nim
Normal file
@@ -0,0 +1,5 @@
|
||||
# issue #12732
|
||||
|
||||
import mmacroamb
|
||||
const s0 = currentPkgDir2 #[tt.Error
|
||||
^ ambiguous identifier: 'currentPkgDir2' -- use one of the following:]#
|
||||
@@ -24,6 +24,9 @@ for i, (x, y) in pairs(data):
|
||||
var (a, b) = (1, 2)
|
||||
type
|
||||
A* = object
|
||||
|
||||
var t04 = 1.0'f128
|
||||
t04 = 2.0'f128
|
||||
'''
|
||||
"""
|
||||
|
||||
@@ -49,3 +52,7 @@ echoTypedAndUntypedRepr:
|
||||
discard
|
||||
var (a,b) = (1,2)
|
||||
type A* = object # issue #22933
|
||||
|
||||
echoUntypedRepr:
|
||||
var t04 = 1'f128
|
||||
t04 = 2'f128
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
discard """
|
||||
nimout: '''intProc; ntyProc; proc[int, int, float]; proc (a: int; b: float): int
|
||||
nimout: '''intProc; ntyProc; proc[int, int, float]; proc (a: int; b: float): int {.nimcall.}
|
||||
void; ntyVoid; void; void
|
||||
int; ntyInt; int; int
|
||||
proc (); ntyProc; proc[void]; proc ()
|
||||
voidProc; ntyProc; proc[void]; proc ()
|
||||
proc () {.nimcall.}; ntyProc; proc[void]; proc () {.nimcall.}
|
||||
voidProc; ntyProc; proc[void]; proc () {.nimcall.}
|
||||
listing fields for ObjType
|
||||
a: string
|
||||
b: int
|
||||
|
||||
28
tests/macros/tprocgettype.nim
Normal file
28
tests/macros/tprocgettype.nim
Normal file
@@ -0,0 +1,28 @@
|
||||
discard """
|
||||
nimout: '''
|
||||
var x: proc () {.cdecl.} = foo
|
||||
var x: iterator (): int {.closure.} = bar
|
||||
'''
|
||||
"""
|
||||
|
||||
# issue #19010
|
||||
|
||||
import macros
|
||||
|
||||
macro createVar(x: typed): untyped =
|
||||
result = nnkVarSection.newTree:
|
||||
newIdentDefs(ident"x", getTypeInst(x), copy(x))
|
||||
|
||||
echo repr result
|
||||
|
||||
block:
|
||||
proc foo() {.cdecl.} = discard
|
||||
|
||||
createVar(foo)
|
||||
x()
|
||||
|
||||
block:
|
||||
iterator bar(): int {.closure.} = discard
|
||||
|
||||
createVar(bar)
|
||||
for a in x(): discard
|
||||
@@ -157,3 +157,12 @@ block t3338:
|
||||
var t2 = Bar[int32]()
|
||||
t2.add()
|
||||
doAssert t2.x == 5
|
||||
|
||||
block: # issue #24203
|
||||
proc b(G: typedesc) =
|
||||
type U = G
|
||||
template s(h: untyped) = h
|
||||
s(b(typeof (0, 0)))
|
||||
b(seq[int])
|
||||
b((int, int))
|
||||
b(typeof (0, 0))
|
||||
|
||||
@@ -7,12 +7,12 @@ mused2a.nim(12, 6) Hint: 'fn1' is declared but not used [XDeclaredButNotUsed]
|
||||
mused2a.nim(16, 5) Hint: 'fn4' is declared but not used [XDeclaredButNotUsed]
|
||||
mused2a.nim(20, 7) Hint: 'fn7' is declared but not used [XDeclaredButNotUsed]
|
||||
mused2a.nim(23, 6) Hint: 'T1' is declared but not used [XDeclaredButNotUsed]
|
||||
mused2a.nim(1, 11) Warning: imported and not used: 'strutils' [UnusedImport]
|
||||
mused2a.nim(3, 9) Warning: imported and not used: 'os' [UnusedImport]
|
||||
mused2a.nim(1, 12) Warning: imported and not used: 'strutils' [UnusedImport]
|
||||
mused2a.nim(3, 10) Warning: imported and not used: 'os' [UnusedImport]
|
||||
mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport]
|
||||
mused2a.nim(6, 9) Warning: imported and not used: 'setutils' [UnusedImport]
|
||||
mused2a.nim(6, 10) Warning: imported and not used: 'setutils' [UnusedImport]
|
||||
tused2.nim(42, 8) Warning: imported and not used: 'mused2a' [UnusedImport]
|
||||
tused2.nim(45, 11) Warning: imported and not used: 'strutils' [UnusedImport]
|
||||
tused2.nim(45, 12) Warning: imported and not used: 'strutils' [UnusedImport]
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
10
tests/objects/trequireinit.nim
Normal file
10
tests/objects/trequireinit.nim
Normal file
@@ -0,0 +1,10 @@
|
||||
discard """
|
||||
errormsg: "The MPlayerObj type doesn't have a default value. The following fields must be initialized: foo."
|
||||
"""
|
||||
|
||||
type
|
||||
MPlayerObj* {.requiresInit.} = object
|
||||
foo: range[5..10] = 5
|
||||
|
||||
var a: MPlayerObj
|
||||
echo a.foo
|
||||
145
tests/overload/mvaruintconv.nim
Normal file
145
tests/overload/mvaruintconv.nim
Normal file
@@ -0,0 +1,145 @@
|
||||
import
|
||||
std/[macros, tables, hashes]
|
||||
|
||||
export
|
||||
macros
|
||||
|
||||
type
|
||||
FieldDescription* = object
|
||||
name*: NimNode
|
||||
isPublic*: bool
|
||||
isDiscriminator*: bool
|
||||
typ*: NimNode
|
||||
pragmas*: NimNode
|
||||
caseField*: NimNode
|
||||
caseBranch*: NimNode
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
func isTuple*(t: NimNode): bool =
|
||||
t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
|
||||
|
||||
macro isTuple*(T: type): untyped =
|
||||
newLit(isTuple(getType(T)[1]))
|
||||
|
||||
proc collectFieldsFromRecList(result: var seq[FieldDescription],
|
||||
n: NimNode,
|
||||
parentCaseField: NimNode = nil,
|
||||
parentCaseBranch: NimNode = nil,
|
||||
isDiscriminator = false) =
|
||||
case n.kind
|
||||
of nnkRecList:
|
||||
for entry in n:
|
||||
collectFieldsFromRecList result, entry,
|
||||
parentCaseField, parentCaseBranch
|
||||
of nnkRecWhen:
|
||||
for branch in n:
|
||||
case branch.kind:
|
||||
of nnkElifBranch:
|
||||
collectFieldsFromRecList result, branch[1],
|
||||
parentCaseField, parentCaseBranch
|
||||
of nnkElse:
|
||||
collectFieldsFromRecList result, branch[0],
|
||||
parentCaseField, parentCaseBranch
|
||||
else:
|
||||
doAssert false
|
||||
|
||||
of nnkRecCase:
|
||||
collectFieldsFromRecList result, n[0],
|
||||
parentCaseField,
|
||||
parentCaseBranch,
|
||||
isDiscriminator = true
|
||||
|
||||
for i in 1 ..< n.len:
|
||||
let branch = n[i]
|
||||
case branch.kind
|
||||
of nnkOfBranch:
|
||||
collectFieldsFromRecList result, branch[^1], n[0], branch
|
||||
of nnkElse:
|
||||
collectFieldsFromRecList result, branch[0], n[0], branch
|
||||
else:
|
||||
doAssert false
|
||||
|
||||
of nnkIdentDefs:
|
||||
let fieldType = n[^2]
|
||||
for i in 0 ..< n.len - 2:
|
||||
var field: FieldDescription
|
||||
field.name = n[i]
|
||||
field.typ = fieldType
|
||||
field.caseField = parentCaseField
|
||||
field.caseBranch = parentCaseBranch
|
||||
field.isDiscriminator = isDiscriminator
|
||||
|
||||
if field.name.kind == nnkPragmaExpr:
|
||||
field.pragmas = field.name[1]
|
||||
field.name = field.name[0]
|
||||
|
||||
if field.name.kind == nnkPostfix:
|
||||
field.isPublic = true
|
||||
field.name = field.name[1]
|
||||
|
||||
result.add field
|
||||
|
||||
of nnkSym:
|
||||
result.add FieldDescription(
|
||||
name: n,
|
||||
typ: getType(n),
|
||||
caseField: parentCaseField,
|
||||
caseBranch: parentCaseBranch,
|
||||
isDiscriminator: isDiscriminator)
|
||||
|
||||
of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
|
||||
discard
|
||||
|
||||
else:
|
||||
doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
|
||||
|
||||
proc collectFieldsInHierarchy(result: var seq[FieldDescription],
|
||||
objectType: NimNode) =
|
||||
var objectType = objectType
|
||||
|
||||
objectType.expectKind {nnkObjectTy, nnkRefTy}
|
||||
|
||||
if objectType.kind == nnkRefTy:
|
||||
objectType = objectType[0]
|
||||
|
||||
objectType.expectKind nnkObjectTy
|
||||
|
||||
var baseType = objectType[1]
|
||||
if baseType.kind != nnkEmpty:
|
||||
baseType.expectKind nnkOfInherit
|
||||
baseType = baseType[0]
|
||||
baseType.expectKind nnkSym
|
||||
baseType = getImpl(baseType)
|
||||
baseType.expectKind nnkTypeDef
|
||||
baseType = baseType[2]
|
||||
baseType.expectKind {nnkObjectTy, nnkRefTy}
|
||||
collectFieldsInHierarchy result, baseType
|
||||
|
||||
let recList = objectType[2]
|
||||
collectFieldsFromRecList result, recList
|
||||
|
||||
proc recordFields*(typeImpl: NimNode): seq[FieldDescription] =
|
||||
if typeImpl.isTuple:
|
||||
for i in 1 ..< typeImpl.len:
|
||||
result.add FieldDescription(typ: typeImpl[i], name: ident("Field" & $(i - 1)))
|
||||
return
|
||||
|
||||
let objectType = case typeImpl.kind
|
||||
of nnkObjectTy: typeImpl
|
||||
of nnkTypeDef: typeImpl[2]
|
||||
else:
|
||||
macros.error("object type expected", typeImpl)
|
||||
return
|
||||
|
||||
collectFieldsInHierarchy(result, objectType)
|
||||
|
||||
macro field*(obj: typed, fieldName: static string): untyped =
|
||||
newDotExpr(obj, ident fieldName)
|
||||
|
||||
proc skipPragma*(n: NimNode): NimNode =
|
||||
if n.kind == nnkPragmaExpr: n[0]
|
||||
else: n
|
||||
|
||||
|
||||
{.pop.}
|
||||
13
tests/overload/tgenericalias.nim
Normal file
13
tests/overload/tgenericalias.nim
Normal file
@@ -0,0 +1,13 @@
|
||||
block: # issue #13799
|
||||
type
|
||||
X[A, B] = object
|
||||
a: A
|
||||
b: B
|
||||
|
||||
Y[A] = X[A, int]
|
||||
template s(T: type X): X = T()
|
||||
template t[A, B](T: type X[A, B]): X[A, B] = T()
|
||||
proc works1(): Y[int] = s(X[int, int])
|
||||
proc works2(): Y[int] = t(X[int, int])
|
||||
proc works3(): Y[int] = t(Y[int])
|
||||
proc broken(): Y[int] = s(Y[int])
|
||||
@@ -16,3 +16,26 @@ block: # bug #8568
|
||||
proc g(a: D|E): string = "foo D|E"
|
||||
proc g(a: D): string = "foo D"
|
||||
doAssert g(D[int]()) == "foo D"
|
||||
|
||||
type Obj1[T] = object
|
||||
v: T
|
||||
converter toObj1[T](t: T): Obj1[T] = return Obj1[T](v: t)
|
||||
block: # issue #10019
|
||||
proc fun1[T](elements: seq[T]): string = "fun1 seq"
|
||||
proc fun1(o: object|tuple): string = "fun1 object|tuple"
|
||||
proc fun2[T](elements: openArray[T]): string = "fun2 openarray"
|
||||
proc fun2(o: object): string = "fun2 object"
|
||||
proc fun_bug[T](elements: openArray[T]): string = "fun_bug openarray"
|
||||
proc fun_bug(o: object|tuple):string = "fun_bug object|tuple"
|
||||
proc main() =
|
||||
var x = @["hello", "world"]
|
||||
block:
|
||||
# no ambiguity error shown here even though this would compile if we remove either 1st or 2nd overload of fun1
|
||||
doAssert fun1(x) == "fun1 seq"
|
||||
block:
|
||||
# ditto
|
||||
doAssert fun2(x) == "fun2 openarray"
|
||||
block:
|
||||
# Error: ambiguous call; both t0065.fun_bug(elements: openarray[T])[declared in t0065.nim(17, 5)] and t0065.fun_bug(o: object or tuple)[declared in t0065.nim(20, 5)] match for: (array[0..1, string])
|
||||
doAssert fun_bug(x) == "fun_bug openarray"
|
||||
main()
|
||||
|
||||
19
tests/overload/tuntypedarg.nim
Normal file
19
tests/overload/tuntypedarg.nim
Normal file
@@ -0,0 +1,19 @@
|
||||
import macros
|
||||
|
||||
block: # issue #7385
|
||||
type CustomSeq[T] = object
|
||||
data: seq[T]
|
||||
macro `[]`[T](s: CustomSeq[T], args: varargs[untyped]): untyped =
|
||||
## The end goal is to replace the joker "_" by something else
|
||||
result = newIntLitNode(10)
|
||||
proc foo1(): CustomSeq[int] =
|
||||
result.data.newSeq(10)
|
||||
# works since no overload matches first argument with type `CustomSeq`
|
||||
# except magic `[]`, which always matches without checking arguments
|
||||
doAssert result[_] == 10
|
||||
doAssert foo1() == CustomSeq[int](data: newSeq[int](10))
|
||||
proc foo2[T](): CustomSeq[T] =
|
||||
result.data.newSeq(10)
|
||||
# works fine with generic return type
|
||||
doAssert result[_] == 10
|
||||
doAssert foo2[int]() == CustomSeq[int](data: newSeq[int](10))
|
||||
207
tests/overload/tvaruintconv.nim
Normal file
207
tests/overload/tvaruintconv.nim
Normal file
@@ -0,0 +1,207 @@
|
||||
discard """
|
||||
action: compile
|
||||
"""
|
||||
|
||||
# https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102
|
||||
# failed with "for a 'var' type a variable needs to be passed; but 'uint64(result)' is immutable"
|
||||
|
||||
import
|
||||
std/[typetraits, macros]
|
||||
|
||||
type
|
||||
DefaultFlavor = object
|
||||
|
||||
template serializationFormatImpl(Name: untyped) {.dirty.} =
|
||||
type Name = object
|
||||
|
||||
template serializationFormat(Name: untyped) =
|
||||
serializationFormatImpl(Name)
|
||||
|
||||
template setReader(Format, FormatReader: distinct type) =
|
||||
when arity(FormatReader) > 1:
|
||||
template Reader(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
|
||||
else:
|
||||
template ReaderType(T: type Format): type = FormatReader
|
||||
template Reader(T: type Format): type = FormatReader
|
||||
|
||||
template useDefaultReaderIn(T: untyped, Flavor: type) =
|
||||
mixin Reader
|
||||
|
||||
template readValue(r: var Reader(Flavor), value: var T) =
|
||||
mixin readRecordValue
|
||||
readRecordValue(r, value)
|
||||
|
||||
import mvaruintconv
|
||||
|
||||
type
|
||||
FieldTag[RecordType: object; fieldName: static string] = distinct void
|
||||
|
||||
func declval*(T: type): T {.compileTime.} =
|
||||
default(ptr T)[]
|
||||
|
||||
macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
|
||||
var typeAst = getType(T)[1]
|
||||
var typeImpl: NimNode
|
||||
let isSymbol = not typeAst.isTuple
|
||||
|
||||
if not isSymbol:
|
||||
typeImpl = typeAst
|
||||
else:
|
||||
typeImpl = getImpl(typeAst)
|
||||
result = newStmtList()
|
||||
|
||||
var i = 0
|
||||
for field in recordFields(typeImpl):
|
||||
let
|
||||
fieldIdent = field.name
|
||||
realFieldName = newLit($fieldIdent.skipPragma)
|
||||
fieldName = realFieldName
|
||||
fieldIndex = newLit(i)
|
||||
|
||||
let fieldNameDefs =
|
||||
if isSymbol:
|
||||
quote:
|
||||
const fieldName {.inject, used.} = `fieldName`
|
||||
const realFieldName {.inject, used.} = `realFieldName`
|
||||
else:
|
||||
quote:
|
||||
const fieldName {.inject, used.} = $`fieldIndex`
|
||||
const realFieldName {.inject, used.} = $`fieldIndex`
|
||||
|
||||
let field =
|
||||
if isSymbol:
|
||||
quote do: declval(`T`).`fieldIdent`
|
||||
else:
|
||||
quote do: declval(`T`)[`fieldIndex`]
|
||||
|
||||
result.add quote do:
|
||||
block:
|
||||
`fieldNameDefs`
|
||||
|
||||
template FieldType: untyped {.inject, used.} = typeof(`field`)
|
||||
|
||||
`body`
|
||||
|
||||
# echo repr(result)
|
||||
|
||||
template enumAllSerializedFields(T: type, body): untyped =
|
||||
enumAllSerializedFieldsImpl(T, body)
|
||||
|
||||
type
|
||||
FieldReader[RecordType, Reader] = tuple[
|
||||
fieldName: string,
|
||||
reader: proc (rec: var RecordType, reader: var Reader)
|
||||
{.gcsafe, nimcall.}
|
||||
]
|
||||
|
||||
proc totalSerializedFieldsImpl(T: type): int =
|
||||
mixin enumAllSerializedFields
|
||||
enumAllSerializedFields(T): inc result
|
||||
|
||||
template totalSerializedFields(T: type): int =
|
||||
(static(totalSerializedFieldsImpl(T)))
|
||||
|
||||
template GetFieldType(FT: type FieldTag): type =
|
||||
typeof field(declval(FT.RecordType), FT.fieldName)
|
||||
|
||||
proc makeFieldReadersTable(RecordType, ReaderType: distinct type,
|
||||
numFields: static[int]):
|
||||
array[numFields, FieldReader[RecordType, ReaderType]] =
|
||||
mixin enumAllSerializedFields, handleReadException
|
||||
var idx = 0
|
||||
|
||||
enumAllSerializedFields(RecordType):
|
||||
proc readField(obj: var RecordType, reader: var ReaderType)
|
||||
{.gcsafe, nimcall.} =
|
||||
|
||||
mixin readValue
|
||||
|
||||
type F = FieldTag[RecordType, realFieldName]
|
||||
field(obj, realFieldName) = reader.readValue(GetFieldType(F))
|
||||
|
||||
result[idx] = (fieldName, readField)
|
||||
inc idx
|
||||
|
||||
proc fieldReadersTable(RecordType, ReaderType: distinct type): auto =
|
||||
mixin readValue
|
||||
type T = RecordType
|
||||
const numFields = totalSerializedFields(T)
|
||||
var tbl {.threadvar.}: ref array[numFields, FieldReader[RecordType, ReaderType]]
|
||||
if tbl == nil:
|
||||
tbl = new typeof(tbl)
|
||||
tbl[] = makeFieldReadersTable(RecordType, ReaderType, numFields)
|
||||
return addr(tbl[])
|
||||
|
||||
proc readValue(reader: var auto, T: type): T =
|
||||
mixin readValue
|
||||
reader.readValue(result)
|
||||
|
||||
template decode(Format: distinct type,
|
||||
input: string,
|
||||
RecordType: distinct type): auto =
|
||||
mixin Reader
|
||||
block: # https://github.com/nim-lang/Nim/issues/22874
|
||||
var reader: Reader(Format)
|
||||
reader.readValue(RecordType)
|
||||
|
||||
template readValue(Format: type,
|
||||
ValueType: type): untyped =
|
||||
mixin Reader, init, readValue
|
||||
var reader: Reader(Format)
|
||||
readValue reader, ValueType
|
||||
|
||||
template parseArrayImpl(numElem: untyped,
|
||||
actionValue: untyped) =
|
||||
actionValue
|
||||
|
||||
serializationFormat Json
|
||||
template createJsonFlavor(FlavorName: untyped,
|
||||
skipNullFields = false) {.dirty.} =
|
||||
type FlavorName = object
|
||||
|
||||
template Reader(T: type FlavorName): type = Reader(Json, FlavorName)
|
||||
type
|
||||
JsonReader[Flavor = DefaultFlavor] = object
|
||||
|
||||
Json.setReader JsonReader
|
||||
|
||||
template parseArray(r: var JsonReader; body: untyped) =
|
||||
parseArrayImpl(idx): body
|
||||
|
||||
template parseArray(r: var JsonReader; idx: untyped; body: untyped) =
|
||||
parseArrayImpl(idx): body
|
||||
|
||||
proc readRecordValue[T](r: var JsonReader, value: var T) =
|
||||
type
|
||||
ReaderType {.used.} = type r
|
||||
T = type value
|
||||
|
||||
discard T.fieldReadersTable(ReaderType)
|
||||
|
||||
proc readValue[T](r: var JsonReader, value: var T) =
|
||||
mixin readValue
|
||||
|
||||
when value is seq:
|
||||
r.parseArray:
|
||||
readValue(r, value[0])
|
||||
|
||||
elif value is object:
|
||||
readRecordValue(r, value)
|
||||
|
||||
type
|
||||
RemoteSignerInfo = object
|
||||
id: uint32
|
||||
RemoteKeystore = object
|
||||
|
||||
proc readValue(reader: var JsonReader, value: var RemoteKeystore) =
|
||||
discard reader.readValue(seq[RemoteSignerInfo])
|
||||
|
||||
createJsonFlavor RestJson
|
||||
useDefaultReaderIn(RemoteSignerInfo, RestJson)
|
||||
proc readValue(reader: var JsonReader[RestJson], value: var uint64) =
|
||||
discard reader.readValue(string)
|
||||
|
||||
discard Json.decode("", RemoteKeystore)
|
||||
block: # https://github.com/nim-lang/Nim/issues/22874
|
||||
var reader: Reader(RestJson)
|
||||
discard reader.readValue(RemoteSignerInfo)
|
||||
3
tests/parser/tbinarynotindented.nim
Normal file
3
tests/parser/tbinarynotindented.nim
Normal file
@@ -0,0 +1,3 @@
|
||||
type Foo = ref int
|
||||
not nil #[tt.Error
|
||||
^ invalid indentation]#
|
||||
10
tests/parser/tbinarynotsameline.nim
Normal file
10
tests/parser/tbinarynotsameline.nim
Normal file
@@ -0,0 +1,10 @@
|
||||
# issue #23565
|
||||
|
||||
func foo: bool =
|
||||
true
|
||||
|
||||
const bar = block:
|
||||
type T = int
|
||||
not foo()
|
||||
|
||||
doAssert not bar
|
||||
@@ -531,3 +531,10 @@ block:
|
||||
|
||||
check(a)
|
||||
check(b)
|
||||
|
||||
block: # https://forum.nim-lang.org/t/12522, backticks
|
||||
template `mypragma`() {.pragma.}
|
||||
# Error: invalid pragma: `mypragma`
|
||||
type Test = object
|
||||
field {.`mypragma`.}: int
|
||||
doAssert Test().field.hasCustomPragma(mypragma)
|
||||
|
||||
@@ -124,3 +124,21 @@ foo31()
|
||||
foo41()
|
||||
|
||||
{.pop.}
|
||||
|
||||
import macros
|
||||
|
||||
block:
|
||||
{.push deprecated.}
|
||||
template test() = discard
|
||||
test()
|
||||
{.pop.}
|
||||
macro foo(): bool =
|
||||
let ast = getImpl(bindSym"test")
|
||||
var found = false
|
||||
if ast[4].kind == nnkPragma:
|
||||
for x in ast[4]:
|
||||
if x.eqIdent"deprecated":
|
||||
found = true
|
||||
break
|
||||
result = newLit(found)
|
||||
doAssert foo()
|
||||
|
||||
@@ -43,3 +43,13 @@ block: # ditto but may be wrong minimization
|
||||
# alternative version, also causes instantiation issue
|
||||
proc baz[T](x: typeof(foo[T]())) = discard
|
||||
baz[int](Foo[int]())
|
||||
|
||||
block: # issue #21346
|
||||
type K[T] = object
|
||||
template s[T](x: int) = doAssert T is K[K[int]]
|
||||
proc b1(n: bool | bool) = s[K[K[int]]](3)
|
||||
proc b2(n: bool) = s[K[K[int]]](3)
|
||||
template b3(n: bool) = s[K[K[int]]](3)
|
||||
b1(false) # Error: cannot instantiate K; got: <T> but expected: <T>
|
||||
b2(false) # Builds, on its own
|
||||
b3(false)
|
||||
|
||||
@@ -67,3 +67,32 @@ block: # issue #24099, modified to work but using float32
|
||||
## Compares colors with given accuracy.
|
||||
abs(a[0] - b[0]) < e and abs(a[1] - b[1]) < e and abs(a[2] - b[2]) < e
|
||||
doAssert ColorRGBU([1.float32, 1, 1]) ~= ColorRGBU([1.float32, 1, 1])
|
||||
|
||||
block: # issue #13270
|
||||
type
|
||||
A = object
|
||||
B = object
|
||||
proc f(a: A) = discard
|
||||
proc g[T](value: T, cb: (proc(a: T)) = f) =
|
||||
cb value
|
||||
g A()
|
||||
# This should fail because there is no f(a: B) overload available
|
||||
doAssert not compiles(g B())
|
||||
|
||||
block: # issue #24121
|
||||
type
|
||||
Foo = distinct int
|
||||
Bar = distinct int
|
||||
FooBar = Foo | Bar
|
||||
|
||||
proc foo[T: distinct](x: T): string = "a"
|
||||
proc foo(x: Foo): string = "b"
|
||||
proc foo(x: Bar): string = "c"
|
||||
|
||||
proc bar(x: FooBar, y = foo(x)): string = y
|
||||
doAssert bar(Foo(123)) == "b"
|
||||
doAssert bar(Bar(123)) == "c"
|
||||
|
||||
proc baz[T: FooBar](x: T, y = foo(x)): string = y
|
||||
doAssert baz(Foo(123)) == "b"
|
||||
doAssert baz(Bar(123)) == "c"
|
||||
|
||||
@@ -250,3 +250,19 @@ block: # `when` in static signature
|
||||
proc foo[T](): T = test()
|
||||
proc bar[T](x = foo[T]()): T = x
|
||||
doAssert bar[int]() == 123
|
||||
|
||||
block: # issue #22276
|
||||
type Foo = enum A, B
|
||||
macro test(y: static[Foo]): untyped =
|
||||
if y == A:
|
||||
result = parseExpr("proc (x: int)")
|
||||
else:
|
||||
result = parseExpr("proc (x: float)")
|
||||
proc foo(y: static[Foo], x: test(y)) = # We want to make the type of `x` depend on what `y` is
|
||||
x(9)
|
||||
foo(A, proc (x: int) = doAssert x == 9)
|
||||
var a: int
|
||||
foo(A, proc (x: int) =
|
||||
a = x * 2)
|
||||
doAssert a == 18
|
||||
foo(B, proc (x: float) = doAssert x == 9)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
discard """
|
||||
matrix: "--mm:refc; --mm:orc"
|
||||
# test C with -d:nimUseCppAtomics as well to check nothing breaks
|
||||
matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics"
|
||||
targets: "c cpp"
|
||||
"""
|
||||
|
||||
# test atomic operations
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
discard """
|
||||
matrix: "--mm:refc; --mm:orc"
|
||||
# test C with -d:nimUseCppAtomics as well to check nothing breaks
|
||||
matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics"
|
||||
targets: "c cpp"
|
||||
"""
|
||||
import std/atomics
|
||||
@@ -17,4 +18,4 @@ block testSize: # issue 12726
|
||||
f: AtomicFlag
|
||||
static:
|
||||
doAssert sizeof(Node) == sizeof(pointer)
|
||||
doAssert sizeof(MyChannel) == sizeof(pointer) * 2
|
||||
doAssert sizeof(MyChannel) == sizeof(pointer) * 2
|
||||
|
||||
54
tests/stdlib/tmarshalsegfault.nim
Normal file
54
tests/stdlib/tmarshalsegfault.nim
Normal file
@@ -0,0 +1,54 @@
|
||||
# issue #12405
|
||||
|
||||
import std/[marshal, streams, times, tables, os, assertions]
|
||||
|
||||
type AiredEpisodeState * = ref object
|
||||
airedAt * : DateTime
|
||||
tvShowId * : string
|
||||
seasonNumber * : int
|
||||
number * : int
|
||||
title * : string
|
||||
|
||||
type ShowsWatchlistState * = ref object
|
||||
aired * : seq[AiredEpisodeState]
|
||||
|
||||
type UiState * = ref object
|
||||
shows: ShowsWatchlistState
|
||||
|
||||
# Helpers to marshal and unmarshal
|
||||
proc load * ( state : var UiState, file : string ) =
|
||||
var strm = newFileStream( file, fmRead )
|
||||
|
||||
strm.load( state )
|
||||
|
||||
strm.close()
|
||||
|
||||
proc store * ( state : UiState, file : string ) =
|
||||
var strm = newFileStream( file, fmWrite )
|
||||
|
||||
strm.store( state )
|
||||
|
||||
strm.close()
|
||||
|
||||
# 1. We fill the state initially
|
||||
var state : UiState = UiState( shows: ShowsWatchlistState( aired: @[] ) )
|
||||
|
||||
# VERY IMPORTANT: For some reason, small numbers (like 2 or 3) don't trigger the bug. Anything above 7 or 8 on my machine triggers though
|
||||
for i in 0..30:
|
||||
var episode = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" )
|
||||
|
||||
state.shows.aired.add( episode )
|
||||
|
||||
# 2. Store it in a file with the marshal module, and then load it back up
|
||||
store( state, "tmarshalsegfault_data" )
|
||||
load( state, "tmarshalsegfault_data" )
|
||||
removeFile("tmarshalsegfault_data")
|
||||
|
||||
# 3. VERY IMPORTANT: Without this line, for some reason, everything works fine
|
||||
state.shows.aired[ 0 ] = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" )
|
||||
|
||||
# 4. And formatting the airedAt date will now trigger the exception
|
||||
for ep in state.shows.aired:
|
||||
let x = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")"
|
||||
let y = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")"
|
||||
doAssert x == y
|
||||
@@ -27,9 +27,8 @@ Raises
|
||||
"""
|
||||
# test os path creation, iteration, and deletion
|
||||
|
||||
import os, strutils, pathnorm
|
||||
from stdtest/specialpaths import buildDir
|
||||
import std/[syncio, assertions]
|
||||
import std/[syncio, assertions, osproc, os, strutils, pathnorm]
|
||||
|
||||
block fileOperations:
|
||||
let files = @["these.txt", "are.x", "testing.r", "files.q"]
|
||||
@@ -161,6 +160,18 @@ block fileOperations:
|
||||
# createDir should not fail if `dir` is empty
|
||||
createDir("")
|
||||
|
||||
|
||||
when defined(linux): # bug #24174
|
||||
createDir("a/b")
|
||||
open("a/file.txt", fmWrite).close
|
||||
|
||||
if not fileExists("a/fifoFile"):
|
||||
doAssert execCmd("mkfifo -m 600 a/fifoFile") == 0
|
||||
|
||||
copyDir("a/", "../dest/a/", skipSpecial = true)
|
||||
copyDirWithPermissions("a/", "../dest2/a/", skipSpecial = true)
|
||||
removeDir("a")
|
||||
|
||||
# Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`,
|
||||
# `copyDir`, `copyDirWithPermissions`, `moveFile`, and `moveDir`.
|
||||
block:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user