Compare commits

..

203 Commits

Author SHA1 Message Date
ringabout
6c34f62785 fixes #24666; Compilation error when formatting a complex number (#24667)
fixes #24666

ref https://github.com/nim-lang/Nim/pull/22924

(cherry picked from commit 485b414fce)
2025-02-05 21:04:43 +01:00
narimiran
46e1322d29 bump NimVersion to 2.2.2 2025-02-04 20:03:53 +01:00
ringabout
27b54fdc76 fixes #24658; cpp compilation failure on Nim 2.2.x (#24663)
fixes #24658

(cherry picked from commit 7695d51fc4)
2025-02-04 20:03:39 +01:00
lit
d594e70d57 doc(tempfiles): update link of getTempDir (#24661)
- tempfiles: update `getTempDir` link... from os.html to appdirs.html
<https://nim-lang.org/docs/appdirs.html#getTempDir>

- ~~nims.md: rm three `std/`, which are out of place~~ (ref
https://github.com/nim-lang/Nim/pull/24661#discussion_r1937293833)

(cherry picked from commit e2bed72b72)
2025-02-04 20:03:30 +01:00
metagn
6bf9265d24 add ambiguous identifier message to generic instantiations (#24646)
fixes #24644

Another option is to include the symbol names and owners in the type
listing as in #24645 but this is a bit verbose.

(cherry picked from commit 0861dabfa7)
2025-01-31 09:37:46 +01:00
metagn
a627c9ba9c don't mark captured field sym in template as fully used (#24660)
fixes #24657

(cherry picked from commit 647c6687f1)
2025-01-31 09:37:31 +01:00
lit
7cec03eb1b fix doc format: testament.md (#24654)
- **doc(format): testament: fix `Commands` not regarded as table**

![image](https://github.com/user-attachments/assets/85238dd5-e199-41ca-a8cb-05849415097a)

- **doc(format): testament: row `--target` not splited as columns**

![image](https://github.com/user-attachments/assets/230ec693-c459-4fee-bc57-f3ab6c34a9b6)

(cherry picked from commit af5fd3fea3)
2025-01-31 09:37:26 +01:00
Peter Munch-Ellingsen
123a7ff29f Fix check for Nintendo Switch target (#24652)
This should fix ringabouts comment here:
https://github.com/nim-lang/Nim/pull/24639#issuecomment-2615107496

I wasn't aware that `nintendoswitch` and `posix` would be active at the
same time, so I falsely inverted a check.

(cherry picked from commit cab3342a2d)
2025-01-27 16:58:20 +01:00
Leon Lysak
c0d50ddc26 Update dom.nim (removeEventListener function) (#24650)
Essentially just an update for the `removeEventListener` function as per
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

(cherry picked from commit 8c3e62e6de)
2025-01-27 08:50:13 +01:00
Tomohiro
2193c3fb70 Fix parseBiggestUInt to detect overflow (#24649)
With some inputs larger than `BiggestUInt.high`, `parseBiggestUInt` proc
in `parseutils.nim` fails to detect overflow and returns random value.
This is because `rawParseUInt` try to detects overflow with `if prev >
res:` but it doesn't detects the overflow from multiplication.
It is possible that `x *= 10` causes overflow and resulting value is
larger than original value.
Here is example values larger than `BiggestUInt.high` but
`parseBiggestUInt` returns without detecting overflow:
```
22751622367522324480000000
41404969074137497600000000
20701551093035827200000000000000000
22546225502460313600000000000000000
204963831854661632000000000000000000
```

Following code search for values larger than `BiggestUInt.high` and
`parseBiggestUInt` cannot detect overflow:
```nim
import std/[strutils]

const
  # Increase this to extend search range
  NBits = 34'u
  NBitsMax1 = 1'u shl NBits
  NBitsMax = NBitsMax1 - 1'u

  # Increase this when there are too many results and want to see only larger result.
  MinMultiply10 = 14

var nfound = 0
for i in (NBitsMax div 10'u + 1'u) .. NBitsMax:
  var
    x = i
    n10 = 0
  for j in 0 ..< NBits:
    let px = x
    x = (x * 10'u) and NBitsMax
    if x < px:
      break
    inc n10
  if n10 >= MinMultiply10:
    echo "i =   ", i
    echo "uint: ", (i shl (64'u - NBits)), '0'.repeat n10
    inc nfound
    if nfound > 15:
      break

echo "found: ", nfound
```

(cherry picked from commit 95b1dda1db)
2025-01-27 08:50:04 +01:00
Peter Munch-Ellingsen
c2b825713c Enable macros to use certain things from the OS module when the target OS is not supported (#24639)
Essentially this PR removes the `{.error.}` pragmas littered around in
the OS module and submodules which prevents them from being imported if
the target OS is not supported. This made it impossible to use certain
supported features of the OS module in macros from a supported host OS.
Instead of the `{.error.}` pragmas the `oscommon` module now has a
constant `supportedSystem` which is false in the cases where the
`{.error.}` pragmas where generated. All procedures which can't be run
by macros is also not declared when `supportedSystem` is false.

It would be possible to create dummy versions of the omitted functions
with an `{.error.}` pragma that would trigger upon their use, but this
is currently not done.

This properly fixes #19414

(cherry picked from commit 1f9cac1f5c)
2025-01-27 08:49:58 +01:00
ringabout
ae011eaeea fixes #21923; nimsuggest "outline" output does not list templates (#24643)
fixes #21923

---------

Co-authored-by: Louis Berube <louis.p.berube@gmail.com>
(cherry picked from commit 67f9bc2f4b)
2025-01-27 08:49:33 +01:00
ringabout
8fe518ed47 fixes #24623; fixes #23692; size pragma only allowed for imported types and enum types (#24640)
fixes #24623
fixes #23692

ref
https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-size-pragma

confines `size` pragma to `enums` and imported `objects` for now

The `typeDefLeftSidePass` carries out the check for pragmas, but the
type is not complete yet. So the `size` pragma checking is postponed at
the final pass.

(cherry picked from commit d6d28a9c79)
2025-01-24 05:10:42 +01:00
metagn
64927c6ae7 don't try to transform objconstr/cast type nodes (#24636)
fixes #24631

[Object
constructors](793baf34ff/compiler/semobjconstr.nim (L462)),
[casts](793baf34ff/compiler/semexprs.nim (L494))
and [type
conversions](793baf34ff/compiler/semexprs.nim (L419))
copy their type nodes verbatim instead of producing semchecked type
nodes. This causes a crash in transf when an untyped expression in the
type node has `nil` type. To deal with this, don't try to transform the
type node in these expressions at all. I couldn't reproduce the problem
with type conversion nodes though so those are unchanged in transf.

(cherry picked from commit 6d59680217)
2025-01-24 05:10:36 +01:00
ringabout
21c0564573 fixes #24630; static openArray backed by seq cannot be passed to another function (#24638)
fixes #24630

(cherry picked from commit 2f402fcb82)
2025-01-24 05:10:28 +01:00
metagn
12347eae74 generate destructor in nodestroy proc for explicit destructor call (#24627)
fixes #24626

`createTypeboundOps` in sempass2 is called when generating destructors
for types including for explicit destructor calls, however it blocks
destructors from getting generated in a `nodestroy` proc. This causes
issues when a destructor is explicitly called in a `nodestroy` proc. To
fix this, allow destructors to get generated only for explicit
destructor calls in nodestroy procs.

(cherry picked from commit 793baf34ff)
2025-01-20 18:46:41 +01:00
Antonis Geralis
52cadfc3d7 Optimize storing into uninit locations for arrays and seqs. (#24619)
(cherry picked from commit 6481482e0e)
2025-01-20 12:48:37 +01:00
ringabout
528e2b2271 fixes compile crashes with one parameter (#24618)
`{.compile("foo.c").}` makes Nim compiler crash

(cherry picked from commit 70d057fcc6)
2025-01-20 12:48:17 +01:00
metagn
0c0df28619 ignore match errors to expected types of tuple constructor elements (#24611)
fixes #24609

A tuple may have an incompatible expected type if there is a converter
match to it. So the compiler should not error when trying to match the
individual elements in the constructor to the elements of the expected
tuple type, this will be checked when the tuple is entirely constructed
anyway.

(cherry picked from commit 8d0e853e0a)
2025-01-20 12:48:10 +01:00
ringabout
a2a1c2b7f1 ci: update to ubuntu 22.04 (#24608)
(cherry picked from commit 41c447b5f4)
2025-01-15 15:31:59 +01:00
Bilog WEB3
41c4ed8dca Update changelog_1_2_0.md (#24607)
(cherry picked from commit 26ed469996)
2025-01-15 15:31:45 +01:00
Loïc Bartoletti
f6167cb0c8 math: Add cumprod and cumproded (#23416)
This pull request adds the `cumproded` function along with its in-place
equivalent, `cumprod`, to the math library. These functions provide
functionality similar to `cumsum` and `cumsummed`, allowing users to
calculate the cumulative sum of elements.

The `cumprod` function computes the cumulative product of elements
in-place, while `cumproded` additionally returns the prod seq.

(cherry picked from commit 4aff12408c)
2025-01-15 15:31:25 +01:00
planetBoy
72ce16990b docs fix spelling issues (#24597)
Hey !
I fixed several spelling issues.Glad I could help .
Br, Guayaba221.

(cherry picked from commit 8ed0a63973)
2025-01-15 15:31:20 +01:00
ringabout
ea3a4203fa fixes #24599; misleading error message with large array bounds (#24601)
fixes #24599

(cherry picked from commit aeeccee50a)
2025-01-15 15:31:13 +01:00
Jacek Sieka
24b24c17b1 fix c_memchr, c_strstr definitions (#24587)
One correct definition is enough

(cherry picked from commit e8bf6af0da)
2025-01-15 15:31:04 +01:00
Jacek Sieka
e80884665d varints: no need for emit (#24585)
(cherry picked from commit 78835562b1)
2025-01-15 15:30:58 +01:00
ringabout
f4009f0957 Update copyright year 2025 (#24593)
(cherry picked from commit 3dda60a8ce)
2025-01-15 10:22:25 +01:00
Skylar Ray
728a99d5cd chore: docs fix spelling issues (#24581)
**handle - handles**
**sensitiviy - sensitivity**

(cherry picked from commit dcc4e07e54)
2025-01-15 10:22:18 +01:00
futreall
019a3180f8 chore: correct typos docs (#24580)
(cherry picked from commit 0df351bf50)
2025-01-15 10:22:07 +01:00
Antonis Geralis
fb13e3608b Consider iterator types (#24577)
According to the macros doc nnkIteratorTy trees use the same structure
as nnkProcTy

(cherry picked from commit d3b6dba616)
2025-01-15 10:21:53 +01:00
Antonis Geralis
f5804a36b9 Support tuple parameter types (#24576)
(cherry picked from commit e1be29942e)
2025-01-15 10:21:37 +01:00
chloefeal
a83c535ed4 docs: fix typos (#24573)
Signed-off-by: chloefeal <188809157+chloefeal@users.noreply.github.com>
(cherry picked from commit cd220fe3e1)
2025-01-15 10:21:20 +01:00
Jake Leahy
3c71429eca Doc search improvements (#24567)
- `/` is now a hotkey to jump to the search
- Search results now are in line with the page (previously on small
screens it would be off centre)
- Jumping to a search result inside the page or via TOC will now hide
the search results (previously the results got in the way)

Example site here: https://tranquil-scone-c159b6.netlify.app/main.html

(cherry picked from commit 86d6f71f5a)
2025-01-15 10:21:11 +01:00
Jake Leahy
9e1b199e78 Minor std/strscans improvements (#24566)
#### Removes UnInit warnings when using `scanTuple`

e.g. this would emit a warning
```nim
import std/strscans

proc main() =
  let (ok, number) = "123".scanTuple()
```

![image](https://github.com/user-attachments/assets/68170ac6-402d-48b0-b8b6-69e71f4b70ae)

#### Error for wrong type now points to the passed in variable

```nim
import std/strscans

var str: string
discard "123".scanf("$i", str)
```

it gave this warning before

![image](https://github.com/user-attachments/assets/096e56d2-0eb5-4c67-9725-25caa97afebd)
now it returns

![image](https://github.com/user-attachments/assets/736a4292-2f56-4cf3-a27a-677045377171)

(cherry picked from commit 5b9ff963c5)
2025-01-15 10:21:00 +01:00
ringabout
b2f2b34fe5 adds a test case (#24565)
closes #19531

(cherry picked from commit 65b26401bc)
2025-01-15 10:20:50 +01:00
Esteban C Borsani
7be1bb572e Improve async stacktraces (#24563)
This makes await point to the caller line instead of asyncmacro. It also
reworks the "Async traceback:" section of the traceback. Follow up PR
#21091 (issue #19931) so it works if there is asynchronous work done.

(cherry picked from commit 2f127bf99f)
2025-01-15 10:20:29 +01:00
ringabout
40476fa24f fixes #23114; Nim v2 regression emit / asm var param dereference inconsistency (#24547)
fixes #23114

As in https://github.com/nim-lang/Nim/pull/22074, expressions in
bracketed emit are strictly typechecked, this PR applies the same check
for symbols in asm statements in order to keep them consistent.

(cherry picked from commit 3c4246dd24)
2025-01-15 10:20:18 +01:00
Tomohiro
3054cbe422 Add inline assembler tests for i386, arm, arm64, riscv32 and riscv64 (#24564)
This fixes one error in https://github.com/nim-lang/Nim/issues/24544 .
I tested this on Raspberry Pi Pico (arm) and Raspberry Pi 3(arm64).
It is not tested on i386, riscv32 and riscv64 CPU.

(cherry picked from commit fc806710cb)
2025-01-15 10:20:08 +01:00
ringabout
b31aed01f1 adds a test case (#24561)
closes #18616

(cherry picked from commit e2a306355c)
2025-01-15 10:19:57 +01:00
metagn
9c7f04dacb fix jsonutils with generic sandwiches, don't use strformat (#24560)
fixes #24559

The strformat macros have the problem that they don't capture symbols,
so don't use them in the generic `fromJson` proc here. Also `fromJson`
refers to `jsonTo` before it is declared which doesn't capture it, so
it's now forward declared.

(cherry picked from commit 5c71fbab30)
2025-01-15 10:19:51 +01:00
Jake Leahy
c5ee216c42 Make 'field is not accessible' and 'field initialized twice' errors point to the field inside the obj construction (#24557)
Fixes two line infos to make the error's clearer inside editors

- 'field is not accessible' would point to the whole object construction
instead of just the field inside the construction
- 'field initialized twice' would point to the colon instead of the
field

(cherry picked from commit 6bc52737b3)
2025-01-15 10:19:40 +01:00
Esteban C Borsani
ede6540c55 fixes #23212; Asyncdispatch leaks under --mm:arc (#24556)
Fixes #23212

Inspired by [this chronos
PR](https://github.com/status-im/nim-chronos/pull/243)

(cherry picked from commit f29234b40f)
2025-01-15 10:19:26 +01:00
ringabout
0c14372a8c remove zippy data from tarballs (#24551)
fixes https://github.com/nim-lang/nightlies/issues/95

(cherry picked from commit 63c884038d)
2025-01-15 10:19:04 +01:00
metagn
6deb3a90ae check if unused import warning is enabled before adding import to stack (#24554)
fixes #24552

Could also implement `{.used.}` for imports but this wouldn't be
backwards compatible. The same problem as #24552 also exists for
`{.hint[XDeclaredButNotUsed].}` but this isn't as much of a problem
since `{.used.}`/`{.push used.}` exist.

(cherry picked from commit 986ca7dcd4)
2025-01-15 10:18:58 +01:00
Daniel Stuart
e0e1061562 Use long int builtins for risc-v 32-bit targets (#24553)
Solves compilation using riscv32-unknown-elf-gcc, compiler defines
int32_t as long int.

(cherry picked from commit 50ed43df42)
2025-01-15 10:18:00 +01:00
ringabout
d96c5b2396 fixes strictdefs warnings (#24550)
(cherry picked from commit ce4304ce97)
2025-01-15 10:17:51 +01:00
Jake Leahy
09835a1d9e Make expandMacro show private fields (#24522)
Was debugging with `--expandMacro` and noticed that private fields
weren't exported.
Passes extra flags to the renderer to make them be shown

(cherry picked from commit f80ce139d5)
2025-01-15 10:17:43 +01:00
ringabout
0823b9d177 fixes #17681; enforce codegen for exportc consts (#24546)
fixes #17681

(cherry picked from commit d5c7abe3d2)
2025-01-15 10:17:37 +01:00
Juan M Gómez
b5068f427a Adds skipParentCfg back. Bump nimble to a commit where it doesnt rely in the parent config (#24545)
(cherry picked from commit 8ce58fab26)
2025-01-15 10:17:27 +01:00
ringabout
1cee3c7f18 fixes #24536; fixes nightlies regression caused by nimble update (#24542)
follow up #24537

Because `nimble` is a bundled repo so it is bundled in the tarballs

i.e.
82421fd705/.github/workflows/nightlies.yml (L264)
has bundled `dist/nimble`, but it only copies the data without `.git`.
So in this PR, we ignore bundled nimble repo.

(cherry picked from commit 70b3232d3a)
2025-01-15 10:17:17 +01:00
ringabout
844ba2168b fixes #20908; Unknown warnings and hints now give a warning (#24543)
fixes #20908

This PR unifies the treatment of unknown warnings and hints, which now
gives an `warnUnknownNotes` instead of an error.

(cherry picked from commit 81d8c0fc17)
2025-01-15 10:17:07 +01:00
ringabout
d5437b7a4a fixes #24538 (#24541)
fixes #24538

(cherry picked from commit 91d1933ea2)
2025-01-15 10:16:20 +01:00
metagn
faa9ae08b0 proper error for const defines with unsupported types (#24540)
fixes #24539

(cherry picked from commit b9c593404c)
2025-01-15 10:16:12 +01:00
Juan M Gómez
7b596b4e1f #Fixes #24536 building nimble 0.16.4 fails when running build_all.sh (#24537)
(cherry picked from commit 556f217b4c)
2025-01-14 13:24:21 +01:00
ringabout
cd06f0769f more strictdef fixes for stdlibs (#24535)
(cherry picked from commit d31cce557b)
2025-01-14 13:23:44 +01:00
Juan M Gómez
0cce145dac Bumps nimble v0.16.4 (#24437)
(cherry picked from commit be4d19e562)
2025-01-14 13:23:38 +01:00
Ryan McConnell
43f7e160ba couple cases of valid concept bindings (#24513)
see tests

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit e0197a8380)
2025-01-14 13:23:26 +01:00
ringabout
5a71c36d25 fixes strictdefs warnings continue (#24520)
(cherry picked from commit d2d810585c)
2025-01-14 13:23:18 +01:00
ringabout
1299dd4651 adds a test case (#24534)
closes #16845

(cherry picked from commit 80af252025)
2025-01-14 13:23:12 +01:00
bptato
51a0f3de6e Fix exitnow signature, mark as .noreturn (#24533)
Like quit, this function never returns.

Also, "code" was marked as "int", even though POSIX _exit takes a C int.

(cherry picked from commit f485973459)
2025-01-14 13:23:06 +01:00
ringabout
e2b6021630 fixes #22101; std/pegs with nim cpp --mm:orc --exceptions:goto creates invalid C++ (#24531)
fixes #22101

The old implementation generates

`auto T = value;` for the cpp backend which causes problems for goto
exceptions. This PR puts the declaration of `T` to the cpsLocals parts
and makes it compatible with goto exceptions.

(cherry picked from commit f7a461a30c)
2025-01-14 13:22:32 +01:00
ringabout
428b64251f adds a test case (#24532)
closes #18070

(cherry picked from commit f796c01e3c)
2025-01-14 13:20:04 +01:00
ringabout
5b0b90fb49 fixes #22153; UB calling allocCStringArray([""]) with --mm:refc (#24529)
fixes #22153

It's a problem for refc because you cannot index a nil string: i.e.
`[""]` is `{((NimStringDesc*) NIM_NIL)}` which cannot be indexed

(cherry picked from commit 9bb7e53e7f)
2025-01-14 13:19:56 +01:00
Jake Leahy
2f5481ce88 Make error appear in user code with invalid format string in strformat (#24528)
With this example
```nim
import std/strformat

echo fmt"{invalid, code}"
```

We get the error message
```
stack trace: (most recent call last)
/home/jake/.choosenim/toolchains/nim-hashdevel/lib/pure/strformat.nim(750, 16) fmt
/home/jake/.choosenim/toolchains/nim-hashdevel/lib/pure/strformat.nim(714, 16) strformatImpl
/home/jake/Documents/projects/Nim/temp.nim(3, 9) template/generic instantiation of `fmt` from here
/home/jake/.choosenim/toolchains/nim-hashdevel/lib/pure/strformat.nim(714, 16) Error: could not parse `invalid, code` in `{invalid, code}`.
(1, 8) Error: invalid indentation
```
After PR it now shortens it to just appear in user code
```
/home/jake/Documents/projects/Nim/lib/pure/strformat.nim(750, 16) fmt
/home/jake/Documents/projects/Nim/lib/pure/strformat.nim(714, 16) strformatImpl
/home/jake/Documents/projects/Nim/temp.nim(3, 9) Error: could not parse `invalid, code` in `{invalid, code}`.
(1, 8) Error: invalid indentation
```

(cherry picked from commit da9f7f671b)
2025-01-14 13:19:50 +01:00
Jake Leahy
9aeb5c254c Fix line info for import (#24523)
Refs #24158

Fixes the line info of the module symbol (cases like `import as` and
grouped imports had wrong line info). Since that symbol's line info is
now used for the warnings, there isn't a separate line info stored for
`unusedImports`

Examples of fixed cases
```nim
import strutils as test #[
                ^ before
                   ^ after ]#

# This case was fixed by #24158, but only for unused imports
import std/[strutils, strutils] #[
        ^ before
                      ^ after ]#

from strutils import split #[
^ before
     ^ after ]#
```

(cherry picked from commit 69e0cdb6c0)
2025-01-14 13:17:34 +01:00
metagn
4e1bc4216a fix nil node in sym ast of exported ref objects [backport:2.2] (#24527)
fixes #24526, follows up #23101

The `shallowCopy` calls do not keep the original node's children, they
just make a new seq with the same length, so the `Ident "*"` node from
the original postfix nodes was not carried over, making it `nil` and
causing the segfault.

(cherry picked from commit b529f69518)
2025-01-14 13:17:24 +01:00
metagn
316141162b test case haul to prevent pileup (#24525)
closes #6013, closes #7009, closes #9190, closes #12487, closes #12831,
closes #13184, closes #13252, closes #14860, closes #14877, closes
#14894, closes #14917, closes #16153, closes #16439, closes #17779,
closes #18074, closes #18202, closes #18314, closes #18648, closes
#19063, closes #19446, closes #20065, closes #20367, closes #22126,
closes #22820, closes #22888, closes #23020, closes #23287, closes
#23510

(cherry picked from commit aeb3fe9505)
2025-01-14 13:17:11 +01:00
ringabout
8d8a90e079 fixes nightlies regression (#24519)
follows up https://github.com/nim-lang/Nim/pull/24507

(cherry picked from commit d408b94063)
2025-01-14 13:17:04 +01:00
ringabout
105e134c3f adds a test case (#24518)
closes #19698

(cherry picked from commit 801733f286)
2025-01-14 13:16:38 +01:00
metagn
85c8b5b304 track call depth separately from loop count in VM (#24512)
refs #24503

Infinite recursions currently are not tracked separately from infinite
loops, because they also increase the loop counter. However the max
infinite loop count is very high by default (10 million) and does not
reliably catch infinite recursions before consuming a lot of memory. So
to protect against infinite recursions, we separately track call depth,
and add a separate option for the maximum call depth, much lower than
the maximum iteration count by default (2000, the same as
`nimCallDepthLimit`).

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 6f4106bf5d)
2025-01-14 13:16:21 +01:00
ringabout
2d470c9afd fixes strictdefs warnings for stdlibs [part two] (#24514)
After some cleanups for stdlibs, then we should enable warningaserror
for all tests

(cherry picked from commit c0861142f8)
2025-01-14 13:15:55 +01:00
ringabout
94a6b85538 fixes #24504; fixes ensureMove for refs (#24505)
fixes #24504

(cherry picked from commit d0288d3b57)
2025-01-14 13:15:46 +01:00
ringabout
90c5dfc32c adds a test case (#24515)
closes #17733

(cherry picked from commit 02fb0476ce)
2025-01-14 13:15:39 +01:00
ringabout
a7b671dad5 don't track result initialization if it is marked noinit (#24499)
We don't track `noinit` for variables introduced in
https://github.com/nim-lang/Nim/pull/10566. It should be applied to
`result` if the function is marked `noinit`

(cherry picked from commit 2e9e7f13ee)
2025-01-14 13:15:25 +01:00
Tomohiro
fb11c4404e fixes #24506; calculate timeout correctly (#24507)
`curTimeout` is calculated incorrectly. So this PR fixes it.
This PR also replaces `now()` with `getMonoTime()`.

(cherry picked from commit bbf6a62c90)
2025-01-14 13:13:35 +01:00
ringabout
1adcab885b remove unnecessary await (#24501)
There is already a when condition, so `await` is not needed to split the
function

(cherry picked from commit 6bbf9c3117)
2025-01-14 13:13:10 +01:00
ringabout
5ddbf2372e fixes some strictdefs warnings (#24502)
(cherry picked from commit 8f4bfda5f4)
2025-01-14 13:12:39 +01:00
ringabout
8f668c2373 adds a test case (#24500)
closes #24040

(cherry picked from commit c3120b6121)
2025-01-14 13:12:29 +01:00
metagn
3daf7dd2ac remove inserted derefs for ref object fields when transforming to dot call (#24498)
fixes #24492

Kind of a goofy way of doing this, but we count how many derefs were
used for the first parameter before calling `builtinFieldAccess`, then
count after, and if there are more now than before, we remove the added
derefs. I thought maybe getting rid of #18298 would simplify it but
maybe this would still be the best way.

For better encapsulation we could make `dotTransformation` take an
`nOrig` param instead but this would be less efficient since it would
need a copy, though `semAsgn` already makes one.

(cherry picked from commit 2529f33760)
2025-01-14 13:12:22 +01:00
ringabout
4b4b97018b prefix NimDestroyGlobals with nimMainPrefix (#24493)
ref https://github.com/nim-lang/Nim/issues/24471

---------

Co-authored-by: metagn <metagngn@gmail.com>
(cherry picked from commit 3bee04d9f3)
2025-01-14 13:11:55 +01:00
metagn
aa5fc4af58 install older version of nimcuda for arraymancer (#24496)
Attempt to fix CI failure, refs
https://github.com/nim-lang/Nim/pull/24495#issuecomment-2511299112,
alternative is to use a commit version like
bc65375ff5

(cherry picked from commit 33dc2367e7)
2025-01-14 13:11:44 +01:00
ringabout
062e77bce0 adds a test case (#24486)
closes #23680

(cherry picked from commit ddf5a9f6c5)
2025-01-14 13:11:16 +01:00
metagn
d04f9c426e fix crash with undeclared proc type pragma macro in generics (#24490)
fixes #24489

(cherry picked from commit 05bba15623)
2025-01-14 12:24:22 +01:00
ringabout
52809cd3dd fixes #24476; remove proc type cast if they are same types for backends (#24480)
fixes #24476

closes https://github.com/nim-lang/Nim/pull/24479

(cherry picked from commit 5340005869)
2025-01-14 12:24:00 +01:00
Andreas Rumpf
05a8b65eea stdlib: minor refactorings and updates (#24482)
(cherry picked from commit 8881017c80)
2025-01-14 12:16:21 +01:00
ringabout
b1a555dd52 Add support for parsing parameterised sql types (#24483)
Co-authored-by: Cletus Igwe <me@cletusigwe.com>
(cherry picked from commit dcd0793f2b)
2025-01-14 12:16:07 +01:00
Ryan McConnell
f5453e453e Fixes 3 small issues with concepts (#24481)
issue 1 - statics in the type:
This probably only handles simple cases. It's probably too accepting
only comparing the base, but that should only affect candidate selection
I think.
issue 2 - `tyArray` of length 3:
This is just a work around since I couldn't get the fix right in
previous PR
issue 3 - shadowing:
The part in `concepts.nim` that iterates candidates does not consider
imported idents if at least once module level ident matches. It does not
have to match in any other way then name.

EDIT: 2 more
issue 4 - composite typeclasses:
when declared in both the concept and the `proc` can cause problems
issue 5 - recursion:
simple recursion and scenarios where more than one concepts recurse
together (only tested two)

(cherry picked from commit e479151473)
2025-01-14 12:15:59 +01:00
ringabout
7d425e712e fixes #24472; let symbol created by template is reused in nimvm branch (#24473)
fixes #24472

Excluding variables which are initialized in the nimvm branch so that
they won't interfere the other branch

(cherry picked from commit e7f48cdd5c)
2025-01-14 12:15:51 +01:00
ringabout
e6f5e49184 minor fix for the command line helper (#24475)
(cherry picked from commit 1a901bd94e)
2025-01-14 12:15:21 +01:00
Judd
cd370e4725 Fix highlite.nim (#24457)
When parsing `a = 1` with `langPython`, Eof is reported unexpectedly.

Fix: allow other languages to fallback to "Identifier" when it is not a
keyword.

This patch is useful as this is a highlighter. `Eof` as annoying.

(cherry picked from commit 6112c51e78)
2025-01-14 12:15:09 +01:00
ringabout
42184227aa fix #19600; No error checking on fclose (#24468)
fix #19600

(cherry picked from commit 555191a3f0)
2025-01-14 12:11:57 +01:00
metagn
14ce1a91ce fix crash with tyBuiltInTypeClass matching itself (#24462)
fixes #24449

The standalone `seq` type is a `tyBuiltInTypeClass` with a single child
of kind `tySequence`, which itself has no children. This is also the
case for most other `tyBuiltInTypeClass` kinds. However this can cause a
crash in sigmatch when calling `isEmptyContainer` on this child type,
which expects the sequence type to have children. This check was added
in #5557 to prevent empty collections like `@[]` from matching their
respective typeclass, but it's not useful when matching against another
typeclass (which is done here to resolve an ambiguity). So to avoid the
crash, this empty container check is disabled when matching against
another typeclass.

(cherry picked from commit 96043bdbb7)
2025-01-14 09:11:14 +01:00
ringabout
ff7b83f266 adds a test case (#24469)
closes #13945

(cherry picked from commit af3181e75b)
2025-01-14 09:11:07 +01:00
metagn
522b184d5a retry thttpclient_ssl twice (#24467)
Flaky on linux_amd64

(cherry picked from commit 652edb229a)
2025-01-14 09:11:01 +01:00
Ryan McConnell
d339c58628 fixes #24451; concept matching generic body (#24458)
I think this might not be a comprehensive solution to dealing with
`tyGenericBody` but we take a step forward
#24451

(cherry picked from commit 08c2a1741d)
2025-01-14 09:10:45 +01:00
metagn
01b6c5d0d1 fix unix stdlib install location after #21328 (#24460)
closes #22369, closes #23197, closes #24385, refs #21328

According to #21328 the standard library on Unix should be installed in
`/usr/lib/nim/lib`, however the installer was not updated for this
change, hence the problem as described in
https://github.com/nim-lang/Nim/issues/23197#issuecomment-2031386896.

Have not tested if this fixes the problem but the comment heavily
implies it does. The problem is also in 2.0 so it could be backported
but I can't say for sure that it works and doesn't break anything.

(cherry picked from commit 33e455c986)
2025-01-14 09:10:40 +01:00
ringabout
1bc501f8ce adds a test case (#24466)
closes https://github.com/nim-lang/Nim/issues/23770 ref
https://github.com/nim-lang/Nim/pull/24442

(cherry picked from commit 9fcc3b0599)
2025-01-14 09:08:50 +01:00
ringabout
aa8d62f89c remove unnecessary imports (#24465)
ref https://github.com/nim-lang/Nim/issues/24272

(cherry picked from commit a788bae318)
2025-01-14 09:08:40 +01:00
ringabout
60a8eaaaa5 adds a test case (#24464)
closes #7784

(cherry picked from commit 3eddb64909)
2025-01-14 09:08:32 +01:00
metagn
f3da96d880 include new concepts in typeclasses, makes containsGenericType work (#24453)
fixes #24450

The new concepts were previously not included in
[containsGenericType][1] which prevents them from being instantiated.
Here they are included by being added to `tyTypeClasses` though this
doesn't have to be done, they can also be added manually to
`containsGenericTypeIter`, but this might be too specific.

[1]:
a2031ec6cf/compiler/types.nim (L1507-L1517)

(cherry picked from commit e28d2f42e9)
2025-01-14 09:08:13 +01:00
metagn
87c306061b disable weird type inference for object constructors (#24455)
closes #24372, refs #20091

This was added in #20091 for some reason but doesn't actually work and
only makes error messages more obscure. So for now, it's disabled.

Can also be backported to 2.0 if necessary.

(cherry picked from commit a610f23060)
2025-01-14 09:08:06 +01:00
metagn
2b1885a0fa remove structural equality check for objects and distinct types (#24448)
follows up #24425, fixes #18861, fixes #22445

Since #24425 generic object and distinct types now accurately link back
to their generic instantiations. To work around this issue previously,
type matching checked if generic objects/distinct types were
*structurally* equal, which caused false positives with types that
didn't use generic parameters in their structures. This structural check
is now removed, in cases where generic objects/distinct types require a
nontrivial equality check, the generic parameters of the `typeInst`
fields are checked for equality instead.

The check is copied from `tyGenericInst`, but the check in
`tyGenericInst` is not always sufficient as this type can be skipped or
unreachable in the case of `ref object`s.

(cherry picked from commit a2031ec6cf)
2025-01-14 09:07:30 +01:00
metagn
9c87f2cb4b always reinstantiate nominal values of generic instantiations (#24425)
fixes #22479, fixes #24374, depends on #24429 and #24430

When instantiating generic types which directly have nominal types
(object, distinct, ref/ptr object but not enums[^1]) as their values,
the nominal type is now copied (in the case of ref objects, its child as
well) so that it receives a fresh ID and `typeInst` field. Previously
this only happened if it contained any generic types in its structure,
as is the case for all other types.

This solves #22479 and #24374 by virtue of the IDs being unique, which
is what destructors check for. Technically types containing generic
param fields work for the same reason. There is also the benefit that
the `typeInst` field is correct. However issues like #22445 aren't
solved because the compiler still uses structural object equality checks
for inheritance etc. which could be removed in a later PR.

Also fixes a pre-existing issue where destructors bound to object types
with generic fields would not error when attempting to define a user
destructor after the fact, but the error message doesn't show where the
implicit destructor was created now since it was only created for
another instance. To do this, a type flag is used that marks the generic
type symbol when a generic instance has a destructor created. Reusing
`tfCheckedForDestructor` for this doesn't work.

Maybe there is a nicer design that isn't an overreliance on the ID
mechanism, but the shortcomings of `tyGenericInst` are too ingrained in
the compiler to use for this. I thought about maybe adding something
like `tyNominalGenericInst`, but it's really much easier if the nominal
type itself directly contains the information of its generic parameters,
or at least its "symbol", which the design is heading towards.

[^1]: See [this
test](21420d8b09/lib/std/enumutils.nim (L102))
in enumutils. The field symbols `b0`/`b1` always have the uninstantiated
type `B` because enum fields don't expect to be generic, so no generic
instance of `B` matches its own symbols. Wouldn't expect anyone to use
generic enums but maybe someone does.

(cherry picked from commit 05c74d6844)
2025-01-14 09:07:03 +01:00
metagn
03b6999499 prevent codegen of inactive case fields in VM object constructor nodes (#24442)
fixes #17571

Objects in the VM are represented as object constructor nodes that
contain every single field, including ones in different case branches.
This is so that every field has a unique invariant index in the object
constructor that can be written to and read from. However when
converting this node back into semantic code, fields from inactive case
branches can remain in the constructor which causes bad codegen,
generating assignments to fields from other case branches.

To fix this, fields from inactive branches are now detected in
`semmacrosanity.annotateType` (called in `fixupTypeAfterEval`) and
marked to prevent the codegen of their assignments. In #24441 these
fields were excluded from the resulting node, but this causes issues
when the node is directly supposed to go back into the VM, for example
as `const` values. I don't know if this is the only case where this
happens, so I wasn't sure about how to keep that implementation working.

(cherry picked from commit 75b512bc6a)
2025-01-14 09:06:58 +01:00
metagn
2690ab01c0 fix wrong error for iterators with no body and pragma macro (#24440)
fixes #16413

`semIterator` checks if the original iterator passed to it has no body,
but it should check the processed node created by `semProcAux`.

(cherry picked from commit e239968b80)
2025-01-14 09:06:49 +01:00
ringabout
c79fb859f1 adds some test cases (#24436)
closes #24043
closes #24045

(cherry picked from commit cc696f18c0)
2025-01-14 09:05:48 +01:00
ringabout
2d658e8de5 fixes #24402; Memory leak under Arc/Orc on inline iterators with nested seq (#24419)
fixes #24402

```nim
iterator myPairsInline*[T](twoDarray: seq[seq[T]]): (int, seq[T]) {.inline.} =
  for indexValuePair in twoDarray.pairs:
    yield indexValuePair

proc innerTestTotalMem() =
  var my2dArray: seq[seq[int32]] = @[]

  # fill with some data...
  for i in 0'i32..100:
    var z = @[i, i+1]
    my2dArray.add z

  for oneDindex, innerArray in myPairsInline(my2dArray):
    discard

innerTestTotalMem()
```

In `for oneDindex, innerArray in myPairsInline(my2dArray)`, `oneDindex`
and `innerArray` becomes `cursors` because they satisfy the criterion of
`isSimpleIteratorVar`. On the one hand, it is not correct to have them
point to the temporary generated by tuple unpacking, which left the
memory of the temporary uncleaned up. On the other hand, we don't need
to generate a temporary for a symbol node when unpacking the tuple.

(cherry picked from commit 21420d8b09)
2025-01-14 09:05:36 +01:00
metagn
0036bb976b fix subtype match of generic object types (#24430)
split from #24425

Matching `tyGenericBody` performs a match on the last child of the
generic body, in this case the uninstantied `tyObject` type. If the
object contains no generic fields, this ends up being the same type as
all instantiated ones, but if it does, a new type is created. This fails
the `sameObjectTypes` check that subtype matching for object types uses.
To fix this, also consider that the pattern type could be the generic
uninstantiated object type of the matched type in subtype matching.

(cherry picked from commit 511ab72342)
2025-01-14 09:05:28 +01:00
metagn
b0f3d1e874 fix jsonutils macro with generic case object (#24429)
split from #24425

The added test did not work previously. The result of `getTypeImpl` is
the uninstantiated AST of the original type symbol, and the macro
attempts to use this type for the result. To fix the issue, the provided
`typedesc` argument is used instead.

(cherry picked from commit 45e21ce8f1)
2025-01-14 09:05:24 +01:00
metagn
9f03b98de5 stricter skip for conversions in array indices in transf (#24424)
fixes #17958

In `transf`, conversions in subscript expressions are skipped (with
`skipConv`'s rules). This is because array indexing can produce
conversions to the range type that is the array's index type, which
causes a `RangeDefect` rather than an `IndexDefect` (and also
`--rangeChecks` and `--indexChecks` are both considered). However this
causes problems when explicit conversions are used, between types of
different bitsizes, because those also get skipped.

To fix this, we only skip the conversion if:

* it's a hidden (implicit) conversion
* it's a range check conversion (produces `nkChckRange`)
* the subscript is on an array type and the result type of the
conversion has the same bounds as the array index type

And `skipConv` rules also still apply (int/float classification).

Another idea would be to prevent the implicit conversion to the array
index type from being generated. But there is no good way to do this:
matching to the base type instead prevents types like `uint32` from
implicitly converting (i.e. it can convert to `range[0..3]` but not
`int`), and analyzing whether this is an array bound check is easier in
`transf`, since `sigmatch` just produces a type conversion.

The rules for skipping the conversion could also receive some other
tweaks: We could add a rule that changing bitsizes also doesn't skip the
conversion, but this breaks the `uint32` case. We could simplify it to
only removing implicit skips to specifically fix #17958, but this is
wrong in general.

We could also add something like `nkChckIndex` that generates index
errors instead but this is weird when it doesn't have access to the
collection type and it might be overkill.

(cherry picked from commit 76c5f16ac5)
2025-01-14 09:05:18 +01:00
Sam
0b7e22635e Fixes #24369 (#24370)
Hope this fixes #24369, happy for any feedback on the PR.

(cherry picked from commit 1fddb61b3b)
2025-01-14 09:05:07 +01:00
metagn
3642f4d375 gensym anonymous proc symbols (#24422)
fixes #14067, fixes #15004, fixes #19019

Anonymous procs are [added to
scope](8091d76306/compiler/semstmts.nim (L2466))
with the name `:anonymous`. This means that if they have the same
signature in a scope, they can consider each other as redefinitions. To
prevent this, mark their symbols as `sfGenSym` so they do not get added
to scope or cause any name conflicts. The commented out `and not isAnon`
check wouldn't work because `isAnon` would not be true if the proc is
being resemmed, in which case the name field in the proc AST would have
the symbol of the anonymous proc rather than being empty.

There is a separate problem of default values in generic/normal procs
not opening new scopes which is partially responsible for #19019.

(cherry picked from commit 3e47725c08)
2025-01-14 09:05:01 +01:00
metagn
f292393816 skip tyAlias in generic alias checks [backport:2.0] (#24417)
fixes #24415

Since #23978 (which is in 2.0), all generic types that alias to another
type now insert a `tyAlias` layer in their value. However the
`skipGenericAlias` etc code which `sigmatch` uses is not updated for
this, so `tyAlias` is now skipped in these.

The relevant code in sigmatch is:
67ad1ae159/compiler/sigmatch.nim (L1668-L1673)

This behavior is also suspicious IMO, not skipping a structural
`tyGenericInst` alias can be useful for code like #10220, but this is
currently arbitrarily decided based on "depth" and whether the alias is
to another `tyGenericInst` type or not. Maybe in the future we could
enforce the use of a nominal type.

(cherry picked from commit 45b8434c7d)
2025-01-14 09:04:54 +01:00
metagn
6ec663f7bc fix standalone explicit generic procs with unresolved arguments (#24404)
fixes issue described in https://forum.nim-lang.org/t/12579

In #24065 explicit generic parameter matching was made to fail matches
on arguments with unresolved types in generic contexts (the sigmatch
diff, following #24010), similar to what is done for regular calls since
#22029. However unlike regular calls, a failed match in a generic
context for a standalone explicit generic instantiation did not convert
the expression into one with `tyFromExpr` type, which means it would
error immediately given any unresolved parameter. This is now done to
fix the issue.

For explicit generic instantiations on single non-overloaded symbols, a
successful match is still instantiated. For multiple overloads (i.e.
symchoice), if any of the overloads fail the match, the entire
expression is considered untyped and any instantiations are not used, so
as to not void overloads that would match later. This means even
symchoices without unresolved arguments aren't instantiated, which may
be too restrictive, but it could also be too lenient and we might need
to make symchoice instantiations always untyped. The behavior for
symchoice is not sound anyway given it causes #9997 so this is something
to consider for a redesign.

Diff follows #24276.

(cherry picked from commit 67ad1ae159)
2025-01-14 09:04:44 +01:00
ringabout
6d02ac1ba0 fixes strictdefs with when nimvm (#24409)
ref https://github.com/nim-lang/Nim/pull/24225
related https://github.com/nim-lang/Nim/pull/24306

> Code in branches must not affect semantics of the code that follows
the
`when nimvm` statement. E.g. it must not define symbols that are used in
  the following code.

The test shouldn't have passed when
https://github.com/nim-lang/Nim/pull/24306
would be implemented somehow. Some third packages have already misused
`when nimvm` by defining symbols in the other branch of `when nimvm`.

e.g. in https://github.com/status-im/nim-unittest2/pull/34

```nim
when nimvm:
  discard
else:
  let suiteName {.inject.} = nameParam

use(suiteName)
```

(cherry picked from commit c71de10608)
2025-01-14 09:04:35 +01:00
metagn
ce1fa86095 disable sfml test on osx (#24615)
Tried installing sfml 2 in #24614 but didn't work

(cherry picked from commit d83ff81695)
2025-01-14 08:40:02 +01:00
metagn
4900550e9c disable all badssl tests indefinitely (#24403)
Flaky not just due to recent ubuntu 24/GCC 14 upgrades, windows fails as
well, assuming the issue is with badssl or it's just not worth testing
here.

(cherry picked from commit 5f056f87b2)
2025-01-14 07:53:56 +01:00
Phil Krylov
9fe2356e74 std/parsesql: Fix JOIN parsing (#22890)
This commit fixes/adds tests for and fixes several issues with `JOIN`
operator parsing:

- For OUTER joins, LEFT | RIGHT | FULL specifier is not optional
```nim
doAssertRaises(SqlParseError): discard parseSql("""
SELECT id FROM a
OUTER JOIN b
ON a.id = b.id
""")
```

- For NATURAL JOIN and CROSS JOIN, ON and USING clauses are forbidden
```nim
doAssertRaises(SqlParseError): discard parseSql("""
SELECT id FROM a
CROSS JOIN b
ON a.id = b.id
""")
```

- JOIN should parse as part of FROM, not after WHERE
```nim
doAssertRaises(SqlParseError): discard parseSql("""
SELECT id FROM a
WHERE a.id IS NOT NULL
INNER JOIN b
ON a.id = b.id
""")
```

- LEFT JOIN should parse
```nim
doAssert $parseSql("""
SELECT id FROM a
LEFT JOIN b
ON a.id = b.id
""") == "select id from a left join b on a.id = b.id;"
```

- NATURAL JOIN should parse
```nim
doAssert $parseSql("""
SELECT id FROM a
NATURAL JOIN b
""") == "select id from a natural join b;"
```

- USING should parse
```nim
doAssert $parseSql("""
SELECT id FROM a
JOIN b
USING (id)
""") == "select id from a join b using (id );"
```

- Multiple JOINs should parse
```nim
doAssert $parseSql("""
SELECT id FROM a
JOIN b
ON a.id = b.id
LEFT JOIN c
USING (id)
""") == "select id from a join b on a.id = b.id left join c using (id );"
```

(cherry picked from commit 46bb47a444)
2025-01-14 07:53:44 +01:00
ringabout
37ab27bd99 improve httpclient docuementation (#24398)
ref https://github.com/nim-lang/Nim/issues/24394

(cherry picked from commit 08b82c90f5)
2025-01-14 07:53:36 +01:00
ringabout
5e22d8bc3c fixes #24395; remove ndi (#24396)
fixes  #24395

(cherry picked from commit 8b88b5fdd8)
2025-01-14 07:53:29 +01:00
ringabout
6b102d5c13 azure-pipelines update to ubuntu 24.04 gcc 14 (#24386)
(cherry picked from commit 7b47987341)
2025-01-14 07:53:23 +01:00
ringabout
51f8649e36 trigger package CI for version-2-2 (#24393)
(cherry picked from commit d55cd40642)
2025-01-14 07:53:11 +01:00
ringabout
df27b427af fixes #24378; supportsCopyMem can fail from macro context with tuples (#24383)
fixes #24378

```nim
type Win = typeof(`body`)
doAssert not supportsCopyMem((int, Win))
```

`semAfterMacroCall` doesn't skip the children aliases types in the tuple
typedesc construction while the normal program seem to skip the aliases
types somewhere

`(int, Win)` is kept as `(int, alias string)` instead of expected `(int,
string)`

(cherry picked from commit 5e56f0a356)
2025-01-14 07:52:42 +01:00
ringabout
e57f755b78 fixes #24371; incorrect importc wrapper incompatible with gcc 14 on Windows (#24388)
fixes #24371

(cherry picked from commit 74df699ff1)
2025-01-14 07:52:35 +01:00
ringabout
d78c7aa697 disable Test on aarch64 (#24389)
ref https://github.com/nim-lang/Nim/issues/24287

(cherry picked from commit 1576563775)
2025-01-14 07:52:28 +01:00
metagn
435a152c66 implement generic default values for object fields (#24384)
fixes #21941, fixes #23594

(cherry picked from commit 4091576ab7)
2025-01-14 07:52:18 +01:00
ringabout
6c2de9b294 fixes #24379; better error messages for ill-formed type symbols from macros (#24380)
fixes #24379

(cherry picked from commit d61897459d)
2025-01-14 07:52:09 +01:00
ringabout
babc7d8c16 fixes #23545; C compiler error when default initializing an object field function (#24375)
fixes #23545

(cherry picked from commit 815bbf0e73)
2025-01-14 07:52:02 +01:00
ringabout
022bd12e82 improve passes.nim (#24376)
(cherry picked from commit 3fc87259bd)
2025-01-14 07:51:55 +01:00
ringabout
3c528c987c fixes #24359; VM problem: dest register is not set with const-bound proc (#24364)
fixes #24359

follow up https://github.com/nim-lang/Nim/pull/11076

It should not try to evaluate the const proc if the proc doesn't have a
return value.

(cherry picked from commit 031ad957ba)
2025-01-14 07:51:44 +01:00
metagn
52d94c1c86 include static types in type bound ops (#24366)
refs https://github.com/nim-lang/Nim/pull/24315#discussion_r1816332587

(cherry picked from commit 40fc2d0e76)
2025-01-14 07:51:38 +01:00
metagn
87de7f9193 don't cascade vmgen errors in nim check without error outputs (#24365)
refs #23625, refs #24289

Encountered in #24360 but could not reproduce minimally: overloading on
static parameters can work with the normal compile commands but crash
`nim check`. Static overloading relies on `tryConstExpr` which recovers
from things like `globalError` and fails softly, in this case this can
happen when a variable etc. is not available to evaluate in the VM. But
with `nim check`, the compiler does not throw an exception in this case,
and instead tries to keep generating the entire expression in the VM,
which can cause crashes.

To fix this, when the compiler has no error outputs even on `nim check`,
we raise a global error so that the VM code generation stops early. This
fixes both `tryConstExpr` and speeds up `nim check`, because no error
outputs means we don't need cascading errors.

(cherry picked from commit efd603eb28)
2025-01-14 07:51:32 +01:00
Jake Leahy
520b16b81e Fix links for succ/pred/inc/dec in system docs (#24363)
Links for succ/pred/inc/dec were incorrect since they link to a symbol
with `int` as their second type.
Uses local referencing instead so that it links to the correct symbol.

Doesn't change the other links since they worked and doing the same
local referencing for `high`/`low` would've make them link to the group
of procs instead of the specific ones for ordinals

(cherry picked from commit 79ce3fe6b7)
2025-01-14 07:51:26 +01:00
ringabout
98403a06e7 fixes #18081; fixes #18079; fixes #18080; nested ref/deref'd types (#24335)
fixes #18081;
fixes https://github.com/nim-lang/Nim/issues/18080
fixes #18079

reverts https://github.com/nim-lang/Nim/pull/20738

It is probably more reasonable to use the type node from `nkObjConstr`
since it is barely changed unlike the external type, which is
susceptible to code transformation e.g. `addr(deref objconstr)`.

(cherry picked from commit aa90d00caf)
2025-01-14 07:50:41 +01:00
ringabout
4bdeddcac5 deprecate NewFinalize with the ref T finalizer (#24354)
pre-existing issues:

```nim
block:
  type
    FooObj = object
      data: int
    Foo = ref ref FooObj

  proc delete(self: Foo) =
    echo self.data

  var s: Foo
  new(s, delete)
```
it crashed with arc/orc in 1.6.x and 2.x.x

```nim
block:
  type
    Foo = ref int

  proc delete(self: Foo) =
    echo self[]

  var s: Foo
  new(s, delete)
```

The simple fix is to add a type restriction for the type `T` for arc/orc
versions
```nim
  proc new*[T: object](a: var ref T, finalizer: proc (x: T) {.nimcall.})
```

(cherry picked from commit 2af602a5c8)
2025-01-14 07:50:33 +01:00
metagn
9be3559ed2 consider calls as complex openarray assignment to iterator params (#24333)
fixes #13417, fixes #19703

When passing an expression to an `openarray` iterator parameter: If the
expression is a statement list (considered "complex"), it's assigned in
a non-deep-copying way to a temporary variable first, then this variable
is used as a parameter. If it's not a statement list, i.e. a call or a
symbol, the parameter is substituted directly with the given expression.
In the case of calls, this results in the call potentially being
executed more than once, or can cause redefined variables in the
codegen.

To fix this, calls are also considered as "complex" assignments to
openarrays, as long as the return type of the call is not `openarray` as
the generated assignment in that case has issues/is unimplemented
(caused a segfault [here in
datamancer](47ba4d81bf/src/datamancer/dataframe.nim (L1580))).

As for why creating a temporary isn't the default only with exceptions
for things like `nkSym`, the "non-deep-copying" way of assignment
apparently still causes arrays to be copied according to a comment in
the code. I'm not sure to what extent this is true: if it still happens
on ARC/ORC, if it happens for every array length, or if we can fix it by
passing arrays by reference. Otherwise, a more general way to assign to
openarrays might be needed, but I'm not sure if the compiler can easily
do this.

(cherry picked from commit d303c289fa)
2025-01-14 07:50:24 +01:00
ringabout
f2a9765014 fixes #23952; Size/Signedness issues with unordered enums (#24356)
fixes #23952

It reorders `type Foo = enum A, B = -1` to `type Foo = enum B = -1, A`
so that `firstOrd` etc. continue to work.

(cherry picked from commit 294b1566e7)
2025-01-14 07:50:17 +01:00
metagn
ac8c44e08d implement type bound operation RFC (#24315)
closes https://github.com/nim-lang/RFCs/issues/380, fixes #4773, fixes
#14729, fixes #16755, fixes #18150, fixes #22984, refs #11167 (only some
comments fixed), refs #12620 (needs tiny workaround)

The compiler gains a concept of root "nominal" types (i.e. objects,
enums, distincts, direct `Foo = ref object`s, generic versions of all of
these). Exported top-level routines in the same module as the nominal
types that their parameter types derive from (i.e. with
`var`/`sink`/`typedesc`/generic constraints) are considered attached to
the respective type, as the RFC states. This happens for every argument
regardless of placement.

When a call is overloaded and overload matching starts, for all
arguments in the call that already have a type, we add any operation
with the same name in the scope of the root nominal type of each
argument (if it exists) to the overload match. This also happens as
arguments gradually get typed after every overload match. This restricts
the considered overloads to ones attached to the given arguments, as
well as preventing `untyped` arguments from being forcefully typed due
to unrelated overloads. There are some caveats:

* If no overloads with a name are in scope, type bound ops are not
triggered, i.e. if `foo` is not declared, `foo(x)` will not consider a
type bound op for `x`.
* If overloads in scope do not have enough parameters up to the argument
which needs its type bound op considered, then type bound ops are also
not added. For example, if only `foo()` is in scope, `foo(x)` will not
consider a type bound op for `x`.

In the cases of "generic interfaces" like `hash`, `$`, `items` etc. this
is not really a problem since any code using it will have at least one
typed overload imported. For arbitrary versions of these though, as in
the test case for #12620, a workaround is to declare a temporary
"template" overload that never matches:

```nim
# neither have to be exported, just needed for any use of `foo`:
type Placeholder = object
proc foo(_: Placeholder) = discard
```

I don't know what a "proper" version of this could be, maybe something
to do with the new concepts.

Possible directions:

A limitation with the proposal is that parameters like `a: ref Foo` are
not attached to any type, even if `Foo` is nominal. Fixing this for just
`ptr`/`ref` would be a special case, parameters like `seq[Foo]` would
still not be attached to `Foo`. We could also skip any *structural* type
but this could produce more than one nominal type, i.e. `(Foo, Bar)`
(not that this is hard to implement, it just might be unexpected).

Converters do not use type bound ops, they still need to be in scope to
implicitly convert. But maybe they could also participate in the nominal
type consideration: if `Generic[T] = distinct T` has a converter to `T`,
both `Generic` and `T` can be considered as nominal roots.

The other restriction in the proposal, being in the same scope as the
nominal type, could maybe be worked around by explicitly attaching to
the type, i.e.: `proc foo(x: T) {.attach: T.}`, similar to class
extensions in newer OOP languages. The given type `T` needs to be
obtainable from the type of the given argument `x` however, i.e.
something like `proc foo(x: ref T) {.attach: T.}` doesn't work to fix
the `ref` issue since the compiler never obtains `T` from a given `ref
T` argument. Edit: Since the module is queried now, this is likely not
possible.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 2864830941)
2025-01-14 07:50:04 +01:00
metagn
850132d37c define flexible array without size for tcc & all C99 (#24355)
fixes #24236

Locally tested to generate a 100 KB file for TCC. Empty flexible array
size is standard in C99 but maybe some compilers still don't support it.
At the very least an array size of 1000000 should be rare.

(cherry picked from commit dd3a4b2aba)
2025-01-14 07:49:52 +01:00
ringabout
a1ee2ee566 adds noise to important_packages (#24352)
ref https://github.com/jangko/nim-noise

(cherry picked from commit b534f34e95)
2025-01-14 07:49:45 +01:00
ringabout
9306b5e917 std/nre now uses destructors instead of finializer (#24353)
Similar to changes in
bafb4f119c

(cherry picked from commit 3aaaed1acf)
2025-01-14 07:49:23 +01:00
ringabout
67a636bec8 closes #19984; adds a test case (#24349)
closes #19984

(cherry picked from commit baf3695c76)
2025-01-14 07:48:09 +01:00
metagn
9a6230ee5a wrap fields iterations in if true scope [backport] (#24343)
fixes #24338

When unrolling each iteration of a `fields` iterator, the compiler only
opens a new scope for semchecking, but doesn't generate a node that
signals to the codegen that a new scope should be created. This causes
issues for reused template instantiations that reuse variable symbols
between each iteration, which causes the codegen to generate multiple
declarations for them in the same scope (regardless of `inject` or
`gensym`). To fix this, we wrap the unrolled iterations in an `if true:
body` node, which both opens a new scope and doesn't interfere with
`break`.

(cherry picked from commit ca5df9ab25)
2025-01-14 07:48:01 +01:00
bptato
7e840e0164 Fix broken poll and nfds_t bindings (#24331)
This fixes several cases of the Nim binding of nfds_t being inconsistent
with the target platform signedness and/or size.

Additionally, it fixes poll's third argument (timeout) being set to Nim
"int" when it should have been "cint".

The former is the same issue that #23045 had attempted to fix, but
failed because it only considered Linux. (Also, it was only applied to
version 2.0, so the two branches now have incompatible versions of the
same bug.)

Notes:

* SVR4's original "unsigned long" definition is cloned by Linux and
Haiku. Nim got this right for Haiku and Linux-amd64, but it was wrong on
non-amd64 Linux.
* Zephyr does not have nfds_t, but simply uses (signed) "int". This was
already correctly reflected by Nim.
* OpenBSD poll.h uses "unsigned int", and other BSD derivatives follow
suit. This being the most commonly copied definition, the fallback case
now returns cuint. (This also seems to be correct for the OS X headers I
could find on the web.)
* This changes Nintendo Switch nfds_t to cuint from culong. It is
purportedly a FreeBSD derivative, so I *think* this is correct, but I
can't tell because I don't have access to the Nintendo Switch headers.

I have also moved the platform-specific Tnfds to posix.nim so that we
can reuse the fallback logic on all platforms. (e.g. specifying the size
in posix_linux_amd64 only to then use when defined(linux) in posix_other
seems redundant.)

(cherry picked from commit 67442471ae)
2025-01-14 07:47:40 +01:00
metagn
cd760b00c2 clean up stdlib with --jsbigint64 (#24255)
refs #6978, refs #6752, refs #21613, refs #24234

The `jsNoInt64`, `whenHasBigInt64`, `whenJsNoBigInt64` templates are
replaced with bool constants to use with `when`. Weird that I didn't do
this in the first place.

The `whenJsNoBigInt64` template was also slightly misleading. The first
branch was compiled for both no bigint64 on JS as well as on C/C++. It
seems only `trandom` depended on this by mistake.

The workaround for #6752 added in #6978 to `times` is also removed with
`--jsbigint64:on`, but #24233 was also encountered with this, so this PR
depends on #24234.

(cherry picked from commit 041098e882)
2025-01-14 07:47:30 +01:00
Jake Leahy
0cce80071b Fix quoted idents in ctags (#24317)
Running `ctags` on files with quoted symbols (e.g. `$`) would list \`
instead of the full ident. Issue was the result getting reassigned at
the end to a \` instead of appending

(cherry picked from commit 93c24fe1c5)
2025-01-14 07:47:19 +01:00
metagn
5aeabdac8f symmetric difference operation for sets via xor (#24286)
closes https://github.com/nim-lang/RFCs/issues/554

Adds a symmetric difference operation to the language bitset type. This
maps to a simple `xor` operation on the backend and thus is likely
faster than the current alternatives, namely `(a - b) + (b - a)` or `a +
b - a * b`. The compiler VM implementation of bitsets already
implemented this via `symdiffSets` but it was never used.

The standalone binary operation is added to `setutils`, named
`symmetricDifference` in line with [hash
sets](https://nim-lang.org/docs/sets.html#symmetricDifference%2CHashSet%5BA%5D%2CHashSet%5BA%5D).
An operator version `-+-` and an in-place version like `toggle` as
described in the RFC are also added, implemented as trivial sugar.

(cherry picked from commit ae9287c4f3)
2025-01-14 07:47:13 +01:00
metagn
2d678fa45c better errors for standalone explicit generic instantiations (#24276)
refs #8064, refs #24010

Error messages for standalone explicit generic instantiations are
revamped. Failing standalone explicit generic instantiations now only
error after overloading has finished and resolved to the default `[]`
magic (this means `[]` can now be overloaded for procs but this isn't
necessarily intentional, in #24010 it was documented that it isn't
possible). The error messages for failed instantiations are also no
longer a simple `cannot instantiate: foo` message, instead they now give
the same type mismatch error message as overloads with mismatching
explicit generic parameters.

This is now possible due to the changes in #24010 that delegate all
explicit generic proc instantiations to overload resolution. Old code
that worked around this is now removed. `maybeInstantiateGeneric` could
maybe also be removed in favor of just `explicitGenericSym`, the `result
== n` case is due to `explicitGenericInstError` which is only for niche
cases.

Also, to cause "ambiguous identifier" error messages when the explicit
instantiation is a symchoice and the expression context doesn't allow
symchoices, we semcheck the sym/symchoice created by
`explicitGenericSym` with the given expression flags.

#8064 isn't entirely fixed because the error message is still misleading
for the original case which does `values[1]`, as a consequence of
#12664.

(cherry picked from commit 0a058a6b8f)
2025-01-14 07:47:08 +01:00
ringabout
ee1c4de48a build documentation for repr_v2 (#24325)
(cherry picked from commit 0806fb0b6f)
2025-01-14 07:47:01 +01:00
ringabout
b3e02ef0c3 make PNode.typ a private field (#24326)
(cherry picked from commit 68b2e9eb6a)
2025-01-14 07:46:40 +01:00
Yuriy Glukhov
893c638485 Fixes #3824, fixes #19154, and hopefully #24094. Re-applies #23787. (#24316)
The first commit reverts the revert of #23787.
The second fixes lambdalifting in convolutedly nested
closures/closureiters. This is considered to be the reason of #24094,
though I can't tell for sure, as I was not able to reproduce #24094 for
complicated but irrelevant reasons. Therefore I ask @jmgomez, @metagn or
anyone who could reproduce it to try it again with this PR.

I would suggest this PR to not be squashed if possible, as the history
is already messy enough.

Some theory behind the lambdalifting fix:
- A closureiter that captures anything outside its body will always have
`:up` in its env. This property is now used as a trigger to lift any
proc that captures such a closureiter.
- Instantiating a closureiter involves filling out its `:up`, which was
previously done incorrectly. The fixed algorithm is to use "current" env
if it is the owner of the iter declaration, or traverse through `:up`s
of env param until the common ancestor is found.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 5fa96ef270)
2025-01-14 07:46:30 +01:00
Tomohiro
9f51b52f5f Document about noinline calling convention and exportcpp pragma in Nim manual (#24323)
It seems exportcpp was implemented in v1.0 but there is no documentation
about it excepts changelog.
`noinline` is used in many procedures in Nim code but there is also no
documentation about it.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit b8f6088ac0)
2025-01-14 07:46:21 +01:00
metagn
bcfb30a8be shallow fold prevention for addr, nkHiddenAddr (#24322)
fixes #24305, refs #23807

Since #23014 `nkHiddenAddr` is produced to fast assign array elements in
iterators. However the array access inside this `nkHiddenAddr` can get
folded at compile time, generating invalid code. In #23807, compile time
folding of regular `addr` expressions was changed to be prevented in
`transf` but `nkHiddenAddr` was not updated alongside it.

The method for preventing folding in `addr` in #23807 was also faulty,
it should only trigger on the immediate child node of the address rather
than all nodes nested inside it. This caused a regression as outlined in
[this
comment](https://github.com/nim-lang/Nim/pull/24322#issuecomment-2419560182).

To fix both issues, `addr` and `nkHiddenAddr` now both shallowly prevent
constant folding for their immediate children.

(cherry picked from commit 52cf7dfde0)
2025-01-14 07:46:13 +01:00
ringabout
7948e2f2c2 fixes #24319; move doesn't work well with (deref (var array)) (#24321)
fixes #24319

`byRefLoc` (`mapType`) requires the Loc `a` to have the right type.
Without `lfEnforceDeref`, it produces the wrong type for `deref (var
array)`, which may come from `mitems`.

(cherry picked from commit 0347536ff2)
2025-01-14 07:46:07 +01:00
ringabout
6b31400ade adds a getter/setter for owner (#24318)
(cherry picked from commit d0b6b9346e)
2025-01-14 07:45:59 +01:00
ringabout
a713aee682 fixes #18896; fixes #20886; importc types alias doesn't work with distinct (#24313)
fixes #18896
fixes #20886

```nim
type
  PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
    # import C's FILE* type; Nim will treat it as a new pointer type
```
This is an excerpt from the Nim manual. In the old Nim versions, it
produces a void pointer type instead of the `FILE*` type that should
have been generated. Because these C types tend to be opaque and adapt
to changes on different platforms. It might affect the portability of
Nim on various OS, i.e. `csource_v2` cannot build on the apline platform
because of `Time` relies on Nim types instead of the `time_t` type.

ref https://github.com/nim-lang/Nim/pull/18851

(cherry picked from commit 8be82c36c9)
2025-01-14 07:45:50 +01:00
ringabout
aa7e7f5f63 make owner a private field of PType (#24314)
follow up https://github.com/nim-lang/Nim/pull/24311

(cherry picked from commit a3aea224c9)
2025-01-14 07:45:39 +01:00
ringabout
8d7b3baf9f make owner a private field of PSym (#24311)
(cherry picked from commit 53460f312c)
2025-01-14 07:45:34 +01:00
ringabout
274cdba334 closes #19585; adds a test case for #21648 (#24310)
closes #19585
follow up #21648

(cherry picked from commit 922f7dfd71)
2025-01-14 07:45:25 +01:00
ringabout
a04dada93d fixes ci_generate produces unnecessary spaces on Windows (#24309)
follow up https://github.com/nim-lang/Nim/pull/17899

(cherry picked from commit 3e8f44b232)
2025-01-14 07:45:19 +01:00
ringabout
4d170ac586 fixes #24258; compiler crash on len of varargs[untyped] (#24307)
fixes #24258

It uses conditionals to guard against ill formed AST to produce better
error messages, rather than crashing

(cherry picked from commit 8b39b2df7d)
2025-01-14 07:39:32 +01:00
ringabout
e3a8d98626 define -d:nimHasDefaultFloatRoundtrip and enable datamancer (#24300)
ref https://github.com/SciNim/Datamancer/pull/73
ref https://github.com/SciNim/Datamancer/issues/72

(cherry picked from commit d4b9c147ab)
2025-01-14 07:37:18 +01:00
ringabout
41145210a8 templates/macros use no expected types when return types are specified (#24298)
fixes #24296
fixes #24295

Templates use `expectedType` for type inference. It's justified that
when templates don't have an actual return type, i.e., `untyped` etc.

When the return type of templates is specified, we should not infer the
type

```nim
template g(): string = ""

let c: cstring = g()
```
In this example, it is not reasonable to annotate the templates
expression with the `cstring` type before the `fitNode` check with its
specified return type.

(cherry picked from commit 80e6b35721)
2025-01-14 07:36:48 +01:00
Aryo
3c2b32aebe Expand enum example tut1.md (#24268)
I couldn't understand why there is "x" declaration. Comparison make it
easier to understand to people not familiar to enums.

(cherry picked from commit 1dbf614858)
2025-01-14 07:36:05 +01:00
metagn
613f1e94ae clean up testament retries, add some comments (#24294)
follows up #24279

`discard finishTest` was wrong if the test still had a `retries` option:
it would just ignore the result of the test. This is an unlikely mistake
but we safeguard against it by splitting `finishTest` into two, one that
completely ignores the retries option and `finishTestRetryable` which
has to be checked for a retry. This also makes the code look slightly
better.

(cherry picked from commit 2f7586c066)
2025-01-14 07:35:59 +01:00
metagn
660a9cecf0 add retries to testament, use it for GC tests (#24279)
Testament now retries a test by a specified amount if it fails in any
way other than an invalid spec. This is to deal with the flaky GC tests
on Windows CI that fail in many different ways, from the linker randomly
erroring, segfaults, etc.

Unfortunately I couldn't do this cleanly in testament's current code.
The proc `addResult`, which is the "final" proc called in a test run's
lifetime, is now wrapped in a proc `finishTest` that returns a bool
`true` if the test failed and has to be retried. This result is
propagated up from `cmpMsgs` and `compilerOutputTests` until it reaches
`testSpecHelper`, which handles these results by recursing if the test
has to be retried. Since calling `testSpecHelper` means "run this test
with one given configuration", this means every single matrix
option/target etc. receive an equal amount of retries each.

The result of `finishTest` is ignored in cases where it's known that it
won't be retried due to passing, being skipped, having an invalid spec
etc. It's also ignored in `testNimblePackages` because it's not
necessary for those specific tests yet and similar retry behavior is
already implemented for part of it.

This was a last resort for the flaky GC tests but they've been a problem
for years at this point, they give us more work to do and turn off
contributors. Ideally GC tests failing should mark as "needs review" in
the CI rather than "failed" but I don't know if Github supports
something like this.

(cherry picked from commit 720d0aee5c)
2025-01-14 07:35:50 +01:00
metagn
bffd2e0330 don't evaluate "cannot eval" errors with nim check (#24289)
fixes #24288, refs #23625

Since #23625 "cannot evaluate" errors during VM code generation are
"soft" errors with `nim check`, i.e. the code generation isn't halted
(except for the current proc which `return`s which can cause wrong
codegen) and the expression is still attempted to be evaluated. Now,
these errors signal to the VM that the current generated VM code cannot
be evaluated, and so instead of evaluating, an error node is returned.
This keeps the benefit of the "soft" errors without potentially crashing
the compiler on improperly generated VM code. Although maybe the
compiler might not be able to handle the generated error node in some
cases.

This fixes the chame example in #24288 but this is not tested in CI.
Presumably it or the compiler was doing something like `compiles()` on
code that can't run in the VM.

I would accept nicer ways of tracking non-evaluability than
`c.cannotEval = true` but I tried to keep it as harmless as possible.

(cherry picked from commit def1fea43a)
2025-01-14 07:35:43 +01:00
Andreas Rumpf
d357a2e9a5 modulegraphs: added a flag useful for gear2 (#24293)
(cherry picked from commit 25c068c070)
2025-01-14 07:35:36 +01:00
metagn
fca3504105 fix type of reconstructed kind field node in field checking analysis [backport] (#24290)
fixes #24021

The field checking for case object branches at some point generates a
negated set `contains` check for the object discriminator. For enum
types, this tries to generate a complement set and convert to a
`contains` check in that instead. It obtains this type from the type of
the element node in the `contains` check.

`buildProperFieldCheck` creates the element node by changing a field
access expression like `foo.z` into `foo.kind`. In order to do this, it
copies the node `foo.z` and sets the field name in the node to the
symbol `kind`. But when copying the node, the type of the original
`foo.z` is retained. This means that the complement is performed on the
type of the accessed field rather than the type of the discriminator,
which causes problems when the accessed field is also an enum.

To fix this, we properly set the type of the copied node to the type of
the kind field. An alternative is just to make a new node instead.

A lot of text for a single line change, I know, but this part of the
codebase could use more explanation.

(cherry picked from commit 1bebc236bd)
2025-01-14 07:35:24 +01:00
metagn
bf45efb1ea use /link before each library linker option on MSVC (#24291)
fixes #24087, refs https://forum.nim-lang.org/t/341, refs #14222, refs
#14221

The Nim compiler calls `cl` for linking as well as compilation. This
means that options to the linker have to be passed after a `/link`
argument. But the Nim compiler doesn't include this option normally,
because users may still want to pass non-linker options to `cl` at link
time.

To deal with this, a workaround is used: every single library link
option adds `/link` before it. The linker simply ignores extraneous
`/link` arguments and gives a warning instead, since it's an
unrecognized option to the linker. This is really hacky but otherwise we
need to separate linker arguments into arguments passed either to the
compiler or to the linker at link time, and this behavior wouldn't be
meaningful outside of MSVC.

I can't really test this manually but I did test that the linker ignores
`/link`. I also can't really do more than this, I don't really use MSVC
so I wouldn't know how to navigate it, or how people use it. Ideally
someone who knows more about/uses MSVC can give their input or take
over.

(cherry picked from commit 449106a5a4)
2025-01-14 07:35:19 +01:00
metagn
517a2fc275 add tables.getOrDefault param name change to changelog (#24271)
refs
https://github.com/nim-lang/Nim/issues/23587#issuecomment-2404406187

(cherry picked from commit bb0006598d)
2025-01-14 07:35:11 +01:00
Miran
4c56f9d675 make package testing faster (#24284)
There's no need to run benchmarks for cow- and sso-strings: they take 15
minutes each to run.

(cherry picked from commit f5cb39289b)
2025-01-14 07:34:56 +01:00
Juan M Gómez
de93f82d6e Bumps nimble to v0.16.2 (#24283)
(cherry picked from commit af23bc2941)
2025-01-14 07:34:39 +01:00
metagn
fa1819eb2d make linter use lineinfo to check originating package (#24270)
fixes #24269, refs #20095

Instead of checking the package of the *used sym* to determine whether a
stylecheck should trigger, we check the package of the lineinfo instead.
Before #20095 this checked for the current compilation context module
instead which caused issues with generic procs, but the lineinfo should
more closely match the AST.

I figured this might cause issues with includes etc but the foreign
package test specifically tests for an include and passes, so maybe the
package determining logic accounts for this already. This still might
not be the correct logic, I'm not too familiar with the package handling
in the compiler.

Package PRs, both merged:

- json_rpc: https://github.com/status-im/nim-json-rpc/pull/226
- json_serialization:
https://github.com/status-im/nim-json-serialization/pull/99

(cherry picked from commit aaf6c408c6)
2025-01-14 07:34:32 +01:00
metagn
0fde5a0cc2 use case instead of set of int in osproc (#24277)
As said in the warning after #21659, a set of ints defaults to
`set[range[0..65535]]` which is very large. So in osproc, a `case`
statement is used instead of an int set to check for an int being one of
2 values.

Also tested all of CI with the warning from #21659 as an error, this
seems to be the only remaining case in CI.

(cherry picked from commit 706985997e)
2025-01-14 07:34:23 +01:00
metagn
090139eb6f fix deref/addr pair deleting assignment location in C++ (#24280)
fixes #24274

The code in the `if` branch replaces the current destination `d` with a
new one. But the location `d` can be an assignment location, in which
case the provided expression isn't generated. To fix this, don't trigger
this code for when the location already exists. An alternative would be
to call `putIntoDest` in this case as is done below.

(cherry picked from commit 9c85f4fd07)
2025-01-14 07:34:03 +01:00
Miran
ee4bf757ea test more Status' packages, refs #24266 (#24275)
This adds several new Status packages to the CIs:

- confutils
- eth
- metrics
- nat_traversal
- toml_serialization

Other packages mentioned in https://github.com/nim-lang/Nim/issues/24266
are currently not ready to test with `devel` for various reasons.

----

This also enables `criterion`, and removes other packages that had been
in the `allowFailure` category — even without them we have plenty of
packages (145) that we test, there's no point in spending CI time on
them just to see them fail every time.
If/when the authors of those packages make them work with Nim devel, we
can re-introduce them then.

(cherry picked from commit 274762638f)
2025-01-14 07:33:52 +01:00
dlesnoff
b0b4b498c8 std/math: Add ^ overload for float32 and float64 (#20898)
I have added a new overload of `^` for float exponents.
Is two overloads for `float32` and `float64` better than just one
overload with `SomeFloat` type ?
I guess this would not work with `SomeFloat`, as `pow` is not defined
for `float`.

Another remark. Maybe we should catch exponents with 0.5 and call `sqrt`
instead ?

---------

Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com>
Co-authored-by: metagn <metagngn@gmail.com>
(cherry picked from commit e9a4d096ab)
2025-01-14 07:33:42 +01:00
metagn
d102571d78 don't allow instantiations resolving to generic body types (#24273)
fixes #24091, refs #24092

Any instantiations resolving to a generic body type now gives an error.
Due to #24092, this does not error in cases like matching against `type
M` in generics because generic body type symbols are just not
instantiated. But this prevents parameters with type `type M` from being
used, although there doesn't seem to be any code which does this. Just
in case such code exists, we still allow `typedesc` types resolving to
generic body types.

(cherry picked from commit 2f904535d0)
2025-01-14 07:33:27 +01:00
metagn
1f418de2cc fix workaround for protobuf not installing combparser fork in CI (#24267)
fixes CI after #24265, the CI passed in the original PR somehow

(cherry picked from commit 96d6eee9bc)
2025-01-14 07:33:20 +01:00
metagn
21bdc8ff0f remove conflicting default call in tables.getOrDefault (#24265)
fixes #23587

As explained in the issue, `getOrDefault` has a parameter named
`default` that can be a proc after generic instantiation. But the
parameter having a proc type [overrides all other
overloads](f73e03b132/compiler/semexprs.nim (L1203))
including the magic `system.default` overload and causes a compile error
if the proc doesn't match the normal use of `default`. To fix this, the
`result = default(B)` initializer call is removed because it's not
needed, `result` is always set in `getOrDefaultImpl` when a default
value is provided.

This is still a suspicious behavior of the compiler but `tables` working
has a higher priority.

(cherry picked from commit 67ea754b7f)
2025-01-14 07:33:10 +01:00
ringabout
9f7b664836 documentation and comments use HTTPS when possible (#24264)
(cherry picked from commit 95a7695810)
2025-01-14 07:33:01 +01:00
ringabout
b24f58183d fixes obsolete documentations about the JS backend (#24263)
ref https://github.com/nim-lang/Nim/pull/21849
ref https://github.com/nim-lang/Nim/pull/21613

(cherry picked from commit f73e03b132)
2025-01-14 07:32:55 +01:00
metagn
b8efee444c process non-language pragma nodes in generics (#24254)
fixes #18649, refs #24183

Same as in #24183 for templates, we now process pragma nodes in generics
so that macro symbols are captured and the pragma arguments are checked,
but ignoring language pragma keywords.

A difference is that we cannot process call nodes as is, we have to
process their children individually so that the early untyped
macro/template instantiation in generics does not kick in.

(cherry picked from commit d72b848d17)
2025-01-14 07:32:47 +01:00
Tomohiro
336549c49d Change how to multiply 1.5 to ints to reduce overflow (#24257)
(cherry picked from commit d6633ae1da)
2025-01-14 07:32:40 +01:00
ringabout
2d4e1f981e improves the 2.2.0 changelog (#24256)
(cherry picked from commit 30e552e3d3)
2025-01-14 07:32:27 +01:00
metagn
13110fc5d3 give int literals matched type on generic match (#24234)
fixes #24233

Integer literals with type `int` can match `int64` with a generic match.
Normally this would generate an conversion via `isFromIntLit`, but when
it matches with a generic match (`isGeneric`) the node is left alone and
continues to have type `int` (related to #4858, but separate; since
`isFromIntLit > isGeneric` it doesn't propagate). This did not cause
problems on the C backend up to this point because either the compiler
generated a cast when generating the C code or it was implicitly casted
in the C code itself. On the JS backend however, we need to generate
`int64` and `int` values differently, so we copy the integer literal and
give it the matched type now instead.

This is somewhat risky even if CI passes but it's required to make the
times module work without [this
workaround](7dfadb8b4e/lib/pure/times.nim (L219-L238))
on `--jsbigint64:on` (the default).

CI exposed an issue: When matching an int literal to a generic parameter
in a generic instantiation, the literal is only treated like a value if
it has `int literal` type, but if it has the type `int`, it gets
transformed into literally the type `int` (#12664, #13906), which breaks
the tests t14193 and t12938. To deal with this, we don't give it the
type `int` if we are in a generic instantiation and preserve the `int
literal` type.

(cherry picked from commit c73eedfe6e)
2025-01-14 07:32:18 +01:00
metagn
700ca2eb60 process non-language pragma nodes in templates (#24183)
fixes #24186

When encountering pragma nodes in templates, if it's a language pragma,
we don't process the name, and only any values if they exist. If it's
not a language pragma, we process the full node. Previously only the
values of colon expressions were processed.

To make this simpler, `whichPragma` is patched to consider bracketed
hint/warning etc pragmas like `{.hint[HintName]: off.}` as being a
pragma of kind `wHint` rather than an invalid pragma which would have to
be checked separately. From looking at the uses of `whichPragma` this
doesn't seem like it would cause problems.

Generics have [the same
problem](a27542195c/compiler/semgnrc.nim (L619))
(causing #18649), but to make it work we need to make sure the
templates/macros don't get evaluated or get evaluated correctly (i.e.
passing the proc node as the final argument), either with #23094 or by
completely disabling template/macro evaluation when processing the
pragma node, which would also cover `{.pragma.}` templates.

(cherry picked from commit 911cef1621)
2025-01-14 07:32:12 +01:00
metagn
5945ad41a1 reset inTypeofContext in generic instantiations (#24229)
fixes #24228, refs #22022

As described in
https://github.com/nim-lang/Nim/issues/24228#issuecomment-2392462221,
instantiating generic routines inside `typeof` causes all code inside to
be treated as being in a typeof context, and thus preventing compile
time proc folding, causing issues when code is generated for the
instantiated routine. Now, instantiated generic procs are treated as
never being inside a `typeof` context.

This is probably an arbitrary special case and more issues with the
`typeof` behavior from #22022 are likely. Ideally this behavior would be
removed but it's necessary to accomodate the current [proc `declval` in
the package `stew`](https://github.com/status-im/nim-stew/pull/190), at
least without changes to `compileTime` that would either break other
code (making it not eagerly fold by default) or still require a change
in stew (adding an option to disable the eager folding).

Alternatively we could also make the eager folding opt-in only for
generic compileTime procs so that #22022 breaks nothing whatsoever, but
a universal solution would be better. Edit: Done in #24230 via
experimental switch

(cherry picked from commit ea9811a4d2)
2025-01-14 07:31:57 +01:00
Andreas Rumpf
7f113dc875 exports more helpers that are needed by nif-gear2 (#24247)
(cherry picked from commit 7f2e6a1359)
2025-01-14 07:31:51 +01:00
ringabout
e13f86a596 enable nimExperimentalLinenoiseExtra (#24227)
follow up https://github.com/nim-lang/Nim/pull/16977

it was added in 1.6.0

(cherry picked from commit a65501325c)
2025-01-14 07:31:40 +01:00
metagn
6c96892d5e refactor to make sigmatch use LayeredIdTable for bindings (#24216)
split from #24198

This is a required refactor for the only good solution I've been able to
think of for #4858 etc. Explanation:

---

`sigmatch` currently [disables
bindings](d6a71a1067/compiler/sigmatch.nim (L1956))
(except for binding to other generic parameters) when matching against
constraints of generic parameters. This is so when the constraint is a
general metatype like `seq`, the type matching will not treat all
following uses of `seq` as the type matched against that generic
parameter.

However to solve #4858 etc we need to bind `or` types with a conversion
match to the type they are supposed to be converted to (i.e. matching
`int literal(123)` against `int8 | int16` should bind `int8`[^1], not
`int`). The generic parameter constraint binding needs some way to keep
track of this so that matching `int literal(123)` against `T: int8 |
int16` also binds `T` to `int8`[^1].

The only good way to do this IMO is to generate a new "binding context"
when matching against constraints, then binding the generic param to
what the constraint was bound to in that context (in #24198 this is
restricted to just `or` types & concrete types with convertible matches,
it doesn't work in general).

---

`semtypinst` already does something similar for bindings of generic
invocations using `LayeredIdTable`, so `LayeredIdTable` is now split
into its own module and used in `sigmatch` for type bindings as well,
rather than a single-layer `TypeMapping`. Other modules which act on
`sigmatch`'s binding map are also updated to use this type instead.

The type is also made into an `object` type rather than a `ref object`
to reduce the pointer indirection when embedding it inside
`TCandidate`/`TReplTypeVars`, but only on arc/orc since there are some
weird aliasing bugs on refc/markAndSweep that cause a segfault when
setting a layer to its previous layer. If we want we can also just
remove the conditional compilation altogether and always use `ref
object` at the cost of some performance.

[^1]: `int8` binding here and not `int16` might seem weird, since they
match equally well. But we need to resolve the ambiguity here, in #24012
I tested disallowing ambiguities like this and it broke many packages
that tries to match int literals to things like `int16 | uint16` or
`int8 | int16`. Instead of making these packages stop working I think
it's better we resolve the ambiguity with a rule like "the earliest `or`
branch with the best match, matches". This is the rule used in #24198.

(cherry picked from commit cad8726907)
2025-01-14 07:31:33 +01:00
ringabout
dd0cc389bb -d:nimPreviewFloatRoundtrip becomes the default (#24217)
(cherry picked from commit aa605da92a)
2025-01-14 07:31:27 +01:00
metagn
75e50f804a delay markUsed for converters until call is resolved (#24243)
fixes #24241

(cherry picked from commit 09043f409f)
2025-01-14 07:31:22 +01:00
metagn
599f1ad6b3 make new concepts match themselves (#24244)
fixes #22839

(cherry picked from commit 9e30b39412)
2025-01-14 07:31:14 +01:00
metagn
d991600a00 update CI to macos 13 (#24157)
Followup to #24154, packages aren't ready for macos 14 (M1/ARM CPU) yet
and it seems to be preview on azure, so upgrade to macos 13 for now.

Macos 12 gives a warning:

```
You are using macOS 12.
We (and Apple) do not provide support for this old version.
It is expected behaviour that some formulae will fail to build in this old version.
It is expected behaviour that Homebrew will be buggy and slow.
Do not create any issues about this on Homebrew's GitHub repositories.
Do not create any issues even if you think this message is unrelated.
Any opened issues will be immediately closed without response.
Do not ask for help from Homebrew or its maintainers on social media.
You may ask for help in Homebrew's discussions but are unlikely to receive a response.
Try to figure out the problem yourself and submit a fix as a pull request.
We will review it but may or may not accept it.
```

(cherry picked from commit 4a63186cda)
2025-01-14 07:30:58 +01:00
tersec
b873eaedf5 update minimum recommended gcc version and fix manual typos (#24240)
ref https://github.com/nim-lang/Nim/issues/24235

(cherry picked from commit 782b75cc08)
2025-01-14 07:30:43 +01:00
Alex
39a6106e8b Update sequtils.nim authors (#24238)
Hello, I am the original developer credited in this file.

I no longer wish to be credited for the it so I've updated it to say
"Nim Contributors".

This is a quick edit from the GitHub Web UI so let me know if I need to
make any changes to get this merged.

Thank you.

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
(cherry picked from commit f420a5a273)
2025-01-14 07:30:33 +01:00
metagn
e262d9506d stricter set type match, implicit conversion for literals (#24176)
fixes #18396, fixes #20142

Set types with base types matching less than a generic match (so
subrange matches, conversion matches, int conversion matches) are now
considered mismatching, as their representation is different on the
backends (except VM and JS), causing codegen issues. An exception is
granted for set literal types, which now implicitly convert each element
to the matched base type, so things like `s == {'a', 'b'}` are still
possible where `s` is `set[range['a'..'z']]`. Also every conversion
match in this case is unified under the normal "conversion" match, so a
literal doesn't match one set type better than the other, unless it's
equal.

However `{'a', 'b'} == s` or `{'a', 'b'} - s` etc is now not possible.
when it used to work in the VM. So this is somewhat breaking, and needs
a changelog entry.

(cherry picked from commit 7dfadb8b4e)
2025-01-14 07:30:25 +01:00
metagn
ddc7f35e05 don't typecheck untyped + allow void typed template param default values (#24219)
Previously, the compiler never differentiated between `untyped`/`typed`
argument default values and other types, it considered any parameter
with a type as typed and called `semExprWithType`, which both
typechecked it and disallowed `void` expressions. Now, we perform no
typechecking at all on `untyped` template param default values, and call
`semExpr` instead for `typed` params, which allows expressions with
`void` type.

(cherry picked from commit 4eed341ba5)
2025-01-14 07:30:19 +01:00
metagn
f70a17f885 don't construct array type for already typed nkBracket node (#24224)
fixes #23010, split from #24195

When resemming bracket nodes, the compiler currently unconditionally
makes a new node with an array type based on the node. However the VM
can generate bracket nodes with `seq` types, which this erases. To fix
this, if a bracket node already has a type, we still resem the bracket
node, but don't construct a new type for it, instead using the type of
the original node.

A version of this was rejected that didn't resem the node at all if it
was typed, but I can't find it. The difference with this one is that the
individual elements are still resemmed.

This should fix the break caused by #24184 so we could redo it after
this PR but it might still have issues, not to mention the related
pre-existing issues like #22793, #12559 etc.

(cherry picked from commit d98ef312f0)
2025-01-14 07:30:06 +01:00
Miran
7a79f465fa bump NimVersion to 2.2.1 (#24215)
(cherry picked from commit d6a71a1067)
2025-01-14 07:28:14 +01:00
ringabout
b6450a98ea improve error messages for illegalCapture (#24214)
ref https://forum.nim-lang.org/t/12536

Use a general recommendation to avoid some weird error messages like
`<ref ref var Test>` etc.

(cherry picked from commit f7cb0322c2)
2025-01-14 07:28:10 +01:00
385 changed files with 6192 additions and 2838 deletions

View File

@@ -41,7 +41,7 @@ jobs:
target: [linux, windows, osx]
include:
- target: linux
os: ubuntu-20.04
os: ubuntu-22.04
- target: windows
os: windows-2019
- target: osx

View File

@@ -1,76 +0,0 @@
name: GCC 14
on:
pull_request:
push:
branches:
- 'devel'
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04]
cpu: [amd64]
name: '${{ matrix.os }}'
runs-on: ${{ matrix.os }}
timeout-minutes: 60 # refs bug #18178
steps:
- name: 'Checkout'
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: 'Install node.js 20.x'
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
run: |
sudo apt update -qq
sudo apt remove needrestart
DEBIAN_FRONTEND='noninteractive' \
sudo apt install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
valgrind libc6-dbg libblas-dev xorg-dev
- name: 'Install dependencies (Linux amd64 gcc 14)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
run: |
sudo add-apt-repository universe
sudo apt update -qq
sudo apt install -y gcc-14 g++-14 libpcre3 liblapack-dev
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
- name: 'Install dependencies (macOS)'
if: runner.os == 'macOS'
run: brew install boehmgc make sfml gtk+3
- name: 'Install dependencies (Windows)'
if: runner.os == 'Windows'
shell: bash
run: |
set -e
. ci/funs.sh
nimInternalInstallDepsWindows
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
- name: 'Add build binaries to PATH'
shell: bash
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
- name: 'NIM_TESTAMENT_DISABLE_SSL'
shell: bash
run: echo "NIM_TESTAMENT_DISABLE_SSL=1" >> $GITHUB_ENV
- name: 'System information'
shell: bash
run: . ci/funs.sh && nimCiSystemInfo
- name: 'Build csourcesAny'
shell: bash
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
- name: 'koch, Run CI'
shell: bash
run: . ci/funs.sh && nimInternalBuildKochAndRunCI

View File

@@ -4,6 +4,7 @@ on:
push:
branches:
- 'devel'
- 'version-2-2'
- 'version-2-0'
- 'version-1-6'
- 'version-1-2'
@@ -17,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-13]
os: [ubuntu-22.04, macos-13]
cpu: [amd64]
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'

View File

@@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04]
os: [ubuntu-22.04]
cpu: [amd64]
name: '${{ matrix.os }}'
runs-on: ${{ matrix.os }}
@@ -21,10 +21,10 @@ jobs:
with:
fetch-depth: 2
- name: 'Install node.js 20.x'
- name: 'Install node.js'
uses: actions/setup-node@v4
with:
node-version: '20.x'
node-version: ''
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
@@ -34,17 +34,6 @@ jobs:
sudo apt-fast install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
valgrind libc6-dbg libblas-dev xorg-dev
- name: 'Install dependencies (macOS)'
if: runner.os == 'macOS'
run: brew install boehmgc make sfml gtk+3
- name: 'Install dependencies (Windows)'
if: runner.os == 'Windows'
shell: bash
run: |
set -e
. ci/funs.sh
nimInternalInstallDepsWindows
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
- name: 'Add build binaries to PATH'
shell: bash

View File

@@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
Linux_amd64:
vmImage: 'ubuntu-20.04'
vmImage: 'ubuntu-24.04'
CPU: amd64
# regularly breaks, refs bug #17325
# Linux_i386:
@@ -80,10 +80,12 @@ jobs:
- bash: |
set -e
. ci/funs.sh
echo_run sudo apt-fast update -qq
echo_run sudo add-apt-repository universe
echo_run sudo apt-get update -qq
DEBIAN_FRONTEND='noninteractive' \
echo_run sudo apt-fast install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
echo_run sudo apt-get install --no-install-recommends -yq \
gcc-14 g++-14 libpcre3 liblapack-dev libpcre3 liblapack-dev libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
echo_run sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
displayName: 'Install dependencies (amd64 Linux)'
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
@@ -100,15 +102,16 @@ jobs:
Pin-Priority: 1001
EOF
# echo_run sudo apt-fast update -qq
echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
# echo_run sudo apt-get update -qq
echo_run sudo apt-get update -qq || echo "failed, see bug #17343"
# `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
# `could not load: libffi.so` during dynamic loading.
DEBIAN_FRONTEND='noninteractive' \
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
echo_run sudo apt-get install --no-install-recommends --allow-downgrades -yq \
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
cat << EOF > bin/gcc
#!/bin/bash

View File

@@ -12,6 +12,13 @@ rounding guarantees (via the
avoid conflicts with `system.default`, so named argument usage for this
parameter like `getOrDefault(..., default = ...)` will have to be changed.
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
- Unknown warnings and hints now gives warnings `warnUnknownNotes` instead of
errors.
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
## Standard library additions and changes
[//]: # "Additions:"
@@ -24,6 +31,40 @@ rounding guarantees (via the
## Language changes
- An experimental option `--experimental:typeBoundOps` has been added that
implements the RFC https://github.com/nim-lang/RFCs/issues/380.
This makes the behavior of interfaces like `hash`, `$`, `==` etc. more
reliable for nominal types across indirect/restricted imports.
```nim
# objs.nim
import std/hashes
type
Obj* = object
x*, y*: int
z*: string # to be ignored for equality
proc `==`*(a, b: Obj): bool =
a.x == b.x and a.y == b.y
proc hash*(a: Obj): Hash =
$!(hash(a.x) &! hash(a.y))
```
```nim
# main.nim
{.experimental: "typeBoundOps".}
from objs import Obj # objs.hash, objs.`==` not imported
import std/tables
var t: Table[Obj, int]
t[Obj(x: 3, y: 4, z: "debug")] = 34
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
```
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#typeminusbound-overloads)
for more information.
## Compiler changes

View File

@@ -53,7 +53,7 @@
- [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
now produces a `CountTable` with values correspoding to the number of occurrences
now produces a `CountTable` with values corresponding to the number of occurrences
of the key in the input. It used to produce a table with all values set to `1`.
Counting occurrences in a sequence used to be:
@@ -339,7 +339,7 @@ for i in a..b:
- Fixed "ReraiseError when using try/except within finally block"
([#5871](https://github.com/nim-lang/Nim/issues/5871))
- Fixed "Range type inference leads to counter-intuitive behvaiour"
- Fixed "Range type inference leads to counter-intuitive behaviour"
([#5854](https://github.com/nim-lang/Nim/issues/5854))
- Fixed "JSON % operator can fail in extern procs with dynamic types"
([#6385](https://github.com/nim-lang/Nim/issues/6385))
@@ -403,7 +403,7 @@ for i in a..b:
([#6589](https://github.com/nim-lang/Nim/issues/6589))
- Fixed "Generated c code calls function twice"
([#6292](https://github.com/nim-lang/Nim/issues/6292))
- Fixed "Range type inference leads to counter-intuitive behvaiour"
- Fixed "Range type inference leads to counter-intuitive behaviour"
([#5854](https://github.com/nim-lang/Nim/issues/5854))
- Fixed "New backward indexing is too limited"
([#6631](https://github.com/nim-lang/Nim/issues/6631))

View File

@@ -22,7 +22,7 @@
- We removed `unicode.Rune16` without any deprecation period as the name
was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
and we didn't find any usages of it in the wild. If you still need it, add this
and we didn't find any usage of it in the wild. If you still need it, add this
piece of code to your project:
```nim
type

View File

@@ -11,7 +11,7 @@
* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
* Fix how `relativePath` handle case sensitiviy
* Fix how `relativePath` handles case sensitivity
* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
* VM: no special casing for big endian machines
@@ -43,7 +43,7 @@
* threadpool: fix link in docs (#12258)
* Fix spellings (#12277)
* fix #12278, don't expose internal PCRE documentation
* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
* Fixed "Documentation of quitprocs is wrong" [#12279](https://github.com/nim-lang/Nim/issues/12279)
* Fix typo in docs
* Fix reference to parseSpec proc in readme
* [doc/tut1] removed discard discussion in comments

View File

@@ -169,7 +169,7 @@ echo f
- The Nim compiler now supports a new pragma called ``.localPassc`` to
pass specific compiler options to the C(++) backend for the C(++) file
that was produced from the current Nim module.
- The compiler now inferes "sink parameters". To disable this for a specific routine,
- The compiler now infers "sink parameters". To disable this for a specific routine,
annotate it with `.nosinks`. To disable it for a section of code, use
`{.push sinkInference: off.}`...`{.pop.}`.
- The compiler now supports a new switch `--panics:on` that turns runtime
@@ -261,7 +261,7 @@ echo f
([#12812](https://github.com/nim-lang/Nim/issues/12812))
- Fixed "Produce static/const initializations for variables when possible"
([#12216](https://github.com/nim-lang/Nim/issues/12216))
- Fixed "Assigning descriminator field leads to internal assert with --gc:destructors"
- Fixed "Assigning discriminator field leads to internal assert with --gc:destructors"
([#12821](https://github.com/nim-lang/Nim/issues/12821))
- Fixed "nimsuggest `use` command does not return all instances of symbol"
([#12832](https://github.com/nim-lang/Nim/issues/12832))

View File

@@ -283,7 +283,7 @@ The definition of `"strictFuncs"` was changed.
The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
The new definition is: "A store to a ref/ptr deref is forbidden."
This new definition is much easier to understand, the price is some expressitivity. The following code used to be
This new definition is much easier to understand, the price is some expressiveness. The following code used to be
accepted:
```nim

View File

@@ -279,7 +279,7 @@ const
GcTypeKinds* = {tyRef, tySequence, tyString}
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
tyUserTypeClass, tyUserTypeClassInst,
tyUserTypeClass, tyUserTypeClassInst, tyConcept,
tyAnd, tyOr, tyNot, tyAnything}
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
@@ -447,6 +447,8 @@ const
tfReturnsNew* = tfInheritable
tfNonConstExpr* = tfExplicitCallConv
## tyFromExpr where the expression shouldn't be evaluated as a static value
tfGenericHasDestructor* = tfExplicitCallConv
## tyGenericBody where an instance has a generated destructor
skError* = skUnknown
var
@@ -1645,7 +1647,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
if mask != {} and propagateHasAsgn:
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
if o2.kind in {tyTuple, tyObject, tyArray,
tySequence, tySet, tyDistinct}:
tySequence, tyString, tySet, tyDistinct}:
o2.flags.incl mask
owner.flags.incl mask

121
compiler/cbuilder.nim Normal file
View 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")

View File

@@ -1,6 +0,0 @@
type
Snippet = string
Builder = string
template newBuilder(s: string): Builder =
s

View File

@@ -1,349 +0,0 @@
type VarKind = enum
Local
Global
Threadvar
Const
AlwaysConst ## const even on C++
proc addVarHeader(builder: var Builder, kind: VarKind) =
## adds modifiers for given var kind:
## Local has no modifier
## Global has `static` modifier
## Const has `static NIM_CONST` modifier
## AlwaysConst has `static const` modifier (NIM_CONST is no-op on C++)
## Threadvar is unimplemented
case kind
of Local: discard
of Global:
builder.add("static ")
of Const:
builder.add("static NIM_CONST ")
of AlwaysConst:
builder.add("static const ")
of Threadvar:
doAssert false, "unimplemented"
proc addVar(builder: var Builder, kind: VarKind = Local, name: string, typ: Snippet, initializer: Snippet = "") =
## adds a variable declaration to the builder
builder.addVarHeader(kind)
builder.add(typ)
builder.add(" ")
builder.add(name)
if initializer.len != 0:
builder.add(" = ")
builder.add(initializer)
builder.add(";\n")
template addVarWithType(builder: var Builder, kind: VarKind = Local, name: string, body: typed) =
## adds a variable declaration to the builder, with the `body` building the type
builder.addVarHeader(kind)
body
builder.add(" ")
builder.add(name)
builder.add(";\n")
template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
typeBody, initializerBody: typed) =
## adds a variable declaration to the builder, with `typeBody` building the type, and
## `initializerBody` building the initializer. initializer must be provided
builder.addVarHeader(kind)
typeBody
builder.add(" ")
builder.add(name)
builder.add(" = ")
initializerBody
builder.add(";\n")
proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
## adds an array variable declaration to the builder
builder.addVarHeader(kind)
builder.add(elementType)
builder.add(" ")
builder.add(name)
builder.add("[")
builder.addInt(len)
builder.add("]")
if initializer.len != 0:
builder.add(" = ")
builder.add(initializer)
builder.add(";\n")
template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
## adds an array variable declaration to the builder with the initializer built according to `body`
builder.addVarHeader(kind)
builder.add(elementType)
builder.add(" ")
builder.add(name)
builder.add("[")
builder.addInt(len)
builder.add("] = ")
body
builder.add(";\n")
template addTypedef(builder: var Builder, name: string, typeBody: typed) =
## adds a typedef declaration to the builder with name `name` and type as
## built in `typeBody`
builder.add("typedef ")
typeBody
builder.add(" ")
builder.add(name)
builder.add(";\n")
template addArrayTypedef(builder: var Builder, name: string, len: int, typeBody: typed) =
## adds an array typedef declaration to the builder with name `name`,
## length `len`, and element type as built in `typeBody`
builder.add("typedef ")
typeBody
builder.add(" ")
builder.add(name)
builder.add("[")
builder.addInt(len)
builder.add("];\n")
type
StructInitializerKind = enum
siOrderedStruct ## struct constructor, but without named fields on C
siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer
siArray ## array constructor
siWrapper ## wrapper for a single field, generates it verbatim
StructInitializer = object
## context for building struct initializers, i.e. `{ field1, field2 }`
kind: StructInitializerKind
## if true, fields will not be named, instead values are placed in order
needsComma: bool
proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer =
## starts building a struct initializer, i.e. braced initializer list
result = StructInitializer(kind: kind, needsComma: false)
if kind != siWrapper:
builder.add("{")
template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) =
## adds a field to a struct initializer, with the value built in `valueBody`
if constr.needsComma:
assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields"
builder.add(", ")
else:
constr.needsComma = true
case constr.kind
of siArray, siWrapper:
# no name, can just add value
valueBody
of siOrderedStruct:
# no name, can just add value on C
assert name.len != 0, "name has to be given for struct initializer field"
valueBody
of siNamedStruct:
assert name.len != 0, "name has to be given for struct initializer field"
builder.add(".")
builder.add(name)
builder.add(" = ")
valueBody
proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
## finishes building a struct initializer
if constr.kind != siWrapper:
builder.add("}")
template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) =
## builds a struct initializer, i.e. `{ field1, field2 }`
## a `var StructInitializer` must be declared and passed as a parameter so
## that it can be used with `addField`
constr = builder.initStructInitializer(kind)
body
builder.finishStructInitializer(constr)
proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
## adds a field inside a struct/union type
obj.add('\t')
obj.add(typ)
obj.add(" ")
obj.add(name)
if isFlexArray:
obj.add("[SEQ_DECL_SIZE]")
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initializer: Snippet = "") =
## adds an array field inside a struct/union type
obj.add('\t')
obj.add(elementType)
obj.add(" ")
obj.add(name)
obj.add("[")
obj.addInt(len)
obj.add("]")
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
## adds an field inside a struct/union type, based on an `skField` symbol
obj.add('\t')
if field.alignment > 0:
obj.add("NIM_ALIGN(")
obj.addInt(field.alignment)
obj.add(") ")
obj.add(typ)
if sfNoalias in field.flags:
obj.add(" NIM_NOALIAS")
obj.add(" ")
obj.add(name)
if isFlexArray:
obj.add("[SEQ_DECL_SIZE]")
if field.bitsize != 0:
obj.add(":")
obj.addInt(field.bitsize)
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
type
BaseClassKind = enum
## denotes how and whether or not the base class/RTTI should be stored
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
StructBuilderInfo = object
## context for building `struct` types
baseKind: BaseClassKind
named: bool
preFieldsLen: int
proc structOrUnion(t: PType): Snippet =
let t = t.skipTypes({tyAlias, tySink})
if tfUnion in t.flags: "union"
else: "struct"
proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
obj.add("struct")
if result.named:
obj.add(" ")
obj.add(name)
if baseType.len != 0:
if m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
if result.baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
result.preFieldsLen = obj.len
if result.baseKind == bcSupField:
obj.addField(name = "Sup", typ = baseType)
proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
if info.baseKind == bcNone and info.preFieldsLen == obj.len:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = "char")
if info.named:
obj.add("};\n")
else:
obj.add("}")
template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
## builds a struct type not based on a Nim type with fields according to `body`,
## `name` can be empty to build as a type expression and not a statement
let info = startSimpleStruct(obj, m, name, baseType)
body
finishSimpleStruct(obj, m, info)
proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
if tfPacked in t.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add(structOrUnion(t))
obj.add(" __attribute__((__packed__))")
else:
obj.add("#pragma pack(push, 1)\n")
obj.add(structOrUnion(t))
else:
obj.add(structOrUnion(t))
if result.named:
obj.add(" ")
obj.add(name)
if t.kind == tyObject:
if t.baseClass == nil:
if lacksMTypeField(t):
result.baseKind = bcNone
elif optTinyRtti in m.config.globalOptions:
result.baseKind = bcNoneTinyRtti
else:
result.baseKind = bcNoneRtti
elif m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
elif baseType.len != 0:
if m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
if result.baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
result.preFieldsLen = obj.len
case result.baseKind
of bcNone:
# rest of the options add a field or don't need it due to inheritance,
# we need to add the dummy field for uncheckedarray ahead of time
# so that it remains trailing
if t.itemId notin m.g.graph.memberProcsPerType and
t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
# only consists of flexible array field, add *initial* dummy field
obj.addField(name = "dummy", typ = "char")
of bcCppInherit: discard
of bcNoneRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
of bcNoneTinyRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
of bcSupField:
obj.addField(name = "Sup", typ = baseType)
proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
if info.baseKind == bcNone and info.preFieldsLen == obj.len and
t.itemId notin m.g.graph.memberProcsPerType:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = "char")
if info.named:
obj.add("};\n")
else:
obj.add("}")
if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
obj.add("#pragma pack(pop)\n")
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
## builds a struct type directly based on `typ` with fields according to `body`,
## `name` can be empty to build as a type expression and not a statement
let info = startStruct(obj, m, typ, name, baseType)
body
finishStruct(obj, m, typ, info)
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
## adds a field with a `struct { ... }` type, building the fields 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) =
## adds an anonymous union i.e. `union { ... };` with fields according to `body`
obj.add "union{\n"
body
obj.add("};\n")

View File

@@ -1,26 +0,0 @@
# XXX make complex ones like bitOr use builder instead
# XXX add stuff like NI, NIM_NIL as constants
proc ptrType(t: Snippet): Snippet =
t & "*"
const
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
"N_STDCALL", "N_CDECL", "N_SAFECALL",
"N_SYSCALL", # this is probably not correct for all platforms,
# but one can #define it to what one wants
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
"N_NOCONV" #ccMember is N_NOCONV
]
proc procPtrType(conv: TCallingConvention, rettype: Snippet, name: string): Snippet =
CallingConvToStr[conv] & "_PTR(" & rettype & ", " & name & ")"
proc cCast(typ, value: Snippet): Snippet =
"((" & typ & ") " & value & ")"
proc cAddr(value: Snippet): Snippet =
"&" & value
proc bitOr(a, b: Snippet): Snippet =
"(" & a & " | " & b & ")"

View File

@@ -135,7 +135,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
d.snippet = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone and p.splitDecls == 0:
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
d = getTempCpp(p, typ.returnType, pl)
else:
if d.k == locNone: d = getTemp(p, typ.returnType)

View File

@@ -13,7 +13,7 @@ when defined(nimCompilerStacktraceHints):
import std/stackframes
proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
result: var Builder; init: var StructInitializer;
result: var Rope; count: var int;
isConst: bool, info: TLineInfo)
# -------------------------- constant expressions ------------------------
@@ -1572,6 +1572,10 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
discard getTypeDesc(p.module, t)
let ty = getUniqueType(t)
for i in 1..<e.len:
if nfPreventCg in e[i].flags:
# this is an object constructor node generated by the VM and
# this field is in an inactive case branch, don't generate assignment
continue
var check: PNode = nil
if e[i].len == 3 and optFieldCheck in p.options:
check = e[i][2]
@@ -2155,6 +2159,8 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
elif etyp.kind == tyBool and srcTyp.kind in IntegralTypes:
putIntoDest(p, d, e, "(($1) != 0)" % [rdCharLoc(a)], a.storage)
elif etyp.kind == tyProc and srcTyp.kind == tyProc and sameBackendType(etyp, srcTyp):
expr(p, e[1], d)
else:
if etyp.kind == tyPtr:
# generates the definition of structs for casts like cast[ptr object](addr x)[]
@@ -3074,16 +3080,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkObjConstr: genObjConstr(p, n, d)
of nkCast: genCast(p, n, d)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d)
of nkHiddenAddr:
if n[0].kind == nkDerefExpr:
# addr ( deref ( x )) --> x
var x = n[0][0]
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
x.typ() = n.typ
expr(p, x, d)
return
genAddr(p, n, d)
of nkAddr: genAddr(p, n, d)
of nkAddr, nkHiddenAddr: genAddr(p, n, d)
of nkBracketExpr: genBracketExpr(p, n, d)
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
of nkDotExpr: genRecordField(p, n, d)
@@ -3114,6 +3111,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkConstSection:
if useAliveDataFromDce in p.module.flags:
genConstStmt(p, n)
else: # enforce addressable consts for exportc
let m = p.module
for it in n:
let symNode = skipPragmaExpr(it[0])
if symNode.kind == nkSym and sfExportc in symNode.sym.flags:
requestConstImpl(p, symNode.sym)
# else: consts generated lazily on use
of nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
of nkCaseStmt: genCase(p, n, d)
@@ -3195,7 +3198,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkMixinStmt, nkBindStmt: discard
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder) =
proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) =
var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc})
case t.kind
of tyBool: result.add rope"NIM_FALSE"
@@ -3206,56 +3209,38 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder)
result.add rope"NIM_NIL"
of tyString, tySequence:
if optSeqDestructors in p.config.globalOptions:
var seqInit: StructInitializer
result.addStructInitializer(seqInit, kind = siOrderedStruct):
result.addField(seqInit, name = "len"):
result.add("0")
result.addField(seqInit, name = "p"):
result.add("NIM_NIL")
result.add "{0, NIM_NIL}"
else:
result.add "NIM_NIL"
of tyProc:
if t.callConv != ccClosure:
result.add "NIM_NIL"
else:
var closureInit: StructInitializer
result.addStructInitializer(closureInit, kind = siOrderedStruct):
result.addField(closureInit, name = "ClP_0"):
result.add("NIM_NIL")
result.addField(closureInit, name = "ClE_0"):
result.add("NIM_NIL")
result.add "{NIM_NIL, NIM_NIL}"
of tyObject:
var objInit: StructInitializer
result.addStructInitializer(objInit, kind = siOrderedStruct):
getNullValueAuxT(p, t, t, t.n, nil, result, objInit, true, info)
var count = 0
result.add "{"
getNullValueAuxT(p, t, t, t.n, nil, result, count, true, info)
result.add "}"
of tyTuple:
var tupleInit: StructInitializer
result.addStructInitializer(tupleInit, kind = siOrderedStruct):
if p.vccAndC and t.isEmptyTupleType:
result.addField(tupleInit, name = "dummy"):
result.add "0"
for i, a in t.ikids:
result.addField(tupleInit, name = "Field" & $i):
getDefaultValue(p, a, info, result)
result.add "{"
if p.vccAndC and t.isEmptyTupleType:
result.add "0"
for i, a in t.ikids:
if i > 0: result.add ", "
getDefaultValue(p, a, info, result)
result.add "}"
of tyArray:
var arrInit: StructInitializer
result.addStructInitializer(arrInit, kind = siArray):
for i in 0..<toInt(lengthOrd(p.config, t.indexType)):
result.addField(arrInit, name = ""):
getDefaultValue(p, t.elementType, info, result)
result.add "{"
for i in 0..<toInt(lengthOrd(p.config, t.indexType)):
if i > 0: result.add ", "
getDefaultValue(p, t.elementType, info, result)
result.add "}"
#result = rope"{}"
of tyOpenArray, tyVarargs:
var openArrInit: StructInitializer
result.addStructInitializer(openArrInit, kind = siOrderedStruct):
result.addField(openArrInit, name = "Field0"):
result.add("NIM_NIL")
result.addField(openArrInit, name = "Field1"):
result.add("0")
result.add "{NIM_NIL, 0}"
of tySet:
if mapSetType(p.config, t) == ctArray:
var setInit: StructInitializer
result.addStructInitializer(setInit, kind = siArray):
discard
if mapSetType(p.config, t) == ctArray: result.add "{}"
else: result.add "0"
else:
globalError(p.config, info, "cannot create null element for: " & $t.kind)
@@ -3266,18 +3251,16 @@ proc isEmptyCaseObjectBranch(n: PNode): bool =
return true
proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
result: var Builder; init: var StructInitializer;
result: var Rope; count: var int;
isConst: bool, info: TLineInfo) =
case obj.kind
of nkRecList:
let isUnion = tfUnion in t.flags
for it in obj.sons:
getNullValueAux(p, t, it, constOrNil, result, init, isConst, info)
if isUnion:
# generate only 1 field for default value of union
return
getNullValueAux(p, t, it, constOrNil, result, count, isConst, info)
of nkRecCase:
getNullValueAux(p, t, obj[0], constOrNil, result, init, isConst, info)
getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info)
var res = ""
if count > 0: res.add ", "
var branch = Zero
if constOrNil != nil:
## find kind value, default is zero if not specified
@@ -3291,186 +3274,140 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
break
let selectedBranch = caseObjDefaultBranch(obj, branch)
res.add "{"
var countB = 0
let b = lastSon(obj[selectedBranch])
# designated initilization is the only way to init non first element of unions
# branches are allowed to have no members (b.len == 0), in this case they don't need initializer
var fieldName: string = ""
if b.kind == nkRecList and not isEmptyCaseObjectBranch(b):
fieldName = "_" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch
result.addField(init, name = "<anonymous union>"):
# XXX figure out name for the union, see use of `addAnonUnion`
var branchInit: StructInitializer
result.addStructInitializer(branchInit, kind = siNamedStruct):
result.addField(branchInit, name = fieldName):
var branchObjInit: StructInitializer
result.addStructInitializer(branchObjInit, kind = siOrderedStruct):
getNullValueAux(p, t, b, constOrNil, result, branchObjInit, isConst, info)
res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info)
res.add "}"
elif b.kind == nkSym:
fieldName = mangleRecFieldName(p.module, b.sym)
result.addField(init, name = "<anonymous union>"):
# XXX figure out name for the union, see use of `addAnonUnion`
var branchInit: StructInitializer
result.addStructInitializer(branchInit, kind = siNamedStruct):
result.addField(branchInit, name = fieldName):
# we need to generate the default value of the single sym,
# to do this create a dummy wrapper initializer and recurse
var branchFieldInit: StructInitializer
result.addStructInitializer(branchFieldInit, kind = siWrapper):
getNullValueAux(p, t, b, constOrNil, result, branchFieldInit, isConst, info)
res.add "." & mangleRecFieldName(p.module, b.sym) & " = "
getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info)
else:
# no fields, don't initialize
return
result.add res
result.add "}"
of nkSym:
if count > 0: result.add ", "
inc count
let field = obj.sym
let sname = mangleRecFieldName(p.module, field)
result.addField(init, name = sname):
block fieldInit:
if constOrNil != nil:
for i in 1..<constOrNil.len:
if constOrNil[i].kind == nkExprColonExpr:
assert constOrNil[i][0].kind == nkSym, "illformed object constr; the field is not a sym"
if constOrNil[i][0].sym.name.id == field.name.id:
genBracedInit(p, constOrNil[i][1], isConst, field.typ, result)
break fieldInit
elif i == field.position:
genBracedInit(p, constOrNil[i], isConst, field.typ, result)
break fieldInit
# not found, produce default value:
getDefaultValue(p, field.typ, info, result)
if constOrNil != nil:
for i in 1..<constOrNil.len:
if constOrNil[i].kind == nkExprColonExpr:
assert constOrNil[i][0].kind == nkSym, "illformed object constr; the field is not a sym"
if constOrNil[i][0].sym.name.id == field.name.id:
genBracedInit(p, constOrNil[i][1], isConst, field.typ, result)
return
elif i == field.position:
genBracedInit(p, constOrNil[i], isConst, field.typ, result)
return
# not found, produce default value:
getDefaultValue(p, field.typ, info, result)
else:
localError(p.config, info, "cannot create null element for: " & $obj)
proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
result: var Builder; init: var StructInitializer;
result: var Rope; count: var int;
isConst: bool, info: TLineInfo) =
var base = t.baseClass
when false:
let oldRes = result
let oldcount = count
let oldRes = result
let oldcount = count
if base != nil:
result.add "{"
base = skipTypes(base, skipPtrs)
result.addField(init, name = "Sup"):
var baseInit: StructInitializer
result.addStructInitializer(baseInit, kind = siOrderedStruct):
getNullValueAuxT(p, orig, base, base.n, constOrNil, result, baseInit, isConst, info)
getNullValueAuxT(p, orig, base, base.n, constOrNil, result, count, isConst, info)
result.add "}"
elif not isObjLackingTypeField(t):
result.addField(init, name = "m_type"):
if optTinyRtti in p.config.globalOptions:
result.add genTypeInfoV2(p.module, orig, obj.info)
else:
result.add genTypeInfoV1(p.module, orig, obj.info)
getNullValueAux(p, t, obj, constOrNil, result, init, isConst, info)
when false: # referring to Sup field, hopefully not a problem
# do not emit '{}' as that is not valid C:
if oldcount == count: result = oldRes
if optTinyRtti in p.config.globalOptions:
result.add genTypeInfoV2(p.module, orig, obj.info)
else:
result.add genTypeInfoV1(p.module, orig, obj.info)
inc count
getNullValueAux(p, t, obj, constOrNil, result, count, isConst, info)
# do not emit '{}' as that is not valid C:
if oldcount == count: result = oldRes
proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Builder) =
proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Rope) =
let t = n.typ.skipTypes(abstractInstOwned)
var count = 0
#if not isObjLackingTypeField(t) and not p.module.compileToCpp:
# result.addf("{$1}", [genTypeInfo(p.module, t)])
# inc count
var objInit: StructInitializer
result.addStructInitializer(objInit, kind = siOrderedStruct):
if t.kind == tyObject:
getNullValueAuxT(p, t, t, t.n, n, result, objInit, isConst, n.info)
result.add "{"
if t.kind == tyObject:
getNullValueAuxT(p, t, t, t.n, n, result, count, isConst, n.info)
result.add("}\n")
proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Builder) =
var arrInit: StructInitializer
result.addStructInitializer(arrInit, kind = siArray):
if p.vccAndC and n.len == 0 and n.typ.kind == tyArray:
result.addField(arrInit, name = ""):
getDefaultValue(p, n.typ.elementType, n.info, result)
for i in 0..<n.len:
let it = n[i]
var ind, val: PNode
if it.kind == nkExprColonExpr:
ind = it[0]
val = it[1]
else:
ind = it
val = it
result.addField(arrInit, name = ""):
genBracedInit(p, val, isConst, ind.typ, result)
proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Rope) =
result.add "{"
if p.vccAndC and n.len == 0 and n.typ.kind == tyArray:
getDefaultValue(p, n.typ.elementType, n.info, result)
for i in 0..<n.len:
let it = n[i]
if i > 0: result.add ",\n"
if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, it[0].typ, result)
else: genBracedInit(p, it, isConst, it.typ, result)
result.add("}\n")
proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Builder) =
var tupleInit: StructInitializer
result.addStructInitializer(tupleInit, kind = siOrderedStruct):
if p.vccAndC and n.len == 0:
result.addField(tupleInit, name = "dummy"):
result.add("0")
for i in 0..<n.len:
var it = n[i]
if it.kind == nkExprColonExpr:
it = it[1]
result.addField(tupleInit, name = "Field" & $i):
genBracedInit(p, it, isConst, tup[i], result)
proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Rope) =
result.add "{"
if p.vccAndC and n.len == 0:
result.add "0"
for i in 0..<n.len:
let it = n[i]
if i > 0: result.add ",\n"
if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, tup[i], result)
else: genBracedInit(p, it, isConst, tup[i], result)
result.add("}\n")
proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Builder) =
proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) =
var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope]
let base = t.skipTypes(abstractInst)[0]
if n.len > 0:
# array part needs extra curlies:
data.add(", {")
for i in 0..<n.len:
if i > 0: data.addf(",$n", [])
genBracedInit(p, n[i], isConst, base, data)
data.add("}")
data.add("}")
let tmpName = getTempName(p.module)
var def = newBuilder("")
def.addVarWithTypeAndInitializer(
if isConst: Const else: Global,
name = tmpName):
def.addSimpleStruct(p.module, name = "", baseType = ""):
def.addField(name = "sup", typ = cgsymValue(p.module, "TGenericSeq"))
def.addArrayField(name = "data", elementType = getTypeDesc(p.module, base), len = n.len)
do:
var structInit: StructInitializer
def.addStructInitializer(structInit, kind = siOrderedStruct):
def.addField(structInit, name = "sup"):
var supInit: StructInitializer
def.addStructInitializer(supInit, kind = siOrderedStruct):
def.addField(supInit, name = "len"):
def.add(n.len.rope)
def.addField(supInit, name = "reserved"):
def.add(bitOr(rope(n.len), "NIM_STRLIT_FLAG"))
if n.len > 0:
def.addField(structInit, name = "data"):
var arrInit: StructInitializer
def.addStructInitializer(arrInit, kind = siArray):
for i in 0..<n.len:
def.addField(arrInit, name = ""):
genBracedInit(p, n[i], isConst, base, def)
p.module.s[cfsStrData].add def
appcg(p.module, cfsStrData,
"static $5 struct {$n" &
" #TGenericSeq Sup;$n" &
" $1 data[$2];$n" &
"} $3 = $4;$n", [
getTypeDesc(p.module, base), n.len, tmpName, data,
if isConst: "NIM_CONST" else: ""])
result.add cCast(typ = getTypeDesc(p.module, t), value = cAddr(tmpName))
result.add "(($1)&$2)" % [getTypeDesc(p.module, t), tmpName]
proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Builder) =
proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) =
let base = t.skipTypes(abstractInst)[0]
var data = rope""
if n.len > 0:
data.add(", {")
for i in 0..<n.len:
if i > 0: data.addf(",$n", [])
genBracedInit(p, n[i], isConst, base, data)
data.add("}")
let payload = getTempName(p.module)
appcg(p.module, cfsStrData,
"static $5 struct {$n" &
" NI cap; $1 data[$2];$n" &
"} $3 = {$2 | NIM_STRLIT_FLAG$4};$n", [
getTypeDesc(p.module, base), n.len, payload, data,
if isConst: "const" else: ""])
result.add "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload]
var def = newBuilder("")
def.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = payload):
def.addSimpleStruct(p.module, name = "", baseType = ""):
def.addField(name = "cap", typ = "NI")
def.addArrayField(name = "data", elementType = getTypeDesc(p.module, base), len = n.len)
do:
var structInit: StructInitializer
def.addStructInitializer(structInit, kind = siOrderedStruct):
def.addField(structInit, name = "cap"):
def.add(bitOr(rope(n.len), "NIM_STRLIT_FLAG"))
if n.len > 0:
def.addField(structInit, name = "data"):
var arrInit: StructInitializer
def.addStructInitializer(arrInit, kind = siArray):
for i in 0..<n.len:
def.addField(arrInit, name = ""):
genBracedInit(p, n[i], isConst, base, def)
p.module.s[cfsStrData].add def
var resultInit: StructInitializer
result.addStructInitializer(resultInit, kind = siOrderedStruct):
result.addField(resultInit, name = "len"):
result.add(rope(n.len))
result.addField(resultInit, name = "p"):
result.add cCast(typ = ptrType(getSeqPayloadType(p.module, t)), value = cAddr(payload))
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Builder) =
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope) =
case n.kind
of nkHiddenStdConv, nkHiddenSubConv:
genBracedInit(p, n[1], isConst, n.typ, result)
@@ -3505,16 +3442,11 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
# in VM closures are initialized with nkPar(nkNilLit, nkNilLit)
# leading to duplicate code like this:
# "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}"
var closureInit: StructInitializer
result.addStructInitializer(closureInit, kind = siOrderedStruct):
result.addField(closureInit, name = "ClP_0"):
if n[0].kind == nkNilLit:
result.add("NIM_NIL")
else:
var d: TLoc = initLocExpr(p, n[0])
result.add(cCast(typ = getClosureType(p.module, typ, clHalfWithEnv), value = rdLoc(d)))
result.addField(closureInit, name = "ClE_0"):
result.add("NIM_NIL")
if n[0].kind == nkNilLit:
result.add "{NIM_NIL,NIM_NIL}"
else:
var d: TLoc = initLocExpr(p, n[0])
result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)]
else:
var d: TLoc = initLocExpr(p, n)
result.add rdLoc(d)
@@ -3526,21 +3458,17 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
if n.kind != nkBracket:
internalError(p.config, n.info, "const openArray expression is not an array construction")
var data = newRopeAppender()
genConstSimpleList(p, n, isConst, data)
let payload = getTempName(p.module)
let ctype = getTypeDesc(p.module, typ.elementType)
let arrLen = n.len
var data = newBuilder("")
data.addArrayVarWithInitializer(
kind = if isConst: AlwaysConst else: Global,
name = payload, elementType = ctype, len = arrLen):
genConstSimpleList(p, n, isConst, data)
p.module.s[cfsStrData].add(data)
var openArrInit: StructInitializer
result.addStructInitializer(openArrInit, kind = siOrderedStruct):
result.addField(openArrInit, name = "Field0"):
result.add(cCast(typ = ptrType(ctype), value = cAddr(payload)))
result.addField(openArrInit, name = "Field1"):
result.add(rope arrLen)
appcg(p.module, cfsStrData,
"static $5 $1 $3[$2] = $4;$n", [
ctype, arrLen, payload, data,
if isConst: "const" else: ""])
result.add "{($1*)&$2, $3}" % [ctype, payload, rope arrLen]
of tyObject:
genConstObjConstr(p, n, isConst, result)

View File

@@ -36,83 +36,50 @@ proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
cgsym(m, "TGenericSeq")
let tmp = getTempName(m)
result.add tmp
var res = newBuilder("")
res.addVarWithTypeAndInitializer(AlwaysConst, name = tmp):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "Sup", typ = "TGenericSeq")
res.addArrayField(name = "data", elementType = "NIM_CHAR", len = s.len + 1)
do:
var strInit: StructInitializer
res.addStructInitializer(strInit, kind = siOrderedStruct):
res.addField(strInit, name = "Sup"):
var seqInit: StructInitializer
res.addStructInitializer(seqInit, kind = siOrderedStruct):
res.addField(seqInit, name = "len"):
res.add(rope(s.len))
res.addField(seqInit, name = "reserved"):
res.add(cCast("NI", bitOr(cCast("NU", rope(s.len)), "NIM_STRLIT_FLAG")))
res.addField(strInit, name = "data"):
res.add(makeCString(s))
m.s[cfsStrData].add(res)
m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n",
[tmp, makeCString(s), rope(s.len)])
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
if s.isNil:
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), "NIM_NIL"))
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
else:
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var name: string = ""
if id == m.labels:
# string literal not found in the cache:
genStringLiteralDataOnlyV1(m, n.strVal, name)
appcg(m, result, "((#NimStringDesc*) &", [])
genStringLiteralDataOnlyV1(m, n.strVal, result)
result.add ")"
else:
name = m.tmpBase & $id
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), cAddr(name)))
appcg(m, result, "((#NimStringDesc*) &$1$2)",
[m.tmpBase, id])
# ------ Version 2: destructor based strings and seqs -----------------------
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
var res = newBuilder("")
res.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = result):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "cap", typ = "NI")
res.addArrayField(name = "data", elementType = "NIM_CHAR", len = s.len + 1)
do:
var structInit: StructInitializer
res.addStructInitializer(structInit, kind = siOrderedStruct):
res.addField(structInit, name = "cap"):
res.add(bitOr(rope(s.len), "NIM_STRLIT_FLAG"))
res.addField(structInit, name = "data"):
res.add(makeCString(s))
m.s[cfsStrData].add(res)
m.s[cfsStrData].addf("static $4 struct {$n" &
" NI cap; NIM_CHAR data[$2+1];$n" &
"} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
[result, rope(s.len), makeCString(s),
rope(if isConst: "const" else: "")])
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var litName: string
if id == m.labels:
let pureLit = getTempName(m)
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
let tmp = getTempName(m)
result.add tmp
cgsym(m, "NimStrPayload")
cgsym(m, "NimStringV2")
# string literal not found in the cache:
litName = getTempName(m)
genStringLiteralDataOnlyV2(m, n.strVal, litName, isConst)
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
[tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
else:
litName = m.tmpBase & $id
let tmp = getTempName(m)
result.add tmp
var res = newBuilder("")
res.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = tmp):
res.add("NimStringV2")
do:
var strInit: StructInitializer
res.addStructInitializer(strInit, kind = siOrderedStruct):
res.addField(strInit, name = "len"):
res.add(rope(n.strVal.len))
res.addField(strInit, name = "p"):
res.add(cCast(ptrType("NimStrPayload"), cAddr(litName)))
m.s[cfsStrData].add(res)
let tmp = getTempName(m)
result.add tmp
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
[tmp, rope(n.strVal.len), m.tmpBase & rope(id),
rope(if isConst: "const" else: "")])
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
@@ -125,12 +92,7 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Ro
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
else:
pureLit = m.tmpBase & rope(id)
var strInit: StructInitializer
result.addStructInitializer(strInit, kind = siOrderedStruct):
result.addField(strInit, name = "len"):
result.add(rope(n.strVal.len))
result.addField(strInit, name = "p"):
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
# ------ Version selector ---------------------------------------------------
@@ -146,7 +108,7 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
localError(m.config, info, "cannot determine how to produce code for string literal")
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), "NIM_NIL"))
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
case detectStrVersion(m)

View File

@@ -279,7 +279,7 @@ proc genGotoVar(p: BProc; value: PNode) =
else:
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Builder)
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope)
proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:

View File

@@ -47,11 +47,7 @@ proc generateThreadLocalStorage(m: BModule) =
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
finishTypeDescriptions(m)
var typedef = newBuilder("")
typedef.addTypedef(name = "NimThreadVars"):
typedef.addSimpleStruct(m, name = "", baseType = ""):
typedef.add(m.g.nimtv)
m.s[cfsSeqTypes].add(typedef)
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
proc generateThreadVarsSize(m: BModule) =
if m.g.nimtv != "":

View File

@@ -81,7 +81,6 @@ proc fillBackendName(m: BModule; s: PSym) =
result.add '_'
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
s.loc.snippet = result
writeMangledName(m.ndi, s, m.config)
proc fillParamName(m: BModule; s: PSym) =
if s.loc.snippet == "":
@@ -105,7 +104,6 @@ proc fillParamName(m: BModule; s: PSym) =
# That would lead to either needing to reload `proxy` or to overwrite the
# executable file for the main module, which is running (or both!) -> error.
s.loc.snippet = res.rope
writeMangledName(m.ndi, s, m.config)
proc fillLocalName(p: BProc; s: PSym) =
assert s.kind in skLocalVars+{skTemp}
@@ -121,7 +119,6 @@ proc fillLocalName(p: BProc; s: PSym) =
result.add "_" & rope(counter+1)
p.sigConflicts.inc(key)
s.loc.snippet = result
if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
proc scopeMangledParam(p: BProc; param: PSym) =
## parameter generation only takes BModule, not a BProc, so we have to
@@ -285,6 +282,15 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
else: result = false
const
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
"N_STDCALL", "N_CDECL", "N_SAFECALL",
"N_SYSCALL", # this is probably not correct for all platforms,
# but one can #define it to what one wants
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
"N_NOCONV" #ccMember is N_NOCONV
]
proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
# returns nil if we need to declare this type
# since types are now unique via the ``getUniqueType`` mechanism, this slow
@@ -373,7 +379,6 @@ proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope =
if result == "": result = cacheGetType(m.typeCache, sig)
proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) =
# XXX should be no-op in NIFC
if m.compileToCpp:
m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
else:
@@ -430,11 +435,10 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TypeDescKind
if cacheGetType(m.typeCache, sig) == "":
m.typeCache[sig] = result
#echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
var struct = newBuilder("")
struct.addSimpleStruct(m, name = result, baseType = ""):
struct.addField(name = "len", typ = "NI")
struct.addField(name = "p", typ = ptrType(result & "_Content"))
m.s[cfsTypes].add(struct)
appcg(m, m.s[cfsTypes],
"struct $1 {\n" &
" NI len; $1_Content* p;\n" &
"};\n", [result])
pushType(m, t)
else:
result = getTypeForward(m, t, sig) & seqStar(m)
@@ -453,13 +457,9 @@ proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
if result == "":
discard getTypeDescAux(m, t, check, dkVar)
else:
var struct = newBuilder("")
struct.addSimpleStruct(m, name = result & "_Content", baseType = ""):
struct.addField(name = "cap", typ = "NI")
struct.addField(name = "data",
typ = getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, dkVar),
isFlexArray = true)
m.s[cfsTypes].add(struct)
appcg(m, m.s[cfsTypes], """
struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE]; };
""", [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, dkVar), result])
proc paramStorageLoc(param: PSym): TStorageLoc =
if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin {
@@ -722,7 +722,6 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
if unionBody.len != 0:
result.addAnonUnion:
# XXX this has to be a named field for NIFC
result.add(unionBody)
of nkSym:
let field = n.sym
@@ -847,12 +846,8 @@ proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKin
result = getTypeName(m, t, sig)
m.typeCache[sig] = result
let elemType = getTypeDescWeak(m, t.elementType, check, kind)
var typedef = newBuilder("")
typedef.addTypedef(name = result):
typedef.addSimpleStruct(m, name = "", baseType = ""):
typedef.addField(name = "Field0", typ = ptrType(elemType))
typedef.addField(name = "Field1", typ = "NI")
m.s[cfsTypes].add(typedef)
m.s[cfsTypes].addf("typedef struct {$n$2* Field0;$nNI Field1;$n} $1;$n",
[result, elemType])
proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope =
# returns only the type's name
@@ -924,28 +919,17 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
(sfImportc in t.sym.flags and t.sym.magic == mNone)):
m.typeCache[sig] = result
var size: int
var typedef = newBuilder("")
if firstOrd(m.config, t) < 0:
typedef.addTypedef(name = result):
typedef.add("NI32")
m.s[cfsTypes].addf("typedef NI32 $1;$n", [result])
size = 4
else:
size = int(getSize(m.config, t))
case size
of 1:
typedef.addTypedef(name = result):
typedef.add("NU8")
of 2:
typedef.addTypedef(name = result):
typedef.add("NU16")
of 4:
typedef.addTypedef(name = result):
typedef.add("NI32")
of 8:
typedef.addTypedef(name = result):
typedef.add("NI64")
of 1: m.s[cfsTypes].addf("typedef NU8 $1;$n", [result])
of 2: m.s[cfsTypes].addf("typedef NU16 $1;$n", [result])
of 4: m.s[cfsTypes].addf("typedef NI32 $1;$n", [result])
of 8: m.s[cfsTypes].addf("typedef NI64 $1;$n", [result])
else: internalError(m.config, t.sym.info, "getTypeDescAux: enum")
m.s[cfsTypes].add(typedef)
when false:
let owner = hashOwner(t.sym)
if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
@@ -962,17 +946,14 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
var rettype, desc: Rope = ""
genProcParams(m, t, rettype, desc, check, true, true)
if not isImportedType(t):
var typedef = newBuilder("")
if t.callConv != ccClosure: # procedure vars may need a closure!
typedef.addTypedef(name = desc):
typedef.add(procPtrType(t.callConv, rettype = rettype, name = result))
m.s[cfsTypes].addf("typedef $1_PTR($2, $3) $4;$n",
[rope(CallingConvToStr[t.callConv]), rettype, result, desc])
else:
typedef.addTypedef(name = result):
typedef.addSimpleStruct(m, name = "", baseType = ""):
typedef.addField(name = desc, typ =
procPtrType(ccNimCall, rettype = rettype, name = "ClP_0"))
typedef.addField(name = "ClE_0", typ = "void*")
m.s[cfsTypes].add(typedef)
m.s[cfsTypes].addf("typedef struct {$n" &
"N_NIMCALL_PTR($2, ClP_0) $3;$n" &
"void* ClE_0;$n} $1;$n",
[result, rettype, desc])
of tySequence:
if optSeqDestructors in m.config.globalOptions:
result = getTypeDescWeak(m, t, check, kind)
@@ -989,14 +970,18 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
m.typeCache[sig] = result & seqStar(m)
if not isImportedType(t):
if skipTypes(t.elementType, typedescInst).kind != tyEmpty:
var struct = newBuilder("")
let baseType = cgsymValue(m, "TGenericSeq")
struct.addSimpleStruct(m, name = result, baseType = baseType):
struct.addField(
name = "data",
typ = getTypeDescAux(m, t.elementType, check, kind),
isFlexArray = true)
m.s[cfsSeqTypes].add struct
const
cppSeq = "struct $2 : #TGenericSeq {$n"
cSeq = "struct $2 {$n" &
" #TGenericSeq Sup;$n"
if m.compileToCpp:
appcg(m, m.s[cfsSeqTypes],
cppSeq & " $1 data[SEQ_DECL_SIZE];$n" &
"};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
else:
appcg(m, m.s[cfsSeqTypes],
cSeq & " $1 data[SEQ_DECL_SIZE];$n" &
"};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
else:
result = rope("TGenericSeq")
result.add(seqStar(m))
@@ -1005,10 +990,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
m.typeCache[sig] = result
if not isImportedType(t):
let foo = getTypeDescAux(m, t.elementType, check, kind)
var typedef = newBuilder("")
typedef.addArrayTypedef(name = result, len = 1):
typedef.add(foo)
m.s[cfsTypes].add(typedef)
m.s[cfsTypes].addf("typedef $1 $2[1];$n", [foo, result])
of tyArray:
var n: BiggestInt = toInt64(lengthOrd(m.config, t))
if n <= 0: n = 1 # make an array of at least one element
@@ -1016,10 +998,8 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
m.typeCache[sig] = result
if not isImportedType(t):
let e = getTypeDescAux(m, t.elementType, check, kind)
var typedef = newBuilder("")
typedef.addArrayTypedef(name = result, len = n):
typedef.add(e)
m.s[cfsTypes].add(typedef)
m.s[cfsTypes].addf("typedef $1 $2[$3];$n",
[e, result, rope(n)])
of tyObject, tyTuple:
let tt = origTyp.skipTypes({tyDistinct})
if isImportedCppType(t) and tt.kind == tyGenericInst:
@@ -1065,8 +1045,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
# with the C macros for defining procs such as N_NIMCALL. We must
# create a typedef for the type and use it in the proc signature:
let typedefName = "TY" & $sig
m.s[cfsTypes].addTypedef(name = typedefName):
m.s[cfsTypes].add(result)
m.s[cfsTypes].addf("typedef $1 $2;$n", [result, typedefName])
m.typeCache[sig] = typedefName
result = typedefName
else:
@@ -1094,12 +1073,9 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
if not isImportedType(t):
let s = int(getSize(m.config, t))
case s
of 1, 2, 4, 8:
m.s[cfsTypes].addTypedef(name = result):
m.s[cfsTypes].add("NU" & rope(s*8))
else:
m.s[cfsTypes].addArrayTypedef(name = result, len = s):
m.s[cfsTypes].add("NU8")
of 1, 2, 4, 8: m.s[cfsTypes].addf("typedef NU$2 $1;$n", [result, rope(s*8)])
else: m.s[cfsTypes].addf("typedef NU8 $1[$2];$n",
[result, rope(getSize(m.config, t))])
of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned,
tyUserTypeClass, tyUserTypeClassInst, tyInferred:
result = getTypeDescAux(m, skipModifier(t), check, kind)
@@ -1127,17 +1103,14 @@ proc getClosureType(m: BModule; t: PType, kind: TClosureTypeKind): Rope =
var rettype, desc: Rope = ""
genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
if not isImportedType(t):
var typedef = newBuilder("")
if t.callConv != ccClosure or kind != clFull:
typedef.addTypedef(name = desc):
typedef.add(procPtrType(t.callConv, rettype = rettype, name = result))
m.s[cfsTypes].addf("typedef $1_PTR($2, $3) $4;$n",
[rope(CallingConvToStr[t.callConv]), rettype, result, desc])
else:
typedef.addTypedef(name = result):
typedef.addSimpleStruct(m, name = "", baseType = ""):
typedef.addField(name = desc, typ =
procPtrType(ccNimCall, rettype = rettype, name = "ClP_0"))
typedef.addField(name = "ClE_0", typ = "void*")
m.s[cfsTypes].add(typedef)
m.s[cfsTypes].addf("typedef struct {$n" &
"N_NIMCALL_PTR($2, ClP_0) $3;$n" &
"void* ClE_0;$n} $1;$n",
[result, rettype, desc])
proc finishTypeDescriptions(m: BModule) =
var i = 0

View File

@@ -14,7 +14,7 @@ import
nversion, nimsets, msgs, bitsets, idents, types,
ccgutils, ropes, wordrecg, treetab, cgmeth,
rodutils, renderer, cgendata, aliases,
lowerings, ndi, lineinfos, pathutils, transf,
lowerings, lineinfos, pathutils, transf,
injectdestructors, astmsgs, modulepaths, pushpoppragmas,
mangleutils
@@ -373,9 +373,7 @@ proc dataField(p: BProc): Rope =
proc genProcPrototype(m: BModule, sym: PSym)
include cbuilderbase
include cbuilderexprs
include cbuilderdecls
include cbuilder
include ccgliterals
include ccgtypes
@@ -2087,9 +2085,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
if sfSystemModule in module.flags:
incl result.flags, preventStackTrace
excl(result.preInitProc.options, optStackTrace)
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCfilePath(g.config, filename), "ndi")
else: AbsoluteFile""
open(result.ndi, ndiName, g.config)
proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
@@ -2219,7 +2214,6 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
# it would generate multiple 'main' procs, for instance.
proc writeModule(m: BModule, pending: bool) =
template onExit() = close(m.ndi, m.config)
let cfile = getCFile(m)
if moduleHasChanged(m.g.graph, m.module):
genInitCode(m)
@@ -2237,12 +2231,10 @@ proc writeModule(m: BModule, pending: bool) =
when hasTinyCBackend:
if m.config.cmd == cmdTcc:
tccgen.compileCCode($code, m.config)
onExit()
return
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf)
onExit()
proc updateCachedModule(m: BModule) =
let cfile = getCFile(m)
@@ -2254,12 +2246,13 @@ proc updateCachedModule(m: BModule) =
addFileToCompile(m.config, cf)
proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym =
let procname = getIdent(graph.cache, "NimDestroyGlobals")
let prefixedName = m.config.nimMainPrefix & "NimDestroyGlobals"
let procname = getIdent(graph.cache, prefixedName)
result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info)
result.typ = newProcType(m.module.info, m.idgen, m.module.owner)
result.typ.callConv = ccCDecl
incl result.flags, sfExportc
result.loc.snippet = "NimDestroyGlobals"
result.loc.snippet = prefixedName
if isDynlib:
incl(result.loc.flags, lfExportLib)

View File

@@ -11,7 +11,7 @@
import
ast, ropes, options,
ndi, lineinfos, pathutils, modulegraphs
lineinfos, pathutils, modulegraphs
import std/[intsets, tables, sets]
@@ -172,7 +172,6 @@ type
# OpenGL wrapper
sigConflicts*: CountTable[SigHash]
g*: BModuleList
ndi*: NdiFile
template config*(m: BModule): ConfigRef = m.g.config
template config*(p: BProc): ConfigRef = p.module.g.config

View File

@@ -203,13 +203,14 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
else: invalidCmdLineOption(conf, pass, orig, info)
let isSomeHint = state in {wHint, wHintAsError}
let isSomeWarning = state in {wWarning, wWarningAsError}
template findNote(noteMin, noteMax, name) =
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
let x = findStr(noteMin, noteMax, id, errUnknown)
if x != errUnknown: notes = {TNoteKind(x)}
else:
if isSomeHint:
message(conf, info, hintUnknownHint, id)
if isSomeHint or isSomeWarning:
message(conf, info, warnUnknownNotes, "unknown $#: $#" % [name, id])
else:
localError(conf, info, "unknown $#: $#" % [name, id])
case id.normalize
@@ -919,6 +920,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
discard parseSaturatedNatural(arg, value)
if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
conf.maxLoopIterationsVM = value
of "maxcalldepthvm":
expectArg(conf, switch, arg, pass, info)
var value: int = 2_000
discard parseSaturatedNatural(arg, value)
if value <= 0: localError(conf, info, "maxCallDepthVM must be a positive integer greater than zero")
conf.maxCallDepthVM = value
of "errormax":
expectArg(conf, switch, arg, pass, info)
# Note: `nim check` (etc) can overwrite this.

View File

@@ -78,6 +78,7 @@ type
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
## cannot be fixed that easily.
## Thus we special case it here.
concpt: PType
proc existingBinding(m: MatchCon; key: PType): PType =
## checks if we bound the type variable 'key' already to some
@@ -88,22 +89,52 @@ proc existingBinding(m: MatchCon; key: PType): PType =
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool
proc acceptsAllTypes(t: PType): bool=
result = false
if t.kind == tyAnything:
result = true
elif t.kind == tyGenericParam:
if tfImplicitTypeParam in t.flags:
result = true
if not t.hasElementType or t.elementType.kind == tyNone:
result = true
proc matchKids(c: PContext; f, a: PType; m: var MatchCon, start=0): bool=
result = true
for i in start..<f.kidsLen - ord(f.kind == tyGenericInst):
if not matchType(c, f[i], a[i], m): return false
proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
## The heart of the concept matching process. 'f' is the formal parameter of some
## routine inside the concept that we're looking for. 'a' is the formal parameter
## of a routine that might match.
const
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
var a = ao
case a.kind
of tyGenericParam:
let binding = m.existingBinding(a)
if binding != nil:
a = binding
else:
discard
case f.kind
of tyAlias:
result = matchType(c, f.skipModifier, a, m)
of tyTypeDesc:
if isSelf(f):
#let oldLen = m.inferred.len
result = matchType(c, a, m.potentialImplementation, m)
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
#m.inferred.setLen oldLen
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
if m.magic in {mArrPut, mArrGet}:
result = false
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
m.inferred.add((a, last m.potentialImplementation))
result = true
else:
result = matchType(c, a, m.potentialImplementation, m)
else:
if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
if f.hasElementType:
@@ -112,14 +143,11 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
result = true # both lack it
else:
result = false
of tyGenericInvocation:
result = false
if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
for i in FirstGenericParamAt ..< f.kidsLen:
if not matchType(c, f[i], a[i], m): return false
return true
result = matchKids(c, f, a, m, start=FirstGenericParamAt)
of tyGenericParam:
let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
@@ -136,7 +164,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
when logBindings: echo "A adding ", f, " ", ak
m.inferred.add((f, ak))
elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}:
when logBindings: echo "B adding ", f, " ", lastSon ak
when logBindings: echo "B adding ", f, " ", last ak
m.inferred.add((f, last ak))
result = true
else:
@@ -151,7 +179,6 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
else:
result = false
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
of tyVar, tySink, tyLent, tyOwned:
# modifiers in the concept must be there in the actual implementation
# too but not vice versa.
@@ -170,22 +197,43 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
result = ak.kind == f.kind or ak.kind == tyOrdinal or
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
of tyConcept:
let oldLen = m.inferred.len
let oldPotentialImplementation = m.potentialImplementation
m.potentialImplementation = a
result = conceptMatchNode(c, f.n.lastSon, m)
m.potentialImplementation = oldPotentialImplementation
if not result:
m.inferred.setLen oldLen
if a.kind == tyConcept and f.n == a.n:
result = true
elif m.concpt.size == szIllegalRecursion:
result = false
else:
let oldLen = m.inferred.len
let oldPotentialImplementation = m.potentialImplementation
m.potentialImplementation = a
m.concpt.size = szIllegalRecursion
let oldConcept = m.concpt
m.concpt = f
result = conceptMatchNode(c, f.n.lastSon, m)
m.potentialImplementation = oldPotentialImplementation
m.concpt = oldConcept
m.concpt.size = szUnknownSize
if not result:
m.inferred.setLen oldLen
of tyGenericBody:
var ak = a
if a.kind == tyGenericBody:
ak = last(a)
result = matchType(c, last(f), ak, m)
of tyCompositeTypeClass:
var ak = if a.kind == tyCompositeTypeClass: a.last else: a
result = matchType(c, last(f), ak, m)
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
tyGenericInst:
# ^ XXX Rewrite this logic, it's more complex than it needs to be.
result = false
let ak = a.skipTypes(ignorableForArgType - {f.kind})
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
for i in 0..<ak.kidsLen:
if not matchType(c, f[i], ak[i], m): return false
return true
if f.kind == tyArray and f.kidsLen == 3:
# XXX: this is a work-around!
# system.nim creates these for the magic array typeclass
result = true
else:
result = false
let ak = a.skipTypes(ignorableForArgType - {f.kind})
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
result = matchKids(c, f, ak, m)
of tyOr:
let oldLen = m.inferred.len
if a.kind == tyOr:
@@ -221,6 +269,16 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
result = true
of tyOrdinal:
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
of tyStatic:
result = false
var scomp = f.base
if scomp.kind == tyGenericParam:
if f.base.kidsLen > 0:
scomp = scomp.base
if a.kind == tyStatic:
result = matchType(c, scomp, a.base, m)
else:
result = matchType(c, scomp, a, m)
else:
result = false
@@ -271,7 +329,8 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
## 'n' is the nkProcDef or similar from the concept that we try to match.
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
for candidate in candidates:
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
m.magic = candidate.magic
@@ -318,7 +377,9 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
## we do not add any bindings at all on an unsuccessful match!
var m = MatchCon(inferred: @[], potentialImplementation: arg)
if arg.containsUnresolvedType:
return false
var m = MatchCon(inferred: @[], potentialImplementation: arg, concpt: concpt)
result = conceptMatchNode(c, concpt.n.lastSon, m)
if result:
for (a, b) in m.inferred:

View File

@@ -231,7 +231,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
for i in 0..n.safeLen-1:
importForwarded(c, n[i], exceptSet, fromMod, importSet)
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, trackUnusedImport: bool): PSym =
result = realModule
template createModuleAliasImpl(ident): untyped =
createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
@@ -246,8 +246,10 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
result = createModuleAliasImpl(realModule.name)
if importHidden:
result.options.incl optImportHidden
let moduleIdent = if n.kind == nkInfix: n[^1] else: n
c.unusedImports.add((result, moduleIdent.info))
let moduleIdent = if n.kind in {nkInfix, nkImportAs}: n[^1] else: n
result.info = moduleIdent.info
if trackUnusedImport:
c.unusedImports.add((result, result.info))
c.importModuleMap[result.id] = realModule.id
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
@@ -288,10 +290,11 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
toFullPath(c.config, c.graph.importStack[i+1])
c.recursiveDep = err
let trackUnusedImport = warnUnusedImportX in c.config.notes
var realModule: PSym
discard pushOptionEntry(c)
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
result = importModuleAs(c, n, realModule, transf.importHidden)
result = importModuleAs(c, n, realModule, transf.importHidden, trackUnusedImport)
popOptionEntry(c)
#echo "set back to ", L
@@ -335,7 +338,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
let m = myImportModule(c, it, importStmtResult)
if m != nil:
# ``addDecl`` needs to be done before ``importAllSymbols``!
addDecl(c, m, it.info) # add symbol to symbol table of module
addDecl(c, m) # add symbol to symbol table of module
importAllSymbols(c, m)
#importForwarded(c, m.ast, emptySet, m)
afterImport(c, m)
@@ -372,7 +375,7 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
var m = myImportModule(c, n[0], result)
if m != nil:
n[0] = newSymNode(m)
addDecl(c, m, n.info) # add symbol to symbol table of module
addDecl(c, m) # add symbol to symbol table of module
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
for i in 1..<n.len:
@@ -387,7 +390,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
var m = myImportModule(c, n[0], result)
if m != nil:
n[0] = newSymNode(m)
addDecl(c, m, n.info) # add symbol to symbol table of module
addDecl(c, m) # add symbol to symbol table of module
importAllSymbolsExcept(c, m, readExceptSet(c, n))
#importForwarded(c, m.ast, exceptSet, m)
afterImport(c, m)

View File

@@ -802,15 +802,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
result = passCopyToSink(n, c, s)
elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
nkCallKinds + nkLiterals:
if n.kind in nkCallKinds and n[0].kind == nkSym:
if n[0].sym.magic == mEnsureMove:
inc c.inEnsureMove
result = p(n[1], c, s, sinkArg)
dec c.inEnsureMove
else:
result = p(n, c, s, consumed)
else:
result = p(n, c, s, consumed)
result = p(n, c, s, consumed)
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
# Sinked params can be consumed only once. We need to reset the memory
@@ -886,12 +878,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
result = ensureDestruction(result, n, c, s)
of nkCallKinds:
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
inc c.inEnsureMove
result = p(n[1], c, s, sinkArg)
dec c.inEnsureMove
return
let inSpawn = c.inSpawn
if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
c.inSpawn.inc
@@ -909,13 +895,19 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
isDangerous = true
result = shallowCopy(n)
for i in 1..<n.len:
if i < L and isCompileTimeOnly(parameters[i]):
result[i] = n[i]
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
result[i] = p(n[i], c, s, sinkArg)
else:
result[i] = p(n[i], c, s, normal)
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
inc c.inEnsureMove
result[1] = p(n[1], c, s, sinkArg)
dec c.inEnsureMove
else:
for i in 1..<n.len:
if i < L and isCompileTimeOnly(parameters[i]):
result[i] = n[i]
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
result[i] = p(n[i], c, s, sinkArg)
else:
result[i] = p(n[i], c, s, normal)
when false:
if isDangerous:

View File

@@ -166,7 +166,7 @@ proc containsVariable(n: PNode): bool =
proc checkIsolate*(n: PNode): bool =
if types.containsTyRef(n.typ):
# XXX Maybe require that 'n.typ' is acyclic. This is not much
# worse than the already exisiting inheritance and closure restrictions.
# worse than the already existing inheritance and closure restrictions.
case n.kind
of nkCharLit..nkNilLit:
result = true

View File

@@ -1585,15 +1585,8 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
of nkObjDownConv:
gen(p, n[0], r)
of nkHiddenDeref:
of nkHiddenDeref, nkDerefExpr:
gen(p, n[0], r)
of nkDerefExpr:
var x = n[0]
if n.kind == nkHiddenAddr:
x = n[0][0]
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
x.typ() = n.typ
gen(p, x, r)
of nkHiddenAddr:
gen(p, n[0], r)
of nkConv:

View File

@@ -1276,6 +1276,10 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
## The later 'injectdestructors' pass depends on it.
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
incl orig.flags, tfCheckedForDestructor
# for user defined generic destructors:
let origRoot = genericRoot(orig)
if origRoot != nil:
incl origRoot.flags, tfGenericHasDestructor
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return

View File

@@ -94,6 +94,7 @@ type
warnImplicitDefaultValue = "ImplicitDefaultValue",
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
warnStdPrefix = "StdPrefix"
warnUnknownNotes = "UnknownNotes"
warnUser = "User",
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
# hints
@@ -111,7 +112,6 @@ type
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
hintMsgOrigin = "MsgOrigin", # since 1.3.5
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
hintUnknownHint = "UnknownHint"
const
MsgKindToStr*: array[TMsgKind, string] = [
@@ -200,6 +200,7 @@ const
warnImplicitDefaultValue: "$1",
warnIgnoredSymbolInjection: "$1",
warnStdPrefix: "$1 needs the 'std' prefix",
warnUnknownNotes: "$1",
warnUser: "$1",
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
hintSuccess: "operation successful: $#",
@@ -236,8 +237,7 @@ const
hintUserRaw: "$1",
hintExtendedContext: "$1",
hintMsgOrigin: "$1",
hintDeclaredLoc: "$1",
hintUnknownHint: "unknown hint: $1"
hintDeclaredLoc: "$1"
]
const

View File

@@ -221,7 +221,14 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
if i == limit: return
inc i
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
proc searchImportsAll*(c: PContext, s: PIdent, filter: TSymKinds, holding: var seq[PSym]) =
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s, c.graph):
if s.kind in filter:
holding.add s
proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = @[]
for scope in allScopes(c.currentScope):
var ti: TIdentIter = default(TIdentIter)
@@ -231,14 +238,12 @@ proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKi
result.add candidate
candidate = nextIdentIter(ti, scope.symbols)
proc searchScopesAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = searchScopes(c,s,filter)
if result.len == 0:
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s, c.graph):
if s.kind in filter:
result.add s
searchImportsAll(c, s, filter, result)
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = @[]
block outer:
for scope in allScopes(c.currentScope):
@@ -252,11 +257,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
candidate = nextIdentIter(ti, scope.symbols)
if result.len == 0:
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s, c.graph):
if s.kind in filter:
result.add s
searchImportsAll(c, s, filter, result)
proc cmpScopes*(ctx: PContext, s: PSym): int =
# Do not return a negative number
@@ -644,7 +645,7 @@ const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
includePureEnum = false): seq[PSym] =
result = searchInScopesFilterBy(c, ident, filter)
result = selectFromScopesElseAll(c, ident, filter)
if skEnumField in filter and (result.len == 0 or includePureEnum):
result.add allPureEnumFields(c, ident)
@@ -725,7 +726,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if n.kind == nkOpenSym:
# maybe the logic in semexprs should be mirrored here instead
# for now it only seems this is called for `pickSym` in `getTypeIdent`
# for now it only seems this is called for `pickSym` in `getTypeIdent`
return initOverloadIter(o, c, n[0])
o.importIdx = -1
o.marked = initIntSet()

View File

@@ -1,52 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2017 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the generation of ``.ndi`` files for better debugging
## support of Nim code. "ndi" stands for "Nim debug info".
import ast, msgs, ropes, options, pathutils
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
type
NdiFile* = object
enabled: bool
f: File
buf: string
filename: AbsoluteFile
syms: seq[PSym]
proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
f.buf.setLen 0
f.buf.addInt s.info.line.int
f.buf.add "\t"
f.buf.addInt s.info.col.int
f.f.write(s.name.s, "\t")
f.f.writeRope(s.loc.snippet)
f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
if f.enabled: f.syms.add s
proc open*(f: var NdiFile; filename: AbsoluteFile; conf: ConfigRef) =
f.enabled = not filename.isEmpty
if f.enabled:
f.filename = filename
f.buf = newStringOfCap(20)
proc close*(f: var NdiFile, conf: ConfigRef) =
if f.enabled:
f.f = open(f.filename.string, fmWrite, 8000)
doAssert f.f != nil, f.filename.string
for s in f.syms:
doWrite(f, s, conf)
close(f.f)
f.syms.reset
f.filename.reset

View File

@@ -9,6 +9,8 @@ define:nimPreviewCstringConversion
define:nimPreviewProcConversion
define:nimPreviewRangeDefault
define:nimPreviewNonVarDestructor
define:nimPreviewCheckedClose
define:nimPreviewAsmSemSymbol
threads:off
#import:"$projectpath/testability"

View File

@@ -25,7 +25,7 @@ const
useEffectSystem* = true
useWriteTracking* = false
hasFFI* = defined(nimHasLibFFI)
copyrightYear* = "2024"
copyrightYear* = "2025"
nimEnableCovariance* = defined(nimEnableCovariance)
@@ -229,6 +229,7 @@ type
# alternative to above:
genericsOpenSym
vtables
typeBoundOps
LegacyFeature* = enum
allowSemcheckedAstModification,
@@ -382,6 +383,7 @@ type
warnCounter*: int
errorMax*: int
maxLoopIterationsVM*: int ## VM: max iterations of all loops
maxCallDepthVM*: int ## VM: max call depth
isVmTrace*: bool
configVars*: StringTableRef
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
@@ -597,6 +599,7 @@ proc newConfigRef*(): ConfigRef =
arguments: "",
suggestMaxResults: 10_000,
maxLoopIterationsVM: 10_000_000,
maxCallDepthVM: 2_000,
vmProfileData: newProfileData(),
spellSuggestMax: spellSuggestSecretSauce,
currentConfigDir: ""

View File

@@ -42,10 +42,7 @@ proc makePass*(open: TPassOpen = nil,
process: TPassProcess = nil,
close: TPassClose = nil,
isFrontend = false): TPass =
result.open = open
result.close = close
result.process = process
result.isFrontend = isFrontend
result = (open, process, close, isFrontend)
const
maxPasses = 10
@@ -100,8 +97,8 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
stream: PLLStream): bool {.discardable.} =
if graph.stopCompile(): return true
var
p: Parser
a: TPassContextArray
p: Parser = default(Parser)
a: TPassContextArray = default(TPassContextArray)
s: PLLStream
fileIdx = module.fileIdx
prepareConfigNotes(graph, module)

View File

@@ -590,7 +590,7 @@ proc processCompile(c: PContext, n: PNode) =
var customArgs = ""
if n.kind in nkCallKinds:
s = getStrLit(c, n, 1)
if n.len <= 3:
if n.len == 3:
customArgs = getStrLit(c, n, 2)
else:
localError(c.config, n.info, "'.compile' pragma takes up 2 arguments")
@@ -637,7 +637,10 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
# XXX what to do here if 'amb' is true?
if e != nil:
incl(e.flags, sfUsed)
result.add newSymNode(e)
if isDefined(con.config, "nimPreviewAsmSemSymbol"):
result.add con.semExprWithType(con, newSymNode(e), {efTypeAllowed})
else:
result.add newSymNode(e)
else:
result.add newStrNode(nkStrLit, sub)
else:

View File

@@ -500,6 +500,21 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
dec(c.config.evalTemplateCounter)
discard c.friendModules.pop()
proc getLineInfo(n: PNode): TLineInfo =
case n.kind
of nkPostfix:
if len(n) > 1:
result = getLineInfo(n[1])
else:
result = n.info
of nkAccQuoted, nkPragmaExpr:
if len(n) > 0:
result = getLineInfo(n[0])
else:
result = n.info
else:
result = n.info
const
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
@@ -526,7 +541,9 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
if efNoSemCheck notin flags:
result = semAfterMacroCall(c, n, result, sym, flags, expectedType)
if c.config.macrosToExpand.hasKey(sym.name.s):
message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
message(c.config, nOrig.info, hintExpandMacro, renderTree(result, {
renderNonExportedFields, renderDocComments, renderNoComments
}))
result = wrapInComesFrom(nOrig.info, sym, result)
popInfoContext(c.config)
@@ -671,8 +688,9 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
if child != nil:
let node = newNode(nkIntLit)
node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
semExprWithType(c, child),
let typeNode = newNode(nkType)
typeNode.typ() = makeTypeDesc(c, aTypSkip[1])
result = semExpr(c, newTree(nkCall, newTree(nkBracketExpr, newSymNode(getSysSym(c.graph, a.info, "arrayWithDefault"), a.info), typeNode),
node
))
result.typ() = aTyp
@@ -733,6 +751,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
result.semInferredLambda = semInferredLambda
result.semGenerateInstance = generateInstance
result.instantiateOnlyProcType = instantiateOnlyProcType
result.fitDefaultNode = fitDefaultNode
result.semTypeNode = semTypeNode
result.instTypeBoundOp = sigmatch.instTypeBoundOp
result.hasUnresolvedArgs = hasUnresolvedArgs
@@ -853,9 +872,9 @@ proc semWithPContext*(c: PContext, n: PNode): PNode =
proc reportUnusedModules(c: PContext) =
if c.config.cmd == cmdM: return
for i in 0..high(c.unusedImports):
if sfUsed notin c.unusedImports[i][0].flags:
message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
for (s, info) in c.unusedImports:
if sfUsed notin s.flags:
message(c.config, info, warnUnusedImportX, s.name.s)
proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
if c.config.cmd == cmdIdeTools and not c.suggestionsMade:

View File

@@ -56,7 +56,7 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
proc name[T: static proc()]() = T()
name[proc() = echo"hello"]()
]#
for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}):
for paramSym in searchScopesAll(c, symx.name, {skConst}):
let paramTyp = paramSym.typ
if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter:
result.add((paramTyp.n.sym, o.lastOverloadScope))
@@ -69,6 +69,39 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
result[0].scope, diagnostics)
best.state = csNoMatch
proc isAttachableRoutineTo(prc: PSym, arg: PType): bool =
result = false
if arg.owner != prc.owner: return false
for i in 1 ..< prc.typ.len:
if prc.typ.n[i].kind == nkSym and prc.typ.n[i].sym.ast != nil:
# has default value, parameter is not considered in type attachment
continue
let t = nominalRoot(prc.typ[i])
if t != nil and t.itemId == arg.itemId:
# parameter `i` is a nominal type in this module
# attachable if the nominal root `t` has the same id as `arg`
return true
proc addTypeBoundSymbols(graph: ModuleGraph, arg: PType, name: PIdent,
filter: TSymKinds, marker: var IntSet,
syms: var seq[tuple[s: PSym, scope: int]]) =
# add type bound ops for `name` based on the argument type `arg`
if arg != nil:
# argument must be typed first, meaning arguments always
# matching `untyped` are ignored
let t = nominalRoot(arg)
if t != nil and t.owner.kind == skModule:
# search module for routines attachable to `t`
let module = t.owner
var iter = default(ModuleIter)
var s = initModuleIter(iter, graph, module, name)
while s != nil:
if s.kind in filter and s.isAttachableRoutineTo(t) and
not containsOrIncl(marker, s.id):
# least priority scope, less than explicit imports:
syms.add((s, -2))
s = nextModuleIter(iter, graph)
proc pickBestCandidate(c: PContext, headSymbol: PNode,
n, orig: PNode,
initialBinding: PNode,
@@ -88,10 +121,23 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
best, alt, o, diagnosticsFlag)
if len(syms) == 0:
return
let allowTypeBoundOps = typeBoundOps in c.features and
# qualified or bound symbols cannot refer to type bound ops
headSymbol.kind in {nkIdent, nkAccQuoted, nkOpenSymChoice, nkOpenSym}
var symMarker = initIntSet()
for s in syms:
symMarker.incl(s.s.id)
# current overload being considered
var sym = syms[0].s
let name = sym.name
var scope = syms[0].scope
if allowTypeBoundOps:
for a in 1 ..< n.len:
# for every already typed argument, add type bound ops
let arg = n[a]
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
# starts at 1 because 0 is already done with setup, only needs checking
var nextSymIndex = 1
var z: TCandidate # current candidate
@@ -106,6 +152,14 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# may introduce new symbols with caveats described in recalc branch
matches(c, n, orig, z)
if allowTypeBoundOps:
# this match may have given some arguments new types,
# in which case add their type bound ops as well
# type bound ops of arguments always matching `untyped` are not considered
for x in z.newlyTypedOperands:
let arg = n[x]
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind == skIterator:
@@ -136,7 +190,14 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# before any further candidate init and compare. SLOW, but rare case.
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
best, alt, o, diagnosticsFlag)
symMarker = initIntSet()
for s in syms:
symMarker.incl(s.s.id)
if allowTypeBoundOps:
for a in 1 ..< n.len:
# for every already typed argument, add type bound ops
let arg = n[a]
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
# reset counter because syms may be in a new order
symCount = c.currentScope.symbols.counter
nextSymIndex = 0
@@ -912,8 +973,14 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
# common case; check the only candidate has the right
# number of generic type parameters:
result = explicitGenericSym(c, n, s, errors, doError)
if doError and result == nil:
notFoundError(c, n, errors)
if result == nil:
if c.inGenericContext > 0:
# same as in semOverloadedCall, make expression untyped,
# may have failed match due to unresolved types
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
elif doError:
notFoundError(c, n, errors)
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
# choose the generic proc with the proper number of type parameters.
result = newNodeI(a.kind, getCallLineInfo(n))
@@ -923,6 +990,14 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
skFunc, skIterator}:
let x = explicitGenericSym(c, n, candidate, errors, doError)
if x != nil: result.add(x)
elif c.inGenericContext > 0:
# same as in semOverloadedCall, make expression untyped,
# may have failed match due to unresolved types
# any failing match stops building the symchoice for correctness,
# can also make it untyped from the start
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
# get rid of nkClosedSymChoice if not ambiguous:
if result.len == 0:
result = nil

View File

@@ -142,6 +142,7 @@ type
instantiateOnlyProcType*: proc (c: PContext, pt: LayeredIdTable,
prc: PSym, info: TLineInfo): PType
# used by sigmatch for explicit generic instantiations
fitDefaultNode*: proc (c: PContext, n: var PNode, expectedType: PType)
includedFiles*: IntSet # used to detect recursive include files
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
userPragmas*: TStrTable

View File

@@ -1006,9 +1006,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
n.typ.flags.incl tfUnresolved
# optimization pass: not necessary for correctness of the semantic pass
if callee.kind == skConst or
if (callee.kind == skConst or
{sfNoSideEffect, sfCompileTime} * callee.flags != {} and
{sfForward, sfImportc} * callee.flags == {} and n.typ != nil:
{sfForward, sfImportc} * callee.flags == {}) and n.typ != nil:
if callee.kind != skConst and
sfCompileTime notin callee.flags and
@@ -1492,6 +1492,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
markUsed(c, n.info, s)
onUse(n.info, s)
if s.typ == nil:
return localErrorNode(c, n, "symbol '$1' has no type" % [s.name.s])
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
return s.typ.n
result = newSymNode(s, n.info)
@@ -1678,29 +1680,46 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
result = tryReadingGenericParam(c, n, i, t)
flags.incl efCannotBeDotCall
proc dotTransformation(c: PContext, n: PNode): PNode =
proc hiddenDerefDepth(n: PNode): int =
result = 0
var n = n
while n.kind == nkHiddenDeref:
inc result
n = n[0]
proc dotTransformation(c: PContext, n: PNode, initialDerefs: int): PNode =
var root = n[0]
let currentDerefs = hiddenDerefDepth(root)
if currentDerefs > initialDerefs:
# hidden derefs were inserted by `builtinFieldAccess` for fields of
# `ref object` etc.
# undo the derefs for overload resolution
for _ in initialDerefs ..< currentDerefs:
root = root[0]
root = copyTree(root)
if isSymChoice(n[1]) or
# generics usually leave field names as symchoices, but not types
(n[1].kind == nkSym and n[1].sym.kind == skType):
result = newNodeI(nkDotCall, n.info)
result.add n[1]
result.add copyTree(n[0])
result.add root
else:
var i = considerQuotedIdent(c, n[1], n)
result = newNodeI(nkDotCall, n.info)
result.flags.incl nfDotField
result.add newIdentNode(i, n[1].info)
result.add copyTree(n[0])
result.add root
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this is difficult, because the '.' is used in many different contexts
# in Nim. We first allow types in the semantic checking.
var f = flags - {efIsDotCall}
let initialDerefDepth = hiddenDerefDepth(n[0])
result = builtinFieldAccess(c, n, f)
if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and
efIsDotCall in flags and callOperator notin c.features and
efCannotBeDotCall notin f):
result = dotTransformation(c, n)
result = dotTransformation(c, n, initialDerefDepth)
proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
result = newNodeI(nkCall, n.info)
@@ -1814,7 +1833,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags, afterOverloading = f
# type parameters: partial generic specialization
n[0] = semSymGenericInstantiation(c, n[0], s)
result = maybeInstantiateGeneric(c, n, s, doError = afterOverloading)
if result != nil:
if result != nil and
# leave untyped generic expression alone:
(result.typ == nil or result.typ.kind != tyFromExpr):
# check newly created sym/symchoice
result = semExpr(c, result, flags)
of skMacro, skTemplate:
@@ -2000,13 +2021,14 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
# --> `f=` (r, x)
let nOrig = n.copyTree
var flags = {efLValue}
let initialDerefDepth = hiddenDerefDepth(a[0])
a = builtinFieldAccess(c, a, flags)
if a == nil:
a = propertyWriteAccess(c, n, nOrig, n[0])
if a != nil: return a
# we try without the '='; proc that return 'var' or macros are still
# possible:
a = dotTransformation(c, n[0])
a = dotTransformation(c, n[0], initialDerefDepth)
if a.kind == nkDotCall:
a.transitionSonsKind(nkCall)
a = semExprWithType(c, a, {efLValue})
@@ -2906,7 +2928,10 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
# hasEmpty/nil check is to not break existing code like
# `const foo = [(1, {}), (2, {false})]`,
# `const foo = if true: (0, nil) else: (1, new(int))`
n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info)
let conversion = indexTypesMatch(c, expectedElemType, n[i][1].typ, n[i][1])
# ignore matching error, full tuple will be matched later which may call converter, see #24609
if conversion != nil:
n[i][1] = conversion
if n[i][1].typ.kind == tyTypeDesc:
localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.")

View File

@@ -17,9 +17,17 @@ type
field: PSym
replaceByFieldName: bool
c: PContext
leftPartOfDefinition: bool
proc instFieldLoopBody(c: var TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
proc wrapNewScope(c: PContext, n: PNode): PNode {.inline.} =
# use `if true` to not interfere with `break`
# just opening scope via `openScope(c)` isn't enough,
# a scope has to be opened in the codegen as well for reused
# template instantiations
let trueLit = newIntLit(c.graph, n.info, 1)
trueLit.typ() = getSysType(c.graph, n.info, tyBool)
result = newTreeI(nkIfStmt, n.info, newTreeI(nkElifBranch, n.info, trueLit, n))
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
if c.field != nil and isEmptyType(c.field.typ):
result = newNode(nkEmpty)
return
@@ -30,9 +38,6 @@ proc instFieldLoopBody(c: var TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
let ident = considerQuotedIdent(c.c, n)
if c.replaceByFieldName:
if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
if c.leftPartOfDefinition:
localError(c.c.config, n.info,
"redefine field variable '$1' in a 'fields' loop" % [ident.s])
let fieldName = if c.tupleType.isNil: c.field.name.s
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
else: c.tupleType.n[c.tupleIndex].sym.name.s
@@ -41,9 +46,6 @@ proc instFieldLoopBody(c: var TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
# other fields:
for i in ord(c.replaceByFieldName)..<forLoop.len-2:
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
if c.leftPartOfDefinition:
localError(c.c.config, n.info,
"redefine field variable '$1' in a 'fields' loop" % [ident.s])
var call = forLoop[^2]
var tupl = call[i+1-ord(c.replaceByFieldName)]
if c.field.isNil:
@@ -55,13 +57,6 @@ proc instFieldLoopBody(c: var TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
result.add(tupl)
result.add(newSymNode(c.field, n.info))
break
of nkIdentDefs, nkVarTuple, nkConstDef:
result = shallowCopy(n)
c.leftPartOfDefinition = true
result[0] = instFieldLoopBody(c, n[0], forLoop)
c.leftPartOfDefinition = false
for i in 1..<n.len:
result[i] = instFieldLoopBody(c, n[i], forLoop)
else:
if n.kind == nkContinueStmt:
localError(c.c.config, n.info,
@@ -86,7 +81,9 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
)
openScope(c.c)
inc c.c.inUnrolledContext
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
var body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
# new scope for each field that codegen should know about:
body = wrapNewScope(c.c, body)
father.add(semStmt(c.c, body, {}))
dec c.c.inUnrolledContext
closeScope(c.c)
@@ -162,6 +159,8 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
replaceByFieldName: m == mFieldPairs
)
var body = instFieldLoopBody(fc, loopBody, n)
# new scope for each field that codegen should know about:
body = wrapNewScope(c, body)
inc c.inUnrolledContext
stmts.add(semStmt(c, body, {}))
dec c.inUnrolledContext

View File

@@ -589,7 +589,7 @@ proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
raise newException(ValueError, "invalid enum value: " & str)
else:
localError(g.config, s.info, "unsupported type $1 for define '$2'" %
[name, typeToString(rawTyp)])
[typeToString(rawTyp), name])
except ValueError as e:
localError(g.config, s.info,
"could not process define '$1' of type $2; $3" %

View File

@@ -218,7 +218,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
let ident = considerQuotedIdent(c, n)
# could be type conversion if like a.T and not a.T()
let symKinds = if inCall: routineKinds else: routineKinds+{skType}
var candidates = searchInScopesFilterBy(c, ident, symKinds)
var candidates = selectFromScopesElseAll(c, ident, symKinds)
if candidates.len > 0:
let s = candidates[0] # XXX take into account the other candidates!
isMacro = s.kind in {skTemplate, skMacro}

View File

@@ -10,9 +10,28 @@
## Implements type sanity checking for ASTs resulting from macros. Lots of
## room for improvement here.
import ast, msgs, types, options
import ast, msgs, types, options, trees, nimsets
proc ithField(n: PNode, field: var int): PSym =
type
FieldTracker = object
index: int
remaining: int
constr: PNode
delete: bool # to delete fields from inactive case branches
FieldInfo = ref object
sym: PSym
delete: bool
proc caseBranchMatchesExpr(branch, matched: PNode): bool =
# copied from sem
result = false
for i in 0 ..< branch.len-1:
if branch[i].kind == nkRange:
if overlap(branch[i], matched): return true
elif exprStructuralEquivalent(branch[i], matched):
return true
proc ithField(n: PNode, field: var FieldTracker): FieldInfo =
result = nil
case n.kind
of nkRecList:
@@ -23,18 +42,42 @@ proc ithField(n: PNode, field: var int): PSym =
if n[0].kind != nkSym: return
result = ithField(n[0], field)
if result != nil: return
# value of the discriminator field, from (index - remaining - 1 + 1):
# - 1 because the `ithField` call above decreased it by 1,
# + 1 because the constructor node has an initial type child
let val = field.constr[field.index - field.remaining][1]
var branchFound = false
for i in 1..<n.len:
let previousDelete = field.delete
case n[i].kind
of nkOfBranch, nkElse:
of nkOfBranch:
if branchFound or previousDelete or
not caseBranchMatchesExpr(n[i], val):
# if this is not the active case branch,
# mark all fields inside as deleted
field.delete = true
else:
branchFound = true
result = ithField(lastSon(n[i]), field)
if result != nil: return
field.delete = previousDelete
of nkElse:
if branchFound:
# if this is not the active case branch,
# mark all fields inside as deleted
field.delete = true
result = ithField(lastSon(n[i]), field)
if result != nil: return
field.delete = previousDelete
else: discard
of nkSym:
if field == 0: result = n.sym
else: dec(field)
if field.remaining == 0:
result = FieldInfo(sym: n.sym, delete: field.delete)
else:
dec(field.remaining)
else: discard
proc ithField(t: PType, field: var int): PSym =
proc ithField(t: PType, field: var FieldTracker): FieldInfo =
var base = t.baseClass
while base != nil:
let b = skipTypes(base, skipPtrs)
@@ -53,13 +96,16 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
n.typ() = t
n[0].typ() = t
for i in 1..<n.len:
var j = i-1
let field = x.ithField(j)
var tracker = FieldTracker(index: i-1, remaining: i-1, constr: n, delete: false)
let field = x.ithField(tracker)
if field.isNil:
globalError conf, n.info, "invalid field at index " & $i
else:
internalAssert(conf, n[i].kind == nkExprColonExpr)
annotateType(n[i][1], field.typ, conf)
annotateType(n[i][1], field.sym.typ, conf)
if field.delete:
# only codegen fields from active case branches
incl(n[i].flags, nfPreventCg)
of nkPar, nkTupleConstr:
if x.kind == tyTuple:
n.typ() = t

View File

@@ -536,31 +536,33 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
else:
if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move
setOwner(fin, fin.instantiatedFrom)
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
selfSymNode.typ() = fin.typ.firstParamType
wrapperSym.flags.incl sfUsed
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
params = nkFormalParams.newTree(c.graph.emptyNode,
newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
),
name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
if fin.typ[1].skipTypes(abstractInst).kind != tyRef:
bindTypeHook(c, fin, n, attachedDestructor)
else:
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
selfSymNode.typ() = fin.typ.firstParamType
wrapperSym.flags.incl sfUsed
var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
setOwner(transFormedSym, fin)
if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
let origParamType = transFormedSym.ast[bodyPos][1].typ
let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
selfPtr.add transFormedSym.ast[bodyPos][1]
selfPtr.typ() = selfSymbolType
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
# TODO: suppress var destructor warnings; if newFinalizer is not
# TODO: deprecated, try to implement plain T destructor
bindTypeHook(c, transFormedSym, n, attachedDestructor, suppressVarDestructorWarning = true)
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
params = nkFormalParams.newTree(c.graph.emptyNode,
newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
),
name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
setOwner(transFormedSym, fin)
if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
let origParamType = transFormedSym.ast[bodyPos][1].typ
let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
selfPtr.add transFormedSym.ast[bodyPos][1]
selfPtr.typ() = selfSymbolType
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
bindTypeHook(c, transFormedSym, n, attachedDestructor)
result = addDefaultFieldForNew(c, n)
proc semPrivateAccess(c: PContext, n: PNode): PNode =

View File

@@ -79,7 +79,7 @@ proc semConstrField(c: PContext, flags: TExprFlags,
if nfSkipFieldChecking in assignment[1].flags:
discard
elif not fieldVisible(c, field):
localError(c.config, initExpr.info,
localError(c.config, assignment[0].info,
"the field '$1' is not accessible." % [field.name.s])
return
@@ -465,11 +465,14 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
if t == nil:
return localErrorNode(c, result, "object constructor needs an object type")
if t.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
expectedType != nil and expectedType.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
t = expectedType
when false:
# attempted type inference for generic object types,
# doesn't work since n[0] isn't set and seems underspecified
if t.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
expectedType != nil and expectedType.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
t = expectedType
t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
if t.kind == tyRef:
@@ -518,7 +521,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
for j in 1..<i:
let prevId = considerQuotedIdent(c, result[j][0])
if prevId.id == id.id:
localError(c.config, field.info, errFieldInitTwice % id.s)
localError(c.config, field[0].info, errFieldInitTwice % id.s)
hasError = true
break
# 2) No such field exists in the constructed type

View File

@@ -124,10 +124,11 @@ proc collectObjectTree(graph: ModuleGraph, n: PNode) =
else:
graph.objectTree[root].add (depthLevel, typ)
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
if typ == nil or sfGeneratedOp in tracked.owner.flags:
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit = false) =
if typ == nil or (sfGeneratedOp in tracked.owner.flags and not explicit):
# don't create type bound ops for anything in a function with a `nodestroy` pragma
# bug #21987
# unless this is an explicit call, bug #24626
return
when false:
let realType = typ.skipTypes(abstractInst)
@@ -212,6 +213,7 @@ proc varDecl(a: PEffects; n: PNode) {.inline.} =
proc skipHiddenDeref(n: PNode): PNode {.inline.} =
result = if n.kind == nkHiddenDeref: n[0] else: n
proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
let n = skipHiddenDeref(n)
if n.kind != nkSym: return
@@ -1071,7 +1073,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
# rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
createTypeBoundOps(tracked, t, n.info)
createTypeBoundOps(tracked, t, n.info, explicit = true)
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
if op != nil:
n[0].sym = op
@@ -1301,7 +1303,12 @@ proc track(tracked: PEffects, n: PNode) =
let last = lastSon(child)
track(tracked, last)
of nkCaseStmt: trackCase(tracked, n)
of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
of nkWhen: # This should be a "when nimvm" node.
let oldState = tracked.init.len
track(tracked, n[0][1])
tracked.init.setLen(oldState)
track(tracked, n[1][0])
of nkIfStmt, nkIfExpr: trackIf(tracked, n)
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
of nkWhileStmt:
# 'while true' loop?
@@ -1638,6 +1645,9 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
s.kind in {skProc, skFunc, skConverter, skMethod}:
var res = s.ast[resultPos].sym # get result symbol
t.scopes[res.id] = t.currentBlock
if sfNoInit in s.flags:
# marks result "noinit"
incl res.flags, sfNoInit
track(t, body)
@@ -1650,13 +1660,14 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
(t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
(isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
createTypeBoundOps(t, typ, param.info)
if isOutParam(typ) and param.id notin t.init:
if isOutParam(typ) and param.id notin t.init and s.magic == mNone:
message(g.config, param.info, warnProveInit, param.name.s)
if not isEmptyType(s.typ.returnType) and
(s.typ.returnType.requiresInit or s.typ.returnType.skipTypes(abstractInst).kind == tyVar or
strictDefs in c.features) and
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone and
sfNoInit notin s.flags:
var res = s.ast[resultPos].sym # get result symbol
if res.id notin t.init and breaksBlock(body) != bsNoReturn:
if tfRequiresInit in s.typ.returnType.flags:

View File

@@ -492,19 +492,8 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = tru
incl(result.flags, sfGlobal)
result.options = c.config.options
proc getLineInfo(n: PNode): TLineInfo =
case n.kind
of nkPostfix:
if len(n) > 1:
return getLineInfo(n[1])
of nkAccQuoted, nkPragmaExpr:
if len(n) > 0:
return getLineInfo(n[0])
else:
discard
result = n.info
let info = getLineInfo(n)
if reportToNimsuggest:
let info = getLineInfo(n)
suggestSym(c.graph, info, result, c.graph.usageSym)
proc checkNilable(c: PContext; v: PSym) =
@@ -612,11 +601,38 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
else:
localError(c.config, n.info, "nkDotNode requires 2 children")
proc checkDefineType(c: PContext; v: PSym; t: PType) =
# see semfold.foldDefine for acceptable types
let typeKinds =
case v.magic
of mStrDefine: {tyString, tyCstring}
# this used to be not typechecked, so anything that accepts int nodes for compatbility:
of mIntDefine: {tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyChar, tyEnum}
of mBoolDefine: {tyBool}
of mGenericDefine: {tyString, tyCstring, tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyEnum}
else: raiseAssert("unreachable")
var skipped = abstractVarRange
if v.magic == mGenericDefine:
# no distinct types for generic define
skipped.excl tyDistinct
if t.skipTypes(skipped).kind notin typeKinds:
let name =
case v.magic
of mStrDefine: "strdefine"
of mIntDefine: "intdefine"
of mBoolDefine: "booldefine"
of mGenericDefine: "define"
else: raiseAssert("unreachable")
localError(c.config, v.info, "unsupported type for constant '" & v.name.s &
"' with ." & name & " pragma: " & typeToString(t))
proc setVarType(c: PContext; v: PSym, typ: PType) =
if v.typ != nil and not sameTypeOrNil(v.typ, typ):
localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" &
v.name.s & "': previous type was: " & typeToString(v.typ, preferDesc) &
"; new type is: " & typeToString(typ, preferDesc))
if v.kind == skConst and v.magic in {mGenericDefine, mIntDefine, mStrDefine, mBoolDefine}:
checkDefineType(c, v, typ)
v.typ = typ
proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
@@ -1698,12 +1714,14 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
obj.ast[0] = a[0].shallowCopy
if a[0][0].kind == nkPostfix:
obj.ast[0][0] = a[0][0].shallowCopy
obj.ast[0][0][0] = a[0][0][0] # ident "*"
obj.ast[0][0][1] = symNode
else:
obj.ast[0][0] = symNode
obj.ast[0][1] = a[0][1]
of nkPostfix:
obj.ast[0] = a[0].shallowCopy
obj.ast[0][0] = a[0][0] # ident "*"
obj.ast[0][1] = symNode
else: assert(false)
obj.ast[1] = a[1]
@@ -1758,6 +1776,17 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
# check the style here after the pragmas have been processed:
styleCheckDef(c, s)
# compute the type's size and check for illegal recursions:
if a[0].kind == nkPragmaExpr:
let pragmas = a[0][1]
for i in 0 ..< pragmas.len:
if pragmas[i].kind == nkExprColonExpr and
pragmas[i][0].kind == nkIdent and
whichKeyword(pragmas[i][0].ident) == wSize:
if s.typ.kind != tyEnum and sfImportc notin s.flags:
# EventType* {.size: sizeof(uint32).} = enum
# AtomicFlag* {.importc: "atomic_flag", header: "<stdatomic.h>", size: 1.} = object
localError(c.config, pragmas[i].info, "size pragma only allowed for enum types and imported types")
if a[1].kind == nkEmpty:
var x = a[2]
if x.kind in nkCallKinds and nfSem in x.flags:
@@ -2035,14 +2064,27 @@ proc canonType(c: PContext, t: PType): PType =
else:
result = t
proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
if sfOverridden notin prevOp.flags:
proc prevDestructor(c: PContext; op: TTypeAttachedOp; prevOp: PSym; obj: PType; info: TLineInfo) =
var msg = "cannot bind another '" & AttachedOpToStr[op] & "' to: " & typeToString(obj)
if prevOp == nil:
# happens if the destructor was implicitly constructed for a specific instance,
# not the entire generic type
msg.add "; previous declaration was constructed implicitly"
elif sfOverridden notin prevOp.flags:
msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
else:
msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
localError(c.config, info, errGenerated, msg)
proc checkedForDestructor(t: PType): bool =
if tfCheckedForDestructor in t.flags:
return true
# maybe another instance was instantiated, marking the generic root:
let root = genericRoot(t)
if root != nil and tfGenericHasDestructor in root.flags:
return true
result = false
proc whereToBindTypeHook(c: PContext; t: PType): PType =
result = t
while true:
@@ -2076,10 +2118,10 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
discard "forward declared destructor"
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
elif ao.isNil and not checkedForDestructor(obj):
setAttachedOp(c.graph, c.module.position, obj, op, s)
else:
prevDestructor(c, ao, obj, n.info)
prevDestructor(c, op, ao, obj, n.info)
noError = true
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
@@ -2092,7 +2134,7 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
incl(s.flags, sfUsed)
incl(s.flags, sfOverridden)
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) =
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
let t = s.typ
var noError = false
let cond = case op
@@ -2116,17 +2158,17 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressV
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
if (not suppressVarDestructorWarning) and op == attachedDestructor and t.firstParamType.kind == tyVar and
if op == attachedDestructor and t.firstParamType.kind == tyVar and
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter")
obj = canonType(c, obj)
let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
discard "forward declared destructor"
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
elif ao.isNil and not checkedForDestructor(obj):
setAttachedOp(c.graph, c.module.position, obj, op, s)
else:
prevDestructor(c, ao, obj, n.info)
prevDestructor(c, op, ao, obj, n.info)
noError = true
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
@@ -2217,10 +2259,10 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
let ao = getAttachedOp(c.graph, obj, k)
if ao == s:
discard "forward declared op"
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
elif ao.isNil and not checkedForDestructor(obj):
setAttachedOp(c.graph, c.module.position, obj, k, s)
else:
prevDestructor(c, ao, obj, n.info)
prevDestructor(c, k, ao, obj, n.info)
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
@@ -2309,7 +2351,7 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) =
isInitializer = false
break
var j = 0
while p[j].sym.kind == skParam:
while p[j].kind == nkSym and p[j].sym.kind == skParam:
initializerCall.add val
inc j
if isInitializer:
@@ -2359,6 +2401,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
of nkEmpty:
s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info)
s.flags.incl sfUsed
s.flags.incl sfGenSym
n[namePos] = newSymNode(s)
of nkSym:
s = n[namePos].sym
@@ -2647,7 +2690,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
incl(s.typ.flags, tfCapturesEnv)
else:
s.typ.callConv = ccInline
if n[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
if result[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
localError(c.config, n.info, errImplOfXexpected % s.name.s)
if optOwnedRefs in c.config.globalOptions and result.typ != nil:
result.typ() = makeVarType(c, result.typ, tyOwned)

View File

@@ -67,7 +67,12 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
# for instance 'nextTry' is both in tables.nim and astalgo.nim ...
if not isField or sfGenSym notin s.flags:
result = newSymNode(s, info)
markUsed(c, info, s)
if isField:
# possibly not final field sym
incl(s.flags, sfUsed)
markOwnerModuleAsUsed(c, s)
else:
markUsed(c, info, s)
onUse(info, s)
else:
result = n
@@ -691,6 +696,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
s = semIdentVis(c, skTemplate, n[namePos], {})
assert s.kind == skTemplate
let info = getLineInfo(n[namePos])
suggestSym(c.graph, info, s, c.graph.usageSym)
styleCheckDef(c, s)
onDef(n[namePos].info, s)
# check parameter list:

View File

@@ -84,6 +84,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
let isPure = result.sym != nil and sfPure in result.sym.flags
var symbols: TStrTable = initStrTable()
var hasNull = false
var needsReorder = false
for i in 1..<n.len:
if n[i].kind == nkEmpty: continue
var useAutoCounter = false
@@ -122,7 +123,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
else:
localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc))
if i != 1:
if x != counter: incl(result.flags, tfEnumHasHoles)
if x != counter:
needsReorder = true
incl(result.flags, tfEnumHasHoles)
e.ast = strVal # might be nil
counter = x
of nkSym:
@@ -173,6 +176,13 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
localError(c.config, n[i].info, errOverflowInEnumX % [e.name.s, $high(typeof(counter))])
else:
inc(counter)
if needsReorder:
result.n.sons.sort(
proc (x, y: PNode): int =
result = cmp(x.sym.position, y.sym.position)
)
if isPure and sfExported in result.sym.flags:
addPureEnum(c, LazySym(sym: result.sym))
if tfNotNil in e.typ.flags and not hasNull:
@@ -247,27 +257,39 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
else:
return false
proc fitDefaultNode(c: PContext, n: PNode): PType =
inc c.inStaticContext
let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
let oldType = n[^1].typ
n[^1].flags.incl nfSem
if n[^2].kind != nkEmpty:
if expectedType != nil and oldType != expectedType:
n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info)
changeType(c, n[^1], expectedType, true) # infer types for default fields value
# bug #22926; be cautious that it uses `semConstExpr` to
# evaulate the default fields; it's only natural to use
# `changeType` to infer types for constant values
# that's also the reason why we don't use `semExpr` to check
# the type since two overlapping error messages might be produced
result = n[^1].typ
proc annotateClosureConv(n: PNode) =
case n.kind
of {nkNone..nkNilLit}:
discard
of nkTupleConstr:
if n.typ.kind == tyProc and n.typ.callConv == ccClosure and
n[0].typ.kind == tyProc and n[0].typ.callConv != ccClosure:
# restores `transf.generateThunk`
n[0] = newTreeIT(nkHiddenSubConv, n[0].info, n.typ,
newNodeI(nkEmpty, n[0].info), n[0])
n.transitionSonsKind(nkClosure)
n.flags.incl nfTransf
else:
result = n[^1].typ
for i in 0..<n.len:
annotateClosureConv(n[i])
proc fitDefaultNode(c: PContext, n: var PNode, expectedType: PType) =
inc c.inStaticContext
n = semConstExpr(c, n, expectedType = expectedType)
let oldType = n.typ
n.flags.incl nfSem
if expectedType != nil and oldType != expectedType:
n = fitNodeConsiderViewType(c, expectedType, n, n.info)
changeType(c, n, expectedType, true) # infer types for default fields value
# bug #22926; be cautious that it uses `semConstExpr` to
# evaulate the default fields; it's only natural to use
# `changeType` to infer types for constant values
# that's also the reason why we don't use `semExpr` to check
# the type since two overlapping error messages might be produced
annotateClosureConv(n)
# xxx any troubles related to defaults fields, consult `semConst` for a potential answer
if n[^1].kind != nkNilLit:
typeAllowedCheck(c, n.info, result, skConst, {taProcContextIsNotMacro, taIsDefaultField})
if n.kind != nkNilLit:
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
dec c.inStaticContext
proc isRecursiveType*(t: PType): bool =
@@ -387,8 +409,12 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
result = makeRangeWithStaticExpr(c, e.typ.n)
elif e.kind in {nkIntLit..nkUInt64Lit}:
if e.intVal < 0:
localError(c.config, n.info,
"Array length can't be negative, but was " & $e.intVal)
if e.kind in {nkIntLit..nkInt64Lit}:
localError(c.config, n.info,
"Array length can't be negative, but was " & $e.intVal)
else:
localError(c.config, n.info,
"Array length can't exceed its maximum value (9223372036854775807), but was " & $cast[BiggestUInt](e.intVal))
result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc):
if e.typ.kind == tyStatic:
@@ -494,7 +520,14 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
checkMinSonsLen(a, 3, c.config)
var hasDefaultField = a[^1].kind != nkEmpty
if hasDefaultField:
typ = fitDefaultNode(c, a)
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
if c.inGenericContext > 0:
a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = a[^1].typ
else:
fitDefaultNode(c, a[^1], typ)
typ = a[^1].typ
elif a[^2].kind != nkEmpty:
typ = semTypeNode(c, a[^2], nil)
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
@@ -858,8 +891,15 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
var typ: PType
var hasDefaultField = n[^1].kind != nkEmpty
if hasDefaultField:
typ = fitDefaultNode(c, n)
propagateToOwner(rectype, typ)
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
if c.inGenericContext > 0:
n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = n[^1].typ
else:
fitDefaultNode(c, n[^1], typ)
typ = n[^1].typ
propagateToOwner(rectype, typ)
elif n[^2].kind == nkEmpty:
localError(c.config, n.info, errTypeExpected)
typ = errorType(c)
@@ -1631,6 +1671,11 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
var err = "cannot instantiate "
err.addTypeHeader(c.config, t)
err.add "\ngot: <$1>\nbut expected: <$2>" % [describeArgs(c, n), describeArgs(c, t.n, 0)]
if m.firstMismatch.kind == kTypeMismatch and m.firstMismatch.arg < n.len:
let nArg = n[m.firstMismatch.arg]
if nArg.kind in nkSymChoices:
err.add "\n"
err.add ambiguousIdentifierMsg(nArg)
localError(c.config, n.info, errGenerated, err)
return newOrPrevType(tyError, prev, c)
@@ -1831,7 +1876,7 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
x.add(operand.copyTreeWithoutNode(p))
# recursion assures that this works for multiple macro annotations too:
var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
if r != nil:
if r != nil and (r.typ == nil or r.typ.kind != tyFromExpr):
doAssert r[0].kind == nkSym
let m = r[0].sym
case m.kind

View File

@@ -80,7 +80,7 @@ type
owner*: PSym # where this instantiation comes from
recursionLimit: int
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode
@@ -95,8 +95,8 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code
debug t
writeStackTrace()
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
result = replaceTypeVarsTAux(cl, t)
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType, isInstValue = false): PType =
result = replaceTypeVarsTAux(cl, t, isInstValue)
checkMetaInvariants(cl, result)
proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
@@ -276,6 +276,11 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
replaceTypeVarsS(cl, n.sym, result.typ)
else:
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
if result.sym.kind == skField and result.sym.ast != nil and
(cl.owner == nil or result.sym.owner == cl.owner):
# instantiate default value of object/tuple field
cl.c.fitDefaultNode(cl.c, result.sym.ast, result.sym.typ)
result.sym.typ = result.sym.ast.typ
# sym type can be nil if was gensym created by macro, see #24048
if result.sym.typ != nil and result.sym.typ.kind == tyVoid:
# don't add the 'void' field
@@ -339,7 +344,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
#[
We cannot naively check for symbol recursions, because otherwise
object types A, B whould share their fields!
object types A, B would share their fields!
import tables
@@ -476,7 +481,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
return
let bbody = last body
var newbody = replaceTypeVarsT(cl, bbody)
var newbody = replaceTypeVarsT(cl, bbody, isInstValue = true)
cl.skipTypedesc = oldSkipTypedesc
newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
result.flags = result.flags + newbody.flags - tfInstClearedFlags
@@ -573,7 +578,7 @@ proc propagateFieldFlags(t: PType, n: PNode) =
propagateFieldFlags(t, son)
else: discard
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType =
template bailout =
if (t.sym == nil) or (t.sym != nil and sfGeneratedType in t.sym.flags):
# In the first case 't.sym' can be 'nil' if the type is a ref/ptr, see
@@ -707,13 +712,17 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
propagateToOwner(result, result.last)
else:
if containsGenericType(t):
if containsGenericType(t) or
# nominal types as direct generic instantiation values
# are re-instantiated even if they don't contain generic fields
(isInstValue and (t.kind in {tyDistinct, tyObject} or isRefPtrObject(t))):
#if not cl.allowMetaTypes:
bailout()
result = instCopyType(cl, t)
result.size = -1 # needs to be recomputed
#if not cl.allowMetaTypes:
cl.localCache[t.itemId] = result
let propagateInstValue = isInstValue and isRefPtrObject(t)
for i, resulti in result.ikids:
if resulti != nil:
@@ -723,7 +732,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
typeToString(result[i], preferDesc) &
"' inside of type definition: '" &
t.owner.name.s & "'; Maybe generic arguments are missing?")
var r = replaceTypeVarsT(cl, resulti)
var r = replaceTypeVarsT(cl, resulti, isInstValue = propagateInstValue)
if result.kind == tyObject:
# carefully coded to not skip the precious tyGenericInst:
let r2 = r.skipTypes({tyAlias, tySink, tyOwned})

View File

@@ -85,6 +85,9 @@ type
inheritancePenalty: int
firstMismatch*: MismatchInfo # mismatch info for better error messages
diagnosticsEnabled*: bool
newlyTypedOperands*: seq[int]
## indexes of arguments that are newly typechecked in this match
## used for type bound op additions
TTypeRelFlag* = enum
trDontBind
@@ -592,45 +595,6 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
else: result = isIntConv
else: result = isNone
proc reduceToBase(f: PType): PType =
#[
Returns the lowest order (most general) type that that is compatible with the input.
E.g.
A[T] = ptr object ... A -> ptr object
A[N: static[int]] = array[N, int] ... A -> array
]#
case f.kind:
of tyGenericParam:
if f.len <= 0 or f.skipModifier == nil:
result = f
else:
result = reduceToBase(f.skipModifier)
of tyGenericInvocation:
result = reduceToBase(f.baseClass)
of tyCompositeTypeClass, tyAlias:
if not f.hasElementType or f.elementType == nil:
result = f
else:
result = reduceToBase(f.elementType)
of tyGenericInst:
result = reduceToBase(f.skipModifier)
of tyGenericBody:
result = reduceToBase(f.typeBodyImpl)
of tyUserTypeClass:
if f.isResolvedUserTypeClass:
result = f.base # ?? idk if this is right
else:
result = f.skipModifier
of tyStatic, tyOwned, tyVar, tyLent, tySink:
result = reduceToBase(f.base)
of tyInferred:
# This is not true "After a candidate type is selected"
result = reduceToBase(f.base)
of tyRange:
result = f.elementType
else:
result = f
proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
if fGenericOrigin != nil and last.kind == tyGenericInst and
last.kidsLen-1 == fGenericOrigin.kidsLen:
@@ -639,12 +603,22 @@ proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
if x == nil:
put(c, fGenericOrigin[i], last[i])
proc isGenericObjectOf(f, a: PType): bool =
## checks if `f` is an unparametrized generic type
## that `a` is an instance of
if not (f.sym != nil and f.sym.typ.kind == tyGenericBody):
# covers the case where `f` is the last child (body) of the `tyGenericBody`
return false
let aRoot = genericRoot(a)
# use sym equality to check if the `tyGenericBody` types are equal
result = aRoot != nil and f.sym == aRoot.sym
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
var t = a
assert t.kind == tyObject
var depth = 0
var last = a
while t != nil and not sameObjectTypes(f, t):
while t != nil and not (sameObjectTypes(f, t) or isGenericObjectOf(f, t)):
if t.kind != tyObject: # avoid entering generic params etc
return -1
t = t.baseClass
@@ -1865,9 +1839,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
let target = f.genericHead
let targetKind = target.kind
var effectiveArgType = reduceToBase(a)
# the skipped child of tyBuiltInTypeClass can be structured differently,
# newConstraint constructs them with no children
let typeClassArg = effectiveArgType.kind == tyBuiltInTypeClass
effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
if targetKind == effectiveArgType.kind:
if effectiveArgType.isEmptyContainer:
if not typeClassArg and effectiveArgType.isEmptyContainer:
return isNone
if targetKind == tyProc:
if target.flags * {tfIterator} != effectiveArgType.flags * {tfIterator}:
@@ -2728,7 +2705,7 @@ proc setSon(father: PNode, at: int, son: PNode) =
# father[i] = newNodeIT(nkEmpty, son.info, getSysType(tyVoid))
# we are allowed to modify the calling node in the 'prepare*' procs:
proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
proc prepareOperand(c: PContext; formal: PType; a: PNode, newlyTyped: var bool): PNode =
if formal.kind == tyUntyped and formal.len != 1:
# {tyTypeDesc, tyUntyped, tyTyped, tyError}:
# a.typ == nil is valid
@@ -2746,15 +2723,17 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
#elif formal.kind == tyTyped: {efDetermineType, efWantStmt}
#else: {efDetermineType}
result = c.semOperand(c, a, flags)
newlyTyped = true
else:
result = a
considerGenSyms(c, result)
if result.kind != nkHiddenDeref and result.typ.kind in {tyVar, tyLent} and c.matchedConcept == nil:
result = newDeref(result)
proc prepareOperand(c: PContext; a: PNode): PNode =
proc prepareOperand(c: PContext; a: PNode, newlyTyped: var bool): PNode =
if a.typ.isNil:
result = c.semOperand(c, a, {efDetermineType})
newlyTyped = true
else:
result = a
considerGenSyms(c, result)
@@ -2880,7 +2859,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
noMatch()
m.baseTypeMatch = false
m.typedescMatched = false
n[a][1] = prepareOperand(c, formal.typ, n[a][1])
var newlyTyped = false
n[a][1] = prepareOperand(c, formal.typ, n[a][1], newlyTyped)
if newlyTyped: m.newlyTypedOperands.add(a)
n[a].typ() = n[a][1].typ
arg = paramTypesMatch(m, formal.typ, n[a].typ,
n[a][1], n[a][1])
@@ -2904,7 +2885,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
if tfVarargs in m.callee.flags:
# is ok... but don't increment any counters...
# we have no formal here to snoop at:
n[a] = prepareOperand(c, n[a])
var newlyTyped = false
n[a] = prepareOperand(c, n[a], newlyTyped)
if newlyTyped: m.newlyTypedOperands.add(a)
if skipTypes(n[a].typ, abstractVar-{tyTypeDesc}).kind==tyString:
m.call.add implicitConv(nkHiddenStdConv,
getSysType(c.graph, n[a].info, tyCstring),
@@ -2918,7 +2901,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
m.baseTypeMatch = false
m.typedescMatched = false
incl(marker, formal.position)
n[a] = prepareOperand(c, formal.typ, n[a])
var newlyTyped = false
n[a] = prepareOperand(c, formal.typ, n[a], newlyTyped)
if newlyTyped: m.newlyTypedOperands.add(a)
arg = paramTypesMatch(m, formal.typ, n[a].typ,
n[a], nOrig[a])
if arg != nil and m.baseTypeMatch and container != nil:
@@ -2954,7 +2939,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
else:
m.baseTypeMatch = false
m.typedescMatched = false
n[a] = prepareOperand(c, formal.typ, n[a])
var newlyTyped = false
n[a] = prepareOperand(c, formal.typ, n[a], newlyTyped)
if newlyTyped: m.newlyTypedOperands.add(a)
arg = paramTypesMatch(m, formal.typ, n[a].typ,
n[a], nOrig[a])
if arg == nil:

View File

@@ -106,6 +106,13 @@ proc transformSons(c: PTransf, n: PNode, noConstFold = false): PNode =
for i in 0..<n.len:
result[i] = transform(c, n[i], noConstFold)
proc transformSonsAfterType(c: PTransf, n: PNode, noConstFold = false): PNode =
result = newTransNode(n)
assert n.len != 0
result[0] = copyTree(n[0])
for i in 1..<n.len:
result[i] = transform(c, n[i], noConstFold)
proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode =
result = newTransNode(kind, ri.info, 2)
result[0] = le
@@ -366,6 +373,19 @@ proc transformAsgn(c: PTransf, n: PNode): PNode =
result[0] = letSection
result[1] = asgnNode
template assignTupleUnpacking(c: PTransf, e: PNode) =
for i in 0..<c.transCon.forStmt.len - 2:
if c.transCon.forStmt[i].kind == nkVarTuple:
for j in 0..<c.transCon.forStmt[i].len-1:
let lhs = c.transCon.forStmt[i][j]
let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, e, i), j))
result.add(asgnTo(lhs, rhs))
else:
let lhs = c.transCon.forStmt[i]
let rhs = transform(c, newTupleAccess(c.graph, e, i))
result.add(asgnTo(lhs, rhs))
proc transformYield(c: PTransf, n: PNode): PNode =
proc asgnTo(lhs: PNode, rhs: PNode): PNode =
# Choose the right assignment instruction according to the given ``lhs``
@@ -400,7 +420,8 @@ proc transformYield(c: PTransf, n: PNode): PNode =
let lhs = c.transCon.forStmt[i]
let rhs = transform(c, v)
result.add(asgnTo(lhs, rhs))
elif e.kind notin {nkAddr, nkHiddenAddr}: # no need to generate temp for address operation
elif e.kind notin {nkAddr, nkHiddenAddr} and e.kind != nkSym:
# no need to generate temp for address operation + nodes without sideeffects
# TODO do not use temp for nodes which cannot have side-effects
var tmp = newTemp(c, e.typ, e.info)
let v = newNodeI(nkVarSection, e.info)
@@ -408,21 +429,9 @@ proc transformYield(c: PTransf, n: PNode): PNode =
result.add transform(c, v)
for i in 0..<c.transCon.forStmt.len - 2:
if c.transCon.forStmt[i].kind == nkVarTuple:
for j in 0..<c.transCon.forStmt[i].len-1:
let lhs = c.transCon.forStmt[i][j]
let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, tmp, i), j))
result.add(asgnTo(lhs, rhs))
else:
let lhs = c.transCon.forStmt[i]
let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
result.add(asgnTo(lhs, rhs))
assignTupleUnpacking(c, tmp)
else:
for i in 0..<c.transCon.forStmt.len - 2:
let lhs = c.transCon.forStmt[i]
let rhs = transform(c, newTupleAccess(c.graph, e, i))
result.add(asgnTo(lhs, rhs))
assignTupleUnpacking(c, e)
else:
if c.transCon.forStmt[0].kind == nkVarTuple:
var notLiteralTuple = false # we don't generate temp for tuples with const value: (1, 2, 3)
@@ -435,7 +444,8 @@ proc transformYield(c: PTransf, n: PNode): PNode =
else:
notLiteralTuple = true
if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple:
if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple and e.kind != nkSym:
# no need to generate temp for address operation + nodes without sideeffects
# TODO do not use temp for nodes which cannot have side-effects
var tmp = newTemp(c, e.typ, e.info)
let v = newNodeI(nkVarSection, e.info)
@@ -507,7 +517,9 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds, isAddr = false)
) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and
n.typ.kind == tyVar and
n.typ.skipTypes(abstractVar).kind == tyOpenArray and
n[0][0].typ.skipTypes(abstractVar).kind == tyString)
n[0][0].typ.skipTypes(abstractVar).kind == tyString) and
not (isAddr and n.typ.kind == tyVar and n[0][0].typ.kind == tyRef and
n[0][0].kind == nkObjConstr)
: # elimination is harmful to `for tuple unpack` because of newTupleAccess
# it is also harmful to openArrayLoc (var openArray) for strings
# addr ( deref ( x )) --> x
@@ -637,6 +649,12 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
case arg.kind
of nkStmtListExpr:
return paComplexOpenarray
of nkCall:
if skipTypes(arg.typ, abstractInst).kind in {tyOpenArray, tyVarargs}:
# XXX incorrect, causes #13417 when `arg` has side effects.
return paDirectMapping
else:
return paComplexOpenarray
of nkBracket:
return paFastAsgnTakeTypeFromArg
else:
@@ -803,7 +821,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, addrExp, true))
newC.mapping[formal.itemId] = newDeref(temp)
of paComplexOpenarray:
# arrays will deep copy here (pretty bad).
# XXX arrays will deep copy here (pretty bad).
var temp = newTemp(c, arg.typ, formal.info)
addVar(v, temp)
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
@@ -857,9 +875,18 @@ proc transformArrayAccess(c: PTransf, n: PNode): PNode =
if n[0].kind == nkSym and n[0].sym.kind == skType:
result = n
else:
result = newTransNode(n)
for i in 0..<n.len:
result[i] = transform(c, skipConv(n[i]))
result = transformSons(c, n)
if n.len >= 2 and result[1].kind in {nkChckRange, nkChckRange64} and
n[1].kind in {nkHiddenStdConv, nkHiddenSubConv}:
# implicit conversion, was transformed into range check
# remove in favor of index check if conversion to array index type
# has to be done here because the array index type needs to be relaxed
# i.e. a uint32 index can implicitly convert to range[0..3] but not int
let arr = skipTypes(n[0].typ, abstractVarRange)
if arr.kind == tyArray and
firstOrd(c.graph.config, arr) == getOrdValue(result[1][1]) and
lastOrd(c.graph.config, arr) == getOrdValue(result[1][2]):
result[1] = result[1].skipConv
proc getMergeOp(n: PNode): PSym =
case n.kind
@@ -1069,9 +1096,7 @@ proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
of nkBreakStmt: result = transformBreak(c, n)
of nkCallKinds:
result = transformCall(c, n)
of nkHiddenAddr:
result = transformAddrDeref(c, n, {nkHiddenDeref}, isAddr = true)
of nkAddr:
of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}, isAddr = true)
of nkDerefExpr:
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
@@ -1084,6 +1109,9 @@ proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = transformConv(c, n)
of nkObjConstr, nkCast:
# don't try to transform type node
result = transformSonsAfterType(c, n)
of nkDiscardStmt:
result = n
if n[0].kind != nkEmpty:

View File

@@ -209,9 +209,6 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = typeAllowedAux(marker, t.skipModifier, kind, c, flags+{taHeap})
else:
result = t
of tyConcept:
if kind != skParam: result = t
else: result = nil
proc typeAllowed*(t: PType, kind: TSymKind; c: PContext; flags: TTypeAllowedFlags = {}): PType =
# returns 'nil' on success and otherwise the part of the type that is

View File

@@ -102,6 +102,9 @@ const
# typedescX is used if we're sure tyTypeDesc should be included (or skipped)
typedescPtrs* = abstractPtrs + {tyTypeDesc}
typedescInst* = abstractInst + {tyTypeDesc, tyOwned, tyUserTypeClass}
# incorrect definition of `[]` and `[]=` for these types in system.nim
arrPutGetMagicApplies* = {tyArray, tyOpenArray, tyString, tySequence, tyCstring, tyTuple}
proc invalidGenericInst*(f: PType): bool =
result = f.kind == tyGenericInst and skipModifier(f) == nil
@@ -1201,17 +1204,19 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
if not result: return
proc isGenericAlias*(t: PType): bool =
return t.kind == tyGenericInst and t.skipModifier.kind == tyGenericInst
return t.kind == tyGenericInst and t.skipModifier.skipTypes({tyAlias}).kind == tyGenericInst
proc genericAliasDepth*(t: PType): int =
result = 0
var it = t
var it = t.skipTypes({tyAlias})
while it.isGenericAlias:
it = it.skipModifier
it = it.skipModifier.skipTypes({tyAlias})
inc result
proc skipGenericAlias*(t: PType): PType =
return if t.isGenericAlias: t.skipModifier else: t
result = t.skipTypes({tyAlias})
if result.isGenericAlias:
result = result.skipModifier.skipTypes({tyAlias})
proc sameFlags*(a, b: PType): bool {.inline.} =
result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
@@ -1304,16 +1309,34 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
cycleCheck()
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
of tyObject:
withoutShallowFlags:
result = sameFlags(a, b)
if result:
ifFastObjectTypeCheckFailed(a, b):
cycleCheck()
result = sameObjectStructures(a, b, c) and sameFlags(a, b)
# should be generic, and belong to the same generic head type:
assert a.typeInst != nil, "generic object " & $a & " has no typeInst"
assert b.typeInst != nil, "generic object " & $b & " has no typeInst"
if result:
withoutShallowFlags:
# this is required because of generic `ref object`s,
# the value of their dereferences are not wrapped in `tyGenericInst`,
# so we need to check the generic parameters here
for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
if not sameTypeAux(ff, aa, c): return false
of tyDistinct:
cycleCheck()
if c.cmp == dcEq:
if sameFlags(a, b):
result = sameFlags(a, b)
if result:
ifFastObjectTypeCheckFailed(a, b):
result = sameTypeAux(a.elementType, b.elementType, c)
# should be generic, and belong to the same generic head type:
assert a.typeInst != nil, "generic distinct type " & $a & " has no typeInst"
assert b.typeInst != nil, "generic distinct type " & $b & " has no typeInst"
withoutShallowFlags:
# just in case `tyGenericInst` was skipped at some point,
# we need to check the generic parameters here
for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
if not sameTypeAux(ff, aa, c): return false
else:
result = sameTypeAux(a.elementType, b.elementType, c) and sameFlags(a, b)
of tyEnum, tyForward:
@@ -1935,3 +1958,125 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
result = false
else:
result = false
proc isRefPtrObject*(t: PType): bool =
t.kind in {tyRef, tyPtr} and tfRefsAnonObj in t.flags
proc nominalRoot*(t: PType): PType =
## the "name" type of a given instance of a nominal type,
## i.e. the type directly associated with the symbol where the root
## nominal type of `t` was defined, skipping things like generic instances,
## aliases, `var`/`sink`/`typedesc` modifiers
##
## instead of returning the uninstantiated body of a generic type,
## returns the type of the symbol instead (with tyGenericBody type)
result = nil
case t.kind
of tyAlias, tyVar, tySink:
# varargs?
result = nominalRoot(t.skipModifier)
of tyTypeDesc:
# for proc foo(_: type T)
result = nominalRoot(t.skipModifier)
of tyGenericInvocation, tyGenericInst:
result = t
# skip aliases, so this works in the same module but not in another module:
# type Foo[T] = object
# type Bar[T] = Foo[T]
# proc foo[T](x: Bar[T]) = ... # attached to type
while result.skipModifier.kind in {tyGenericInvocation, tyGenericInst}:
result = result.skipModifier
result = nominalRoot(result[0])
of tyGenericBody:
result = t
# this time skip the aliases but take the generic body
while result.skipModifier.kind in {tyGenericInvocation, tyGenericInst}:
result = result.skipModifier[0]
let val = result.skipModifier
if val.kind in {tyDistinct, tyEnum, tyObject} or
isRefPtrObject(val):
# atomic nominal types, this generic body is attached to them
discard
else:
result = nominalRoot(val)
of tyCompositeTypeClass:
# parameter with type Foo
result = nominalRoot(t.skipModifier)
of tyGenericParam:
if t.genericParamHasConstraints:
# T: Foo
result = nominalRoot(t.genericConstraint)
else:
result = nil
of tyDistinct, tyEnum, tyObject:
result = t
of tyPtr, tyRef:
if tfRefsAnonObj in t.flags:
# in the case that we have `type Foo = ref object` etc
result = t
else:
# we could allow this in general, but there's things like `seq[Foo]`
#result = nominalRoot(t.skipModifier)
result = nil
of tyStatic:
result = nominalRoot(t.base)
else:
# skips all typeclasses
# is this correct for `concept`?
result = nil
proc genericRoot*(t: PType): PType =
## gets the root generic type (`tyGenericBody`) from `t`,
## if `t` is a generic type or the body of a generic instantiation
case t.kind
of tyGenericBody:
result = t
of tyGenericInst, tyGenericInvocation:
result = t.genericHead
else:
if t.typeInst != nil:
result = t.typeInst.genericHead
elif t.sym != nil and t.sym.typ.kind == tyGenericBody:
# can happen if `t` is the last child (body) of the generic body
result = t.sym.typ
else:
result = nil
proc reduceToBase*(f: PType): PType =
#[
Returns the lowest order (most general) type that that is compatible with the input.
E.g.
A[T] = ptr object ... A -> ptr object
A[N: static[int]] = array[N, int] ... A -> array
]#
case f.kind:
of tyGenericParam:
if f.len <= 0 or f.skipModifier == nil:
result = f
else:
result = reduceToBase(f.skipModifier)
of tyGenericInvocation:
result = reduceToBase(f.baseClass)
of tyCompositeTypeClass, tyAlias:
if not f.hasElementType or f.elementType == nil:
result = f
else:
result = reduceToBase(f.elementType)
of tyGenericInst:
result = reduceToBase(f.skipModifier)
of tyGenericBody:
result = reduceToBase(f.typeBodyImpl)
of tyUserTypeClass:
if f.isResolvedUserTypeClass:
result = f.base # ?? idk if this is right
else:
result = f.skipModifier
of tyStatic, tyOwned, tyVar, tyLent, tySink:
result = reduceToBase(f.base)
of tyInferred:
# This is not true "After a candidate type is selected"
result = reduceToBase(f.base)
of tyRange:
result = f.elementType
else:
result = f

View File

@@ -516,6 +516,8 @@ const
errIllegalConvFromXtoY = "illegal conversion from '$1' to '$2'"
errTooManyIterations = "interpretation requires too many iterations; " &
"if you are sure this is not a bug in your code, compile with `--maxLoopIterationsVM:number` (current value: $1)"
errCallDepthExceeded = "maximum call depth for the VM exceeded; " &
"if you are sure this is not a bug in your code, compile with `--maxCallDepthVM:number` (current value: $1)"
errFieldXNotFound = "node lacks field: "
@@ -590,6 +592,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let newPc = c.cleanUpOnReturn(tos)
# Perform any cleanup action before returning
if newPc < 0:
inc(c.callDepth)
pc = tos.comesFrom
let retVal = regs[0]
tos = tos.next
@@ -1445,6 +1448,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
newFrame.slots[i] = regs[rb+i]
if isClosure:
newFrame.slots[rc] = TFullReg(kind: rkNode, node: regs[rb].node[1])
if c.callDepth <= 0:
if allowInfiniteRecursion in c.features:
c.callDepth = c.config.maxCallDepthVM
else:
msgWriteln(c.config, "stack trace: (most recent call last)", {msgNoUnitSep})
stackTraceAux(c, tos, pc)
globalError(c.config, c.debug[pc], errCallDepthExceeded % $c.config.maxCallDepthVM)
dec(c.callDepth)
tos = newFrame
updateRegsAlias
# -1 for the following 'inc pc'
@@ -2311,6 +2322,7 @@ proc execute(c: PCtx, start: int): PNode =
proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
c.loopIterations = c.config.maxLoopIterationsVM
c.callDepth = c.config.maxCallDepthVM
if sym.kind in routineKinds:
if sym.typ.paramsLen != args.len:
result = nil

View File

@@ -201,6 +201,7 @@ type
TSandboxFlag* = enum ## what the evaluation engine should allow
allowCast, ## allow unsafe language feature: 'cast'
allowInfiniteLoops ## allow endless loops
allowInfiniteRecursion ## allow infinite recursion
TSandboxFlags* = set[TSandboxFlag]
TSlotKind* = enum # We try to re-use slots in a smart way to
@@ -257,7 +258,7 @@ type
mode*: TEvalMode
features*: TSandboxFlags
traceActive*: bool
loopIterations*: int
loopIterations*, callDepth*: int
comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces
callbacks*: seq[VmCallback]
callbackIndex*: Table[string, int]
@@ -294,6 +295,7 @@ proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator
PCtx(code: @[], debug: @[],
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM,
callDepth: g.config.maxCallDepthVM,
comesFromHeuristic: unknownLineInfo, callbacks: @[], callbackIndex: initTable[string, int](), errorFlag: "",
cache: cache, config: g.config, graph: g, idgen: idgen)
@@ -301,6 +303,7 @@ proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) =
c.module = module
c.prc = PProc(blocks: @[])
c.loopIterations = c.config.maxLoopIterationsVM
c.callDepth = c.config.maxCallDepthVM
c.idgen = idgen
proc reverseName(s: string): string =

View File

@@ -1543,7 +1543,9 @@ proc setSlot(c: PCtx; v: PSym) =
v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
template cannotEval(c: PCtx; n: PNode) =
if c.config.cmd == cmdCheck:
if c.config.cmd == cmdCheck and c.config.m.errorOutputs != {}:
# nim check command with no error outputs doesn't need to cascade here,
# includes `tryConstExpr` case which should not continue generating code
localError(c.config, n.info, "cannot evaluate at compile time: " &
n.renderTree)
c.cannotEval = true
@@ -2038,7 +2040,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
c.gABx(n, opcLdNull, dest, c.genType(n.typ))
let intType = getSysType(c.graph, n.info, tyInt)
let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
let seqType = n.typ.skipTypes(abstractVar+{tyStatic}-{tyTypeDesc})
if seqType.kind == tySequence:
var tmp = c.getTemp(intType)
c.gABx(n, opcLdImmInt, tmp, n.len)

View File

@@ -263,7 +263,7 @@ proc registerAdditionalOps*(c: PCtx) =
wrap2si(readLines, ioop)
systemop getCurrentExceptionMsg
systemop getCurrentException
registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
registerCallback c, "stdlib.staticos.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
setResult(a, dirExists(getString(a, 0)))

View File

@@ -15,7 +15,6 @@ proc dispatch(x: Base, params: ...) =
var disp = newNodeI(nkIfStmt, base.info)
var vTableAccess = newNodeIT(nkBracketExpr, base.info, base.typ)
let nimGetVTableSym = getCompilerProc(g, "nimGetVTable")
let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ
@@ -33,7 +32,7 @@ proc dispatch(x: Base, params: ...) =
dispatchObject,
newIntNode(nkIntLit, index)
)
getVTableCall.typ() = base.typ
getVTableCall.typ() = getSysType(g, unknownLineInfo, tyPointer)
var vTableCall = newNodeIT(nkCall, base.info, base.typ.returnType)
var castNode = newTree(nkCast,
newNodeIT(nkType, base.info, base.typ),

View File

@@ -1,7 +1,7 @@
=====================================================
Nim -- a Compiler for Nim. https://nim-lang.org/
Copyright (C) 2006-2024 Andreas Rumpf. All rights reserved.
Copyright (C) 2006-2025 Andreas Rumpf. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -51,7 +51,7 @@ Advanced options:
--spellSuggest:num show at most `num >= 0` spelling suggestions on typos.
if `num` is not specified (or `auto`), return
an implementation defined set of suggestions.
--hints:on|off|list. `on|off` enables or disables hints.
--hints:on|off|list `on|off` enables or disables hints.
`list` reports which hints are selected.
--hint:X:on|off turn specific hint X on|off. `hint:X` means `hint:X:on`,
as with similar flags. `all` is the set of all hints
@@ -165,6 +165,7 @@ Advanced options:
--verbosity:0|1|2|3 set Nim's verbosity level (1 is default)
--errorMax:N stop compilation after N errors; 0 means unlimited
--maxLoopIterationsVM:N set max iterations for all VM loops
--maxCallDepthVM:N set max call depth in the VM
--experimental:$1
enable experimental language feature
--legacy:$2

View File

@@ -249,7 +249,7 @@ which will likely make your program crash at runtime.
The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch.
Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`.
When compiling to static or dynamic libraries, they don't call destructors of global variables as normal Nim programs would do. A C API `NimDestroyGlobals` is provided to call these global destructors.
When compiling to static or dynamic libraries, they don't call destructors of global variables as normal Nim programs would do. A C API `NimDestroyGlobals` is provided to call these global destructors. It is influenced by the `--nimMainPrefix:prefix` switch, too.
### Nim invocation example from C
@@ -358,7 +358,7 @@ Memory management
In the previous sections, the `NimMain()` function reared its head. Since
JavaScript already provides automatic memory management, you can freely pass
objects between the two languages without problems. In C and derivate languages
objects between the two languages without problems. In C and derivative languages
you need to be careful about what you do and how you share memory. The
previous examples only dealt with simple scalar values, but passing a Nim
string to C, or reading back a C string in Nim already requires you to be

View File

@@ -6264,7 +6264,7 @@ The default for symbols of entity `type`, `var`, `let` and `const`
is `gensym`. For `proc`, `iterator`, `converter`, `template`,
`macro`, the default is `inject`, but if a `gensym` symbol with the same name
is defined in the same syntax-level scope, it will be `gensym` by default.
This can be overriden by marking the routine as `inject`.
This can be overridden by marking the routine as `inject`.
If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol:

View File

@@ -2667,3 +2667,114 @@ proc nothing() =
```
The current C(C++) backend implementation cannot generate code for gcc and for vcc at the same time. For example, `{.asmSyntax: "vcc".}` with the ICC compiler will not generate code with intel asm syntax, even though ICC can use both gcc-like and vcc-like asm.
Type-bound overloads
====================
With the experimental option `--experimental:typeBoundOps`, each "root"
nominal type (namely `object`, `enum`, `distinct`, direct `Foo = ref object`
types as well as their generic versions) can have operations attached to it.
Exported top-level routines declared in the same scope as a nominal type
with a parameter having a type directly deriving from that nominal type (i.e.
with `var`/`sink`/`typedesc` modifiers or being in a generic constraint)
are considered "attached" to the respective nominal type.
This applies to every parameter regardless of placement.
When a call to a symbol is openly overloaded and overload matching starts,
for all arguments in the call that have already undergone type checking,
routines with the same name attached to the root nominal type (if it exists)
of each given argument are added as a candidate to the overload match.
This also happens as arguments gradually get typed after every match to an overload.
This is so that the only overloads considered out of scope are
attached to the types of the given arguments, and that matches to
`untyped` or missing parameters are not influenced by outside overloads.
If no overloads with a given name are in scope, then overload matching
will not begin, and so type-bound overloads are not considered for that name.
Similarly, if the only overloads with a given name require a parameter to be
`untyped` or missing, then type-bound overloads will not be considered for
the argument in that position.
Generally this means that a "base" overload with a compliant signature should
be in scope so that type-bound overloads can be used.
In the case of ambiguity between distinct local/imported and type-bound symbols
in overload matching, type-bound symbols are considered as a less specific
scope than imports.
An example with the `hash` interface in the standard library is as follows:
```nim
# objs.nim
import std/hashes
type
Obj* = object
x*, y*: int
z*: string # to be ignored for equality
proc `==`*(a, b: Obj): bool =
a.x == b.x and a.y == b.y
proc hash*(a: Obj): Hash =
$!(hash(a.x) &! hash(a.y))
# here both `==` and `hash` are attached to Obj
# 1. they are both exported
# 2. they are in the same scope as Obj
# 3. they have parameters with types directly deriving from Obj
# 4. Obj is nominal
```
```nim
# main.nim
{.experimental: "typeBoundOps".}
from objs import Obj # objs.hash, objs.`==` not imported
import std/tables
# tables use `hash`, only using the overloads in `std/hashes` and
# the ones in instantiation scope (in this case, there are none)
var t: Table[Obj, int]
# because tables use `hash` and `==` in a compliant way,
# the overloads bound to Obj are also considered, and in this case match best
t[Obj(x: 3, y: 4, z: "debug")] = 34
# if `hash` for all objects as in `std/hashes` was used, this would error:
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
```
Another example, this time with `$` and indirect imports:
```nim
# foo.nim
type Foo* = object
x*, y*: int
proc `$`*(f: Foo): string =
"Foo(" & $f.x & ", " & $f.y & ")"
```
```nim
# bar.nim
import foo
proc makeFoo*(x, y: int): Foo =
Foo(x: x, y: y)
proc useFoo*(f: Foo) =
echo "used: ", f # directly calls `foo.$` from scope
```
```nim
# debugger.nim
proc debug*[T](obj: T) =
echo "debugging: ", obj # calls generic `$`
```
```nim
# main.nim
{.experimental: "typeBoundOps".}
import bar, debugger # `foo` not imported, so `foo.$` not in scope
let f = makeFoo(123, 456)
useFoo(f) # used: Foo(123, 456)
debug(f) # debugging: Foo(123, 456)
```

View File

@@ -770,11 +770,10 @@ div.topic {
div.search_results {
background-color: var(--third-background);
margin: 3vh 5vw;
padding: 1em;
border: 1px solid #4d4d4d;
position: fixed;
top: 10px;
position: sticky;
top: 1em;
isolation: isolate;
max-width: calc(100vw - 6em);
z-index: 1;

View File

@@ -30,28 +30,30 @@ a working NodeJS on `PATH`.
Commands
========
========================== ==========================
p|pat|pattern <glob> run all the tests matching the given pattern
all run all tests inside of category folders
c|cat|category <category> run all the tests of a certain category
r|run <test> run single test file
html generate testresults.html from the database
========================== ==========================
Options
=======
--print print results to the console
--verbose print commands (compiling and running tests)
--simulate see what tests would be run but don't run them (for debugging)
--failing only show failing/ignored tests
--targets:"c cpp js objc" run tests for specified targets (default: c)
--nim:path use a particular nim executable (default: $PATH/nim)
--directory:dir Change to directory dir before reading the tests or doing anything else.
--colors:on|off Turn messages coloring on|off.
--backendLogging:on|off Disable or enable backend logging. By default turned on.
--megatest:on|off Enable or disable megatest. Default is on.
--valgrind:on|off Enable or disable valgrind support. Default is on.
--skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored
--print print results to the console
--verbose print commands (compiling and running tests)
--simulate see what tests would be run but don't run them (for debugging)
--failing only show failing/ignored tests
--targets:"c cpp js objc" run tests for specified targets (default: c)
--nim:path use a particular nim executable (default: $PATH/nim)
--directory:dir Change to directory dir before reading the tests or doing anything else.
--colors:on|off Turn messages coloring on|off.
--backendLogging:on|off Disable or enable backend logging. By default turned on.
--megatest:on|off Enable or disable megatest. Default is on.
--valgrind:on|off Enable or disable valgrind support. Default is on.
--skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored
Running a single test

View File

@@ -11,7 +11,7 @@
const
# examples of possible values for repos: Head, ea82b54
NimbleStableCommit = "46e2ae13eeb95619a371a74d16efc5aff30ea371" # 0.16.2
NimbleStableCommit = "123f97a5e4ee9ba35720c0869e19a047c43c797e" # 0.16.4
AtlasStableCommit = "5faec3e9a33afe99a7d22377dd1b45a5391f5504"
ChecksumsStableCommit = "bd9bf4eaea124bf8d01e08f92ac1b14c6879d8d3"
SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
@@ -159,13 +159,12 @@ proc bundleNimbleExe(latest: bool, args: string) =
let commit = if latest: "HEAD" else: NimbleStableCommit
cloneDependency(distDir, "https://github.com/nim-lang/nimble.git",
commit = commit, allowBundled = true)
cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/checksums.git",
commit = ChecksumsStableCommit, allowBundled = true) # or copy it from dist?
cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/sat.git",
commit = SatStableCommit, allowBundled = true)
# installer.ini expects it under $nim/bin
updateSubmodules(distDir / "nimble", allowBundled = true)
nimCompile("dist/nimble/src/nimble.nim",
options = "-d:release -d:nimNimbleBootstrap --noNimblePath " & args)
options = "-d:release --noNimblePath " & args)
const zippyTests = "dist/nimble/vendor/zippy/tests"
if dirExists(zippyTests):
removeDir(zippyTests)
proc bundleAtlasExe(latest: bool, args: string) =
let commit = if latest: "HEAD" else: AtlasStableCommit

View File

@@ -44,6 +44,9 @@ runnableExamples:
assert mcCounter.value == 3
when defined(nimPreviewSlimSystem):
import std/assertions
type
CacheSeq* = distinct string
## Compile-time sequence of `NimNode`s.
@@ -207,7 +210,7 @@ proc hasKey*(t: CacheTable; key: string): bool =
mcTable["foo"] = newEmptyNode()
# Will now be true since we inserted a value
assert mcTable.hasKey("foo")
discard "Implemented in vmops"
raiseAssert "implemented in the vmops"
proc contains*(t: CacheTable; key: string): bool {.inline.} =
## Alias of [hasKey][hasKey(CacheTable, string)] for use with the `in` operator.

View File

@@ -365,7 +365,7 @@ proc symBodyHash*(s: NimNode): string {.noSideEffect.} =
## and owning module, but also implementation body. All procs/variables used in
## the implementation of this symbol are hashed recursively as well, including
## magics from system module.
discard
raiseAssert "implemented in the vmops"
proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
## Version of `getTypeImpl` which takes a `typedesc`.
@@ -1327,7 +1327,7 @@ proc `name=`*(someProc: NimNode; val: NimNode) =
else: someProc[0] = val
proc params*(someProc: NimNode): NimNode =
if someProc.kind == nnkProcTy:
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
someProc[0]
else:
someProc.expectRoutine
@@ -1335,7 +1335,7 @@ proc params*(someProc: NimNode): NimNode =
proc `params=`* (someProc: NimNode; params: NimNode) =
expectKind(params, nnkFormalParams)
if someProc.kind == nnkProcTy:
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
someProc[0] = params
else:
someProc.expectRoutine
@@ -1344,7 +1344,7 @@ proc `params=`* (someProc: NimNode; params: NimNode) =
proc pragma*(someProc: NimNode): NimNode =
## Get the pragma of a proc type.
## These will be expanded.
if someProc.kind == nnkProcTy:
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
result = someProc[1]
else:
someProc.expectRoutine
@@ -1352,7 +1352,7 @@ proc pragma*(someProc: NimNode): NimNode =
proc `pragma=`*(someProc: NimNode; val: NimNode) =
## Set the pragma of a proc type.
expectKind(val, {nnkEmpty, nnkPragma})
if someProc.kind == nnkProcTy:
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
someProc[1] = val
else:
someProc.expectRoutine
@@ -1440,7 +1440,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
## it.basename.ident == ident"foo")
## ```
block:
var res: NimNode
var res: NimNode = nil
for it in n.children:
if cond:
res = it
@@ -1508,6 +1508,7 @@ proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} =
proc hasArgOfName*(params: NimNode; name: string): bool =
## Search `nnkFormalParams` for an argument.
result = false
expectKind(params, nnkFormalParams)
for i in 1..<params.len:
for j in 0..<params[i].len-2:
@@ -1575,6 +1576,7 @@ proc extractTypeImpl(n: NimNode): NimNode =
else: error("Invalid node to retrieve type implementation of: " & $n.kind)
proc customPragmaNode(n: NimNode): NimNode =
result = nil
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr})
let
typ = n.getTypeInst()
@@ -1730,7 +1732,7 @@ macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
for i in 0 ..< args.len:
result.add args[i]
proc getProjectPath*(): string = discard
proc getProjectPath*(): string = raiseAssert "implemented in the vmops"
## Returns the path to the currently compiling project.
##
## This is not to be confused with `system.currentSourcePath <system.html#currentSourcePath.t>`_
@@ -1773,6 +1775,7 @@ proc getOffset*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
proc isExported*(n: NimNode): bool {.noSideEffect.} =
## Returns whether the symbol is exported or not.
raiseAssert "implemented in the vmops"
proc extractDocCommentsAndRunnables*(n: NimNode): NimNode =
## returns a `nnkStmtList` containing the top-level doc comments and

View File

@@ -24,7 +24,7 @@ type
proc initRLock*(lock: var RLock) {.inline.} =
## Initializes the given lock.
when defined(posix):
var a: SysLockAttr
var a: SysLockAttr = default(SysLockAttr)
initSysLockAttr(a)
setSysLockType(a, SysLockType_Reentrant)
initSysLock(lock, a.addr)

View File

@@ -145,7 +145,7 @@ proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
of 2: d = int(cast[ptr uint16](a +% n.offset)[])
of 4: d = int(cast[ptr uint32](a +% n.offset)[])
of 8: d = int(cast[ptr uint64](a +% n.offset)[])
else: assert(false)
else: raiseAssert "unreachable"
return d
proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
@@ -158,7 +158,7 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
result = n.sons[n.len]
proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
result.value = value
result = Any(value: value)
result.rawType = rawType
proc toAny*[T](x: var T): Any {.inline.} =
@@ -169,7 +169,7 @@ proc toAny*[T](x: var T): Any {.inline.} =
proc kind*(x: Any): AnyKind {.inline.} =
## Gets the type kind.
result = AnyKind(ord(x.rawType.kind))
result = cast[AnyKind](ord(x.rawType.kind))
proc size*(x: Any): int {.inline.} =
## Returns the size of `x`'s type.
@@ -178,12 +178,16 @@ proc size*(x: Any): int {.inline.} =
proc baseTypeKind*(x: Any): AnyKind {.inline.} =
## Gets the base type's kind. If `x` has no base type, `akNone` is returned.
if x.rawType.base != nil:
result = AnyKind(ord(x.rawType.base.kind))
result = cast[AnyKind](ord(x.rawType.base.kind))
else:
result = akNone
proc baseTypeSize*(x: Any): int {.inline.} =
## Returns the size of `x`'s base type. If `x` has no base type, 0 is returned.
if x.rawType.base != nil:
result = x.rawType.base.size
else:
result = 0
proc invokeNew*(x: Any) =
## Performs `new(x)`. `x` needs to represent a `ref`.
@@ -263,7 +267,7 @@ proc `[]`*(x: Any, i: int): Any =
if i >=% cast[PGenSeq](s).len:
raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
return newAny(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), x.rawType.base)
else: assert false
else: raiseAssert "unreachable"
proc `[]=`*(x: Any, i: int, y: Any) =
## Accessor for an any `x` that represents an array or a sequence.
@@ -292,7 +296,7 @@ proc `[]=`*(x: Any, i: int, y: Any) =
raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
assert y.rawType == x.rawType.base
genericAssign(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), y.value, y.rawType)
else: assert false
else: raiseAssert "unreachable"
proc len*(x: Any): int =
## `len` for an any `x` that represents an array or a sequence.
@@ -308,14 +312,13 @@ proc len*(x: Any): int =
result = 0
else:
result = pgenSeq.len
else: assert false
else: raiseAssert "unreachable"
proc base*(x: Any): Any =
## Returns the base type of `x` (useful for inherited object types).
result = Any(value: x.value)
result.rawType = x.rawType.base
result.value = x.value
proc isNil*(x: Any): bool =
## `isNil` for an `x` that represents a cstring, proc or
@@ -367,7 +370,7 @@ iterator fields*(x: Any): tuple[name: string, any: Any] =
# XXX BUG: does not work yet, however is questionable anyway
when false:
if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
var ret: seq[tuple[name: cstring, any: Any]]
var ret: seq[tuple[name: cstring, any: Any]] = @[]
if t.kind == tyObject:
while true:
fieldsAux(p, t.node, ret)
@@ -379,8 +382,9 @@ iterator fields*(x: Any): tuple[name: string, any: Any] =
yield ($name, any)
proc getFieldNode(p: pointer, n: ptr TNimNode, name: cstring): ptr TNimNode =
result = nil
case n.kind
of nkNone: assert(false)
of nkNone: raiseAssert "unreachable"
of nkSlot:
if cmpNimIdentifier(n.name, name) == 0:
result = n
@@ -418,7 +422,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
assert x.rawType.kind in {tyTuple, tyObject}
let n = getFieldNode(x.value, t.node, fieldName)
if n != nil:
result.value = x.value +!! n.offset
result = Any(value: x.value +!! n.offset)
result.rawType = n.typ
elif x.rawType.kind == tyObject and x.rawType.base != nil:
return `[]`(newAny(x.value, x.rawType.base), fieldName)
@@ -428,7 +432,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
proc `[]`*(x: Any): Any =
## Dereference operator for `Any`. `x` needs to represent a ptr or a ref.
assert x.rawType.kind in {tyRef, tyPtr}
result.value = cast[ppointer](x.value)[]
result = Any(value: cast[ppointer](x.value)[])
result.rawType = x.rawType.base
proc `[]=`*(x, y: Any) =
@@ -481,12 +485,12 @@ proc getBiggestInt*(x: Any): BiggestInt =
of 2: result = int64(cast[ptr uint16](x.value)[])
of 4: result = BiggestInt(cast[ptr int32](x.value)[])
of 8: result = BiggestInt(cast[ptr int64](x.value)[])
else: assert false
else: raiseAssert "unreachable"
of tyUInt: result = BiggestInt(cast[ptr uint](x.value)[])
of tyUInt8: result = BiggestInt(cast[ptr uint8](x.value)[])
of tyUInt16: result = BiggestInt(cast[ptr uint16](x.value)[])
of tyUInt32: result = BiggestInt(cast[ptr uint32](x.value)[])
else: assert false
else: raiseAssert "unreachable"
proc setBiggestInt*(x: Any, y: BiggestInt) =
## Sets the integer value of `x`. `x` needs to represent
@@ -548,7 +552,7 @@ proc getBiggestUint*(x: Any): uint64 =
of tyUInt16: result = uint64(cast[ptr uint16](x.value)[])
of tyUInt32: result = uint64(cast[ptr uint32](x.value)[])
of tyUInt64: result = uint64(cast[ptr uint64](x.value)[])
else: assert false
else: raiseAssert "unreachable"
proc setBiggestUint*(x: Any; y: uint64) =
## Sets the unsigned integer value of `x`. `x` needs to represent an
@@ -560,7 +564,7 @@ proc setBiggestUint*(x: Any; y: uint64) =
of tyUInt16: cast[ptr uint16](x.value)[] = uint16(y)
of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
of tyUInt64: cast[ptr uint64](x.value)[] = uint64(y)
else: assert false
else: raiseAssert "unreachable"
proc getChar*(x: Any): char =
## Retrieves the `char` value out of `x`. `x` needs to represent a `char`.
@@ -577,8 +581,8 @@ proc getBool*(x: Any): bool =
proc skipRange*(x: Any): Any =
## Skips the range information of `x`.
assert x.rawType.kind == tyRange
result = Any(value: x.value)
result.rawType = x.rawType.base
result.value = x.value
proc getEnumOrdinal*(x: Any, name: string): int =
## Gets the enum field ordinal from `name`. `x` needs to represent an enum
@@ -640,7 +644,7 @@ proc getBiggestFloat*(x: Any): BiggestFloat =
of tyFloat: result = BiggestFloat(cast[ptr float](x.value)[])
of tyFloat32: result = BiggestFloat(cast[ptr float32](x.value)[])
of tyFloat64: result = BiggestFloat(cast[ptr float64](x.value)[])
else: assert false
else: raiseAssert "unreachable"
proc setBiggestFloat*(x: Any, y: BiggestFloat) =
## Sets the float value of `x`. `x` needs to represent
@@ -649,7 +653,7 @@ proc setBiggestFloat*(x: Any, y: BiggestFloat) =
of tyFloat: cast[ptr float](x.value)[] = y
of tyFloat32: cast[ptr float32](x.value)[] = y.float32
of tyFloat64: cast[ptr float64](x.value)[] = y
else: assert false
else: raiseAssert "unreachable"
proc getString*(x: Any): string =
## Retrieves the `string` value out of `x`. `x` needs to represent a `string`.
@@ -682,7 +686,7 @@ iterator elements*(x: Any): int =
let typ = x.rawType
let p = x.value
# "typ.slots.len" field is for sets the "first" field
var u: int64
var u: int64 = 0'i64
case typ.size
of 1: u = int64(cast[ptr uint8](p)[])
of 2: u = int64(cast[ptr uint16](p)[])

View File

@@ -82,7 +82,7 @@ proc diffCodes(aText: string; h: var Table[string, int]): DiffData =
## `trimSpace` ignore leading and trailing space characters
## Returns a array of integers.
var lastUsedCode = h.len
result.data = newSeq[int]()
result = DiffData(data: newSeq[int]())
for s in aText.splitLines:
if h.contains s:
result.data.add h[s]
@@ -123,7 +123,7 @@ proc sms(dataA: var DiffData; lowerA, upperA: int; dataB: DiffData; lowerB, uppe
## `downVector` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.
## `upVector` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.
## Returns a MiddleSnakeData record containing x,y and u,v.
result = Smsrd()
let max = dataA.len + dataB.len + 1
let downK = lowerA - lowerB # the k-line to start the forward search
@@ -250,6 +250,7 @@ proc lcs(dataA: var DiffData; lowerA, upperA: int; dataB: var DiffData; lowerB,
proc createDiffs(dataA, dataB: DiffData): seq[Item] =
## Scan the tables of which lines are inserted and deleted,
## producing an edit script in forward order.
result = @[]
var startA = 0
var startB = 0
var lineA = 0

View File

@@ -74,7 +74,14 @@ when defined(nimPreviewSlimSystem):
export options
type
Regex* = ref object
RegexDesc* = object
pattern*: string
pcreObj: ptr pcre.Pcre ## not nil
pcreExtra: ptr pcre.ExtraData ## nil
captureNameToId: Table[string, int]
Regex* = ref RegexDesc
## Represents the pattern that things are matched against, constructed with
## `re(string)`. Examples: `re"foo"`, `re(r"(*ANYCRLF)(?x)foo #
## comment".`
@@ -145,11 +152,6 @@ type
## `DOLLAR_ENDONLY`, `FIRSTLINE`, `NO_AUTO_CAPTURE`,
## `JAVASCRIPT_COMPAT`, `U`, `NO_STUDY`. In other PCRE wrappers, you
## will need to pass these as separate flags to PCRE.
pattern*: string
pcreObj: ptr pcre.Pcre ## not nil
pcreExtra: ptr pcre.ExtraData ## nil
captureNameToId: Table[string, int]
RegexMatch* = object
## Usually seen as Option[RegexMatch], it represents the result of an
@@ -216,12 +218,28 @@ type
## for whatever reason. The message contains the error
## code.
proc destroyRegex(pattern: Regex) =
`=destroy`(pattern.pattern)
pcre.free_substring(cast[cstring](pattern.pcreObj))
if pattern.pcreExtra != nil:
pcre.free_study(pattern.pcreExtra)
`=destroy`(pattern.captureNameToId)
when defined(gcDestructors):
when defined(nimAllowNonVarDestructor) and defined(nimPreviewNonVarDestructor):
proc `=destroy`(pattern: RegexDesc) =
`=destroy`(pattern.pattern)
pcre.free_substring(cast[cstring](pattern.pcreObj))
if pattern.pcreExtra != nil:
pcre.free_study(pattern.pcreExtra)
`=destroy`(pattern.captureNameToId)
else:
proc `=destroy`(pattern: var RegexDesc) =
`=destroy`(pattern.pattern)
pcre.free_substring(cast[cstring](pattern.pcreObj))
if pattern.pcreExtra != nil:
pcre.free_study(pattern.pcreExtra)
`=destroy`(pattern.captureNameToId)
else:
proc destroyRegex(pattern: Regex) =
`=destroy`(pattern.pattern)
pcre.free_substring(cast[cstring](pattern.pcreObj))
if pattern.pcreExtra != nil:
pcre.free_study(pattern.pcreExtra)
`=destroy`(pattern.captureNameToId)
proc getinfo[T](pattern: Regex, opt: cint): T =
let retcode = pcre.fullinfo(pattern.pcreObj, pattern.pcreExtra, opt, addr result)
@@ -251,7 +269,10 @@ proc getNameToNumberTable(pattern: Regex): Table[string, int] =
result[name] = num
proc initRegex(pattern: string, flags: int, study = true): Regex =
new(result, destroyRegex)
when defined(gcDestructors):
result = Regex()
else:
new(result, destroyRegex)
result.pattern = pattern
var errorMsg: cstring

View File

@@ -1490,7 +1490,9 @@ proc clearInterval*(i: Interval) {.importc, nodecl.}
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
proc dispatchEvent*(et: EventTarget, ev: Event)
proc removeEventListener*(et: EventTarget; ev: cstring; cb: proc(ev: Event))
proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
# Window "methods"
proc alert*(w: Window, msg: cstring)

View File

@@ -15,6 +15,7 @@ __TINYC__
__clang__
__AVR__
__arm__
__riscv
__EMSCRIPTEN__
*/
@@ -469,10 +470,17 @@ typedef char* NCSTRING;
#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
#define STRING_LITERAL(name, str, length) \
static const struct { \
TGenericSeq Sup; \
NIM_CHAR data[(length) + 1]; \
} name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str}
/* declared size of a sequence/variable length array: */
#if defined(__cplusplus) && defined(__clang__)
# define SEQ_DECL_SIZE 1
#elif defined(__GNUC__) || defined(_MSC_VER)
#elif defined(__GNUC__) || defined(_MSC_VER) || defined(__TINYC__) || \
(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)) // C99
# define SEQ_DECL_SIZE /* empty is correct! */
#else
# define SEQ_DECL_SIZE 1000000
@@ -579,8 +587,8 @@ NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "P
#define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
#if NIM_INTBITS == 32
#if defined(__arm__) && defined(__GNUC__)
/* arm-none-eabi-gcc targets defines int32_t as long int */
#if (defined(__arm__) || defined(__riscv)) && defined(__GNUC__)
/* arm-none-eabi-gcc and riscv32-unknown-elf-gcc targets define int32_t as long int */
#define nimAddInt(a, b, res) __builtin_saddl_overflow(a, b, res)
#define nimSubInt(a, b, res) __builtin_ssubl_overflow(a, b, res)
#define nimMulInt(a, b, res) __builtin_smull_overflow(a, b, res)

View File

@@ -301,6 +301,8 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) =
g.kind = nimGetKeyword(id)
elif isKeyword(keywords, id) >= 0:
g.kind = gtKeyword
else:
g.kind = gtIdentifier
of '0':
inc(pos)
case g.buf[pos]

View File

@@ -503,7 +503,7 @@ proc pthread_spin_unlock*(a1: ptr Pthread_spinlock): cint {.
proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
proc exitnow*(code: int) {.importc: "_exit", header: "<unistd.h>".}
proc exitnow*(code: cint) {.importc: "_exit", header: "<unistd.h>", noreturn.}
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}

View File

@@ -1300,7 +1300,8 @@ else:
# `rwlist` associated with file descriptor MUST BE emptied before
# dispatching callback (See https://github.com/nim-lang/Nim/issues/5128),
# or it can be possible to fall into endless cycle.
var curList: seq[Callback]
result = (0, 0)
var curList: seq[Callback] = @[]
let selector = getGlobalDispatcher().selector
withData(selector, fd.int, fdData):
@@ -1352,7 +1353,7 @@ else:
# {Event.Timer, Event.Signal, Event.Process, Event.Vnode}.
# There can be only one callback registered with one descriptor,
# so there is no need to iterate over list.
var curList: seq[Callback]
var curList: seq[Callback] = @[]
withData(p.selector, fd.int, adata) do:
curList = move adata.readList
@@ -1550,7 +1551,7 @@ else:
var retFuture = newFuture[void]("sendTo")
# we will preserve address in our stack
var staddr: array[128, char] # SOCKADDR_STORAGE size is 128 bytes
var staddr {.noinit.} : array[128, char] # SOCKADDR_STORAGE size is 128 bytes
var stalen = saddrLen
zeroMem(addr(staddr[0]), 128)
copyMem(addr(staddr[0]), saddr, saddrLen)
@@ -1604,7 +1605,7 @@ else:
client: AsyncFD]]("acceptAddr")
proc cb(sock: AsyncFD): bool {.gcsafe.} =
result = true
var sockAddress: Sockaddr_storage
var sockAddress: Sockaddr_storage = default(Sockaddr_storage)
var addrLen = sizeof(sockAddress).SockLen
var client =
when declared(accept4):
@@ -1696,12 +1697,12 @@ proc drain*(timeout = 500) =
## Waits for completion of **all** events and processes them. Raises `ValueError`
## if there are no pending operations. In contrast to `poll` this
## processes as many events as are available until the timeout has elapsed.
var curTimeout = timeout
let start = now()
var elapsed = 0
let start = getMonoTime()
while hasPendingOperations():
discard runOnce(curTimeout)
curTimeout -= (now() - start).inMilliseconds.int
if curTimeout < 0:
discard runOnce(timeout - elapsed)
elapsed = int (getMonoTime() - start).inMilliseconds
if elapsed >= timeout:
break
proc poll*(timeout = 500) =
@@ -2045,7 +2046,7 @@ when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or
elif defined(zephyr) or defined(freertos):
result = FD_MAX
else:
var fdLim: RLimit
var fdLim: RLimit = default(RLimit)
if getrlimit(RLIMIT_NOFILE, fdLim) < 0:
raiseOSError(osLastError())
result = int(fdLim.rlim_cur) - 1

View File

@@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
import std/[os, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits]
import std/[os, sets, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits]
import system/stacktraces
@@ -293,57 +293,47 @@ template getFilenameProcname(entry: StackTraceEntry): (string, string) =
else:
($entry.filename, $entry.procname)
proc getHint(entry: StackTraceEntry): string =
## We try to provide some hints about stack trace entries that the user
## may not be familiar with, in particular calls inside the stdlib.
proc format(entry: StackTraceEntry): string =
let (filename, procname) = getFilenameProcname(entry)
let left = "$#($#)" % [filename, $entry.line]
result = spaces(2) & "$# $#\n" % [left, procname]
result = ""
if procname == "processPendingCallbacks":
if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
return "Executes pending callbacks"
elif procname == "poll":
if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
return "Processes asynchronous completion events"
if procname.endsWith(NimAsyncContinueSuffix):
if cmpIgnoreStyle(filename, "asyncmacro.nim") == 0:
return "Resumes an async procedure"
proc isInternal(entry: StackTraceEntry): bool =
# --excessiveStackTrace:off
const internals = [
"asyncdispatch.nim",
"asyncfutures.nim",
"threadimpl.nim", # XXX ?
]
let (filename, procname) = getFilenameProcname(entry)
for line in internals:
if filename.endsWith line:
return true
return false
proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string =
result = ""
when defined(nimStackTraceOverride):
let entries = addDebuggingInfo(stackTraceEntries)
else:
let entries = stackTraceEntries
result = ""
# Find longest filename & line number combo for alignment purposes.
var longestLeft = 0
for entry in entries:
let (filename, procname) = getFilenameProcname(entry)
if procname == "": continue
let leftLen = filename.len + len($entry.line)
if leftLen > longestLeft:
longestLeft = leftLen
# Format the entries.
for entry in entries:
let (filename, procname) = getFilenameProcname(entry)
if procname == "" and entry.line == reraisedFromBegin:
break
let left = "$#($#)" % [filename, $entry.line]
result.add((spaces(2) & "$# $#\n") % [
left,
procname
])
let hint = getHint(entry)
if hint.len > 0:
result.add(spaces(4) & "## " & hint & "\n")
var seenEntries = initHashSet[StackTraceEntry]()
let L = entries.len-1
var i = L
var j = 0
while i >= 0:
if entries[i].line == reraisedFromBegin or i == 0:
j = i + int(i != 0)
while j < L:
if entries[j].line == reraisedFromBegin:
break
if entries[j].line >= 0 and not isInternal(entries[j]):
# this skips recursive calls sadly
if entries[j] notin seenEntries:
result.add format(entries[j])
seenEntries.incl entries[j]
inc j
dec i
proc injectStacktrace[T](future: Future[T]) =
when not defined(release):
@@ -356,7 +346,6 @@ proc injectStacktrace[T](future: Future[T]) =
let start = exceptionMsg.find(header)
exceptionMsg = exceptionMsg[0..<start]
var newMsg = exceptionMsg & header
let entries = getStackTraceEntries(future.error)

View File

@@ -146,6 +146,7 @@ proc respondError(req: Request, code: HttpCode): Future[void] =
result = req.client.send(msg)
proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
result = default(tuple[orig: string, major, minor: int])
var i = protocol.skipIgnoreCase("HTTP/")
if i != 5:
raise newException(ValueError, "Invalid request protocol. Got: " &

View File

@@ -22,36 +22,39 @@ proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimN
result = newCall(theProc, args)
result.copyLineInfo(fromNode)
template createCb(retFutureSym, iteratorNameSym,
strName, identName, futureVarCompletions: untyped) =
type
ClosureIt[T] = iterator(f: Future[T]): owned(FutureBase)
template createCb(futTyp, strName, identName, futureVarCompletions: untyped) =
bind finished
var nameIterVar = iteratorNameSym
proc identName {.closure, stackTrace: off.} =
{.push stackTrace: off.}
proc identName(fut: Future[futTyp], it: ClosureIt[futTyp]) {.effectsOf: it.} =
try:
if not nameIterVar.finished:
var next = nameIterVar()
if not it.finished:
var next = it(fut)
# Continue while the yielded future is already finished.
while (not next.isNil) and next.finished:
next = nameIterVar()
if nameIterVar.finished:
next = it(fut)
if it.finished:
break
if next == nil:
if not retFutureSym.finished:
if not fut.finished:
let msg = "Async procedure ($1) yielded `nil`, are you await'ing a `nil` Future?"
raise newException(AssertionDefect, msg % strName)
else:
{.gcsafe.}:
next.addCallback cast[proc() {.closure, gcsafe.}](identName)
next.addCallback(cast[proc() {.closure, gcsafe.}](proc =
identName(fut, it)))
except:
futureVarCompletions
if retFutureSym.finished:
if fut.finished:
# Take a look at tasyncexceptions for the bug which this fixes.
# That test explains it better than I can here.
raise
else:
retFutureSym.fail(getCurrentException())
identName()
fut.fail(getCurrentException())
{.pop.}
proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode): NimNode =
result = newNimNode(nnkStmtList, fromNode)
@@ -68,7 +71,7 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode
)
)
proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode =
proc processBody(ctx: Context; node, needsCompletionSym, retFutParamSym: NimNode, futureVarIdents: seq[NimNode]): NimNode =
result = node
case node.kind
of nnkReturnStmt:
@@ -80,14 +83,14 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode,
ctx.hasRet = true
if node[0].kind == nnkEmpty:
if ctx.inTry == 0:
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, newIdentNode("result"))
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutParamSym, newIdentNode("result"))
else:
result.add newAssignment(needsCompletionSym, newLit(true))
else:
let x = processBody(ctx, node[0], needsCompletionSym, retFutureSym, futureVarIdents)
let x = processBody(ctx, node[0], needsCompletionSym, retFutParamSym, futureVarIdents)
if x.kind == nnkYieldStmt: result.add x
elif ctx.inTry == 0:
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, x)
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutParamSym, x)
else:
result.add newAssignment(newIdentNode("result"), x)
result.add newAssignment(needsCompletionSym, newLit(true))
@@ -100,10 +103,10 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode,
of nnkTryStmt:
if result[^1].kind == nnkFinally:
inc ctx.inTry
result[0] = processBody(ctx, result[0], needsCompletionSym, retFutureSym, futureVarIdents)
result[0] = processBody(ctx, result[0], needsCompletionSym, retFutParamSym, futureVarIdents)
dec ctx.inTry
for i in 1 ..< result.len:
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutParamSym, futureVarIdents)
if ctx.inTry == 0 and ctx.hasRet:
let finallyNode = copyNimNode(result[^1])
let stmtNode = newNimNode(nnkStmtList)
@@ -111,7 +114,7 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode,
stmtNode.add child
stmtNode.add newIfStmt(
( needsCompletionSym,
newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym,
newCallWithLineInfo(node, newIdentNode("complete"), retFutParamSym,
newIdentNode("result")
)
)
@@ -120,10 +123,10 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode,
result[^1] = finallyNode
else:
for i in 0 ..< result.len:
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutParamSym, futureVarIdents)
else:
for i in 0 ..< result.len:
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutParamSym, futureVarIdents)
# echo result.repr
@@ -167,7 +170,8 @@ template await*[T](f: Future[T]): auto {.used.} =
when compiles(yieldFuture):
var internalTmpFuture: FutureBase = f
yield internalTmpFuture
(cast[typeof(f)](internalTmpFuture)).read()
{.line: instantiationInfo(fullPaths = true).}:
(cast[typeof(f)](internalTmpFuture)).read()
else:
macro errorAsync(futureError: Future[T]) =
error(
@@ -218,6 +222,7 @@ proc asyncSingleProc(prc: NimNode): NimNode =
elif returnType.kind == nnkEmpty:
baseType = returnType
else:
baseType = nil
verifyReturnType(repr(returnType), returnType)
let futureVarIdents = getFutureVarIdents(prc.params)
@@ -228,29 +233,21 @@ proc asyncSingleProc(prc: NimNode): NimNode =
# transformation even more complex.
let body2 = extractDocCommentsAndRunnables(prc.body)
# -> var retFuture = newFuture[T]()
var retFutureSym = genSym(nskVar, "retFuture")
var subRetType =
if returnType.kind == nnkEmpty: newIdentNode("void")
else: baseType
outerProcBody.add(
newVarStmt(retFutureSym,
newCall(
newNimNode(nnkBracketExpr, prc.body).add(
newIdentNode("newFuture"),
subRetType),
newLit(prcName)))) # Get type from return type of this proc
let retFutParamSym = genSym(nskParam, "retFutParamSym")
# -> iterator nameIter(): FutureBase {.closure.} =
# -> iterator nameIter(retFutParam: Future[T]): FutureBase {.closure.} =
# -> {.push warning[resultshadowed]: off.}
# -> var result: T
# -> {.pop.}
# -> <proc_body>
# -> complete(retFuture, result)
# -> complete(retFutParam, result)
var iteratorNameSym = genSym(nskIterator, $prcName & " (Async)")
var needsCompletionSym = genSym(nskVar, "needsCompletion")
var ctx = Context()
var procBody = processBody(ctx, prc.body, needsCompletionSym, retFutureSym, futureVarIdents)
var procBody = processBody(ctx, prc.body, needsCompletionSym, retFutParamSym, futureVarIdents)
# don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty:
# fix #13899, defer should not escape its original scope
@@ -274,9 +271,11 @@ proc asyncSingleProc(prc: NimNode): NimNode =
var `needsCompletionSym` = false
procBody.add quote do:
complete(`retFutureSym`, `resultIdent`)
complete(`retFutParamSym`, `resultIdent`)
var closureIterator = newProc(iteratorNameSym, [quote do: owned(FutureBase)],
var retFutureTyp = newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(subRetType)
var retFutureParam = newNimNode(nnkIdentDefs, prc).add(retFutParamSym).add(retFutureTyp).add(newEmptyNode())
var closureIterator = newProc(iteratorNameSym, [quote do: owned(FutureBase), retFutureParam],
procBody, nnkIteratorDef)
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom = prc.body)
closureIterator.addPragma(newIdentNode("closure"))
@@ -286,17 +285,31 @@ proc asyncSingleProc(prc: NimNode): NimNode =
closureIterator.addPragma(newIdentNode("gcsafe"))
outerProcBody.add(closureIterator)
# -> createCb(retFuture)
# -> createCb()
# NOTE: The NimAsyncContinueSuffix is checked for in asyncfutures.nim to produce
# friendlier stack traces:
var cbName = genSym(nskProc, prcName & NimAsyncContinueSuffix)
var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prcName),
cbName,
createFutureVarCompletions(futureVarIdents, nil)
)
var procCb = getAst createCb(
subRetType,
newStrLitNode(prcName),
cbName,
createFutureVarCompletions(futureVarIdents, nil)
)
outerProcBody.add procCb
# -> var retFuture = newFuture[T]()
let retFutureSym = genSym(nskVar, "retFuture")
outerProcBody.add(
newVarStmt(retFutureSym,
newCall(
newNimNode(nnkBracketExpr, prc.body).add(
newIdentNode("newFuture"),
subRetType),
newLit(prcName)))) # Get type from return type of this proc
# -> cb(retFuture, nameIter)
outerProcBody.add newCall(cbName, retFutureSym, iteratorNameSym)
# -> return retFuture
outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym)

View File

@@ -850,7 +850,7 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string,
var
it = aiList
success = false
lastException: ref Exception
lastException: ref Exception = nil
while it != nil:
let fut = sendTo(socket.fd.AsyncFD, cstring(data), len(data), it.ai_addr,
@@ -915,16 +915,16 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int,
"Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead")
assert(not socket.closed, "Cannot `recvFrom` on a closed socket")
assert(size == len(data.mget()),
"`date` was not initialized correctly. `size` != `len(data.mget())`")
"`data` was not initialized correctly. `size` != `len(data.mget())`")
assert(46 == len(address.mget()),
"`address` was not initialized correctly. 46 != `len(address.mget())`")
case socket.domain
of AF_INET6:
var sAddr: Sockaddr_in6
var sAddr: Sockaddr_in6 = default(Sockaddr_in6)
adaptRecvFromToDomain(AF_INET6)
of AF_INET:
var sAddr: Sockaddr_in
var sAddr: Sockaddr_in = default(Sockaddr_in)
adaptRecvFromToDomain(AF_INET)
else:
raise newException(ValueError, "Unknown socket address family")

View File

@@ -187,7 +187,7 @@ proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n",
if e.len <= lineLen or newLine.len == 0:
return e
result = newString(e.len + newLine.len * ((e.len div lineLen) - int(e.len mod lineLen == 0)))
var i, j, k, b: int
var i, j, k, b: int = 0
let nd = e.len - lineLen
while j < nd:
cpy(i + lineLen, e, j)
@@ -197,6 +197,7 @@ proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n",
proc initDecodeTable*(): array[256, char] =
# computes a decode table at compile time
result = default(array[256, char])
for i in 0 ..< 256:
let ch = char(i)
var code = invalidChar
@@ -219,6 +220,7 @@ proc decode*(s: string): string =
runnableExamples:
assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
assert decode(" SGVsbG8gV29ybGQ=") == "Hello World"
result = ""
if s.len == 0: return
proc decodeSize(size: int): int =

View File

@@ -83,6 +83,8 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string =
else:
if methodNone notin allowedMethods:
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
else:
result = ""
iterator decodeData*(data: string): tuple[key, value: string] =
## Reads and decodes CGI data and yields the (name, value) pairs the

View File

@@ -533,5 +533,5 @@ proc toCritBitTree*(items: sink openArray[string]): CritBitTree[void] {.since: (
## Creates a new `CritBitTree` that contains the given `items`.
runnableExamples:
doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void]
result = default(CritBitTree[void])
for item in items: result.incl item

View File

@@ -91,7 +91,7 @@ proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] =
echo typeof(extractRGB(a))
echo extractRGB(b)
echo typeof(extractRGB(b))
result = default(tuple[r, g, b: range[0..255]])
result.r = a.int shr 16 and 0xff
result.g = a.int shr 8 and 0xff
result.b = a.int and 0xff

View File

@@ -33,7 +33,7 @@ runnableExamples:
{.push checks: off, line_dir: off, stack_trace: off, debugger: off.}
# the user does not want to trace a part of the standard library!
import std/[math, strformat]
import std/[math, strformat, strutils]
type
Complex*[T: SomeFloat] = object

View File

@@ -65,10 +65,10 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
proc fscanf(c: File, frmt: cstring) {.varargs, importc,
header: "<stdio.h>".}
var f: File
var f: File = default(File)
if f.open("/proc/loadavg"):
var b: float
var busy, total: int
var busy, total: int = 0
fscanf(f,"%lf %lf %lf %ld/%ld",
addr b, addr b, addr b, addr busy, addr total)
f.close()

View File

@@ -155,6 +155,8 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
signal(w.taskArrived)
blockUntil(w.taskStarted)
result = true
else:
result = false
proc cleanFlowVars(w: ptr Worker) =
let q = addr(w.q)
@@ -277,7 +279,7 @@ proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int =
## to be able to wait on, -1 is returned.
##
## **Note:** This results in non-deterministic behaviour and should be avoided.
var ai: AwaitInfo
var ai: AwaitInfo = AwaitInfo()
ai.cv.initSemaphore()
var conflicts = 0
result = -1

View File

@@ -34,11 +34,13 @@ func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} =
assert startsWith(cstring"Hello", cstring"")
when nimvm:
result = false
startsWithImpl(s, prefix)
else:
when defined(js):
result = jsStartsWith(s, prefix)
else:
result = false
var i = 0
while true:
if prefix[i] == '\0': return true
@@ -55,11 +57,13 @@ func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} =
assert endsWith(cstring"Hello", cstring"")
when nimvm:
result = false
endsWithImpl(s, suffix)
else:
when defined(js):
result = jsEndsWith(s, suffix)
else:
result = false
let slen = s.len
var i = 0
var j = slen - len(suffix)

View File

@@ -90,6 +90,7 @@ proc loadLibPattern*(pattern: string, globalSymbols = false): LibHandle =
## pragma does. Returns nil if the library could not be loaded.
##
## .. warning:: this proc uses the GC and so cannot be used to load the GC.
result = default(LibHandle)
var candidates = newSeq[string]()
libCandidates(pattern, candidates)
for c in candidates:

View File

@@ -464,7 +464,7 @@ else:
var outLen = csize_t len(result)
var src = cstring(s)
var dst = cstring(result)
var iconvres: csize_t
var iconvres: csize_t = csize_t(0)
while inLen > 0:
iconvres = iconv(c, addr src, addr inLen, addr dst, addr outLen)
if iconvres == high(csize_t):

View File

@@ -55,7 +55,7 @@ when useBuiltinSwap:
## We have to use `copyMem` here instead of a simple dereference because they
## may point to a unaligned address. A sufficiently smart compiler _should_
## be able to elide them when they're not necessary.
var tmp: T
var tmp: T = default(T)
copyMem(addr tmp, inp, sizeof(T))
tmp = op(tmp)
copyMem(outp, addr tmp, sizeof(T))

View File

@@ -122,6 +122,7 @@ proc hiXorLo(a, b: uint64): uint64 {.inline.} =
when Hash.sizeof < 8:
result = hiXorLoFallback64(a, b)
elif defined(gcc) or defined(llvm_gcc) or defined(clang):
result = uint64(0)
{.emit: """__uint128_t r = `a`; r *= `b`; `result` = (r >> 64) ^ r;""".}
elif defined(windows) and not defined(tcc):
proc umul128(a, b: uint64, c: ptr uint64): uint64 {.importc: "_umul128", header: "intrin.h".}
@@ -316,7 +317,7 @@ proc murmurHash(x: openArray[byte]): Hash =
stepSize = 4 # 32-bit
n = size div stepSize
var
h1: uint32
h1: uint32 = uint32(0)
i = 0
@@ -328,7 +329,7 @@ proc murmurHash(x: openArray[byte]): Hash =
# body
while i < n * stepSize:
var k1: uint32
var k1: uint32 = uint32(0)
when nimvm:
impl()
@@ -348,7 +349,7 @@ proc murmurHash(x: openArray[byte]): Hash =
h1 = h1*5 + n1
# tail
var k1: uint32
var k1: uint32 = uint32(0)
var rem = size mod stepSize
while rem > 0:
dec rem
@@ -396,13 +397,17 @@ proc load8e(s: openArray[byte], o=0): uint64 {.inline.} =
proc load4(s: openArray[byte], o=0): uint32 {.inline.} =
when nimvm: result = load4e(s, o)
else:
when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
when declared copyMem:
result = uint32(0)
copyMem result.addr, s[o].addr, result.sizeof
else: result = load4e(s, o)
proc load8(s: openArray[byte], o=0): uint64 {.inline.} =
when nimvm: result = load8e(s, o)
else:
when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
when declared copyMem:
result = uint64(0)
copyMem result.addr, s[o].addr, result.sizeof
else: result = load8e(s, o)
proc lenU(s: openArray[byte]): uint64 {.inline.} = s.len.uint64
@@ -488,9 +493,9 @@ proc hashFarm(s: openArray[byte]): uint64 {.inline.} =
x = seed
y = seed*k1 + 113
z = shiftMix(y*k2 + 113)*k2
v, w: Pair
v, w: Pair = default(Pair)
x = x*k2 + load8(s)
let eos = ((s.len - 1) div 64)*64
let eos = ((s.len - 1) div 64)*64
let last64 = eos + ((s.len - 1) and 63) - 63
while true:
x = rotR(x + y + v[0] + load8(s, o+8), 37)*k1

View File

@@ -74,6 +74,8 @@ proc delete[T](s: var seq[T], attr: T): bool =
s[idx] = s[L-1]
setLen(s, L-1)
result = true
else:
result = false
proc xmlCheckedTag*(argsList: NimNode, tag: string, optAttr = "", reqAttr = "",
isLeaf = false): NimNode =

View File

@@ -1961,6 +1961,7 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode,
result.addNode(parse(x, errors))
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
result = nil
case x.kind
of xmlComment:
result = newComment(x.rawData)
@@ -2017,7 +2018,7 @@ proc parseHtml*(s: Stream, filename: string,
errors: var seq[string]): XmlNode =
## Parses the XML from stream `s` and returns a `XmlNode`. Every
## occurred parsing error is added to the `errors` sequence.
var x: XmlParser
var x: XmlParser = default(XmlParser)
open(x, s, filename, {reportComments, reportWhitespace, allowUnquotedAttribs,
allowEmptyAttribs})
next(x)

View File

@@ -62,6 +62,7 @@
## validated to the server.
##
## ```Nim
## import std/[httpclient]
## var client = newHttpClient()
## var data = newMultipartData()
## data["output"] = "soap12"
@@ -79,6 +80,7 @@
## it, you can pass your own via the `mimeDb` parameter to avoid this.
##
## ```Nim
## import std/[httpclient, mimetypes]
## let mimes = newMimetypes()
## var client = newHttpClient()
## var data = newMultipartData()
@@ -160,7 +162,7 @@
## Example of setting SSL verification parameters in a new client:
##
## ```Nim
## import httpclient
## import std/[net, httpclient]
## var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer))
## ```
##
@@ -401,6 +403,7 @@ proc `$`*(data: MultipartData): string {.since: (1, 1).} =
## convert MultipartData to string so it's human readable when echo
## see https://github.com/nim-lang/Nim/issues/11863
const sep = "-".repeat(30)
result = ""
for pos, entry in data.content:
result.add(sep & center($pos, 3) & sep)
result.add("\nname=\"" & entry.name & "\"")
@@ -474,7 +477,7 @@ proc addFiles*(p: MultipartData, xs: openArray[tuple[name, file: string]],
## data.addFiles({"uploaded_file": "public/test.html"})
## ```
for name, file in xs.items:
var contentType: string
var contentType: string = ""
let (_, fName, ext) = splitFile(file)
if ext.len > 0:
contentType = mimeDb.getMimetype(ext[1..ext.high], "")
@@ -862,7 +865,7 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
while true:
linei = 0
when client is HttpClient:
line = await client.socket.recvLine(client.timeout)
line = client.socket.recvLine(client.timeout)
else:
line = await client.socket.recvLine()
if line == "":
@@ -968,7 +971,7 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
else: nativesockets.Port(connectionUrl.port.parseInt)
when client is HttpClient:
client.socket = await net.dial(connectionUrl.hostname, port)
client.socket = net.dial(connectionUrl.hostname, port)
elif client is AsyncHttpClient:
client.socket = await asyncnet.dial(connectionUrl.hostname, port)
else: {.fatal: "Unsupported client type".}
@@ -1032,12 +1035,13 @@ proc format(entry: MultipartEntry, boundary: string): string =
proc format(client: HttpClient | AsyncHttpClient,
multipart: MultipartData): Future[seq[string]] {.multisync.} =
result = @[]
let bound = getBoundary(multipart)
client.headers["Content-Type"] = "multipart/form-data; boundary=" & bound
await client.readFileSizes(multipart)
var length: int64
var length: int64 = int64(0)
for entry in multipart.content:
result.add(format(entry, bound) & httpNewLine)
if entry.isFile:
@@ -1080,7 +1084,7 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri,
var newHeaders: HttpHeaders
var data: seq[string]
var data: seq[string] = @[]
if multipart != nil and multipart.content.len > 0:
# `format` modifies `client.headers`, see
# https://github.com/nim-lang/Nim/pull/18208#discussion_r647036979
@@ -1103,7 +1107,7 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri,
await client.socket.send(headerString)
if data.len > 0:
var buffer: string
var buffer: string = ""
for i, entry in multipart.content:
buffer.add data[i]
if not entry.isFile: continue

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