Compare commits

..

420 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
Miran
78983f1876 update the changelog (#24213) 2024-10-01 22:22:30 +02:00
ringabout
0e1df88f7e check fileExists for outputFile (#24211)
ref
a5f46a72ba (commitcomment-147402726)
2024-10-01 08:11:07 +02:00
Miran
a5f46a72ba bump NimVersion to 2.2.0 (#24210) 2024-09-30 20:59:38 +02:00
ringabout
4974baf7fa fixes #24008; triggers a recompilation on output executables changes when switching release/debug modes (#24193)
fixes #24008

The old logic didn't check the contents of the output executables, when
it switched release->debug->release, it picked up the Json files used in
the first release building, the content of which didn't change. So it
mistook the executables which are built by the second debug building as
the functioning one.

`changeDetectedViaJsonBuildInstructions` needs a way to distinguish the
executables generated by different buildings.
2024-09-30 20:55:47 +02:00
metagn
b82ff5a87b make C++ atomic opt in via -d:nimUseCppAtomics (#24209)
refs #24207

The `-d:nimUseCAtomics` flag added in #24207 is now inverted and made
into `-d:nimUseCppAtomics`, which means C++ atomics are only enabled
with the define. This flag is now also documented and tested.
2024-09-30 20:54:07 +02:00
metagn
2a48182288 remove prev == nil requirement for typedesc params as type nodes (#24206)
fixes #24203

`semTypeNode` is called twice for RHS'es of type sections,
[here](b0e6d28782/compiler/semstmts.nim (L1612))
and
[here](b0e6d28782/compiler/semstmts.nim (L1646)).
Each time `prev` is `s.typ`, but the assertion expects `prev == nil`
which is false since `s.typ` is not nil the second time. To fix this,
the `prev == nil` part of the assertion is removed.

The reason this only happens for types like `seq[int]`, `(int, int)` etc
is because they don't have syms: `semTypeIdent` attempts to directly
[replace the typedesc param
itself](b0e6d28782/compiler/semtypes.nim (L1916))
with the sym of the base type of the resolved typedesc type if it
exists, which means `semTypeNode` doesn't receive the typedesc param sym
to perform the assert.
2024-09-30 17:34:49 +02:00
metagn
febc58e036 allow C atomics on C++ with -d:nimUseCAtomics (#24207)
refs https://github.com/nim-lang/Nim/pull/24200#issuecomment-2382501282

Workaround for C++ Atomic[T] issues that doesn't require a compiler
change. Not tested or documented in case it's not meant to be officially
supported, locally tested `tatomics` and #24159 to work with it though,
can add these as tests if required.
2024-09-30 17:34:09 +02:00
metagn
b0e6d28782 fix logic for dcEqIgnoreDistinct in sameType (#24197)
fixes #22523

There were 2 problems with the code in `sameType` for
`dcEqIgnoreDistinct`:

1. The code that skipped `{tyDistinct, tyGenericInst}` only ran if the
given types had different kinds. This is fixed by always performing this
skip.
2. The code block below that checks if `tyGenericInst`s have different
values still ran for `dcEqIgnoreDistinct` since it checks if the given
types are generic insts, not the skipped types (and also only the 1st
given type). This is fixed by only invoking this block for `dcEq`;
`dcEqOrDistinctOf` (which is unused) also skips the first given type.
Arguably there is another issue here that `skipGenericAlias` only ever
skips 1 type.

These combined fix the issue (`T` is `GenericInst(V, 1, distinct int)`
and `D[0]` is `GenericInst(D, 0, distinct int)`).

#24037 shouldn't be a dependency but the diff follows it.
2024-09-29 10:23:59 +02:00
metagn
7974a2208c fix cyclic node flag getting added to sink call [backport:2.0] (#24194)
Sorry I don't have a test case or issue for this. `injectdestructors` is
supposed to add a final bool argument to `=copy` and `=dup` to mark
cyclic types, as generated by `liftdestructors`. Hence this flag is
added after every call to `genCopy`, but `genCopy` can generate a
`=sink` call when passed the flag `IsExplicitSink` by `nkSinkAsgn`. This
creates a codegen error, saying the sink received an extra argument.
This is fixed by not adding the argument on the flag `IsExplicitSink`.

This is a followup to #20585 which is on the 2.0 branch, hence this is
marked backport.
2024-09-29 10:21:45 +02:00
metagn
7cbe031909 fix trivial segfault in sigmatch for static types (#24196)
refs #7382, caused by #24005
2024-09-29 10:20:38 +02:00
ringabout
4f5c0efaf2 fixes #24174; allow copyDir and copyDirWithPermissions skipping special files (#24190)
fixes  #24174
2024-09-27 16:36:31 +02:00
metagn
821d0806fe Revert "make default values typed in proc AST same as param sym AST" (#24191)
Reverts #24184, reopens #12942, reopens #19118

#24184 seems to have caused a regression in
https://github.com/c-blake/thes and
https://github.com/c-blake/bu/blob/main/rp.nim#L84 reproducible with
`git clone https://github.com/c-blake/cligen; git clone
https://github.com/c-blake/thes; cd thes; nim c -p=../cligen thes`.
Changing the `const` to `let` makes it compile.

A minimization that is probably the same issue is:

```nim
const a: seq[string] = @[]

proc foo(x = a) =
  echo typeof(x)
  echo x

import macros

macro resemFoo() =
  result = getImpl(bindSym"foo")

block:
  resemFoo() # Error: cannot infer the type of parameter 'x'
```

This should be a regression test in a future reimplementation of #24184.
2024-09-27 15:34:09 +02:00
metagn
2cdc0e913f use cbuilder for tuple/object generation (#24145)
based on #24127

Needs some tweaks to replace the other `struct` type generations, e.g.
seqs, maybe by exposing `BaseTypeKind` as a parameter. C++ and
codegenDecl etc seem like they are going to need attention.

Also `Builder` should really be `distinct string` that one has to call
`extract` on, but for this to be optimal in the current codegen, we
would need something like:

```nim
template buildInto(s: var string, builderName: untyped, body) =
  template `builderName`: untyped = Builder(s)
  body

buildInto(result, builder):
  builder.add ...
```

but this could be a separate PR since it might not work with the
compiler. The possibly-not-optimal alternative is to do:

```nim
template build(builderName: untyped, body): string =
  var `builderName` = Builder("")
  body
  extract(`builderName`)

result = build(builder):
  builder.add ...
```

where the compiler maybe copies the built string but shouldn't.

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
2024-09-27 11:12:39 +02:00
metagn
dc3ffb6a71 consider calling convention and iterator in getType for procs (#24181)
fixes #19010

`getType` for proc types generated an `nkProcTy` for iterator types
instead of `nkIteratorTy`, and didn't generate a calling convention
pragma unless it was in the proc AST. Iterator types now generate
`nkIteratorTy`, and a calling convention pragma is added if the calling
convention isn't `closure` or was explicitly provided.
2024-09-27 11:11:21 +02:00
metagn
56a3dd57fb evaluate all hidden conversions in case branches (#24187)
fixes #11422, refs #8336/#8333, refs #20130

The compiler generates conversion nodes *after* evaluating the branches
of case statements as constants, the reasoning is that case branches
accept constants of different types, like arrays or sets. But this means
that conversion nodes that need to be evaluated like converter calls
don't get evaluated as a constant for codegen. #8336 fixed this by
re-evaluating the node if an `nkHiddenCallConv` was created, and in
#20130 this logic also had to be added for `nkHiddenStdConv` for
cstrings. This logic was only for single case elements, it has now been
added to range elements as well to fix #11422. Additionally, all
conversion nodes are now evaluated for simplicity, but maybe this won't
pass CI.
2024-09-27 11:09:49 +02:00
ringabout
7cccf36d7b improve changelogs (#24188)
Co-authored-by: metagn <metagngn@gmail.com>
2024-09-27 16:46:16 +08:00
ringabout
d4027f25c4 fixes #24175; Sink parameters not copied at compile time (#24178)
fixes #24175
2024-09-27 09:36:09 +02:00
metagn
75b9d66582 treat resolved symbols on RHS of module qualification as identifiers (#24180)
fixes #19866 given #23997

When searching for a module-qualified symbol, `qualifiedLookUp` tries to
obtain the raw identifier from the RHS of the dot field. However it only
does this when the RHS is either an `nkIdent` or an `nkAccQuoted` node,
not when the node is a resolved symbol or a symchoice, such as in
templates and generics when the module symbol can't be resolved yet.
Since the LHS is a module symbol when the compiler checks for this, any
resolved symbol information doesn't matter, since it has to be a member
of the module. So we now obtain the identifier from these nodes as well
as the unresolved identifier nodes.

The test is a bit niche and possibly not officially supported, but this
is likely a more general problem and I just couldn't think of another
test that would be more "proper". It's better than the error message
`'a' has no type` at least.
2024-09-27 09:34:13 +02:00
metagn
c21bf7f41b make default values typed in proc AST same as param sym AST (#24184)
fixes #12942, fixes #19118

This is the exact same as #20735 but maybe the situation has improved
after #24065.
2024-09-27 09:33:40 +02:00
ringabout
62a5bb4d0a fixes #24173; always bundle checksums (#24189)
fixes #24173

`cloneDependency` always has its logic to use existing deps
2024-09-27 09:33:13 +02:00
metagn
fd379c2f94 fix nimsuggest crash with arrow type sugar (#24185)
fixes #24179 

The original fix made it so calls to `skError`/`skUnknown` (in this case
`->`, for some reason `sugar` couldn't be imported) returned an error
node, however this breaks tsug_accquote for some reason I don't
understand (it even parses as `tsug_accquote.discard`) so I've just
added a guard based on the stacktrace.
2024-09-27 06:23:29 +02:00
metagn
1bd5a4a99e render float128 literals (#24182)
fixes #23639

Not sure if these are meant to be supported but it's better than
crashing.
2024-09-27 06:11:21 +02:00
metagn
a27542195c only merge valid implicit pragmas to routine AST, include templates (#24171)
fixes #19277, refs #24169, refs #18124

When pragmas are pushed to a routine, if the routine symbol AST isn't
nil by the time the pushed pragmas are being processed, the pragmas are
implicitly added to the symbol AST. However this is done without
restriction on the pragma, if the pushed pragma isn't supposed to apply
to the routine, it's still added to the routine. This is why the symbol
AST for templates wasn't set before the pushed pragma processing in
#18124. Now, the pragmas added to the AST are restricted to ones that
apply to the given routine. This means we can set the template symbol
AST earlier so that the pragmas get added to the template AST.
2024-09-26 06:34:50 +02:00
Alfred Morgan
69b2a6effc sort modules and added std/setutils (#24168) 2024-09-26 06:29:25 +02:00
ringabout
6d6489a9ab fixes requiresInit for var statements without initialization (#24177)
ref https://forum.nim-lang.org/t/12530
2024-09-26 06:28:40 +02:00
ringabout
3b85c1a2e9 fixes #24167; {.push deprecated.} for templates (#24170)
fixes #24167
2024-09-25 13:00:06 +02:00
metagn
b9de2bb4f3 fix nil literal giving itself type untyped/typed [backport] (#24165)
fixes #24164, regression from #20091

The expression `nil` as the default value of template parameter `x:
untyped` is typechecked with expected type `untyped` since #20091. The
expected type is checked if it matches the `nil` literal with a match
better than a subtype match, and the type is set to it if it does.
However `untyped` matches with a generic match which is better, so the
`nil` literal has type `untyped`. This breaks type matching for the
literal. So if the expected type is `untyped` or `typed`, it is now
ignored and the `nil` literal just has the `nil` type.
2024-09-23 18:18:22 +03:00
Jake Leahy
6f6e34ebb0 Fix line info used for UnunsedImport from subdirectories (#24158)
When importing from subdirectories, the line info used in `UnusedImport`
warning would be the `/` node and not the actual module node. More
obvious with grouped imports where all unused imports would show the
same column

![image](https://github.com/user-attachments/assets/42850130-1e0e-46b9-bd72-54864a1ad41c)

Fix is to just use the last child node for infixes when getting the line
info
2024-09-23 10:14:26 +02:00
metagn
a55c15c651 fix tmarshalsegfault depending on execution time (#24153)
Added in #24119, the test checks if every string produced is equal, but
the value of the strings depend on the `now()` timestamp of when they
were produced. 30 of them are produced in a for loop in sequence with
each other, but the first one is set after the data is marshalled into
and unmarshalled from a file. This means the timestamp strings can
differ depending on the execution time and causes this test to be flaky.
Instead we just make 2 strings from the same data and check if they
equal each other.
2024-09-22 13:57:03 +02:00
metagn
7da2ffb751 fix custom pragma with backticks not working [backport] (#24151)
refs https://forum.nim-lang.org/t/12522
2024-09-22 13:56:40 +02:00
ringabout
5c843d3d60 fixes #24147; Copy hook causes an incompatible-pointer-types (#24149)
fixes #24147
2024-09-22 13:51:51 +02:00
metagn
a1777200c1 fix inTypeofContext leaking after compiles raises exception [backport:2.0] (#24152)
fixes #24150, refs #22022

An exception is raised in the `semExprWithType` call, which means `dec
c.inTypeofContext` is never called, but `compiles` allows compilation to
continue. This means `c.inTypeofContext` is left perpetually nonzero,
which prevents `compileTime` evaluation for the rest of the program.

To fix this, `defer:` is used for the `dec c.inTypeofContext` call, as
is done for
[`instCounter`](d51d88700b/compiler/seminst.nim (L374))
in other parts of the compiler.
2024-09-22 13:51:19 +02:00
tocariimaa
d51d88700b Implement removeHandler in std/logging module (fixes #23757) (#24143)
Since the module allows for a handler to be added multiple times, for
the sake of consistency, `removeHandler` only removes the first found
instance of the handler in the `handlers` seq. So for n calls of
`addHandler` using the same handler, n calls of `removeHandler` are
required.

fixes #23757

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2024-09-20 17:32:23 +02:00
Ryan McConnell
37dba853c9 Fix incorrect inheritance penalty for some objects (#24144)
This fixes a logic error in  #23870
The inheritance penalty should be -1 if there is no inheritance
relationship. Not sure how to write a test case for this one honestly.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2024-09-20 17:32:07 +02:00
ringabout
755307be61 fixes #24141; Calling algorithm reverse causes a SIGSEGV on ORC (#24142)
fixes #24141
2024-09-19 15:17:25 +02:00
metagn
05a7a48a2b fix inverted order of resolved tyFromExpr match (#24138)
fixes #22276

When matching against `tyFromExpr`, the compiler tries to instantiate it
then operates on the potentially instantiated type. But the way it does
this is inverted, it checks if the instantiated type matches the
argument type, not if the argument type matches the instantiated type.
This has been the case since
ac271e76b1 (diff-251afcd01d239369019495096c187998dd6695b6457528953237a7e4a10f7138),
which doesn't comment on it, so I'm guessing this isn't intended. I
don't know if it would break anything though.
2024-09-19 07:20:29 +02:00
tocariimaa
84f5060e94 Create IPPROTO_NONE alias & Add test for Unix socket (#24139)
closes #24116
2024-09-19 07:19:59 +02:00
metagn
ff005ad7dc fix segfault in generic param mismatch error, skip typedesc (#24140)
refs #24010, refs
https://github.com/nim-lang/Nim/issues/24125#issuecomment-2358377076

The generic mismatch errors added in #24010 made it possible for `nArg`
to be `nil` in the error reporting since it checked the call argument
list, not the generic parameter list for the mismatching argument node,
which causes a segfault. This is fixed by checking the generic parameter
list immediately on any generic mismatch error.

Also the `typedesc` type is skipped for the value of the generic params
since it's redundant and the generic parameter constraints don't have
it.
2024-09-19 07:19:07 +02:00
metagn
6cc50ec316 fix system for nimscript config files on js backend (#24135)
fixes #21441

When compiling for JS, nimscript config files have both `defined(js)`
and `defined(nimscript)` be true at the same time. This is required so
that the nimscript config file knows the current compilation is for the
JS backend. However the system module doesn't account for this in some
cases, defining JS-specific code or not defining nimscript-specific code
when compiling such nimscript files. To fix this, have the `nimscript`
define take priority over the `js` one.
2024-09-19 00:35:29 +02:00
metagn
58cf62451d fix typed case range not counting for exhaustiveness (#24136)
fixes #22661

Range expressions in `of` branches in `case` statements start off as
calls to `..` then become `nkRange` when getting typed. For this reason
the compiler leaves `nkRange` alone when type checking the case
statements again, but it still does the exhaustiveness checking for the
entire case statement, and leaving the range alone means it doesn't
count the values of the range for exhaustiveness. So the counting is now
also done on `nkRange` nodes in the same way as when typechecking it the
first time.
2024-09-18 23:50:58 +02:00
metagn
00ac961ab1 require not nil to be on the same line after a type (#24134)
fixes #23565
2024-09-18 22:45:19 +02:00
metagn
0c3573e4a0 make genericsOpenSym work at instantiation time, new behavior in openSym (#24111)
alternative to #24101

#23892 changed the opensym experimental switch so that it has to be
enabled in the context of the generic/template declarations capturing
the symbols, not the context of the instantiation of the
generics/templates. This was to be in line with where the compiler gives
the warnings and changes behavior in a potentially breaking way.

However `results` [depends on the old
behavior](71d404b314/results.nim (L1428)),
so that the callers of the macros provided by results always take
advantage of the opensym behavior. To accomodate this, we change the
behavior of the old experimental option that `results` uses,
`genericsOpenSym`, so that ignores the information of whether or not
symbols are intentionally opened and always gives the opensym behavior
as long as it's enabled at instantiation time. This should keep
`results` working as is. However this differs from the normal opensym
switch in that it doesn't generate `nnkOpenSym`.

Before it was just a generics-only version of `openSym` along with
`templateOpenSym` which was only for templates. So `templateOpenSym` is
removed along with this change, but no one appears to have used it.
2024-09-18 19:27:09 +02:00
Miran
79b17b7c05 workaround for strunicode package no longer needed (#24132) 2024-09-18 19:16:08 +02:00
metagn
1660ddf98a make var/pointer types not match if base type has to be converted (#24130)
split again from #24038, fixes
https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102

`var`/pointer types are no longer implicitly convertible to each other
if their element types either:

* require an int conversion or another conversion operation as long as
it's not to `openarray`,
* are subtypes with pointer indirection,

Previously any conversion below a subrange match would match if the
element type wasn't a pointer type, then it would error later in
`analyseIfAddressTaken`.

Different from #24038 in that the preview define that made subrange
matches also fail to match is removed for a simpler diff so that it can
be backported.
2024-09-18 17:37:18 +02:00
ringabout
c759d7abd1 fixes rst parsing Markdown CodeblockFields blocking the loop (#24128)
```nim
import packages/docutils/[rst, rstgen]

let message = """```llvm-profdata"""

echo rstgen.rstToHtml(message, {roSupportMarkdown}, nil)
```
2024-09-18 17:35:46 +02:00
metagn
04ccd2f4f0 revert second argument of inc not being generic (#24129)
refs #22328, fixes regression in
https://forum.nim-lang.org/t/12465#76998
2024-09-17 21:28:54 +02:00
metagn
680a13a142 fix segfault in effect tracking for sym node with nil type (#24114)
fixes #24112

Sym nodes in templates that could be open are [given `nil`
type](22d2cf2175/compiler/semtempl.nim (L274))
when `--experimentalOpenSym` is disabled so that they can be semchecked
to give a warning since #24007. The first nodes of object constructors
(in this case) and in type conversions don't replace their first node
(the symbol) with a typechecked one, they only call `semTypeNode` on it
and leave it as is.

Effect tracking checks if the type of a sym node has a destructor to
check if the node type should be replaced with the sym type. But this
causes a segfault when the type of the node is nil. To fix this, we
always set the node type to the sym type if the node type is nil.

Alternatively `semObjConstr` and `semConv` could be changed to set the
type of their first node to the found type but I'm not sure if this
would break anything. They could call `semExprWithType` on the first
node but `semTypeNode` would still have to be called (maybe call it
before?). This isn't a problem if the sym node has a type but is just
nested in `nkOpenSym` or `nkOpenSymChoice` which have nil type instead
(i.e. with openSym enabled), so maybe this still is the "most general"
solution, I don't know.
2024-09-17 14:01:48 +02:00
ringabout
21a161a535 remove nimfrs and varslot (#24126)
was introduced for debugger
b63f322a46 (diff-abd3a10386cf1ae32bfd3ffae82335a1938cc6c6d92be0ee492fcb44b9f2b552)


b63f322a46/lib/system/debugger.nim
2024-09-17 14:01:21 +02:00
metagn
1fbb67ffe9 make distinct conversions addressable in VM (#24124)
fixes #24097

For `nkConv` addresses where the conversion is between 2 types that are
equal between backends, treat assignments the same as assignments to the
argument of the conversion. In the VM this seems to be in `genAsgn` and
`genAsgnPatch`, as evidenced by the special logic for `nkDerefExpr` etc.

This doesn't handle ranges after #24037 because `sameBackendType` is
used and not `sameBackendTypeIgnoreRange`. This is so this is
backportable without #24037 and another PR can be opened that implements
it for ranges and adds tests as well. We can also merge
`sameBackendTypeIgnoreRange` with `sameBackendType` since it doesn't
seem like anything that uses it would be affected (only cycle checks and
the VM), but then we still have to add tests.
2024-09-17 06:29:49 +02:00
metagn
b5f2eafed1 don't match arguments with typeclass type in generics (#24123)
fixes #24121

Proc arguments can have typeclass type like `Foo | Bar` that `sigmatch`
handles specially before matching them to the param type, because they
wouldn't match otherwise. Not exactly sure why this existed but matching
any typeclass or unresolved type in generic contexts now fails the match
so typing the call is delayed until instantiation.

Also it turns out default values with `tyFromExpr` type depending on
other parameters was never tested, this also needs a patch to make the
`tyFromExpr` type `tfNonConstExpr` so it doesn't try to evaluate the
other parameter at compile time.
2024-09-17 06:22:45 +02:00
metagn
fe55dcb2be test case haul before 2.2 (#24119)
closes #4774, closes #7385, closes #10019, closes #12405, closes #12732,
closes #13270, closes #13799, closes #15247, closes #16128, closes
#16175, closes #16774, closes #17527, closes #20880, closes #21346
2024-09-17 09:50:10 +08:00
Juan M Gómez
651fdbe586 Fixes #23624 "nim check crash" (#23625) 2024-09-16 20:45:00 +02:00
ringabout
d0dc4ac22f minor improvement (#24113) 2024-09-16 22:31:39 +08:00
metagn
22d2cf2175 disable closure iterator changes in #23787 unless -d:nimOptIters is enabled (#24108)
refs #24094, soft reverts #23787

#23787 turned out to cause issues as described in #24094, but the
changes are still positive, so it is now only enabled if compiling with
`-d:nimOptIters`. Unfortunately the changes are really interwoven with
each other so the checks for this switch in the code are a bit messy,
but searching for `nimOptIters` should give the necessary clues to
remove the switch properly later on.

Locally tested that nimlangserver works but others can also check.
2024-09-16 07:16:21 +02:00
ringabout
ecc6a1d92b fixes #24109; gdb.SYMBOL_FUNCTION_DOMAIN (#24110)
fixes #24109
2024-09-16 07:10:56 +02:00
Juan M Gómez
6c35a36043 bumps nimble to 0.16.1 (#24102) 2024-09-15 07:57:16 +02:00
ringabout
4ff35585f8 minor: export dllOverrides (#24106) 2024-09-14 11:11:40 +08:00
metagn
6d362e0ffe fix regression with uint constant losing abstract type (#24105)
fixes #24104, refs #23955

The line `result.typ = dstTyp` added in #23955 changes the type of
`result`, which was the type of `n` due to the argument passed to
`newIntNodeT`, to the abstract type skipped `dstTyp`. The line is
removed to just keep the type as abstract.
2024-09-14 10:20:30 +08:00
metagn
61e04ba0ed fix calls to untyped arbitrary expressions in generics (#24100)
fixes #24099

If an arbitrary expression without a proc type (in this case
`tyFromExpr`) is called, the compiler [passes it to overload
resolution](793cee4de1/compiler/semexprs.nim (L1223)).
But overload resolution also can't handle arbitrary expressions and
treats them as not participating at all, matching with the state
`csEmpty`. The compiler checks for this and gives an "identifier
expected" error. Instead, now if we are in a generic context and an
arbitrary expression call matched with `csEmpty`, we now return
`csNoMatch` so that `semResolvedCall` can leave it untyped as
appropriate.

The expression flag `efNoDiagnostics` is replaced with this check. It's
not checked anywhere else and the only place that uses it is
`handleCaseStmtMacro`. Replacing it with `efNoUndeclared`, we just get
`csEmpty` instead of `csNoMatch`, which is handled the same way here. So
`efNoDiagnostics` is now removed and `efNoUndeclared` is used instead.
2024-09-13 11:08:22 +02:00
metagn
793cee4de1 treat generic body type as atomic in iterOverType (#24096)
follows up #24095

In #24095 a check was added that used `iterOverType` to check if a type
contained unresolved types, with the aim of always treating
`tyGenericBody` as resolved. But the body of the `tyGenericBody` is also
iterated over in `iterOverType`, so if the body of the type actually
used generic parameters (which isn't the case in the test added in
#24095, but is now), the check would still count the type as unresolved.

This is handled by not iterating over the children of `tyGenericBody`,
the only users of `iterOverType` are `containsGenericType` and
`containsUnresolvedType`, the first one always returns true for
`tyGenericBody` and the second one aims to always return false.
Unfortunately this means `iterOverType` isn't as generic of an API
anymore but maybe it shouldn't be used anymore for these procs.
2024-09-11 16:13:28 +02:00
metagn
9dda7ff7bc make sigmatch use prepareNode for tyFromExpr (#24095)
fixes regression remaining after #24092

In #24092 `prepareNode` was updated so it wouldn't try to instantiate
generic type symbols (like `Generic` when `type Generic[T] = object`,
and `prepareNode` is what `tyFromExpr` uses in most of the compiler. An
exception is in sigmatch, which is now changed to use `prepareNode` to
make generic type symbols work in the same way as usual. However this
requires another change to work:

Dot fields and matches to `typedesc` on generic types generate
`tyFromExpr` in generic contexts since #24005, including generic type
symbols. But this means when we try to instantiate the `tyFromExpr` in
sigmatch, which increases `c.inGenericContext` for potentially remaining
unresolved expressions, dotcalls stay as `tyFromExpr` and so never
match. To fix this, we change the "generic type" check in dot fields and
`typedesc` matching to an "unresolved type" check which excludes generic
body types; and for generic body types, we only generate `tyFromExpr` if
the dot field is a generic parameter of the generic type (so that it
gets resolved only at instantiation).

Notes for the future:

* Sigmatch shouldn't have to `inc c.inGenericContext`, if a `tyFromExpr`
can't instantiate it's fine if we just fail the match (i.e. redirect the
instantiation errors from `semtypinst` to a match failure). Then again
maybe this is the best way to check for inability to instantiate.
* The `elif c.inGenericContext > 0 and t.containsUnresolvedType` check
in dotfields could maybe be simplified to just checking for `tyFromExpr`
and `tyGenericParam`, but I don't know if this is an exhaustive list.
2024-09-11 11:55:09 +02:00
metagn
771369237c implement template default values using other params (#24073)
fixes #23506

#24065 broke compilation of template parameter default values that
depended on other template parameters. But this was never implemented
anyway, actually attempting to use those default values breaks the
compiler as in #23506. So these are now implemented as well as fixing
the regression.

First, if a default value expression uses any unresolved arguments
(generic or normal template parameters) in a template header, we leave
it untyped, instead of applying the generic typechecking (fixing the
regression). Then, just before the body of the template is about to be
explored, the default value expressions are handled in the same manner
as the body as well. This captures symbols including the parameters, so
the expression is checked again if it contains a parameter symbol, and
marked with `nfDefaultRefsParam` if it does (as an optimization to not
check it later).

Then when the template is being evaluated, when substituting a
parameter, if we try to substitute with a node marked
`nfDefaultRefsParam`, we also evaluate it as we would the template body
instead of passing it as just a copy (the reason why it never worked
before). This way we save time if a default value doesn't refer to
another parameter and could just be copied regardless.
2024-09-11 09:05:39 +02:00
metagn
baec1955b5 don't instantiate generic body type symbols in generic expressions (#24092)
fixes #24090

Generic body types are normally a sign of an uninstantiated type, and so
give errors when trying to instantiate them. However when instantiating
free user expressions like the nodes of `tyFromExpr`, generic default
params, static values etc, they can be used as arguments to macros or
templates etc (as in the issue). So, we don't try to instantiate generic
body type symbols at all in free expressions such as these (but not in
for example type nodes), and avoid the error.

In the future there should be a "concrete type" check for generic body
types different from the check in type instantiation to deal with things
like #24091, if we do want to allow this use of them.
2024-09-10 12:19:39 +02:00
metagn
21771765a2 add posix uint changes to changelog + fix Nlink, Dev on FreeBSD (#24088)
refs #24078, refs #24076

Since these changes are potentially breaking, add them to changelog,
also add Nlink as mentioned in
https://github.com/nim-lang/Nim/issues/24076#issuecomment-2337666555.
2024-09-09 14:44:49 +02:00
ringabout
3a55bae53f enable closures tests for JS & implement finished for JS (#23521) 2024-09-09 14:20:40 +02:00
CharlesEnding
fcee829d85 Adds an example of using ident to name procedures to the macros tutorial (#22973)
As discussed here: https://forum.nim-lang.org/t/10653

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
2024-09-09 11:46:48 +02:00
Tobias Dély
8b895afcb5 fix: InotifyEvent.name should be UncheckedArray[char] (#23413) 2024-09-09 11:45:58 +02:00
Nikolay Nikolov
09682ac7f1 + show the effectsOf pragma (if present) of procs in nimsuggest hints… (#23305)
… and compiler messages
2024-09-09 11:45:22 +02:00
metagn
a6595e5b49 open new scope for const values (#24084)
fixes #5395

Previously values of `const` statements used the same scope as the
`const` statement itself, meaning variables could be declared inside
them and referred to in other statements in the same block. Now each
`const` value opens its own scope, so any variable declared in the value
of a constant can only be accessed for that constant.

We could change this to open a new scope for the `const` *section*
rather than each constant, so the variables can be used in other
constants, but I'm not sure if this is sound.
2024-09-09 11:29:30 +02:00
ringabout
9ff0333a4c fixes #21353; fixes default closure in the VM (#24070)
fixes #21353

```nim
  result = newNodeIT(nkTupleConstr, info, t)
  result.add(newNodeIT(nkNilLit, info, t))
  result.add(newNodeIT(nkNilLit, info, t))
```
The old implementation uses `t` which is the type of the closure
function as its type. It is not correct and generates ((nil, nil), (nil,
nil)) for `default(closures)`. This PR creates `(tyPointer, tyPointer)`
for fake closure types just like what cctypes do.
2024-09-09 11:22:37 +02:00
metagn
24e5b21c90 fix regression with generic params in static type (#24075)
Caught in https://github.com/metagn/applicates, I'm not sure which
commit causes this but it's also in the 2.0 branch (but not 2.0.2), so
it's not any recent PRs.

If a proc has a static parameter with type `static Foo[T]`, then another
parameter with type `static Bar[T, U]`, the generic instantiation for
`Bar` doesn't match `U` which has type `tyGenericParam`, but matches `T`
since it has type `tyTypeDesc`. The reason is that `concreteType`
returns the type itself for `tyTypeDesc` if `c.isNoCall` (i.e. matching
a generic invocation), but returns `nil` for `tyGenericParam`. I'm
guessing `tyGenericParam` is received here because of #22618, but that
doesn't explain why `T` is still `tyTypeDesc`. I'm not sure.

Regardless, we can just copy the behavior for `tyTypeDesc` to
`tyGenericParam` and also return the type itself when `c.isNoCall`. This
feels like it defeats the purpose of `concreteType` but the way it's
used doesn't make sense without it (generic param can't match another
generic param?). Alternatively we could loosen the `if concrete == nil:
return isNone` checks in some places for specific conditions, whether
`c.isNoCall` or `c.inGenericContext == 0` (though this would need
#24005).
2024-09-09 10:12:10 +02:00
metagn
f223f016f3 show symchoices as ambiguous in overload type mismatches (#24077)
fixes #23397

All ambiguous symbols generate symchoices for call arguments since
#23123. So, if a type mismatch receives a symchoice node for an
argument, we now treat it as an ambiguous identifier and list the
ambiguous symbols in the error message.
2024-09-09 09:50:45 +02:00
metagn
7de4ace949 fix int32's that should be uint32 on BSD & OSX (#24078)
fixes #24076

As described in #24076, misannotating these types causes codegen errors.
Sources for the types are https://github.com/openbsd/src/blob/master/sys
for BSD and https://opensource.apple.com/source/Libinfo/Libinfo-391/ and
[_types.h](https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/_types.h.auto.html)
for OSX.
2024-09-09 09:46:47 +02:00
Jake Leahy
6a5aa00701 Fix CSS for number-lines code blocks (#24081)
Adds a few fixes for when using code blocks with lines numbered

- Use CSS variables for the colours so that it works in dark mode
- Don't turn on normal table effects like hover and smaller font when
its a line number table
- With dochack.nim, don't add a clipboard copy button for the line
numbers at the side

[Example page showing the
changes](https://66dcde6e4a655efb70771d9a--dazzling-kitten-6c3419.netlify.app/)
2024-09-09 09:42:45 +02:00
metagn
79a65da22a fix CI, sem whole when stmts as generic stmt (#24072)
fixes CI, refs #24066, refs #24065

The combination of #24065 and #24066 caused a CI failure where a `when`
branch that was never compiled gave an undeclared identifier error. This
is because the `when` branch was being semchecked with `semGenericStmt`
without `withinMixin`, which is the flag `semgnrc` normally gives to
`when` branch bodies. To fix this, just pass the whole `when` stmt to
`semGenericStmt` rather than the individual blocks.

The alternative would be to just replace the calls to `semGenericStmt`
with a new proc that does the same thing, just with the flags
`{withinMixin}`.
2024-09-08 22:50:30 +02:00
bptato
29a7d60acb Fix ioselectors_kqueue raising wrong exceptions (#24079)
kqueue will remove pipes automatically if their read end is closed.
Unfortunately this means that trying to unregister it (which is
necessary to clean up resources & for consistency with other ioselectors
implementations) will set an ENOENT error, which currently raises an
exception.

(ETA: in other words, it is currently impossible to call unregister on a
pipe fd without potentially getting the selector into an invalid state
on platforms with kqueue.)

Avoid this issue by ignoring ENOENT errors returned from kqueue.

(Tested on FreeBSD. I added a test case to the tioselectors file; the
seemingly unrelated change is to fix a race condition that doesn't
appear on Linux, so that it would run my code too.)
2024-09-08 22:50:10 +02:00
metagn
ca28c256f3 fix subscript in generics, typeof, lent with bracket (#24067)
fixes #15959

Another followup of #22029 and #24005, subscript expressions now
recognize when their parameters are generic types, then generating
tyFromExpr. `typeof` also now properly sets `tfNonConstExpr` to make it
usable in proc signatures. `lent` with brackets like `lent[T]` is also
now allowed.
2024-09-08 22:49:27 +02:00
metagn
ebcfd96ae1 improve compiler performance on dot fields after #24005 (#24074)
I noticed after #24005 the auto-reported boot time in PRs increased from
around 8 seconds to 8.8 seconds, but I wasn't sure what could cause a
performance problem that made the compiler itself compile slower, most
of the changes were related to `static` which the compiler code doesn't
use too often. So I figured it was unrelated.

However there is still a performance problem with the changes to
`tryReadingGenericParam`. If an expression like `a.b` doesn't match any
of the default dot field behavior (for example, is actually a call
`b(a)`), the compiler does a final check to see if `b` is a generic
parameter of `a`. Since #24005, if the type of `a` is not
`tyGenericInst` or an old concept type, the compiler does a full
traversal of the type of `a` to see if it contains a generic type, only
then checking for `c.inGenericContext > 0` to not return `nil`. This
happens on *every* dot call.

Instead, we now check for `c.inGenericContext > 0` first, only then
checking if it contains a generic type, saving performance by virtue of
`c.inGenericContext > 0` being both cheap and less commonly true. The
`containsGenericType` could also be swapped out for more generic type
kind checks, but I think this is incorrect even if it might pass CI.
2024-09-08 22:11:12 +02:00
metagn
cd22560af5 fix string literal assignment with different lengths on ARC (#24083)
fixes #24080
2024-09-08 20:17:26 +02:00
metagn
7cd1777218 generate tyFromExpr for when in generics (#24066)
fixes #22342, fixes #22607

Another followup of #22029, `when` expressions in general in generic
type bodies now behave like `nkRecWhen` does since #24042, leaving them
as `tyFromExpr` if a condition is uncertain. The tests for the issues
were originally added but left disabled in #24005.
2024-09-06 11:46:17 +02:00
metagn
a93c5d79b9 adapt generic default parameters to recent generics changes (#24065)
fixes #16700, fixes #20916, refs #24010

Fixes the instantiation issues for proc param default values encountered
in #24010 by:

1. semchecking generic default param values with `inGenericContext` for
#22029 and followups to apply (the bigger change in semtypes),
2. rejecting explicit generic instantiations with unresolved generic
types inside `inGenericContext` (sigmatch change),
3. instantiating the default param values using `prepareNode` rather
than an insufficient manual method (the bigger change in seminst).

This had an important side effect of references to other parameters not
working since they would be resolved as a symbol belonging to the
uninstantiated original generic proc rather than the later instantiated
proc. There is a more radical way to fix this which is generating ident
nodes with `tyFromExpr` in specifically this context, but instead we
just count them as belonging to the same proc in
`hoistParamsUsedInDefault`.

Other minor bugfixes:

* To make the error message in t20883 make sense, we now give a "cannot
instantiate" error when trying to instantiate a proc generic param with
`tyFromExpr`.
* Object constructors as default param values generated default values
of private fields going through `evalConstExpr` more than once, but the
VM doesn't mark the object fields as `nfSkipFieldChecking` which gives a
"cannot access field" error. So the VM now marks object fields it
generates as `nfSkipFieldChecking`. Not sure if this affects VM
performance, don't see why it would.
* The nkRecWhen changes in #24042 didn't cover the case where all
conditions are constantly false correctly, this is fixed with a minor
change. This isn't needed for this PR now but I encountered it after
forgetting to `dec c.inGenericContext`.
2024-09-06 11:44:38 +02:00
ringabout
c10f84b9d7 fixes #24053; fixes #18288; relax reorder with push/pop pragmas restrictions; no crossing push/pop barriers (#24061)
fixes #24053;
fixes #18288

makes push pragmas depend on each node before it and nodes after it
depend on it
2024-09-06 11:26:24 +02:00
metagn
4a548deb08 proper errors for subscript overloads (#24068)
The magic `mArrGet`/`mArrPut` subscript overloads always match, so if a
subscript doesn't match any other subscript overloads and isn't a
regular language-handled subscript, it creates a fake overload mismatch
error in `semArrGet` that doesn't have any information (gives stuff like
"first mismatch at index: 0" for every single mismatch). Instead of
generating the fake mismatches, we only generate the fake mismatch for
`mArrGet`/`mArrPut`, and process every overload except them as a real
call and get the errors from there.
2024-09-06 11:25:51 +02:00
metagn
d77ea07837 expose rangeBase typetrait, fix enum conversion warning (#24056)
refs #21682, refs #24038

The `rangeBase` typetrait added in #21682 which gives the base type of a
range type is now added publicly to `typetraits`. Previously it was only
privately used in `repr_v2` and in `enumutils` since #24052
(coincidentally I didn't see this until now). This is part of an effort
to make range types easier to work with in generics, as mentioned in
#24038. Its use combined with #24037 is also tested.

The condition for the "enum to enum conversion" warning is now also
restricted to conversions between different enum base types, i.e.
conversion between an enum type and a range type of itself doesn't give
a warning. I put this in this PR since the test gave the warning and so
works as a regression test.
2024-09-06 11:18:20 +02:00
metagn
bf865fa75a fix undeclared identifier in templates in generics (#24069)
fixes #13979

Fixes templates in generics that use identifiers that aren't defined
yet, giving an early `undeclared identifier` error, by just marking
template bodies as in a mixin context in `semgnrc`.
2024-09-06 11:16:43 +02:00
ringabout
d91297a330 remove unused config field: keepComments (#24063) 2024-09-05 20:55:06 +02:00
ringabout
9ff15da426 fixes #23897; Useless empty C files with arc/orc (#24064)
fixes #23897

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

Since #22472 triggers `nimTestErrorFlag` in every module that isn't
empty, this PR removes unnecessary logic
2024-09-05 20:44:00 +02:00
lit
e265b3dfdd Make math.isNaN,copySign,etc available on objc (#24025)
fixes #23922

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
2024-09-05 14:21:30 +08:00
ringabout
99f4cfa438 remove unused nimStdlibVersion (#24060) 2024-09-04 15:09:29 +02:00
c-blake
6908fb4011 Make $ on 0-length MemSlice produce Nim "" as per DMisener idea (#24015)
in https://forum.nim-lang.org/t/12463 which seems reasonable enough to
me.

stdlib has several dozens of places doing result.setLen now, but if
`experimental:strictDefs` move sot on by default and there is no easy
way to locally suppress the warning we can revisit this.
2024-09-04 17:01:55 +08:00
metagn
080b0a03bd streams: implement readStr for VM, document VM limitations (#24058)
fixes #24054

`readData` is not implemented for the VM as mentioned in the issue, but
`readDataStr` is, so that is used for `readStr` instead on the VM. We
could also just use it in general since it falls back to `readData`
anyway but it's kept the same otherwise for now.

Also where and why streams in general don't work in VM is now documented
on the top level `streams` module documentation.
2024-09-04 09:25:01 +02:00
metagn
c314155fb3 document partial generics breaking changes (#24055)
refs #24010, split from #24051
2024-09-04 09:13:28 +02:00
metagn
f69809bb17 proper error for calling nil closure in VM (#24059)
fixes #24057

Instead of crashing the compiler, the VM now gives a stacktrace if a nil
closure is attempted to be called.
2024-09-04 09:13:04 +02:00
ringabout
c948ab9b85 fixes symbolName for range enums (#24052) 2024-09-03 16:35:04 +02:00
ringabout
4bf323d6c4 fixes push warnings for sempass2 (#23603)
ref https://forum.nim-lang.org/t/11590
2024-09-03 09:19:52 +02:00
metagn
538603e01d allow conversions between var types of range types and base types (#24037)
refs #24032, split from #24036

Conversion from variables of range types or base types of range types to
the other are now considered mutable for `var` params, similar to how
distinct types are mutable when converted to their base type or vice
versa. There are 2 main differences:

1. Conversions from base types to range types need to emit
`nkChckRange`, which is not generated for things like tuple/object
fields.
2. Range types can still correspond to different types in the backend
when nested in other types, such as `set[range[3..5]]` vs
`set[range[0..5]]`.

Since the convertibility check for `var` params and a check whether to
emit a no-op for `nkConv` (and now also `nkChckRange`) so that the
output is still addressable both use `sameType`, we accomplish this by
adding a new flag to `sameType` that ignores range types, but only when
they're not nested in other types. The implementation for this might be
flawed, I didn't include children of some metatypes as "nested in other
types", but stuff like `tyGenericInst` params are respected.
2024-09-03 09:18:38 +02:00
metagn
1ebdcb3bca fully disable static paramTypesMatch for tyFromExpr in generics (#24049)
fixes #24044

When matching a `tyFromExpr` against a `static` generic parameter,
`paramTypesMatch` tries to evaluate it as a constant expression, which
causes a segfault in the case of #24044. In #24005 a consequence of the
same behavior was an issue where `nkStaticExpr` was created for
`tyFromExpr` which made it not instantiate, so only the generation of
`nkStaticExpr` was disabled. Instead we now just completely ignore
`tyFromExpr` matching a `static` generic parameter in generic contexts
and keep it untouched.
2024-09-03 05:45:44 +02:00
metagn
d27061f6da fix segfault with gensym node instantiation (#24050)
fixes #24048

Generic lambdas get instantiated via `replaceTypesInBody` which calls
`replaceTypeVarsN` on the body of the lambda. This body can contain sym
nodes of gensym symbols generated by macros, which have `nil` type. But
a piece of code in `replaceTypeVarsN` checks whether the type of a
symbol is equal to `void` without checking if it's `nil` first, which
causes a segfault. Now it also checks that the type of the symbol isn't
`nil` for it to be `void`.
2024-09-03 05:45:08 +02:00
metagn
71de7fca9e handle explicit generic routine instantiations in sigmatch (#24010)
fixes #16376

The way the compiler handled generic proc instantiations in calls (like
`foo[int](...)`) up to this point was to instantiate `foo[int]`, create
a symbol for the instantiated proc (or a symchoice for multiple procs
excluding ones with mismatching generic param counts), then perform
overload resolution on this symbol/symchoice. The exception to this was
when the called symbol was already a symchoice node, in which case it
wasn't instantiated and overloading was called directly ([these
lines](b7b1313d21/compiler/semexprs.nim (L3366-L3371))).

This has several problems:

* Templates and macros can't create instantiated symbols, so they
couldn't participate in overloaded explicit generic instantiations,
causing the issue #16376.
* Every single proc that can be instantiated with the given generic
params is fully instantiated including the body. #9997 is about this but
isn't fixed here since the instantiation isn't in a call.

The way overload resolution handles explicit instantiations by itself is
also buggy:

* It doesn't check constraints.
* It allows only partially providing the generic parameters, which makes
sense for implicit generics, but can cause ambiguity in overloading.

Here is how this PR deals with these problems:

* Overload resolution now always handles explicit generic instantiations
in calls, in `initCandidate`, as long as the symbol resolves to a
routine symbol.
* Overload resolution now checks the generic params for constraints and
correct parameter count (ignoring implicit params). If these don't
match, the entire overload is considered as not matching and not
instantiated.
* Special error messages are added for mismatching/missing/extra generic
params. This is almost all of the diff in `semcall`.
* Procs with matching generic parameters now instantiate only the type
of the signature in overload resolution, not the proc itself, which also
works for templates and macros.

Unfortunately we can't entirely remove instantiations because overload
resolution can't handle some cases with uninstantiated types even though
it's resolved in the binding (see the last 2 blocks in
`texplicitgenerics`). There are also some instantiation issues with
default params that #24005 didn't fix but I didn't want this to become
the 3rd huge generics PR in a row so I didn't dive too deep into trying
to fix them. There is still a minor instantiation fix in `semtypinst`
though for subscripts in calls.

Additional changes:

* Overloading of `[]` wasn't documented properly, it somewhat is now
because we need to mention the limitation that it can't be done for
generic procs/types.
* Tests can now enable the new type mismatch errors with just
`-d:testsConciseTypeMismatch` in the command.

Package PRs:

- using fork for now:
[combparser](https://github.com/PMunch/combparser/pull/7) (partial
generic instantiation)
- merged: [cligen](https://github.com/c-blake/cligen/pull/233) (partial
generic instantiation but non-overloaded + template)
- merged: [neo](https://github.com/andreaferretti/neo/pull/56) (trying
to instantiate template with no generic param)
2024-09-02 18:22:20 +02:00
ringabout
c8af0996fd fixes #24033; Yielding from var fails with pairs and destructuring (#24046)
fixes #24033
2024-09-02 18:12:48 +02:00
metagn
5e55e16ad8 check constant conditions in generic when in objects (#24042)
fixes #24041

`when` statements in generic object types normally just leave their
conditions as expressions and still typecheck their branch bodies.
Instead of this, when the condition can be evaluated as a constant as
well as the ones before it and it resolves to `true`, it now uses the
body of that branch without typechecking the remaining ones.
2024-09-02 18:11:59 +02:00
ringabout
4789af71fe fixes #24031; js codegen bug for case statement with just else branch (#24047)
fixes #24031
2024-09-02 18:10:01 +02:00
metagn
fc853cb726 generic issues test cases (#24028)
closes #1969, closes #7547, closes #7737, closes #11838, closes #12283,
closes #12714, closes #12720, closes #14053, closes #16118, closes
#19670, closes #22645

I was going to wait on these but regression tests even for recent PRs
are turning out to be important in wide reaching PRs like #24010.

The other issues with the working label felt either finnicky (#7385,
#9156, #12732, #15247), excessive to test (#12405, #12424, #17527), or I
just don't know what fixed them/what the issue was (#16128: the PR link
gives a server error by Github, #12457, #12487).
2024-08-30 16:12:38 +02:00
ringabout
11ead19bc1 fixes #24034; fixes lent types after taking implicit address (#24035)
fixes #24034
2024-08-30 16:08:59 +02:00
metagn
aa0d8e9cfe remove noise from bug report issue form (#24030)
Removes some noise from the bug report issue template so people who
aren't familiar can submit issues with less friction. Some of these
might be a bit subjective, feel free to call out any nitpicky parts, I
didn't want to rewrite entirely. Mostly fixed typos & language issues,
other than that:

* Just "detailed information", no need to say "descriptive", and changed
"reproducible code and detailed information" to "or detailed
information" because code is good enough
* Simplified description label, mention code example can go there
* Changed "Possible Solution" to "Known Workarounds" because it's what
issue authors are more likely to know (and usually write anyway) and is
more useful to most readers.
* Doc link for "rebuilding the compiler" was broken, replaced it with
mentioning choosenim, nightlies and building a temp compiler which is
hopefully less offputting

Weird looking preview
[here](https://github.com/metagn/Nim/blob/issue-templates/.github/ISSUE_TEMPLATE/bug_report.yml)

Just to complain: Github issue forms have some problems themselves,
there's no option to disable the "No response" text when an optional
area is unfilled, and the textboxes are really small at least on Firefox
(though you can expand them). The alternative, issue templates, isn't
much better. Github also hides the ability to open a blank issue in a
text link in a small line after all the issue form buttons, I'm guessing
this is intentional for repositories which don't want blank issues but
we don't have such a problem. Worst comes to worst we can add a button
for a minimal/empty issue template.
2024-08-29 21:37:24 +02:00
metagn
b7b1313d21 proper error message for out-of-range enum sets (#24027)
fixes #17848
2024-08-29 16:13:06 +02:00
metagn
d7e77b330f fix include in templates, with prefix operators (#24029)
fixes #12539, refs #21636

`evalInclude` now uses `getPIdent` to check if a symbol isn't `/`
anymore instead of assuming it's an ident, which it's not when used in
templates. Also it just checks if the symbol is `as` now, because there
are other infix operators that could be used like `/../`.

It also works with prefix operators now by copying what was done in
#21636.
2024-08-29 16:11:37 +02:00
ringabout
da7670c8e0 fixes typo (#24026) 2024-08-29 16:20:04 +08:00
ringabout
1244ffbf39 fixes #23923; type-aliased seq[T] get different backend C/C++ pointer type names causing invalid codegen (#23924)
… type names causing invalid codegen

fixes #23923
2024-08-28 20:59:25 +02:00
握猫猫
5e8cd318ef Fix linux start process errorCode always 0 (#24001)
#23992 The test case provided does not cover the Windows situation, I
fixed it in this new PR.

Fixed an issue where errorCode was always 0 when startProcess didn't use
the poEvalCommand flag.

Tthe sleep command might not be available in all Windows installations,
so I skipped the relevant test.

Added a test case, tested on my fedora and windows systems.
2024-08-28 20:52:00 +02:00
metagn
770f8d5513 opensym for templates + move behavior of opensymchoice to itself (#24007)
fixes #15314, fixes #24002

The OpenSym behavior first added to generics in #23091 now also applies
to templates, since templates can also capture symbols that are meant to
be replaced by local symbols if the context imports symbols with the
same name, as in the issue #24002. The experimental switch
`templateOpenSym` is added to enable this behavior for templates only,
and the experimental switch `openSym` is added to enable it for both
templates and generics, and the documentation now mainly mentions this
switch.

Additionally the logic for `nkOpenSymChoice` nodes that were previously
wrapped in `nkOpenSym` now apply to all `nkOpenSymChoice` nodes, and so
these nodes aren't wrapped in `nkOpenSym` anymore. This means
`nkOpenSym` can only have children of kind `nkSym` again, so it is more
in line with the structure of symchoice nodes. As for why they aren't
merged with `nkOpenSymChoice` nodes yet, we need some way to signal that
the node shouldn't become ambiguous if other options exist at
instantiation time, we already captured a symbol at the beginning and
another symbol can only replace it if it's closer in scope and
unambiguous.
2024-08-28 20:51:13 +02:00
metagn
d3af51e3ce remove fauxMatch for tyFromExpr, remove tyProxy and tyUnknown aliases (#24018)
updated version of #22193

After #22029 and the followups #23983 and #24005 which fixed issues with
it, `tyFromExpr` no longer match any proc params in generic type bodies
but delay all non-matching calls until the type is instantiated.
Previously the mechanism `fauxMatch` was used to pretend that any
failing match against `tyFromExpr` actually matched, but prevented the
instantiation of the type until later.

Since this mechanism is not needed anymore for `tyFromExpr`, it is now
only used for `tyError` to prevent cascading errors and changed to a
bool field for simplicity. A change in `semtypes` was also needed to
prevent calling `fitNode` on default param values resolving to type
`tyFromExpr` in generic procs for params with non-generic types, as this
would try to coerce the expression into a concrete type when it can't be
instantiated yet.

The aliases `tyProxy` and `tyUnknown` for `tyError` and `tyFromExpr` are
also removed for uniformity.
2024-08-28 20:46:36 +02:00
ringabout
ea7c2a4409 fixes #14623; Top-level volatileLoad/volatileStore leads to invalid codegen (#24020)
fixes #14623
2024-08-28 20:44:06 +02:00
ringabout
d53a9cf288 use the official URL of neo (#24019)
ref https://github.com/andreaferretti/neo/pull/55
2024-08-28 22:59:09 +08:00
autumngray
540b414c86 fixes #23925; VM generates wrong cast for negative enum values (#23951)
Follow up of #23927 which solves the build error.

This is still only a partial fix as it doesn't take into account
unordered enums. I'll make a separate issue for those.

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
2024-08-27 14:03:56 +02:00
metagn
f09c549d42 make int literals with range type match their base type better than other int types (#24017)
This is a very niche case encountered in #24012, where an int literal
got a `range` type as a result of a generic instantiation (in
`tgenericcomputedrange`), I can't think of another test case. The base
type of the range being `int` made it match `int` with `isSubrange` as
in the first `if` branch, but other int types like `int32` matched with
`isFromIntLit` which is a better match.

Instead, int literals with range type now:

1. match their base type with `isFromIntLit`,
2. don't match other int types with `isFromIntLit`, instead giving
`isConvertible` as in the last `if` branch in `handleRange`.
2024-08-27 09:58:05 +02:00
ringabout
7e88091de3 fixes #22553; regression of offsetof(T, anFieldOfUncheckedArray) (#24014)
fixes #22553

follow up https://github.com/nim-lang/Nim/pull/21979 which introduced a
char dummy member prepended to objs only containing an UncheckedArray
2024-08-27 09:45:30 +02:00
Miran
4c250d69a8 bump NimVersion to 2.1.99 (2.0.2 RC2) (#24016) 2024-08-27 02:49:46 +02:00
metagn
69ea1336fb sem generic proc param types like generic types + static instantiation fixes (#24005)
fixes #4228, fixes #4990, fixes #7006, fixes #7008, fixes #8406, fixes
#8551, fixes #11112, fixes #20027, fixes #22647, refs #23854 and #23855
(remaining issue fixed), refs #8545 (works properly now with
`cast[static[bool]]` changed to `cast[bool]`), refs #22342 and #22607
(disabled tests added), succeeds #23194

Parameter and return type nodes in generic procs now undergo the same
`inGenericContext` treatment that nodes in generic type bodies do. This
allows many of the fixes in #22029 and followups to also apply to
generic proc signatures. Like #23983 however this needs some more
compiler fixes, but this time mostly in `sigmatch` and type
instantiations.

1. `tryReadingGenericParam` no longer treats `tyCompositeTypeClass` like
a concrete type anymore, so expressions like `Foo.T` where `Foo` is a
generic type don't look for a parameter of `Foo` in non-generic code
anymore. It also doesn't generate `tyFromExpr` in non-generic code for
any generic LHS. This is to handle a very specific case in `asyncmacro`
which used `FutureVar.astToStr` where `FutureVar` is generic.
2. The `tryResolvingStaticExpr` call when matching `tyFromExpr` in
sigmatch now doesn't consider call nodes in general unresolved, only
nodes with `tyFromExpr` type, which is emitted on unresolved expressions
by increasing `c.inGenericContext`. `c.inGenericContext == 0` is also
now required to attempt instantiating `tyFromExpr`. So matching against
`tyFromExpr` in proc signatures works in general now, but I'm
speculating it depends on constant folding in `semExpr` for statics to
match against it properly.
3. `paramTypesMatch` now doesn't try to change nodes with `tyFromExpr`
type into `tyStatic` type when fitting to a static type, because it
doesn't need to, they'll be handled the same way (this was a workaround
in place of the static type instantiation changes, only one of the
fields in the #22647 test doesn't work with it).
4. `tyStatic` matching now uses `inferStaticParam` instead of just range
type matching, so `Foo[N div 2]` can infer `N` in the same way `array[N
div 2, int]` can. `inferStaticParam` also disabled itself if the
inferred static param type already had a node, but `makeStaticExpr`
generates static types with unresolved nodes, so we only disable it if
it also doesn't have a binding. This might not work very well but the
static type instantiation changes should really lower the amount of
cases where it's encountered.
5. Static types now undergo type instantiation. Previously the branch
for `tyStatic` in `semtypinst` was a no-op, now it acts similarly to
instantiating any other type with the following differences:
- Other types only need instantiation if `containsGenericType` is true,
static types also get instantiated if their value node isn't a literal
node. Ideally any value node that is "already evaluated" should be
ignored, but I'm not sure of a better way to check this, maybe if
`evalConstExpr` emitted a flag. This is purely for optimization though.
- After instantiation, `semConstExpr` is called on the value node if
`not cl.allowMetaTypes` and the type isn't literally a `static` type.
Then the type of the node is set to the base type of the static type to
deal with `semConstExpr` stripping abstract types.
We need to do this because calls like `foo(N)` where `N` is `static int`
and `foo`'s first parameter is just `int` do not generate `tyFromExpr`,
they are fully typed and so `makeStaticExpr` is called on them, giving a
static type with an unresolved node.
2024-08-26 06:54:38 +02:00
metagn
09dcff71c8 generate symchoice for ambiguous types in templates & generics + handle types in symchoices (#23997)
fixes #23898, supersedes #23966 and #23990

Since #20631 ambiguous type symbols in templates are rejected outright,
now we generate a symchoice for type nodes if they're ambiguous, a
generalization of what was done in #22375. This is done for generics as
well. Symchoices also handle type symbols better now, ensuring their
type is a `typedesc` type; this probably isn't necessary for everything
to work but it makes the logic more robust.

Similar to #23989, we have to prepare for the fact that ambiguous type
symbols behave differently than normal type symbols and either error
normally or relegate to other routine symbols if the symbol is being
called. Generating a symchoice emulates this behavior, `semExpr` will
find the type symbol first, but since the symchoice has other symbols,
it will count as an ambiguous type symbol.

I know it seems spammy to carry around an ambiguity flag everywhere, but
in the future when we have something like #23104 we could just always
generate a symchoice, and the symchoice itself would carry the info of
whether the first symbol was ambiguous. But this could harm compiler
performance/memory use, it might be better to generate it only when we
have to, which in the case of type symbols is only when they're
ambiguous.
2024-08-25 22:24:20 +02:00
ringabout
0d53b6e027 fixes #23915; std/random produces different results on c/js (#24003)
fixes #23915
2024-08-25 22:23:30 +02:00
ringabout
4ef06a5cc5 fixes cast expressions introduces unnecessary copies (#24004)
It speeds up
```nim
proc foo =
  let piece = cast[seq[char]](newSeqUninit[uint8](5220600386'i64))

foo()
```

Notes that `cast[ref](...)` is excluded because we need to keep the ref
alive if the parameter is something with pointer types (e.g.
`cast[ref](pointer)`or `cast[ref](makePointer(...))`)

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2024-08-23 20:07:00 +02:00
metagn
446501b53b fix error messages for wrongly typed generic param default values (#24006)
fixes #21258

When a generic proc is instantiated, if one of the default values
doesn't match the type of the parameter, `seminst` sets the default
parameter node to an `nkEmpty` node with `tyError` type. `sigmatch`
checks for this to give an error message if the default param is
actually used, but only while actively matching the proc signature,
before the proc is even instantiated. The error message also gives very
little information.

Now, we check for this in `updateDefaultParams` at the end of
`semResolvedCall`, after the proc has been instantiated. The `nkEmpty`
node also is given the original mismatching type instead rather than
`tyError`, only setting `tyError` after erroring to prevent cascading
errors. The error message is changed to the standard type mismatch error
also giving the instantiation info of the routine.
2024-08-23 18:01:43 +02:00
Alfred Morgan
cb7bcae7f7 fixes #23956; bindUnix loses the last character on OpenBSD (#23961)
uses calculation as shown in
https://man7.org/linux/man-pages/man7/unix.7.html
> offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1
2024-08-22 10:31:35 +02:00
ringabout
79f5a74408 fixes #23454; IndexDefect thrown when destructuring a lent tuple (#23993)
fixes #23454
2024-08-22 07:21:13 +02:00
metagn
04da0a6028 fix subscript magic giving unresolved generic param type (#23988)
fixes #19737

As in the diff, `semResolvedCall` sets the return type of a call to a
proc to the type of the call. But in the case of the [subscript
magic](https://nim-lang.org/docs/system.html#%5B%5D%2CT%2CI), this type
is the first generic param which is also supposed to be the type of the
first argument, but this is invalid, the correct type is the element
type eventually given by `semSubscript`. Some lines above also [prevent
the subscript magics from instantiating their
params](dda638c1ba/compiler/semcall.nim (L699))
so this type ends up being an unresolved generic param.

Since the type of the node is not `nil`, `prepareOperand` doesn't try to
type it again, and this unresolved generic param type ends up being the
final type of the node. To prevent this, we just never set the type of
the node if we encountered a subscript magic.

Maybe we could also rename the generic parameters of the subscript
magics to stuff like `DummyT`, `DummyI` if we want this to be easier to
debug in the future.
2024-08-22 07:20:20 +02:00
ringabout
ac0179ced9 fixes #23943; simple default value for range (#23996)
fixes #23943

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2024-08-22 07:20:00 +02:00
metagn
2311049b27 don't require symbol with enum type to be constant in fitNode (#23999)
fixes #23998

In `fitNode` the first symbol of a symchoice that expects an enum type
with the same enum type is given as the result of the `fitNode`. But
`getConstExpr` is also called on it, which will return a `nil` node for
nodes that aren't constant but have the enum type, like variables or
proc parameters. Instead we just return the node directly since it's
already typed.

Normally, this `if` branch in `fitNode` shouldn't exist since
`paramTypesMatch` handles it, but the way pure enum symbols work makes
it really impractical to check their ambiguity, which `paramTypesMatch`
won't like. If it causes problems for regular enums we can restrict this
branch to just pure enums until they are hopefully eventually removed.
2024-08-22 07:19:43 +02:00
ringabout
832ba815d1 Revert "Fixed an issue where errorCode was always 0 when startProcess did…" (#23995)
Reverts nim-lang/Nim#23992
2024-08-21 20:53:38 +08:00
握猫猫
12b90d7c07 Fixed an issue where errorCode was always 0 when startProcess did… (#23992)
…n't use the `poEvalCommand` flag

https://forum.nim-lang.org/t/12310

Added a test case, tested on my fedora system.
2024-08-21 11:44:53 +02:00
metagn
e38cbd3c84 consider ambiguity for qualified symbols (#23989)
fixes #23893

When type symbols are ambiguous, calls to them aren't allowed to be type
conversions and only routine symbols are considered instead. But the
compiler doesn't acknowledge that qualified symbols can be ambiguous,
`qualifiedLookUp` directly tries to access the identifier from the
module string table. Now it checks the relevant symbol iterators for any
symbol after the first received symbol, in which case the symbol is
considered ambiguous. `nkDotExpr` is also included in the whitelist of
node kinds for ambiguous type symbols (not entirely sure why this
exists, it's missing `nkAccQuoted` as well).
2024-08-20 21:32:35 +02:00
metagn
ab18962085 sem all call nodes in generic type bodies + many required fixes (#23983)
fixes #23406, closes #23854, closes #23855 (test code of both compiles
but separate issue exists), refs #23432, follows #23411

In generic bodies, previously all regular `nkCall` nodes like `foo(a,
b)` were directly treated as generic statements and delayed immediately,
but other call kinds like `a.foo(b)`, `foo a, b` etc underwent
typechecking before making sure they have to be delayed, as implemented
in #22029. Since the behavior for `nkCall` was slightly buggy (as in
#23406), the behavior for all call kinds is now to call `semTypeExpr`.

However the vast majority of calls in generic bodies out there are
`nkCall`, and while there isn't a difference in the expected behavior,
this exposes many issues with the implementation started in #22029 given
how much more code uses it now. The portion of these issues that CI has
caught are fixed in this PR but it's possible there are more.

1. Deref expressions, dot expressions and calls to dot expressions now
handle and propagate `tyFromExpr`. This is most of the changes in
`semexprs`.
2. For deref expressions to work in `typeof`, a new type flag
`tfNonConstExpr` is added for `tyFromExpr` that calls `semExprWithType`
with `efInTypeof` on the expression instead of `semConstExpr`. This type
flag is set for every `tyFromExpr` type of a node that `prepareNode`
encounters, so that the node itself isn't evaluated at compile time when
just trying to get the type of the node.
3. Unresolved `static` types matching `static` parameters is now treated
the same as unresolved generic types matching `typedesc` parameters in
generic type bodies, it causes a failed match which delays the call
instantiation.
4. `typedesc` parameters now reject all types containing unresolved
generic types like `seq[T]`, not just generic param types by themselves.
(using `containsGenericType`)
5. `semgnrc` now doesn't leave generic param symbols it encounters in
generic type contexts as just identifiers, and instead turns them into
symbol nodes. Normally in generic procs, this isn't a problem since the
generic param symbols will be provided again at instantiation time (and
in fact creating symbol nodes causes issues since `seminst` doesn't
actually instantiate proc body node types).
But generic types can try to be instantiated early in `sigmatch` which
will give an undeclared identifier error when the param is not provided.
Nodes in generic types (specifically in `tyFromExpr` which should be the
only use for `semGenericStmt`) undergo full generic type instantiation
with `prepareNode`, so there is no issue of these symbols remaining as
uninstantiated generic types.
6. `prepareNode` now has more logic for which nodes to avoid
instantiating.
Subscripts and subscripts turned into calls to `[]` by `semgnrc` need to
avoid instantiating the first operand, since it may be a generic body
type like `Generic` in an expression like `Generic[int]`.
Dot expressions cannot instantiate their RHS as it may be a generic proc
symbol or even an undeclared identifier for generic param fields, but
have to instantiate their LHS, so calls and subscripts need to still
instantiate their first node if it's a dot expression.
This logic still isn't perfect and needs the same level of detail as in
`semexprs` for which nodes can be left as "untyped" for overloading/dot
exprs/subscripts to handle, but should handle the majority of cases.

Also the `efDetermineType` requirement for which calls become
`tyFromExpr` is removed and as a result `efDetermineType` is entirely
unused again.
2024-08-20 21:31:19 +02:00
metagn
6320b0cd5b allow qualifying macro pragmas (#23985)
fixes #12696
2024-08-20 21:27:55 +02:00
ringabout
eed9cb0d3f Try to revert "disable presto" (#23987)
Reverts nim-lang/Nim#23958

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

ref https://github.com/nim-lang/Nim/pull/23958#issuecomment-2294848209
2024-08-20 22:41:28 +08:00
metagn
1befb8d4a3 include generic bodies in allowMetaTypes (#23968)
fixes #19848

Not sure why this wasn't the case already. The `if cl.allowMetaTypes:
return` line below for `tyFromExpr` [was added 10 years
ago](d5798b43de).
Hopefully it was just negligence?
2024-08-20 16:20:35 +02:00
ringabout
6336d2681b adds a ubuntu 24.04 matrix with gcc 14 for tests (#23673)
ref https://forum.nim-lang.org/t/11587
2024-08-20 16:07:11 +02:00
ringabout
dda638c1ba fixes #23945; type checking for whenvm expresssions (#23970)
fixes #23945
2024-08-20 20:41:07 +08:00
ringabout
43274bfb92 fixes #23982; codegen regression passing pointer expressions to inline iterators (#23986)
fixes #23982
2024-08-20 19:09:06 +08:00
ringabout
26107e931c fixes #23973; fixes #23974; Memory corruption with lent and ORC (#23981)
fixes #23973;
fixes #23974
2024-08-20 11:57:47 +02:00
metagn
34719cad9d allow untyped arguments to fail to compile in overload mismatch error (#23984)
fixes #8697, fixes #9620, fixes #23265

When matching a `template` with an `untyped` argument fails because of a
mismatching typed argument, `presentFailedCandidates` tries to sem every
single argument to show their types, but trying to type the `untyped`
argument can fail if it's supposed to use an injected symbol, so we get
an unrelated error message like "undeclared identifier".

Instead we use `tryExpr` as the comment suggests, setting the type to
`untyped` if it fails to compile. We could also maybe check if an
`untyped` argument is expected in its place and not try to compile the
expression if it is but this would require a bit of reorganizing the
code here and IMO it's better to have the information of what type it
would be if it can be typed.
2024-08-20 11:43:11 +02:00
metagn
8bd0422767 fix infinite recursion in term rewriting macros tests (#23979)
Noticed Hint [Pattern] spam in CI
2024-08-20 11:42:14 +02:00
metagn
58813a3b2e make all generic aliases tyAlias (#23978)
fixes #23977

The problem is that for *any* body of a generic declaration,
[semstmts](2e4d344b43/compiler/semstmts.nim (L1610-L1611))
sets the sym of its value to the generic type name, and
[semtypes](2e4d344b43/compiler/semtypes.nim (L2143))
just directly gives the referenced type *specifically* when the
expression is a generic body. I'm blaming `semtypes` here because it's
responsible for the type given but the exact opposite behavior
specifically written in makes me think generating an alias type here
maybe breaks something.
2024-08-20 11:41:50 +02:00
ringabout
a4dff1a03e fixes for 32bit system (#23980) 2024-08-19 20:58:44 +08:00
Juan M Gómez
2e4d344b43 Fixes #23962 resetLocdoenst produce any cgen code in importcpp types (#23964) 2024-08-18 13:21:17 +02:00
metagn
f7c11a8978 allow generic compileTime proc folding (#22022)
fixes #10753, fixes #22021, refs #19365 (was fixed by #22029, but more
faithful test added)

For whatever reason `compileTime` proc calls did not fold if the proc
was generic ([since this folding was
introduced](c25ffbf262 (diff-539da3a63df08fa987f1b0c67d26cdc690753843d110b6bf0805a685eeaffd40))).
I'm guessing the intention was for *unresolved* generic procs to not
fold, which is now the logic.

Non-magic `compileTime` procs also now don't fold at compile time in
`typeof` contexts to avoid possible runtime errors (only the important)
and prevent double/needless evaluation.
2024-08-18 00:52:32 +02:00
metagn
a354b18fe1 always lookup pure enum symbols if expected type is enum (#23976)
fixes #23689

Normally pure enum symbols only "exist" in lookup if nothing else with
the same name is in scope. But if an expression is expected to be an
enum type, we know that ambiguity can be resolved between different
symbols based on their type, so we can include the normally inaccessible
pure enum fields in the ambiguity resolution in the case that the
expected enum type is actually a pure enum. This handles the use case in
the issue of the type inference for enums reverted in #23588.

I know pure enums are supposed to be on their way out so this might seem
excessive, but the `pure` pragma can't be removed in the code in the
issue due to a redefinition error, they have to be separated into
different modules. Normal enums can still resolve the ambiguity here
though. I always think about making a list of all the remaining use
cases for pure enums and I always forget.

Will close #23694 if CI passes
2024-08-17 16:50:48 +02:00
ringabout
0ffc0493a3 bump checksums (#23975)
ref https://github.com/nim-lang/checksums/pull/16
ref https://github.com/nim-lang/Nim/pull/23970
2024-08-17 16:48:46 +02:00
ringabout
253fafb305 fixes docgen regression: don't add newLine for code if it's the first line (#23154)
Before (devel)


![image](https://github.com/nim-lang/Nim/assets/43030857/cde37109-027a-46c1-a37e-1d6062e6c609)

After (this PR and stable)


![image](https://github.com/nim-lang/Nim/assets/43030857/3366877c-7223-4749-a584-fe93f731281f)

It now keeps the same behavior as before
2024-08-17 20:02:36 +08:00
ringabout
e96fad1eed fixes default float ranges (#23957) 2024-08-16 15:50:31 +02:00
metagn
995081b56a fix is with type/typedesc crashing the compiler (#23967)
fixes #22850

The `is` operator checks the type of the left hand side, and if it's
generic or if it's a `typedesc` type with no base type, it leaves it to
be evaluated later. But `typedesc` types with no base type precisely
describe the default typeclass `type`/`typeclass`, so this condition is
removed. Maybe at some point this represented an unresolved generic
type?
2024-08-16 08:22:49 +02:00
metagn
d43a5954c5 remove nontoplevel type hack + consider symbol disamb in type hash (#23969)
fixes #22571

Removes the hack added in #13589 which made non-top-level object type
symbols `gensym` because they couldn't be mangled into different names
for codegen vs. top-level types. Now we consider the new `disamb` field
(added in #21667) of the type symbols in the type hash (which is used
for the mangled name) to differentiate between the types.

In other parts of the compiler, specifically the [proc
mangling](298ada3412/compiler/mangleutils.nim (L59)),
`itemId.item` is used instead of the `disamb` field, but I didn't use it
in case it's the outdated method.
2024-08-16 06:33:43 +02:00
ringabout
298ada3412 fixes #23954; uint8 > 8 bit at compile-time (#23955)
fixes #23954
2024-08-15 19:28:13 +08:00
ringabout
06b25bd2c4 disable presto (#23958) 2024-08-15 18:00:56 +08:00
Archar Gelod
2a046e6487 better examples for std/inotify (#23415)
Previous example wouldn't run unless `std/posix` was imported and it
wasn't mentioned anywhere in the docs.

Other changes in the example:
- replaced magic number with constant `MaxWatches` .
- changed seq buffer to array, because length is already constant +
pointer is a bit nicer: `addr seq[0]` vs `addr arr`
- added example for getting a `cstring` name value from `InotifyEvent`
struct with explicit cast.
- added a bit more description to `inotify_init1` (copied from `man
inotify(7)`)

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
2024-08-14 22:35:40 +08:00
ringabout
a33e2b76ae supports default for range types using firstOrd with nimPreviewRangeDefault (#23950)
ref https://github.com/nim-lang/Nim/issues/23943
2024-08-13 17:08:30 +02:00
ringabout
ddc47fecca fixes #23947; .uint8 compile-time error (#23948)
fixes #23947
2024-08-13 14:02:52 +02:00
Mark Leyva
c5b206d4ac fix #23817; Use __builtin_saddl_overflow variants for arm-none-eabi-gcc. (#23835)
Provides a fix for #23817. 

With target `arm-none-eabi`, GCC defines `int32_t` to `long int`. Nim
uses `__builtin_sadd_overflow` for 32-bit targets, but this emits
warnings on GCC releases 13 and under, while generating an error on GCC
14. More info regarding this
[here](https://gcc.gnu.org/gcc-14/porting_to.html#c) and
[here](https://gcc.gnu.org/pipermail/gcc-cvs/2023-December/394351.html).

The proposed PR attempts to address this issue for these targets by
defining the `nimAddInt`, `nimSubInt`, and `nimMulInt` macros to use the
appropriate compiler intrinsics for this platform.

As for as we know, the LLVM toolchain for bare metal Arm does not define
`int32_t` as `long int` and has no need for this patch. Thus, we only
define the above macros for GCC targeting `arm-non-eabi`.
2024-08-12 18:10:33 +02:00
Juan M Gómez
630c304a2d Adds SEQ_DECL_SIZE 1 back under clang and a test (#23942) 2024-08-12 18:10:17 +02:00
metagn
0c890ff9a7 opensym as node kind + fixed experimental switch (#23892)
refs https://github.com/nim-lang/Nim/pull/23873#discussion_r1687995060,
fixes #23386, fixes #23385, supersedes #23572

Turns the `nfOpenSym` node flag implemented in #23091 and extended in
#23102 and #23873, into a node kind `nkOpenSym` that forms a unary node
containing either `nkSym` or `nkOpenSymChoice`. Since this affects
macros working on generic proc AST, the node kind is now only generated
when the experimental switch `genericsOpenSym` is enabled, and a new
node flag `nfDisabledOpenSym` is set to the `nkSym` or `nkOpenSymChoice`
when the switch is not enabled so that we can give a warning.

Now that the experimental switch has more reasonable semantics, we
define `nimHasGenericsOpenSym2`.
2024-08-12 15:33:26 +02:00
Tomohiro
7a0069a134 fixes #23913; empty SEQ_DECL_SIZE (#23940) 2024-08-12 15:19:42 +02:00
Don-Duong Quach
20043ea09e Implemented compileOption for experimental to test if a feature i… (#23933)
…s enabled at compile time.

#8644 This doesn't handle the case if `{.push experimental.}` is used,
but at least we can test if a feature was enabled globally.
2024-08-12 15:18:56 +02:00
ringabout
b215ec3735 fixes #23936; opcParseFloat accepts the wrong register as the first param [backport] (#23941)
fixes #23936
follow up https://github.com/nim-lang/Nim/pull/20527
2024-08-12 14:43:13 +02:00
lit
e0e698be9a impr: std/cpuinfo: use documented impl ; support JS (#23911)
Currently `cpuinfo.countProcessor` uses hard-coded `HW_AVAILCPU=25` for
both MacOS and BSD;

However,

[There is no HW_AVAILCPU on FreeBSD, NetBSD, and OpenBSD](
https://bugs.webkit.org/show_bug.cgi?id=132542)

Also, `HW_AVAILCPU` is undocmented in MacOS,
while `sysctlbyname("hw.logicalcpu",...)` is documented and used
by many other languages' implementations, like
[Haskell](https://gitlab.haskell.org/ghc/ghc/-/blob/master/rts/posix/OSThreads.c?ref_type=heads#L376)

---

This PR:

- use `importc` value instead of hard-coded values for `HW_*` macros.
- use "hw.logicialcpu" over undocumented HW_AVAILCPU.
- reduce 2 elements of `mib` array when calling `sysctl` as they're no
use.
2024-08-11 17:32:58 +02:00
ringabout
fbf9e94145 fixes jsbigint64 regression; keeps convs to Number in danger mode (#23926)
fixes jsbigint64 regression
2024-08-11 17:31:17 +02:00
ringabout
b9b24e192a fixes #23932; vmopsDanger for os.getCurrentDir errors (#23934)
fixes #23932
ref https://github.com/jmgomez/NimForUE/issues/36
2024-08-11 16:13:26 +02:00
metagn
a64aa51fe9 don't treat template/macro/module as overloaded for opensym (#23939)
actually fixes #23865 following up #23873

In the handling of `nkIdent` in `semExpr`, the compiler looks for the
closest symbol with the name and [checks the symbol
kind](6126a0bf46/compiler/semexprs.nim (L3171))
to also consider the overloads if the symbol kind is overloadable. But
it treats the normally overloadable template/macro/module sym kinds the
same as non-overloadable symbols, just calling `semSym` on it. We need
to mirror this behavior in `semOpenSym`; we treat the captured symchoice
as a fresh identifier, so if the symbol we find is a
template/macro/module, we use that symbol immediately as opposed to
waiting for overloads.
2024-08-11 16:12:57 +02:00
ringabout
f0e1eef65e fixes #14522 #22085 #12700 #23132; no range check for uints (#23930)
fixes #14522
fixes #22085
fixes #12700
fixes #23132
closes https://github.com/nim-lang/Nim/pull/22343 (succeeded by this PR)
completes https://github.com/nim-lang/RFCs/issues/175

follow up https://github.com/nim-lang/Nim/pull/12688
2024-08-11 13:10:04 +02:00
ringabout
4954469259 fixes #23914; jsondoc broken in devel (#23916)
follows up https://github.com/nim-lang/Nim/pull/23064

fixes #23914
2024-08-11 10:13:16 +02:00
ringabout
1d59e1cbb6 fixes #23907; Double destroy using proc type alias with a sink (#23909)
fixes #23907
2024-08-11 10:12:48 +02:00
ringabout
2a2474d395 fixes #23902; Compiler infers sink in return type from auto (#23904)
fixes #23902
2024-08-11 10:12:00 +02:00
ringabout
d164f87fbc special handlings for nimble packages to shorten function names (#23891)
If we need keep readabilities for functions' names, we might put the
original names in the comments or in the identifiers like what currently
has been done.

The new nimble having been shipped since Nim 2.0.0 uses a directory
ending with a full hash of a commit for cloned repos, the function names
are burderen by this. This PR strips these from package paths and
prepends "pkg" for readability.

Before:


raiseNilAccess__OOZOOZOnimbleZpkgs2Zthreading450O2O045288108d1dfa34d5ade5ce4d922af51909c83cebfZthreadingZsmartptrs_u4

After:

raiseNilAccess__pkgZthreadingZsmartptrs_u4
2024-08-11 10:10:28 +02:00
Antonis Geralis
c0aa951ee0 Fixed nimscript docs (#23938) 2024-08-11 10:35:09 +08:00
Juan M Gómez
6126a0bf46 bump nimble to include the fix to nimble dump (#23918) 2024-08-10 20:24:43 +08:00
ringabout
d76ea4f323 closes #6549; adds a test case (#23929)
closes #6549
2024-08-09 10:39:40 +08:00
ringabout
c97a20ce49 closes #21347; adds a test case (#23917)
closes #21347
2024-08-04 07:24:59 +08:00
Tomohiro
12b9680291 Add a document to toOpenArray proc (#23905) 2024-08-01 12:27:10 +08:00
ringabout
cb156648d6 fixes #13391; VM: Can't get address of object (#23903)
fixes #13391
2024-07-29 21:38:29 +02:00
ringabout
39629a1adc fixes JS semicolon omissions (#23896) 2024-07-26 20:45:52 +02:00
ringabout
bd063113ec fixes #23894; succ/pred shouldn't raise OverflowDefect for unsigned integers (#23895)
fixes #23894

keeps it consistent with `inc`
2024-07-26 14:50:59 +02:00
metagn
469a6044c0 implement genericsOpenSym for symchoices (#23873)
fixes #23865

The node flag `nfOpenSym` implemented in #23091 for sym nodes is now
also implemented for open symchoices. This means the intended behavior
is still achieved when multiple overloads are in scope to be captured,
so the issue is fixed. The code for the flag is documented and moved
into a helper proc and the experimental switch is now enabled for the
compiler test suite.
2024-07-25 22:10:15 +02:00
Ryan McConnell
c1f91c26a5 Overload resultion with generic variables an inheritance (#23870)
The test case diff is self explanatory
2024-07-24 23:59:45 +02:00
Juan M Gómez
ccf90f5bcb bump nimble to 0.16.0 (#23883) 2024-07-24 23:55:16 +02:00
ringabout
02871c74de minor improvement on cgen (#23887) 2024-07-24 20:17:54 +02:00
ringabout
c770c0ad08 improve mangling packages version names with checksums (#23888)
follow up https://github.com/nim-lang/Nim/pull/19821

dights cannot clash with letters 'Z' and 'O'


For `threading-0.2.0-288108d1dfa34d5ade5ce4d922af51909c83cebf`

Before: 


raiseNilAccess__OOZOOZOnimbleZpkgs50Zthreading4548O50O4845505656494856d49dfa5152d53ade53ce52d575050af5349574857c5651cebfZthreadingZsmartptrs_u4


After:


raiseNilAccess__OOZOOZOnimbleZpkgs2Zthreading450O2O045288108d1dfa34d5ade5ce4d922af51909c83cebfZthreadingZsmartptrs_u4


<del> nimble or something might use `git rev-parse --short HEAD` to
shorten the length of package version names ref
https://github.com/nim-lang/nimble/pull/913 </del>
2024-07-24 20:13:32 +02:00
Buldram
925dc5c131 fixes #19171; have openArray converted from ptr UncheckedArray be mutable (#23882)
Makes `toOpenArray(x: ptr UncheckedArray)` always return a `var
openArray` regardless of if `x` is mutable.
2024-07-24 08:13:55 +02:00
ringabout
0db742df7c fixes #23867; fixes #23316; rework nimsuggest for ORC (#23879)
fixes #23867
fixes #23316 


follow up https://github.com/nim-lang/Nim/pull/22805; fixes
https://github.com/nim-lang/Nim/issues/22794 in a different method
2024-07-23 16:46:49 +02:00
ringabout
759b8e46be turn some sym flag aliases into enums (#23884) 2024-07-23 16:02:34 +02:00
lit
03973dca30 doc,test(times): followup #23861 (#23881)
followup #23861
2024-07-23 09:56:36 +08:00
SirOlaf
881fbb8f81 Allocator: Always place free cells into the active chunk and add documentation (#23871)
Lets single threaded applications benefit from tracking foreign cells as
well.
After this, `SmallChunk` technically doesn't need to act as a linked
list anymore I think, gotta investigate that more though.
The likelihood of overflowing `chunk.free` also rises, so to work around
that it might make sense to check `foreignCells` instead of adjusting
free space or replace free with a counter for the local capacity.

For Nim compile I can observe a ~10mb reduction, and smaller ones for
other projects.
2024-07-22 16:36:46 +02:00
Ward
c83a9c4c5c fixes #23838: Compilation by MinGW for cpu=i386 with time_t bug (#23876)
Change Time type in std/time_t to `distinct clong` instead of `distinct
int32`
2024-07-22 14:23:18 +02:00
Ryan McConnell
7b50d05d6b fixes #23869; sink generic typeclass (#23874)
Still have to look this over some. We'll see. I put sink in this branch
simply because I saw `tyVar` there and for no other reason. In any case
the problem appears to be coming from `liftParamType` as it removes the
`sink` type from the formals.
#23869
2024-07-22 07:13:43 +02:00
Buldram
2d2a7f2347 Fix out-of-bounds slicing in std/varints (#23868)
Corrects a slicing mistake in the `std/varints` implementation which
caused it to fail when writing large numbers into buffers smaller than
10..13-bytes, now 9-byte buffers are sufficient as the documentation
states.
2024-07-22 07:11:14 +02:00
ringabout
1c287fb960 remove unused field in ConfigRef (#23875)
follow up https://github.com/nim-lang/Nim/pull/14763
2024-07-22 06:56:59 +02:00
SirOlaf
9ca646acd4 Merge tyUncheckedArray with tySeq in typeRel (#23866)
Ref https://github.com/nim-lang/Nim/issues/23836#issuecomment-2233957324

Their types are basically equivalent so they should behave the same way
for type relations.
2024-07-20 15:46:25 +02:00
metagn
31ee75f10e bypass constraints for tyFromExpr in generic bodies (#23863)
fixes #19819, fixes #23339

Since #22029 `tyFromExpr` does not match anything in overloading, so
generic bodies can know which call expressions to delay until the type
can be evaluated. However generic type invocations also run overloading
to check for generic constraints even in generic bodies. To prevent them
from failing early from the overload not matching, pretend that
`tyFromExpr` matches. This mirrors the behavior of the compiler in more
basic cases like:

```nim
type
  Foo[T: int] = object
    x: T
  Bar[T] = object
    y: Foo[T]
```

Unfortunately this case doesn't respect the constraint (#21181, some
other bugs) but `tyFromExpr` should easily use the same principle when
it does.
2024-07-20 09:02:08 +02:00
ringabout
2f5cfd6829 fixes nim secret not flushing stdout (#23862)
related to https://github.com/nim-lang/Nim/pull/19584

On Vscode wsl2

Before:


![image](https://github.com/user-attachments/assets/4bb4f92d-757d-4edf-9dcf-17fcb98f0b60)

After


![image](https://github.com/user-attachments/assets/289a113e-c27c-4b76-9d13-725ca28f2828)
2024-07-20 05:40:38 +02:00
SirOlaf
fd1e62a7e2 Allocator: Track number of foreign cells a small chunk has access to (#23856)
Ref: https://github.com/nim-lang/Nim/issues/23788

There was a small leak in the above issue even after fixing the
segfault. The sizes of `free` and `acc` were changed to 32bit because
adding the `foreignCells` field will drastically increase the memory
usage for programs that hold onto memory for a long time if they stay as
64bit.
2024-07-20 05:40:00 +02:00
metagn
97f5474545 fix generics treating symchoice symbols as uninstantiated (#23860)
fixes #23853

Since #22610 generics turns the `Name` in the `GT.Name` expression in
the test code into a sym choice. The problem is when the compiler tries
to instantiate `GT.Name` it also instantiates the sym choice symbols.
`Name` has type `template (E: type ExtensionField)` which contains the
unresolved generic type `ExtensionField`, which the compiler mistakes as
an uninstantiated node, when it's just part of the type of the template.
The compilation of the node itself and hence overloading will handle the
instantiation of the proc, so we avoid instantiating it in `semtypinst`,
similar to how the first nodes of call nodes aren't instantiated.
2024-07-19 13:53:35 +02:00
c-blake
24f5dfbed2 Add '.' (period, dot, ..) to FormatLiterals so that ss.fff can work. (#23861)
Honestly, to me the entire design of a (highly!) restricted set of
`FormatLiterals` characters seems antithetical to the very idea of a
format string template. Fixing that is a much larger change, though.

So, this PR just adds `'.'` so that the standard (both input & output!)
notation for decimal numbers in Nim can be used for the seconds part of
a time format in `lib/pure/times.format(.., f)`. It should only make
legal what was illegal and should be harmless since `'.'` is not used in
any special way otherwise.
2024-07-19 12:38:02 +02:00
ringabout
3a103669d1 fixes #23858; 2.2.0 rc1 regression with cdecl functions (#23859)
fixes #23858

We should not assign fields to fields for returns of function calls
because calls might have side effects.
2024-07-18 20:53:07 +02:00
lit
6aa54d533b doc: times.nim: DD -> dd (#23857)
`YYYY-MM-dd` was mistaken as `YYYY-MM-DD`.
2024-07-18 13:59:48 +02:00
SirOlaf
f765898a75 Set type of object constructor during annotateType (#23852)
Fix https://github.com/nim-lang/Nim/issues/23547

Tested locally with the included test, the test from constantine and the
original issue.
2024-07-17 23:54:15 +02:00
Mamy Ratsimbazafy
ddb31ce968 Add constantine to important_packages.nim (#23801)
This adds Constantine to the important packages. Release announcements:
- https://forum.nim-lang.org/t/11935
- https://github.com/mratsim/constantine/releases/tag/v0.1.0

Unfortunately at the moment I'm in a conundrum.

- Constantine cannot compile on devel due to
https://github.com/nim-lang/Nim/issues/23547
- The workaround is changing 
  ```Nim
func mulCheckSparse*(a: var QuadraticExt, b: static QuadraticExt)
{.inline.} =
  ```
  to
  ```Nim
  template mulCheckSparse*(a: var QuadraticExt, b: QuadraticExt) =
  ```
but this does not compile on v2.0.8 due to `gensym` issues despite
https://github.com/nim-lang/Nim/pull/23716

![image](https://github.com/nim-lang/Nim/assets/22738317/21c875d7-512f-4c21-8547-d12534e93a58).
i.e. as mentioned in the issue
https://github.com/nim-lang/Nim/issues/23711 there is another gensym bug
within templates that was fixed in devel but not the v2.0.x series and
that is not fixed by #23716
2024-07-17 19:04:50 +02:00
ringabout
494c24a7ce fixes #23848; The comand nim gendepend defaults to ORC (#23851)
fixes #23848
2024-07-17 18:25:19 +02:00
EuklidAlexandria
2b7c47b122 Fixes #23846; prepend nimArgs to args (#23847)
Fixes #23846. Probably, nimArgs should be prepended in other places
(e.g. `buildDocSamples`).
2024-07-17 22:48:59 +08:00
ringabout
9de74b7097 fixes #23844; Nim devel nightly i386 build failing (#23849)
fixes #23844
follow up https://github.com/nim-lang/Nim/pull/23834

```nim
type
  Timespec* {.importc: "struct timespec",
               header: "<time.h>", final, pure.} = object ## struct timespec
    tv_sec*: Time  ## Seconds.
    tv_nsec*: clong  ## Nanoseconds.
```
2024-07-17 10:50:33 +02:00
Antonis Geralis
ad5b5e3ec0 Add warnings about exec usage. (#23820)
Related to https://github.com/nim-lang/Nim/issues/23819 and also found
in discord
https://discord.com/channels/371759389889003530/371759389889003532/1260845467147829372
Since nothing can be done, besides deprecating the function, a warning
is a better option.

---------

Co-authored-by: Juan Carlos <juancarlospaco@gmail.com>
2024-07-17 08:45:52 +02:00
ringabout
fe48de4406 fixes #23837; cursor now processes distinct types with a destructor (#23845)
fixes #23837
2024-07-17 05:17:52 +02:00
metagn
cd946084ab make routine implicitly gensym when other gensym symbol exists again (#23842)
fixes #23813, partially reverts #23392

Before #23392, if a `gensym` symbol was defined before a proc with the
same name in a template even with an `inject` annotation, the proc would
be `gensym`. After #23392 the proc was instead changed to be `inject` as
long as no `gensym` annotation was given. Now, to keep compatibility
with the old behavior, the behavior is changed back to infer the proc as
`gensym` when no `inject` annotation is given, however an explicit
`inject` annotation will still inject the proc. This is also documented
in the manual as the old behavior was undocumented and the new behavior
is slightly different.
2024-07-16 08:47:06 +02:00
ringabout
648f82c2ed fixes semi-regression; discard check now skips nkHiddenSubConv (#23840)
follow up https://github.com/nim-lang/Nim/pull/23681

ref https://forum.nim-lang.org/t/11987
2024-07-16 07:37:33 +02:00
ringabout
b7a275da1d fixes regression; block can have arbitrary exit points; too hard for a simple analysis (#23839)
follow up https://github.com/nim-lang/Nim/pull/23681

ref https://forum.nim-lang.org/t/11987
ref https://github.com/nim-lang/Nim/issues/23836#issuecomment-2227267251
2024-07-16 07:37:06 +02:00
ringabout
284a80e96d [minor] fixes wrong error messages (#23841) 2024-07-16 09:25:43 +08:00
c-blake
c11b3f3fc7 Silence hint:performance message when using very basic http client (#23832)
code such as:
```Nim
import std/httpclient # nim c --hint:performance:on
echo newHttpClient(proxy=nil,
  headers=newHttpHeaders({"Accept": "*/*"})).getContent("x")
```
(Fix was suggested by @ringabout in a private channel.)

Seems useful since `httpclient` is so basic/probably pervasive with many
hundreds of `import`s across the NimbleVerse.
2024-07-15 14:11:06 +02:00
Mark Leyva
a5186a9d8b Use monotonic timestamp to calculate timeouts refs #23826 (#23834)
Related to #23826. This address issues raised
[here](https://github.com/nim-lang/Nim/pull/23826#issuecomment-2226877361)
by using a monotonic timestamp to calculate timeouts and increasing the
max sleep time to 50ms.
2024-07-15 14:10:31 +02:00
Miran
f6aeca5765 bump NimVersion to 2.1.9 (#23831)
This is a 2.2 RC1.
2024-07-12 21:06:29 +02:00
Mark Leyva
58b36bd85e fixes #23825; Busy wait on waitid, sleeping at regular intervals (#23826)
Addresses #23825 by using the approaching described in
https://github.com/nim-lang/Nim/pull/23743#issuecomment-2199523110.

This takes the approach from Python's `subprocess` library which calls
`waitid` in loop, while sleeping at regular intervals.

CC @alex65536
2024-07-12 15:25:18 +02:00
Andreas Rumpf
6d7ab08dee refactor: The popular 'r' field is now named 'snippet' (#23829) 2024-07-12 15:23:09 +02:00
733 changed files with 15767 additions and 4845 deletions

View File

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

View File

@@ -41,11 +41,11 @@ jobs:
target: [linux, windows, osx]
include:
- target: linux
os: ubuntu-20.04
os: ubuntu-22.04
- target: windows
os: windows-2019
- target: osx
os: macos-12
os: macos-13
name: ${{ matrix.target }}
runs-on: ${{ matrix.os }}

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-12]
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 }})'
@@ -48,6 +49,7 @@ jobs:
- name: 'Install dependencies (macOS)'
if: runner.os == 'macOS'
run: brew install boehmgc make sfml gtk+3
# XXX can't find boehm and gtk on macos 13
- name: 'Install dependencies (Windows)'
if: runner.os == 'Windows'
shell: bash

View File

@@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-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:
@@ -29,10 +29,10 @@ jobs:
# vmImage: 'ubuntu-18.04'
# CPU: i386
OSX_amd64:
vmImage: 'macOS-12'
vmImage: 'macOS-13'
CPU: amd64
OSX_amd64_cpp:
vmImage: 'macOS-12'
vmImage: 'macOS-13'
CPU: amd64
NIM_COMPILE_TO_CPP: true
Windows_amd64_batch0_3:
@@ -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
@@ -130,6 +133,7 @@ jobs:
- bash: brew install boehmgc make sfml
displayName: 'Install dependencies (OSX)'
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
# XXX can't find boehm on macos 13
- bash: |
set -e

View File

@@ -24,6 +24,6 @@ if not exist %nim_csources% (
cd ..
copy /y bin\nim.exe %nim_csources%
)
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
koch tools --skipUserCfg --skipParentCfg --hints:off
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
koch tools --skipUserCfg --skipParentCfg --hints:off

View File

@@ -1,120 +1,74 @@
# v2.2.0 - yyyy-mm-dd
# v2.x.x - yyyy-mm-dd
## Changes affecting backward compatibility
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to be in the same module where their type has been defined.
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
```nim
var a, b: int
(a, b) = (1, 2, 3, 4)
```
will no longer compile.
- `internalNew` is removed from system, use `new` instead.
- `-d:nimPreviewFloatRoundtrip` becomes the default. `system.addFloat` and `system.$` now can produce string representations of
floating point numbers that are minimal in size and possess round-trip and correct
rounding guarantees (via the
[Dragonbox](https://raw.githubusercontent.com/jk-jeon/dragonbox/master/other_files/Dragonbox.pdf) algorithm). Use `-d:nimLegacySprintf` to emulate old behaviors.
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
- The `default` parameter of `tables.getOrDefault` has been renamed to `def` to
avoid conflicts with `system.default`, so named argument usage for this
parameter like `getOrDefault(..., default = ...)` will have to be changed.
- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behavior.
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
- `owner` in `std/macros` is deprecated.
- 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
[//]: # "Changes:"
- Changed `std/osfiles.copyFile` to allow to specify `bufferSize` instead of a hardcoded one.
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
[//]: # "Additions:"
- `setutils.symmetricDifference` along with its operator version
`` setutils.`-+-` `` and in-place version `setutils.toggle` have been added
to more efficiently calculate the symmetric difference of bitsets.
- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
- Added `setLenUninit` to system, which doesn't initialize
slots when enlarging a sequence.
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
- Added Viewport API for the JavaScript targets in the `dom` module.
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
instead of `--mm:orc`.
- A `$` template is provided for `Path` in `std/paths`.
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
on Google's Farm Hash) which is also often faster than the present one. Define
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
produces the old values. This may impact your automated tests if they depend
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
is often an easy workaround.
[//]: # "Deprecations:"
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
[//]: # "Removals:"
[//]: # "Changes:"
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
## Language changes
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.htm#constructor-initializer
- `member` can be used to attach a procedure to a C++ type.
- C++ `constructor` now reuses `result` instead creating `this`.
- Tuple unpacking changes:
- Tuple unpacking assignment now supports using underscores to discard values.
```nim
var a, c: int
(a, _, c) = (1, 2, 3)
```
- Tuple unpacking variable declarations now support type annotations, but
only for the entire tuple.
```nim
let (a, b): (int, int) = (1, 2)
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
```
- An experimental option `genericsOpenSym` has been added to allow captured
symbols in generic routine bodies to be replaced by symbols injected locally
by templates/macros at instantiation time. `bind` may be used to keep the
captured symbols over the injected ones regardless of enabling the option.
Since this change may affect runtime behavior, the experimental switch
`genericsOpenSym` needs to be enabled, and a warning is given in the case
where an injected symbol would replace a captured symbol not bound by `bind`
and the experimental switch isn't enabled.
- 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
const value = "captured"
template foo(x: int, body: untyped) =
let value {.inject.} = "injected"
body
# objs.nim
import std/hashes
proc old[T](): string =
foo(123):
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:genericsOpenSym`
echo old[int]() # "captured"
type
Obj* = object
x*, y*: int
z*: string # to be ignored for equality
{.experimental: "genericsOpenSym".}
proc `==`*(a, b: Obj): bool =
a.x == b.x and a.y == b.y
proc bar[T](): string =
foo(123):
return value
assert bar[int]() == "injected" # previously it would be "captured"
proc baz[T](): string =
bind value
foo(123):
return value
assert baz[int]() == "captured"
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
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
## Tool changes
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.

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

@@ -1,12 +1,248 @@
# v2.2.0 - 2023-mm-dd
# v2.2.0 - 2024-10-02
## Changes affecting backward compatibility
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` is out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backward compatibility.
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to the same module where their type has been defined.
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
```nim
var a, b: int
(a, b) = (1, 2, 3, 4)
```
will no longer compile.
- `internalNew` is removed from the `system` module, use `new` instead.
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
- The JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors.
- The JS backend now supports closure iterators.
- `owner` in `std/macros` is deprecated.
- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
Previously; in templates they would error immediately at the template definition,
and in generic procs a type symbol would arbitrarily be captured, losing the
information of the other symbols. This means that generic code can now give
errors for ambiguous type symbols, and macros operating on generic proc AST
may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
- Partial generic instantiation of routines is no longer allowed. Previously
it compiled in niche situations due to bugs in the compiler.
```nim
proc foo[T, U](x: T, y: U) = echo (x, y)
proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
proc bar[T]() =
foo[float](1, "abc")
bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
```
- `const` values now open a new scope for each constant, meaning symbols
declared in them can no longer be used outside or in the value of
other constants.
```nim
const foo = (var a = 1; a)
const bar = a # error
let baz = a # error
```
- The following POSIX wrappers have had their types changed from signed to
unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
- `Gid` (was `int32`, is now `uint32`)
- `Uid` (was `int32`, is now `uint32`)
- `Dev` (was `int32`, is now `uint32` on FreeBSD)
- `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
- `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
(were `int32`, are now `uint32`)
- `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
- The `Atomic[T]` type on C++ now uses C11 primitives by default instead of
`std::atomic`. To use `std::atomic` instead, `-d:nimUseCppAtomics` can be defined.
## Standard library additions and changes
[//]: # "Changes:"
- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.
[//]: # "Additions:"
- Added `newStringUninit` to the `system` module, which creates a new string of length `len` like `newString` but with uninitialized content.
- Added `setLenUninit` to the `system` module, which doesn't initialize
slots when enlarging a sequence.
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
convert a value with a range type to its base type.
- Added Viewport API for the JavaScript targets in the `dom` module.
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
instead of `--mm:orc`.
- A `$` template is provided for `Path` in `std/paths`.
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
on Google's Farm Hash) which is also often faster than the present one. Define
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
produces the old values. This may impact your automated tests if they depend
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
is often an easy workaround.
[//]: # "Deprecations:"
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
[//]: # "Removals:"
## Language changes
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
- `member` can be used to attach a procedure to a C++ type.
- Inside a C++ constructor, `result` can be used to access the created object rather than `this`.
- Tuple unpacking changes:
- Tuple unpacking assignment now supports using underscores to discard values.
```nim
var a, c: int
(a, _, c) = (1, 2, 3)
```
- Tuple unpacking variable declarations now support type annotations, but
only for the entire tuple.
```nim
let (a, b): (int, int) = (1, 2)
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
```
- The experimental option `--experimental:openSym` has been added to allow
captured symbols in generic routine and template bodies respectively to be
replaced by symbols injected locally by templates/macros at instantiation
time. `bind` may be used to keep the captured symbols over the injected ones
regardless of enabling the option, but other methods like renaming the
captured symbols should be used instead so that the code is not affected by
context changes.
Since this change may affect runtime behavior, the experimental switch
`openSym` needs to be enabled; and a warning is given in the case where an
injected symbol would replace a captured symbol not bound by `bind` and
the experimental switch isn't enabled.
```nim
const value = "captured"
template foo(x: int, body: untyped): untyped =
let value {.inject.} = "injected"
body
proc old[T](): string =
foo(123):
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
echo old[int]() # "captured"
template oldTempl(): string =
block:
foo(123):
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
echo oldTempl() # "captured"
{.experimental: "openSym".}
proc bar[T](): string =
foo(123):
return value
assert bar[int]() == "injected" # previously it would be "captured"
proc baz[T](): string =
bind value
foo(123):
return value
assert baz[int]() == "captured"
template barTempl(): string =
block:
foo(123):
value
assert barTempl() == "injected" # previously it would be "captured"
template bazTempl(): string =
bind value
block:
foo(123):
value
assert bazTempl() == "captured"
```
This option also generates a new node kind `nnkOpenSym` which contains
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
modified `nnkOpenSymChoice` node but macros that want to support the
experimental feature should still handle `nnkOpenSym`, as the node kind would
simply not be generated as opposed to being removed.
Another experimental switch `genericsOpenSym` exists that enables this behavior
at instantiation time, meaning templates etc can enable it specifically when
they are being called. However this does not generate `nnkOpenSym` nodes
(unless the other switch is enabled) and so doesn't reflect the regular
behavior of the switch.
```nim
const value = "captured"
template foo(x: int, body: untyped): untyped =
let value {.inject.} = "injected"
{.push experimental: "genericsOpenSym".}
body
{.pop.}
proc bar[T](): string =
foo(123):
return value
echo bar[int]() # "injected"
template barTempl(): string =
block:
var res: string
foo(123):
res = value
res
assert barTempl() == "injected"
```
## Compiler changes
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
## Tool changes
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package.

View File

@@ -1,4 +1,4 @@
# v1.xx.x - yyyy-mm-dd
# v2.xx.x - yyyy-mm-dd
This is an example file.
The changes should go to changelog.md!

View File

@@ -41,7 +41,7 @@ type
TNodeKinds* = set[TNodeKind]
type
TSymFlag* = enum # 53 flags!
TSymFlag* = enum # 63 flags!
sfUsed, # read access of sym (for warnings) or simply used
sfExported, # symbol is exported from module
sfFromGeneric, # symbol is instantiation of a generic; this is needed
@@ -52,7 +52,6 @@ type
sfForward, # symbol is forward declared
sfWasForwarded, # symbol had a forward declaration
# (implies it's too dangerous to patch its type signature)
sfNosinks, # symbol has a `nosinks` pragma
sfImportc, # symbol is external; imported
sfExportc, # symbol is exported (under a specified name)
sfMangleCpp, # mangle as cpp (combines with `sfExportc`)
@@ -94,7 +93,7 @@ type
sfNamedParamCall, # symbol needs named parameter call syntax in target
# language; for interfacing with Objective C
sfDiscardable, # returned value may be discarded implicitly
sfOverridden, # proc is overridden
sfOverridden, # proc is overridden
sfCallsite # A flag for template symbols to tell the
# compiler it should use line information from
# the calling side of the macro, not from the
@@ -130,23 +129,22 @@ type
sfWasGenSym # symbol was 'gensym'ed
sfForceLift # variable has to be lifted into closure environment
sfDirty # template is not hygienic (old styled template) module,
# compiled from a dirty-buffer
sfCustomPragma # symbol is custom pragma template
sfBase, # a base method
sfGoto # var is used for 'goto' code generation
sfAnon, # symbol name that was generated by the compiler
# the compiler will avoid printing such names
# in user messages.
sfAllUntyped # macro or template is immediately expanded in a generic context
sfTemplateRedefinition # symbol is a redefinition of an earlier template
TSymFlags* = set[TSymFlag]
const
sfNoInit* = sfMainModule # don't generate code to init the variable
sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
# in a generic context
sfDirty* = sfPure
# template is not hygienic (old styled template)
# module, compiled from a dirty-buffer
sfAnon* = sfDiscardable
# symbol name that was generated by the compiler
# the compiler will avoid printing such names
# in user messages.
sfNoForward* = sfRegister
# forward declarations are not required (per module)
sfReorder* = sfForward
@@ -155,12 +153,8 @@ const
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
sfExperimental* = sfOverridden # module uses the .experimental switch
sfGoto* = sfOverridden # var is used for 'goto' code generation
sfWrittenTo* = sfBorrow # param is assigned to
# currently unimplemented
sfBase* = sfDiscriminant
sfCustomPragma* = sfRegister # symbol is custom pragma template
sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template
sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition
const
@@ -220,7 +214,8 @@ type
tyUncheckedArray
# An array with boundaries [0,+∞]
tyProxy # used as errornous type (for idetools)
tyError # used as erroneous type (for idetools)
# as an erroneous node should match everything
tyBuiltInTypeClass
# Type such as the catch-all object, tuple, seq, etc
@@ -282,13 +277,9 @@ static:
const
tyPureObject* = tyTuple
GcTypeKinds* = {tyRef, tySequence, tyString}
tyError* = tyProxy # as an errornous node should match everything
tyUnknown* = tyFromExpr
tyUnknownTypes* = {tyError, tyFromExpr}
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
tyUserTypeClass, tyUserTypeClassInst,
tyUserTypeClass, tyUserTypeClassInst, tyConcept,
tyAnd, tyOr, tyNot, tyAnything}
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
@@ -331,7 +322,9 @@ type
nfFirstWrite # this node is a first write
nfHasComment # node has a comment
nfSkipFieldChecking # node skips field visable checking
nfOpenSym # node is a captured sym but can be overriden by local symbols
nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot
# because openSym experimental switch is disabled
# gives warning instead
TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47)
@@ -452,6 +445,10 @@ const
tfGcSafe* = tfThread
tfObjHasKids* = tfEnumHasHoles
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
@@ -496,7 +493,7 @@ type
mAnd, mOr,
mImplies, mIff, mExists, mForall, mOld,
mEqStr, mLeStr, mLtStr,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
mConStrStr, mSlice,
mDotDot, # this one is only necessary to give nice compile time warnings
mFields, mFieldPairs, mOmpParFor,
@@ -564,7 +561,7 @@ const
mStrToStr, mEnumToStr,
mAnd, mOr,
mEqStr, mLeStr, mLtStr,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInSet, mRepr, mOpenArrayToSeq}
@@ -596,7 +593,7 @@ type
TNode*{.final, acyclic.} = object # on a 32bit machine, this takes 32 bytes
when defined(useNodeIds):
id*: int
typ*: PType
typField: PType
info*: TLineInfo
flags*: TNodeFlags
case kind*: TNodeKind
@@ -657,7 +654,7 @@ type
storage*: TStorageLoc
flags*: TLocFlags # location's flags
lode*: PNode # Node where the location came from; can be faked
r*: Rope # rope value of location (code generators)
snippet*: Rope # C code snippet of location (code generators)
# ---------------- end of backend information ------------------------------
@@ -711,7 +708,7 @@ type
when defined(nimsuggest):
endInfo*: TLineInfo
hasUserSpecifiedType*: bool # used for determining whether to display inlay type hints
owner*: PSym
ownerField: PSym
flags*: TSymFlags
ast*: PNode # syntax tree of proc, iterator, etc.:
# the whole proc including header; this is used
@@ -780,7 +777,7 @@ type
# formal param list
# for concepts, the concept body
# else: unused
owner*: PSym # the 'owner' of the type
ownerField: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them
# it is used for converting types to strings
size*: BiggestInt # the size of the type in bytes
@@ -819,6 +816,15 @@ type
template nodeId(n: PNode): int = cast[int](n)
template typ*(n: PNode): PType =
n.typField
proc owner*(s: PSym|PType): PSym {.inline.} =
result = s.ownerField
proc setOwner*(s: PSym|PType, owner: PSym) {.inline.} =
s.ownerField = owner
type Gconfig = object
# we put comments in a side channel to avoid increasing `sizeof(TNode)`, which
# reduces memory usage given that `PNode` is the most allocated type by far.
@@ -886,7 +892,7 @@ const
nfFromTemplate, nfDefaultRefsParam,
nfExecuteOnReload, nfLastRead,
nfFirstWrite, nfSkipFieldChecking,
nfOpenSym}
nfDisabledOpenSym}
namePos* = 0
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2
@@ -900,7 +906,7 @@ const
nfAllFieldsSet* = nfBase2
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
nkClosedSymChoice}
nkClosedSymChoice, nkOpenSym}
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
nkLiterals* = {nkCharLit..nkTripleStrLit}
@@ -928,6 +934,7 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
of nkSym: a.sym.name
of nkIdent: a.ident
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
of nkOpenSym: getPIdent(a.sons[0])
else: nil
const
@@ -1061,8 +1068,11 @@ proc getDeclPragma*(n: PNode): PNode =
proc extractPragma*(s: PSym): PNode =
## gets the pragma node of routine/type/var/let/const symbol `s`
if s.kind in routineKinds:
result = s.ast[pragmasPos]
if s.kind in routineKinds: # bug #24167
if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
result = s.ast[pragmasPos]
else:
result = nil
elif s.kind in {skType, skVar, skLet, skConst}:
if s.ast != nil and s.ast.len > 0:
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
@@ -1133,7 +1143,7 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
## new node with line info, type, and no children
result = newNode(kind)
result.info = info
result.typ = typ
result.typ() = typ
proc newNode*(kind: TNodeKind, info: TLineInfo): PNode =
## new node with line info, no type, and no children
@@ -1198,7 +1208,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
assert not name.isNil
let id = nextSymId idgen
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
options: options, owner: owner, offset: defaultOffset,
options: options, ownerField: owner, offset: defaultOffset,
disamb: getOrDefault(idgen.disambTable, name).int32)
idgen.disambTable.inc name
when false:
@@ -1279,15 +1289,18 @@ proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode =
proc newSymNode*(sym: PSym): PNode =
result = newNode(nkSym)
result.sym = sym
result.typ = sym.typ
result.typ() = sym.typ
result.info = sym.info
proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
result = newNode(nkSym)
result.sym = sym
result.typ = sym.typ
result.typ() = sym.typ
result.info = info
proc newOpenSym*(n: PNode): PNode {.inline.} =
result = newTreeI(nkOpenSym, n.info, n)
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
result = newNode(kind)
result.intVal = intVal
@@ -1362,7 +1375,7 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
result = newNode(nkIntLit)
else: raiseAssert $kind
result.intVal = intVal
result.typ = typ
result.typ() = typ
proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
# XXX: introduce range check
@@ -1501,7 +1514,7 @@ iterator signature*(t: PType): PType =
proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType =
let id = nextTypeId idgen
result = PType(kind: kind, owner: owner, size: defaultSize,
result = PType(kind: kind, ownerField: owner, size: defaultSize,
align: defaultAlignment, itemId: id,
uniqueId: id, sons: @[])
if son != nil: result.sons.add son
@@ -1512,13 +1525,14 @@ proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType
proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons
proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son]
proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len)
proc mergeLoc(a: var TLoc, b: TLoc) =
if a.k == low(typeof(a.k)): a.k = b.k
if a.storage == low(typeof(a.storage)): a.storage = b.storage
a.flags.incl b.flags
if a.lode == nil: a.lode = b.lode
if a.r == "": a.r = b.r
if a.snippet == "": a.snippet = b.snippet
proc newSons*(father: PNode, length: int) =
setLen(father.sons, length)
@@ -1555,7 +1569,7 @@ proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
result.sym = t.sym # backend-info should not be copied
proc exactReplica*(t: PType): PType =
result = PType(kind: t.kind, owner: t.owner, size: defaultSize,
result = PType(kind: t.kind, ownerField: t.owner, size: defaultSize,
align: defaultAlignment, itemId: t.itemId,
uniqueId: t.uniqueId)
assignType(result, t)
@@ -1633,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
@@ -1663,7 +1677,7 @@ proc copyNode*(src: PNode): PNode =
return nil
result = newNode(src.kind)
result.info = src.info
result.typ = src.typ
result.typ() = src.typ
result.flags = src.flags * PersistentNodeFlags
result.comment = src.comment
when defined(useNodeIds):
@@ -1681,7 +1695,7 @@ proc copyNode*(src: PNode): PNode =
template transitionNodeKindCommon(k: TNodeKind) =
let obj {.inject.} = n[]
n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags)
n[] = TNode(kind: k, typField: n.typ, info: obj.info, flags: obj.flags)
# n.comment = obj.comment # shouldn't be needed, the address doesnt' change
when defined(useNodeIds):
n.id = obj.id
@@ -1704,7 +1718,7 @@ proc transitionNoneToSym*(n: PNode) =
template transitionSymKindCommon*(k: TSymKind) =
let obj {.inject.} = s[]
s[] = TSym(kind: k, itemId: obj.itemId, magic: obj.magic, typ: obj.typ, name: obj.name,
info: obj.info, owner: obj.owner, flags: obj.flags, ast: obj.ast,
info: obj.info, ownerField: obj.ownerField, flags: obj.flags, ast: obj.ast,
options: obj.options, position: obj.position, offset: obj.offset,
loc: obj.loc, annex: obj.annex, constraint: obj.constraint)
when hasFFI:
@@ -1732,7 +1746,7 @@ template copyNodeImpl(dst, src, processSonsStmt) =
dst.info = src.info
when defined(nimsuggest):
result.endInfo = src.endInfo
dst.typ = src.typ
dst.typ() = src.typ
dst.flags = src.flags * PersistentNodeFlags
dst.comment = src.comment
when defined(useNodeIds):

View File

@@ -75,7 +75,7 @@ proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet;
res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)])
if card(n.loc.flags) > 0:
res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
res.addf("\n$1r: $2", [istr, n.loc.r])
res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
res.addf("\n$1lode: $2", [istr])
res.treeToYamlAux(conf, n.loc.lode, marker, indent + 1, maxRecDepth - 1)

View File

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

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

@@ -132,16 +132,16 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.r = pl
d.snippet = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone and p.splitDecls == 0:
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)
var list = initLoc(locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
list.snippet = pl
genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
if canRaise: raiseExit(p)
elif isHarmlessStore(p, canRaise, d):
@@ -151,16 +151,16 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
d = getTemp(p, typ.returnType)
assert(d.t != nil) # generate an assignment to d:
var list = initLoc(locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, flags) # no need for deep copying
list.snippet = pl
genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying
if canRaise:
if not (useTemp and cleanupTemp(p, typ.returnType, d)):
raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var list = initLoc(locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, tmp, list, flags) # no need for deep copying
list.snippet = pl
genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying
if canRaise:
if not cleanupTemp(p, typ.returnType, tmp):
raiseExit(p)
@@ -274,7 +274,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
var t = TLoc(r: "(*$1)" % [a.rdLoc])
var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
dataFieldAccessor(p, "*" & a.rdLoc)]
@@ -286,7 +286,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
of tyPtr, tyRef:
case elementType(a.t).kind
of tyString, tySequence:
var t = TLoc(r: "(*$1)" % [a.rdLoc])
var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
dataFieldAccessor(p, "*" & a.rdLoc)]
@@ -336,7 +336,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
# variable. Thus, we create a temporary pointer variable instead.
let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
if needsIndirect:
n.typ = n.typ.exactReplica
n.typ() = n.typ.exactReplica
n.typ.flags.incl tfVarIsPtr
a = initLocExprSingleUse(p, n)
a = withTmpIfNeeded(p, a, needsTmp)
@@ -353,10 +353,10 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
addRdLoc(a, result)
else:
a = initLocExprSingleUse(p, n)
if param.typ.kind in abstractPtrs:
if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
let typ = skipTypes(param.typ, abstractPtrs)
if typ.sym != nil and sfImportc in typ.sym.flags:
a.r = "(($1) ($2))" %
if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
a.snippet = "(($1) ($2))" %
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
#assert result != nil
@@ -532,9 +532,9 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
else:
@@ -542,9 +542,9 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, tmp, list, {})
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {})
@@ -727,7 +727,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var typ = skipTypes(ri[0].typ, abstractInst)
assert(typ.kind == tyProc)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.r
let pat = $ri[0].sym.loc.snippet
internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@', '\''}):
var pl = newRopeAppender()
@@ -740,13 +740,13 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.r = pl
d.snippet = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
list.r = pl
list.snippet = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add(";\n")
@@ -756,7 +756,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var argsCounter = 0
if 1 < ri.len:
genThisArg(p, ri, 1, typ, pl)
pl.add(op.r)
pl.add(op.snippet)
var params = newRopeAppender()
for i in 2..<ri.len:
genOtherArg(p, ri, i, typ, params, argsCounter)
@@ -771,12 +771,12 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
assert(typ.kind == tyProc)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.r
let pat = $ri[0].sym.loc.snippet
internalAssert p.config, pat.len > 0
var start = 3
if ' ' in pat:
start = 1
pl.add(op.r)
pl.add(op.snippet)
if ri.len > 1:
pl.add(": ")
genArg(p, ri[1], typ.n[1].sym, ri, pl)
@@ -785,7 +785,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
if ri.len > 1:
genArg(p, ri[1], typ.n[1].sym, ri, pl)
pl.add(" ")
pl.add(op.r)
pl.add(op.snippet)
if ri.len > 2:
pl.add(": ")
genArg(p, ri[2], typ.n[2].sym, ri, pl)
@@ -820,7 +820,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
if d.k == locNone: d = getTemp(p, typ.returnType)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, ri, OnUnknown)
list.r = pl
list.snippet = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add("];\n")

View File

@@ -224,7 +224,7 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
result = TLoc(k: locField,
storage: a.storage,
lode: lodeTyp t,
r: rdLoc(a) & "." & field
snippet: rdLoc(a) & "." & field
)
proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
@@ -254,9 +254,9 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
case t.kind
of nkSym:
let field = t.sym
if field.loc.r == "": fillObjectFields(p.module, typ)
genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r),
optAsgnLoc(src, field.typ, field.loc.r), newflags)
if field.loc.snippet == "": fillObjectFields(p.module, typ)
genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.snippet),
optAsgnLoc(src, field.typ, field.loc.snippet), newflags)
of nkRecList:
for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ)
else: discard
@@ -379,7 +379,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
elif not isObjLackingTypeField(ty):
genGenericAsgn(p, dest, src, flags)
elif containsGarbageCollectedRef(ty):
if ty[0].isNil and asgnComplexity(ty.n) <= 4:
if ty[0].isNil and asgnComplexity(ty.n) <= 4 and
needAssignCall notin flags: # calls might contain side effects
discard getTypeDesc(p.module, ty)
internalAssert p.config, ty.n != nil
genOptAsgnObject(p, dest, src, flags, ty.n, ty)
@@ -482,7 +483,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
if d.k != locNone:
var a: TLoc = initLoc(locData, n, OnStatic)
# need to generate an assignment here
a.r = r
a.snippet = r
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
else: genAssignment(p, d, a, {needToCopy})
else:
@@ -490,13 +491,13 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
# the flags field!
d.k = locData
d.lode = n
d.r = r
d.snippet = r
proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
if d.k != locNone:
# need to generate an assignment here
var a: TLoc = initLoc(locExpr, n, s)
a.r = r
a.snippet = r
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
else: genAssignment(p, d, a, {needToCopy})
else:
@@ -504,7 +505,7 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
# the flags field!
d.k = locExpr
d.lode = n
d.r = r
d.snippet = r
proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) =
if d.k != locNone: internalError(p.config, e.info, "binaryStmt")
@@ -589,7 +590,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
# skipping 'range' is correct here as we'll generate a proper range check
# later via 'chckRange'
let t = e.typ.skipTypes(abstractRange)
if optOverflowCheck notin p.options:
if optOverflowCheck notin p.options or (m in {mSucc, mPred} and t.kind in {tyUInt..tyUInt64}):
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
putIntoDest(p, d, e, res)
else:
@@ -757,7 +758,10 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
typ = typ.last
typ = typ.skipTypes(abstractInstOwned)
if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and
p.module.compileToCpp and e[0].kind == nkHiddenAddr and
# don't override existing location:
d.k == locNone:
d = initLocExprSingleUse(p, e[0][0])
return
else:
@@ -808,7 +812,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
# careful 'addr(myptrToArray)' needs to get the ampersand:
if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}:
var a: TLoc = initLocExpr(p, e[0])
putIntoDest(p, d, e, "&" & a.r, a.storage)
putIntoDest(p, d, e, "&" & a.snippet, a.storage)
#Message(e.info, warnUser, "HERE NEW &")
elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ):
expr(p, e[0], d)
@@ -879,10 +883,10 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
else:
var rtyp: PType = nil
let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
if field.loc.r == "" and rtyp != nil: fillObjectFields(p.module, rtyp)
if field.loc.r == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
if field.loc.snippet == "" and rtyp != nil: fillObjectFields(p.module, rtyp)
if field.loc.snippet == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
r.add "."
r.add field.loc.r
r.add field.loc.snippet
putIntoDest(p, d, e, r, a.storage)
r.freeze
@@ -901,10 +905,10 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
test = initLoc(locNone, it, OnStack)
u = initLocExpr(p, it[1])
v = initLoc(locExpr, disc, OnUnknown)
v.r = newRopeAppender()
v.r.add obj
v.r.add(".")
v.r.add(disc.sym.loc.r)
v.snippet = newRopeAppender()
v.snippet.add obj
v.snippet.add(".")
v.snippet.add(disc.sym.loc.snippet)
genInExprAux(p, it, u, v, test)
var msg = ""
if optDeclaredLocs in p.config.globalOptions:
@@ -962,12 +966,12 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
var r = rdLoc(a)
let f = e[0][1].sym
let field = lookupFieldAgain(p, ty, f, r)
if field.loc.r == "": fillObjectFields(p.module, ty)
if field.loc.r == "":
if field.loc.snippet == "": fillObjectFields(p.module, ty)
if field.loc.snippet == "":
internalError(p.config, e.info, "genCheckedRecordField") # generate the checks:
genFieldCheck(p, e, r, field)
r.add(".")
r.add field.loc.r
r.add field.loc.snippet
putIntoDest(p, d, e[0], r, a.storage)
r.freeze
else:
@@ -1102,7 +1106,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if d.k == locNone: d.storage = OnHeap
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
a.r = ropecg(p.module, "(*$1)", [a.r])
a.snippet = ropecg(p.module, "(*$1)", [a.snippet])
if lfPrepareForMutation in d.flags and ty.kind == tyString and
optSeqDestructors in p.config.globalOptions:
@@ -1168,9 +1172,9 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
var tmpB = initLocExprSingleUse(p, e[2])
tmpB.k = locExpr
if m == mOr:
tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
tmpB.snippet = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
else:
tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
tmpB.snippet = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
if d.k == locNone:
d = tmpB
else:
@@ -1273,7 +1277,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
lens.add(lenExpr(p, a))
lens.add(" + ")
appends.add(ropecg(p.module, "#appendString($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.r, lens, L])
linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.snippet, lens, L])
p.s(cpsStmts).add appends
if d.k == locNone:
d = tmp
@@ -1319,7 +1323,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
[byRefLoc(p, dest), lens, L])
else:
call = initLoc(locCall, e, OnHeap)
call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
call.snippet = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
genAssignment(p, dest, call, {})
gcUsage(p.config, e)
p.s(cpsStmts).add appends
@@ -1334,12 +1338,12 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
var call = initLoc(locCall, e, OnHeap)
if not p.module.compileToCpp:
const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)"
call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
getTypeDesc(p.module, e[1].typ),
genTypeInfoV1(p.module, seqType, e.info)])
else:
const seqAppendPattern = "($2) #incrSeqV3($1, $3)"
call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
getTypeDesc(p.module, e[1].typ),
genTypeInfoV1(p.module, seqType, e.info)])
# emit the write barrier if required, but we can always move here, so
@@ -1349,8 +1353,8 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
var dest = initLoc(locExpr, e[2], OnHeap)
var tmpL = getIntTemp(p)
lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)])
dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)])
lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.snippet, rdLoc(a), lenField(p)])
dest.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.snippet, dataField(p)])
genAssignment(p, dest, b, {needToCopy})
gcUsage(p.config, e)
@@ -1370,10 +1374,10 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
if optTinyRtti in p.config.globalOptions:
if needsInit:
b.r = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
b.snippet = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
[getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
else:
b.r = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
b.snippet = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
[getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
genAssignment(p, a, b, {})
else:
@@ -1398,15 +1402,15 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
if p.config.selectedGC == gcGo:
# newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
# implement the write barrier
b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
[addrLoc(p.config, a), b.rdLoc])
else:
# use newObjRC1 as an optimization
b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
b.snippet = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else:
b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
genAssignment(p, a, b, {})
# set the object type:
genObjectInit(p, cpsStmts, bt, a, constructRefObj)
@@ -1432,18 +1436,18 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
if not lenIsZero:
if p.config.selectedGC == gcGo:
# we need the write barrier
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc])
else:
call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
call.snippet = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc])
else:
if lenIsZero:
call.r = rope"NIM_NIL"
call.snippet = rope"NIM_NIL"
else:
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
genAssignment(p, dest, call, {})
@@ -1486,7 +1490,7 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) =
if id == p.module.labels:
# expression not found in the cache:
inc(p.module.labels)
var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, t), d.r]
var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, t), d.snippet]
# bug #23627; when generating const object fields, it's likely that
# we need to generate type infos for the object, which may be an object with
# custom hooks. We need to generate potential consts in the hooks first.
@@ -1503,14 +1507,14 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) =
var tmp2 = TLoc(r: r)
let field = lookupFieldAgain(p, ty, nField.sym, tmp2.r)
if field.loc.r == "": fillObjectFields(p.module, ty)
if field.loc.r == "": internalError(p.config, info, "genFieldObjConstr")
var tmp2 = TLoc(snippet: r)
let field = lookupFieldAgain(p, ty, nField.sym, tmp2.snippet)
if field.loc.snippet == "": fillObjectFields(p.module, ty)
if field.loc.snippet == "": internalError(p.config, info, "genFieldObjConstr")
if check != nil and optFieldCheck in p.options:
genFieldCheck(p, check, r, field)
tmp2.r.add(".")
tmp2.r.add(field.loc.r)
tmp2.snippet.add(".")
tmp2.snippet.add(field.loc.snippet)
if useTemp:
tmp2.k = locTemp
tmp2.storage = if isRef: OnHeap else: OnStack
@@ -1568,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]
@@ -1609,7 +1617,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
arr = initLoc(locExpr, n[i], OnHeap)
var lit = newRopeAppender()
intLiteral(i, lit)
arr.r = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)])
arr.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)])
arr.storage = OnHeap # we know that sequences are on the heap
expr(p, n[i], arr)
gcUsage(p.config, n)
@@ -1622,7 +1630,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
var elem, arr: TLoc
if n[1].kind == nkBracket:
n[1].typ = n.typ
n[1].typ() = n.typ
genSeqConstr(p, n[1], d)
return
if d.k == locNone:
@@ -1645,19 +1653,19 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
var lit = newRopeAppender()
intLiteral(i, lit)
elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)])
elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)])
elem.storage = OnHeap # we know that sequences are on the heap
arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), lit])
arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), lit])
genAssignment(p, elem, arr, {needToCopy})
else:
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, L])
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.snippet, L])
elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
elem.storage = OnHeap # we know that sequences are on the heap
arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
genAssignment(p, elem, arr, {needToCopy})
lineF(p, cpsStmts, "}$n", [])
@@ -1673,7 +1681,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
b = initLoc(locExpr, a.lode, OnHeap)
ti = genTypeInfo(p.config, p.module, refType, e.info)
p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
b.snippet = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
getTypeDesc(p.module, refType),
ti, getTypeDesc(p.module, skipTypes(refType.elementType, abstractRange))])
genAssignment(p, a, b, {}) # set the object type:
@@ -1838,7 +1846,7 @@ proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) =
template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
var a: TLoc = initLocExpr(p, n[1])
a.r = ropecg(p.module, frmt, [rdLoc(a)])
a.snippet = ropecg(p.module, frmt, [rdLoc(a)])
a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR)
if d.k == locNone: d = getTemp(p, n.typ)
genAssignment(p, d, a, {})
@@ -1892,8 +1900,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
var a = initLocExpr(p, e[1])
var x = lenExpr(p, a)
if op == mHigh: x = "($1-1)" % [x]
lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x])
putIntoDest(p, d, e, tmp.r)
lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.snippet, x])
putIntoDest(p, d, e, tmp.snippet)
of tyArray:
# YYY: length(sideeffect) is optimized away incorrectly?
if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ)))
@@ -1915,13 +1923,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
var call = initLoc(locCall, e, OnHeap)
if not p.module.compileToCpp:
const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)"
call.r = ropecg(p.module, setLenPattern, [
call.snippet = ropecg(p.module, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
else:
const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)"
call.r = ropecg(p.module, setLenPattern, [
call.snippet = ropecg(p.module, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
@@ -1937,7 +1945,7 @@ proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
var b = initLocExpr(p, e[2])
var call = initLoc(locCall, e, OnHeap)
call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
call.snippet = ropecg(p.module, "#setLengthStr($1, $2)", [
rdLoc(a), rdLoc(b)])
genAssignment(p, a, call, {})
gcUsage(p.config, e)
@@ -2014,23 +2022,23 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
a = initLocExpr(p, ea)
b = initLoc(locExpr, e, OnUnknown)
if e[1].len > 0:
b.r = rope("(")
b.snippet = rope("(")
for i in 0..<e[1].len:
let it = e[1][i]
if it.kind == nkRange:
x = initLocExpr(p, it[0])
y = initLocExpr(p, it[1])
b.r.addf("$1 >= $2 && $1 <= $3",
b.snippet.addf("$1 >= $2 && $1 <= $3",
[rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
else:
x = initLocExpr(p, it)
b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
if i < e[1].len - 1: b.r.add(" || ")
b.r.add(")")
b.snippet.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
if i < e[1].len - 1: b.snippet.add(" || ")
b.snippet.add(")")
else:
# handle the case of an empty set
b.r = rope("0")
putIntoDest(p, d, e, b.r)
b.snippet = rope("0")
putIntoDest(p, d, e, b.snippet)
else:
assert(e[1].typ != nil)
assert(e[2].typ != nil)
@@ -2040,7 +2048,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
const
lookupOpr: array[mLeSet..mMinusSet, string] = [
lookupOpr: array[mLeSet..mXorSet, string] = [
"for ($1 = 0; $1 < $2; $1++) { $n" &
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
" if (!$3) break;}$n",
@@ -2050,7 +2058,8 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
"if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n",
"&",
"|",
"& ~"]
"& ~",
"^"]
var a, b: TLoc
var i: TLoc
var setType = skipTypes(e[1].typ, abstractVar)
@@ -2081,6 +2090,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mMulSet: binaryExpr(p, e, d, "($1 & $2)")
of mPlusSet: binaryExpr(p, e, d, "($1 | $2)")
of mMinusSet: binaryExpr(p, e, d, "($1 & ~ $2)")
of mXorSet: binaryExpr(p, e, d, "($1 ^ $2)")
of mInSet:
genInOp(p, e, d)
else: internalError(p.config, e.info, "genSetOp()")
@@ -2108,7 +2118,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
var a = initLocExpr(p, e[1])
var b = initLocExpr(p, e[2])
putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size]))
of mMulSet, mPlusSet, mMinusSet:
of mMulSet, mPlusSet, mMinusSet, mXorSet:
# we inline the simple for loop for better code generation:
i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
a = initLocExpr(p, e[1])
@@ -2149,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)[]
@@ -2168,7 +2180,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
inc(p.labels)
var lbl = p.labels.rope
var tmp: TLoc = default(TLoc)
tmp.r = "LOC$1.source" % [lbl]
tmp.snippet = "LOC$1.source" % [lbl]
let destsize = getSize(p.config, destt)
let srcsize = getSize(p.config, srct)
@@ -2233,12 +2245,16 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
raiseInstr(p, p.s(cpsStmts))
linefmt p, cpsStmts, "}$n", []
putIntoDest(p, d, n, "(($1) ($2))" %
if sameBackendTypeIgnoreRange(dest, n[0].typ):
# don't cast so an address can be taken for `var` conversions
putIntoDest(p, d, n, "($1)" % [rdCharLoc(a)], a.storage)
else:
putIntoDest(p, d, n, "(($1) ($2))" %
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
proc genConv(p: BProc, e: PNode, d: var TLoc) =
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
if sameBackendType(destType, e[1].typ):
if sameBackendTypeIgnoreRange(destType, e[1].typ):
expr(p, e[1], d)
else:
genSomeCast(p, e, d)
@@ -2313,7 +2329,7 @@ proc genWasMoved(p: BProc; n: PNode) =
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
proc genMove(p: BProc; n: PNode; d: var TLoc) =
var a: TLoc = initLocExpr(p, n[1].skipAddr)
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref})
if n.len == 4:
# generated by liftdestructors:
var src: TLoc = initLocExpr(p, n[2])
@@ -2461,7 +2477,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
var call = initLoc(locCall, e, OnHeap)
var dest = initLocExpr(p, e[1])
var b = initLocExpr(p, e[2])
call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
call.snippet = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
genAssignment(p, dest, call, {})
of mAppendStrStr: genStrAppend(p, e, d)
of mAppendSeqElem:
@@ -2525,7 +2541,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
let member =
if t.kind == tyTuple:
"Field" & rope(dotExpr[1].sym.position)
else: dotExpr[1].sym.loc.r
else: dotExpr[1].sym.loc.snippet
putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member])
of mChr: genSomeCast(p, e, d)
of mOrd: genOrd(p, e, d)
@@ -2540,7 +2556,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mSetLengthStr: genSetLengthStr(p, e, d)
of mSetLengthSeq: genSetLengthSeq(p, e, d)
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
mInSet:
mInSet, mXorSet:
genSetOp(p, e, d, op)
of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat:
var opr = e[0].sym
@@ -2548,8 +2564,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
# - not sure, and it wouldn't work if the symbol behind the magic isn't
# somehow forward-declared from some other usage, but it is *possible*
if lfNoDecl notin opr.loc.flags:
let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r)
assert prc != nil, $opr.loc.r
let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.snippet)
assert prc != nil, $opr.loc.snippet
# HACK:
# Explicitly add this proc as declared here so the cgsym call doesn't
# add a forward declaration - without this we could end up with the same
@@ -2562,7 +2578,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id)
# Make the function behind the magic get actually generated - this will
# not lead to a forward declaration! The genCall will lead to one.
cgsym(p.module, $opr.loc.r)
cgsym(p.module, $opr.loc.snippet)
# make sure we have pointer-initialising code for hot code reloading
if not wasDeclared and p.hcrOn:
p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
@@ -2691,7 +2707,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
var it = n[i]
if it.kind == nkExprColonExpr: it = it[1]
rec = initLoc(locExpr, it, dest[].storage)
rec.r = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
rec.snippet = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
rec.flags.incl(lfEnforceDeref)
expr(p, it, rec)
@@ -2741,12 +2757,12 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
var lit = newRopeAppender()
intLiteral(i, lit)
arr.r = "$1[$2]" % [rdLoc(d), lit]
arr.snippet = "$1[$2]" % [rdLoc(d), lit]
expr(p, n[i], arr)
proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) =
requestConstImpl(p, sym)
assert((sym.loc.r != "") and (sym.loc.t != nil))
assert((sym.loc.snippet != "") and (sym.loc.t != nil))
putLocIntoDest(p, d, sym.loc)
template genStmtListExprImpl(exprOrStmt) {.dirty.} =
@@ -2878,24 +2894,24 @@ proc genConstSetup(p: BProc; sym: PSym): bool =
result = lfNoDecl notin sym.loc.flags
proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) =
if sym.loc.r == "":
if sym.loc.snippet == "":
if not genConstSetup(p, sym): return
assert(sym.loc.r != "", $sym.name.s & $sym.itemId)
assert(sym.loc.snippet != "", $sym.name.s & $sym.itemId)
if m.hcrOn:
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]);
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]);
m.initProc.procSec(cpsLocals).addf(
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(q, sym)])
else:
let headerDecl = "extern NIM_CONST $1 $2;$n" %
[getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]
[getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]
m.s[cfsData].add(headerDecl)
if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
p.module.g.generatedHeader.s[cfsData].add(headerDecl)
proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
# add a suffix for hcr - will later init the global pointer with this data
let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r
let actualConstName = if q.hcrOn: sym.loc.snippet & "_const" else: sym.loc.snippet
var data = newRopeAppender()
data.addf("N_LIB_PRIVATE NIM_CONST $1 $2 = ",
[getTypeDesc(q, sym.typ), actualConstName])
@@ -2904,16 +2920,16 @@ proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
q.s[cfsData].add data
if q.hcrOn:
# generate the global pointer with the real name
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.r])
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.snippet])
# register it (but ignore the boolean result of hcrRegisterGlobal)
q.initProc.procSec(cpsLocals).addf(
"\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
[getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)])
[getModuleDllPath(q, sym), sym.loc.snippet, rdLoc(sym.loc)])
# always copy over the contents of the actual constant with the _const
# suffix ==> this means that the constant is reloadable & updatable!
q.initProc.procSec(cpsLocals).add(ropecg(q,
"\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
[sym.loc.r, actualConstName, rdLoc(sym.loc)]))
[sym.loc.snippet, actualConstName, rdLoc(sym.loc)]))
proc genConstStmt(p: BProc, n: PNode) =
# This code is only used in the new DCE implementation.
@@ -2953,7 +2969,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
genProcPrototype(p.module, sym)
else:
genProc(p.module, sym)
if sym.loc.r == "" or sym.loc.lode == nil:
if sym.loc.snippet == "" or sym.loc.lode == nil:
internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
of skConst:
@@ -2963,7 +2979,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
putIntoDest(p, d, n, lit, OnStatic)
elif useAliveDataFromDce in p.module.flags:
genConstHeader(p.module, p.module, p, sym)
assert((sym.loc.r != "") and (sym.loc.t != nil))
assert((sym.loc.snippet != "") and (sym.loc.t != nil))
putLocIntoDest(p, d, sym.loc)
else:
genComplexConst(p, sym, d)
@@ -2978,14 +2994,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if sfCompileTime in sym.flags:
genSingleVar(p, sym, n, astdef(sym))
if sym.loc.r == "" or sym.loc.t == nil:
if sym.loc.snippet == "" or sym.loc.t == nil:
#echo "FAILED FOR PRCO ", p.prc.name.s
#echo renderTree(p.prc.ast, {renderIds})
internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
if sfThread in sym.flags:
accessThreadLocalVar(p, sym)
if emulatedThreadVars(p.config):
putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.snippet)
else:
putLocIntoDest(p, d, sym.loc)
else:
@@ -2993,17 +3009,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of skTemp:
when false:
# this is more harmful than helpful.
if sym.loc.r == "":
if sym.loc.snippet == "":
# we now support undeclared 'skTemp' variables for easier
# transformations in other parts of the compiler:
assignLocalVar(p, n)
if sym.loc.r == "" or sym.loc.t == nil:
if sym.loc.snippet == "" or sym.loc.t == nil:
#echo "FAILED FOR PRCO ", p.prc.name.s
#echo renderTree(p.prc.ast, {renderIds})
internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id)
putLocIntoDest(p, d, sym.loc)
of skParam:
if sym.loc.r == "" or sym.loc.t == nil:
if sym.loc.snippet == "" or sym.loc.t == nil:
# echo "FAILED FOR PRCO ", p.prc.name.s
# debug p.prc.typ.n
# echo renderTree(p.prc.ast, {renderIds})
@@ -3064,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)
@@ -3093,7 +3100,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkLambdaKinds:
var sym = n[namePos].sym
genProc(p.module, sym)
if sym.loc.r == "" or sym.loc.lode == nil:
if sym.loc.snippet == "" or sym.loc.lode == nil:
internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
of nkClosure: genClosure(p, n, d)
@@ -3104,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)
@@ -3124,7 +3137,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if ex.kind != nkEmpty:
genLineDir(p, n)
var a: TLoc = initLocExprSingleUse(p, ex)
line(p, cpsStmts, "(void)(" & a.r & ");\L")
line(p, cpsStmts, "(void)(" & a.snippet & ");\L")
of nkAsmStmt: genAsmStmt(p, n)
of nkTryStmt, nkHiddenTryStmt:
case p.config.exc

View File

@@ -24,10 +24,10 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
of nkRecCase:
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
let disc = n[0].sym
if disc.loc.r == "": fillObjectFields(p.module, typ)
if disc.loc.snippet == "": fillObjectFields(p.module, typ)
if disc.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
for i in 1..<n.len:
let branch = n[i]
assert branch.kind in {nkOfBranch, nkElse}
@@ -38,14 +38,14 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
specializeResetN(p, accessor, lastSon(branch), typ)
lineF(p, cpsStmts, "break;$n", [])
lineF(p, cpsStmts, "} $n", [])
specializeResetT(p, "$1.$2" % [accessor, disc.loc.r], disc.loc.t)
specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t)
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
if field.loc.r == "": fillObjectFields(p.module, typ)
if field.loc.snippet == "": fillObjectFields(p.module, typ)
if field.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
else: internalError(p.config, n.info, "specializeResetN()")
proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
@@ -59,8 +59,8 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
let arraySize = lengthOrd(p.config, typ.indexType)
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.r, arraySize])
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ.elementType)
[i.snippet, arraySize])
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
lineF(p, cpsStmts, "}$n", [])
of tyObject:
var x = typ.baseClass
@@ -96,7 +96,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
raiseAssert "unexpected set type kind"
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
discard

View File

@@ -110,10 +110,10 @@ proc genVarTuple(p: BProc, n: PNode) =
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
var field = initLoc(locExpr, vn, tup.storage)
if t.kind == tyTuple:
field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
field.snippet = "$1.Field$2" % [rdLoc(tup), rope(i)]
else:
if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
field.snippet = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
putLocIntoDest(p, v.loc, field)
if forHcr or isGlobalInBlock:
hcrGlobals.add((loc: v.loc, tp: "NULL"))
@@ -128,7 +128,7 @@ proc genVarTuple(p: BProc, n: PNode) =
lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", [hcrCond])
for curr in hcrGlobals:
lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N",
[hcrCond, curr.loc.r, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
[hcrCond, curr.loc.snippet, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
@@ -267,11 +267,11 @@ proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
if n[0].kind == nkClosure:
a = initLocExpr(p, n[0][1])
d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
d.snippet = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
else:
a = initLocExpr(p, n[0])
# the environment is guaranteed to contain the 'state' field at offset 1:
d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
d.snippet = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}:
@@ -405,7 +405,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
# put it in the locals section - mainly because of loops which
# use the var in a call to resetLoc() in the statements section
lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n",
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
[v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
# nothing special left to do later on - let's avoid closing and reopening blocks
forHcr = false
@@ -414,7 +414,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
# be able to re-run it but without the top level code - just the init of globals
if forHcr:
lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
[v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
startBlock(targetProc)
if value.kind != nkEmpty and valueAsRope.len == 0:
genLineDir(targetProc, vn)
@@ -1545,7 +1545,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
else:
discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
fillBackendName(p.module, sym)
res.add($sym.loc.r)
res.add($sym.loc.snippet)
of nkTypeOfExpr:
res.add($getTypeDesc(p.module, it.typ))
else:
@@ -1627,9 +1627,9 @@ proc genPragma(p: BProc, n: PNode) =
case whichPragma(it)
of wEmit: genEmit(p, it)
of wPush:
processPushBackendOption(p.optionsStack, p.options, n, i+1)
processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
of wPop:
processPopBackendOption(p.optionsStack, p.options)
processPopBackendOption(p.config, p.optionsStack, p.options)
else: discard

View File

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

View File

@@ -34,10 +34,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
var p = c.p
let disc = n[0].sym
if disc.loc.r == "": fillObjectFields(c.p.module, typ)
if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
if disc.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
for i in 1..<n.len:
let branch = n[i]
assert branch.kind in {nkOfBranch, nkElse}
@@ -51,10 +51,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
if field.loc.r == "": fillObjectFields(c.p.module, typ)
if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
if field.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
else: internalError(c.p.config, n.info, "genTraverseProc()")
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
@@ -78,9 +78,9 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
var oldCode = p.s(cpsStmts)
freeze oldCode
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.r, arraySize])
[i.snippet, arraySize])
let oldLen = p.s(cpsStmts).len
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ.elementType)
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
if p.s(cpsStmts).len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
@@ -120,12 +120,12 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
var oldCode = p.s(cpsStmts)
freeze oldCode
var a = TLoc(r: accessor)
var a = TLoc(snippet: accessor)
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.r, lenExpr(c.p, a)])
[i.snippet, lenExpr(c.p, a)])
let oldLen = p.s(cpsStmts).len
genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ.elementType)
genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType)
if p.s(cpsStmts).len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode

View File

@@ -69,7 +69,7 @@ proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
m.g.mangledPrcs.incl(result)
proc fillBackendName(m: BModule; s: PSym) =
if s.loc.r == "":
if s.loc.snippet == "":
var result: Rope
if not m.compileToCpp and s.kind in routineKinds and optCDebug in m.g.config.globalOptions and
m.g.config.symbolFiles == disabledSf:
@@ -80,11 +80,10 @@ proc fillBackendName(m: BModule; s: PSym) =
if m.hcrOn:
result.add '_'
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
s.loc.r = result
writeMangledName(m.ndi, s, m.config)
s.loc.snippet = result
proc fillParamName(m: BModule; s: PSym) =
if s.loc.r == "":
if s.loc.snippet == "":
var res = s.name.s.mangle
res.add mangleParamExt(s)
#res.add idOrSig(s, res, m.sigConflicts, m.config)
@@ -104,13 +103,12 @@ proc fillParamName(m: BModule; s: PSym) =
# and a function called in main or proxy uses `socket` as a parameter name.
# That would lead to either needing to reload `proxy` or to overwrite the
# executable file for the main module, which is running (or both!) -> error.
s.loc.r = res.rope
writeMangledName(m.ndi, s, m.config)
s.loc.snippet = res.rope
proc fillLocalName(p: BProc; s: PSym) =
assert s.kind in skLocalVars+{skTemp}
#assert sfGlobal notin s.flags
if s.loc.r == "":
if s.loc.snippet == "":
var key = s.name.s.mangle
let counter = p.sigConflicts.getOrDefault(key)
var result = key.rope
@@ -120,8 +118,7 @@ proc fillLocalName(p: BProc; s: PSym) =
elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key):
result.add "_" & rope(counter+1)
p.sigConflicts.inc(key)
s.loc.r = result
if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
s.loc.snippet = result
proc scopeMangledParam(p: BProc; param: PSym) =
## parameter generation only takes BModule, not a BProc, so we have to
@@ -147,23 +144,23 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
var t = typ
while true:
if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
return t.sym.loc.r
return t.sym.loc.snippet
if t.kind in irrelevantForBackend:
t = t.skipModifier
else:
break
let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.elementType else: typ
if typ.loc.r == "":
typ.typeName(typ.loc.r)
typ.loc.r.add $sig
if typ.loc.snippet == "":
typ.typeName(typ.loc.snippet)
typ.loc.snippet.add $sig
else:
when defined(debugSigHashes):
# check consistency:
var tn = newRopeAppender()
typ.typeName(tn)
assert($typ.loc.r == $(tn & $sig))
result = typ.loc.r
assert($typ.loc.snippet == $(tn & $sig))
result = typ.loc.snippet
if result == "": internalError(m.config, "getTypeName: " & $typ.kind)
proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
@@ -321,7 +318,7 @@ proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope =
if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone:
useHeader(m, t.sym)
result = t.sym.loc.r
result = t.sym.loc.snippet
else:
result = rope(literal)
@@ -349,7 +346,12 @@ proc getSimpleTypeDesc(m: BModule; typ: PType): Rope =
of tyNil: result = typeNameOrLiteral(m, typ, "void*")
of tyInt..tyUInt64:
result = typeNameOrLiteral(m, typ, NumericalTypeToStr[typ.kind])
of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.skipModifier)
of tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.skipModifier)
of tyDistinct:
result = getSimpleTypeDesc(m, typ.skipModifier)
if isImportedType(typ) and result != "":
useHeader(m, typ.sym)
result = typ.sym.loc.snippet
of tyStatic:
if typ.n != nil: result = getSimpleTypeDesc(m, skipModifier typ)
else:
@@ -376,11 +378,6 @@ proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope =
result = getSimpleTypeDesc(m, typ)
if result == "": result = cacheGetType(m.typeCache, sig)
proc structOrUnion(t: PType): Rope =
let t = t.skipTypes({tyAlias, tySink})
if tfUnion in t.flags: "union"
else: "struct"
proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) =
if m.compileToCpp:
m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
@@ -539,10 +536,10 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
fillLoc(this.loc, locParam, t.n[1],
this.paramStorageLoc)
if this.typ.kind == tyPtr:
this.loc.r = "this"
this.loc.snippet = "this"
else:
this.loc.r = "(*this)"
names.add this.loc.r
this.loc.snippet = "(*this)"
names.add this.loc.snippet
types.add getTypeDescWeak(m, this.typ, check, dkParam)
let firstParam = if isCtor: 1 else: 2
@@ -570,7 +567,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
if sfNoalias in param.flags:
typ.add("NIM_NOALIAS ")
name = param.loc.r
name = param.loc.snippet
types.add typ
names.add name
if sfCodegenDecl notin param.flags:
@@ -630,9 +627,9 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
typ.add("NIM_NOALIAS ")
if sfCodegenDecl notin param.flags:
params.add(typ)
params.add(param.loc.r)
params.add(param.loc.snippet)
else:
params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.r])
params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.snippet])
# declare the len field for open arrays:
var arr = param.typ.skipTypes({tyGenericInst})
if arr.kind in {tyVar, tyLent, tySink}: arr = arr.elementType
@@ -641,7 +638,7 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
# this fixes the 'sort' bug:
if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown
# need to pass hidden parameter:
params.addf(", NI $1Len_$2", [param.loc.r, j.rope])
params.addf(", NI $1Len_$2", [param.loc.snippet, j.rope])
inc(j)
arr = arr[0].skipTypes({tySink})
if t.returnType != nil and isInvalidReturnType(m.config, t):
@@ -669,7 +666,7 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
proc mangleRecFieldName(m: BModule; field: PSym): Rope =
if {sfImportc, sfExportc} * field.flags != {}:
result = field.loc.r
result = field.loc.snippet
else:
result = rope(mangleField(m, field.name))
if result == "": internalError(m.config, field.info, "mangleRecFieldName")
@@ -698,7 +695,7 @@ proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool)
proc genRecordFieldsAux(m: BModule; n: PNode,
rectype: PType,
check: var IntSet; result: var Rope; unionPrefix = "") =
check: var IntSet; result: var Builder; unionPrefix = "") =
case n.kind
of nkRecList:
for i in 0..<n.len:
@@ -715,64 +712,51 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
let k = lastSon(n[i])
if k.kind != nkSym:
let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i
var a = newRopeAppender()
var a = newBuilder("")
genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".")
if a != "":
if tfPacked notin rectype.flags:
unionBody.add("struct {")
else:
if hasAttribute in CC[m.config.cCompiler].props:
unionBody.add("struct __attribute__((__packed__)){")
else:
unionBody.addf("#pragma pack(push, 1)$nstruct{", [])
unionBody.add(a)
unionBody.addf("} $1;$n", [structName])
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
unionBody.addf("#pragma pack(pop)$n", [])
if a.len != 0:
unionBody.addFieldWithStructType(m, rectype, structName):
unionBody.add(a)
else:
genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix)
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
if unionBody != "":
result.addf("union{\n$1};$n", [unionBody])
if unionBody.len != 0:
result.addAnonUnion:
result.add(unionBody)
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
#assert(field.ast == nil)
let sname = mangleRecFieldName(m, field)
fillLoc(field.loc, locField, n, unionPrefix & sname, OnUnknown)
if field.alignment > 0:
result.addf "NIM_ALIGN($1) ", [rope(field.alignment)]
# for importcpp'ed objects, we only need to set field.loc, but don't
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
# with heavily templatized C++ code:
if not isImportedCppType(rectype):
let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: ""
let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
var typ: Rope = ""
var isFlexArray = false
var initializer = ""
if fieldType.kind == tyUncheckedArray:
result.addf("\t$1 $2[SEQ_DECL_SIZE];$n",
[getTypeDescAux(m, fieldType.elemType, check, dkField), sname])
typ = getTypeDescAux(m, fieldType.elemType, check, dkField)
isFlexArray = true
elif fieldType.kind == tySequence:
# we need to use a weak dependency here for trecursive_table.
result.addf("\t$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, dkField), sname, noAlias])
elif field.bitsize != 0:
result.addf("\t$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, rope($field.bitsize), noAlias])
typ = getTypeDescWeak(m, field.loc.t, check, dkField)
else:
typ = getTypeDescAux(m, field.loc.t, check, dkField)
# don't use fieldType here because we need the
# tyGenericInst for C++ template support
let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags)
if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)):
var didGenTemp = false
var initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer])
else:
result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias])
initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
result.addField(field, sname, typ, isFlexArray, initializer)
else: internalError(m.config, n.info, "genRecordFieldsAux()")
proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false)
proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope =
result = newRopeAppender()
proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) =
genRecordFieldsAux(m, typ.n, typ, check, result)
if typ.itemId in m.g.graph.memberProcsPerType:
let procs = m.g.graph.memberProcsPerType[typ.itemId]
@@ -794,88 +778,34 @@ proc fillObjectFields*(m: BModule; typ: PType) =
# sometimes generic objects are not consistently merged. We patch over
# this fact here.
var check = initIntSet()
discard getRecordFields(m, typ, check)
var ignored = newBuilder("")
addRecordFields(ignored, m, typ, check)
proc mangleDynLibProc(sym: PSym): Rope
proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope,
check: var IntSet, hasField:var bool): Rope =
result = ""
if typ.kind == tyObject:
if typ.baseClass == nil:
if lacksMTypeField(typ):
appcg(m, result, " {$n", [])
else:
if optTinyRtti in m.config.globalOptions:
appcg(m, result, " {$n#TNimTypeV2* m_type;$n", [])
else:
appcg(m, result, " {$n#TNimType* m_type;$n", [])
hasField = true
elif m.compileToCpp:
appcg(m, result, " : public $1 {$n", [baseType])
if typ.isException and m.config.exc == excCpp:
when false:
appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions
if typ.sym.magic == mException:
# Add cleanup destructor to Exception base class
appcg(m, result, "~$1();$n", [name])
# define it out of the class body and into the procs section so we don't have to
# artificially forward-declare popCurrentExceptionEx (very VERY troublesome for HCR)
appcg(m, cfsProcs, "inline $1::~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name])
hasField = true
else:
appcg(m, result, " {$n $1 Sup;$n", [baseType])
hasField = true
else:
result.addf(" {$n", [name])
proc getRecordDesc(m: BModule; typ: PType, name: Rope,
check: var IntSet): Rope =
# declare the record:
var hasField = false
var structOrUnion: string
if tfPacked in typ.flags:
if hasAttribute in CC[m.config.cCompiler].props:
structOrUnion = structOrUnion(typ) & " __attribute__((__packed__))"
else:
structOrUnion = "#pragma pack(push, 1)\L" & structOrUnion(typ)
else:
structOrUnion = structOrUnion(typ)
var baseType: string = ""
if typ.baseClass != nil:
baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField)
if typ.sym == nil or sfCodegenDecl notin typ.sym.flags:
result = structOrUnion & " " & name
result.add(getRecordDescAux(m, typ, name, baseType, check, hasField))
let desc = getRecordFields(m, typ, check)
if not hasField and typ.itemId notin m.g.graph.memberProcsPerType:
if desc == "":
result.add("\tchar dummy;\n")
elif typ.n.len == 1 and typ.n[0].kind == nkSym:
let field = typ.n[0].sym
let fieldType = field.typ.skipTypes(abstractInst)
if fieldType.kind == tyUncheckedArray:
result.add("\tchar dummy;\n")
result.add(desc)
else:
result.add(desc)
result.add("};\L")
result = newBuilder("")
result.addStruct(m, typ, name, baseType):
result.addRecordFields(m, typ, check)
else:
let desc = getRecordFields(m, typ, check)
var desc = newBuilder("")
desc.addRecordFields(m, typ, check)
result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType])
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
result.add "#pragma pack(pop)\L"
proc getTupleDesc(m: BModule; typ: PType, name: Rope,
check: var IntSet): Rope =
result = "$1 $2 {$n" % [structOrUnion(typ), name]
var desc: Rope = ""
for i, a in typ.ikids:
desc.addf("$1 Field$2;$n",
[getTypeDescAux(m, a, check, dkField), rope(i)])
if desc == "": result.add("char dummy;\L")
else: result.add(desc)
result.add("};\L")
result = newBuilder("")
result.addStruct(m, typ, name, ""):
for i, a in typ.ikids:
result.addField(
name = "Field" & $i,
typ = getTypeDescAux(m, a, check, dkField))
proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
# A helper proc for handling cppimport patterns, involving numeric
@@ -932,12 +862,12 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
let sig = hashType(origTyp, m.config)
result = "" # todo move `result = getTypePre(m, t, sig)` here ?
# tyDistinct matters if it is an importc type
result = getTypePre(m, origTyp.skipTypes(irrelevantForBackend-{tyOwned, tyDistinct}), sig)
defer: # defer is the simplest in this case
if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
addAbiCheck(m, t, result)
result = getTypePre(m, t, sig)
if result != "" and t.kind != tyOpenArray:
excl(check, t.id)
if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst:
@@ -1260,7 +1190,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool =
superCall = ""
else:
if not isCtor:
prc.loc.r = "$1$2(@)" % [memberOp, name]
prc.loc.snippet = "$1$2(@)" % [memberOp, name]
elif superCall != "":
superCall = " : " & superCall
@@ -1281,7 +1211,7 @@ proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false)
# handle the 2 options for hotcodereloading codegen - function pointer
# (instead of forward declaration) or header for function body with "_actual" postfix
let asPtrStr = rope(if asPtr: "_PTR" else: "")
var name = prc.loc.r
var name = prc.loc.snippet
if not asPtr and isReloadable(m, prc):
name.add("_actual")
# careful here! don't access ``prc.ast`` as that could reload large parts of
@@ -1421,13 +1351,13 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope;
var tmp = discriminatorTableName(m, typ, field)
var L = lengthOrd(m.config, field.typ)
assert L > 0
if field.loc.r == "": fillObjectFields(m, typ)
if field.loc.snippet == "": fillObjectFields(m, typ)
if field.loc.t == nil:
internalError(m.config, n.info, "genObjectFields")
m.s[cfsTypeInit3].addf("$1.kind = 3;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
"$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.r,
"$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.snippet,
genTypeInfoV1(m, field.typ, info),
makeCString(field.name.s),
tmp, rope(L)])
@@ -1459,13 +1389,13 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope;
# Do not produce code for void types
if isEmptyType(field.typ): return
if field.bitsize == 0:
if field.loc.r == "": fillObjectFields(m, typ)
if field.loc.snippet == "": fillObjectFields(m, typ)
if field.loc.t == nil:
internalError(m.config, n.info, "genObjectFields")
m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"$1.name = $5;$n", [expr, getTypeDesc(m, origType, dkVar),
field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)])
field.loc.snippet, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)])
else: internalError(m.config, n.info, "genObjectFields")
proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
@@ -1570,7 +1500,7 @@ include ccgtrav
proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
genProc(m, s)
m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
[result, s.loc.r])
[result, s.loc.snippet])
proc declareNimType(m: BModule; name: string; str: Rope, module: int) =
let nr = rope(name)
@@ -1658,10 +1588,10 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result:
let wrapper = generateRttiDestructor(m.g.graph, t, theProc.owner, attachedDestructor,
theProc.info, m.idgen, theProc)
genProc(m, wrapper)
result.add wrapper.loc.r
result.add wrapper.loc.snippet
else:
genProc(m, theProc)
result.add theProc.loc.r
result.add theProc.loc.snippet
when false:
if not canFormAcycle(m.g.graph, t) and op == attachedTrace:
@@ -1709,7 +1639,7 @@ proc genVTable(seqs: seq[PSym]): string =
result = "{"
for i in 0..<seqs.len:
if i > 0: result.add ", "
result.add "(void *) " & seqs[i].loc.r
result.add "(void *) " & seqs[i].loc.snippet
result.add "}"
proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =

View File

@@ -14,8 +14,8 @@ import
nversion, nimsets, msgs, bitsets, idents, types,
ccgutils, ropes, wordrecg, treetab, cgmeth,
rodutils, renderer, cgendata, aliases,
lowerings, ndi, lineinfos, pathutils, transf,
injectdestructors, astmsgs, modulepaths, backendpragmas,
lowerings, lineinfos, pathutils, transf,
injectdestructors, astmsgs, modulepaths, pushpoppragmas,
mangleutils
from expanddefaults import caseObjDefaultBranch
@@ -72,8 +72,7 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc =
result = TLoc(k: k, storage: s, lode: lode,
r: "", flags: flags
)
snippet: "", flags: flags)
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} =
# fills the loc if it is not already initialized
@@ -81,7 +80,7 @@ proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.i
a.k = k
a.lode = lode
a.storage = s
if a.r == "": a.r = r
if a.snippet == "": a.snippet = r
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} =
# fills the loc if it is not already initialized
@@ -98,7 +97,7 @@ proc t(a: TLoc): PType {.inline.} =
proc lodeTyp(t: PType): PNode =
result = newNode(nkEmpty)
result.typ = t
result.typ() = t
proc isSimpleConst(typ: PType): bool =
let t = skipTypes(typ, abstractVar)
@@ -341,15 +340,15 @@ proc getTempName(m: BModule): Rope =
proc rdLoc(a: TLoc): Rope =
# 'read' location (deref if indirect)
if lfIndirect in a.flags:
result = "(*" & a.r & ")"
result = "(*" & a.snippet & ")"
else:
result = a.r
result = a.snippet
proc addRdLoc(a: TLoc; result: var Rope) =
if lfIndirect in a.flags:
result.add "(*" & a.r & ")"
result.add "(*" & a.snippet & ")"
else:
result.add a.r
result.add a.snippet
proc lenField(p: BProc): Rope {.inline.} =
result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
@@ -374,6 +373,7 @@ proc dataField(p: BProc): Rope =
proc genProcPrototype(m: BModule, sym: PSym)
include cbuilder
include ccgliterals
include ccgtypes
@@ -386,22 +386,22 @@ template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) =
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
result.add "(&" & a.r & ")"
result.add "(&" & a.snippet & ")"
else:
result.add a.r
result.add a.snippet
proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
result = "(&" & a.r & ")"
result = "(&" & a.snippet & ")"
else:
result = a.r
result = a.snippet
proc byRefLoc(p: BProc; a: TLoc): Rope =
if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a) == skParam) != ctArray and not
p.module.compileToCpp:
result = "(&" & a.r & ")"
result = "(&" & a.snippet & ")"
else:
result = a.r
result = a.snippet
proc rdCharLoc(a: TLoc): Rope =
# read a location that may need a char-cast:
@@ -414,6 +414,7 @@ type
needToCopy
needToCopySinkParam
needTempForOpenArray
needAssignCall
TAssignmentFlags = set[TAssignmentFlag]
proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
@@ -482,9 +483,12 @@ include ccgreset
proc resetLoc(p: BProc, loc: var TLoc) =
let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t)
let typ = skipTypes(loc.t, abstractVarRange)
if isImportedCppType(typ): return
if isImportedCppType(typ):
var didGenTemp = false
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(loc), genCppInitializer(p.module, p, typ, didGenTemp)])
return
if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
assert loc.r != ""
assert loc.snippet != ""
let atyp = skipTypes(loc.t, abstractInst)
if atyp.kind in {tyVar, tyLent}:
@@ -494,7 +498,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
elif not isComplexValueType(typ):
if containsGcRef:
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
nilLoc.r = rope("NIM_NIL")
nilLoc.snippet = rope("NIM_NIL")
genRefAssign(p, loc, nilLoc)
else:
linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)])
@@ -532,7 +536,7 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
elif not isComplexValueType(typ):
if containsGarbageCollectedRef(loc.t):
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
nilLoc.r = rope("NIM_NIL")
nilLoc.snippet = rope("NIM_NIL")
genRefAssign(p, loc, nilLoc)
else:
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
@@ -560,14 +564,14 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
inc(p.labels)
result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
storage: OnStack, flags: {})
if p.module.compileToCpp and isOrHasImportedCppType(t):
var didGenTemp = false
linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r,
linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.snippet,
genCppInitializer(p.module, p, t, didGenTemp)])
else:
linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r])
linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.snippet])
constructLoc(p, result, not needsInit)
when false:
# XXX Introduce a compiler switch in order to detect these easily.
@@ -580,16 +584,16 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc =
inc(p.labels)
result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
storage: OnStack, flags: {})
linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.r, value])
linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.snippet, value])
proc getIntTemp(p: BProc): TLoc =
inc(p.labels)
result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp,
result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp,
storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt),
flags: {})
linefmt(p, cpsLocals, "NI $1;$n", [result.r])
linefmt(p, cpsLocals, "NI $1;$n", [result.snippet])
proc localVarDecl(p: BProc; n: PNode): Rope =
result = ""
@@ -611,9 +615,9 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
if sfVolatile in s.flags: result.add(" volatile")
if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
result.add(" ")
result.add(s.loc.r)
result.add(s.loc.snippet)
else:
result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet])
proc assignLocalVar(p: BProc, n: PNode) =
#assert(s.loc.k == locNone) # not yet assigned
@@ -654,9 +658,9 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) =
if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
else:
if value != "":
decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.snippet, value])
else:
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.snippet])
proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool)
@@ -672,7 +676,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
if q != nil and not containsOrIncl(q.declaredThings, s.id):
varInDynamicLib(q, s)
else:
s.loc.r = mangleDynLibProc(s)
s.loc.snippet = mangleDynLibProc(s)
if value != "":
internalError(p.config, n.info, ".dynlib variables cannot have a value")
return
@@ -703,11 +707,11 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
# [^0]: https://en.cppreference.com/w/cpp/language/aggregate_initialization
# [^1]: https://cplusplus.github.io/CWG/issues/1518.html
# [^2]: https://eel.is/c++draft/over.match.ctor
decl.addf(" $1;$n", [s.loc.r])
decl.addf(" $1;$n", [s.loc.snippet])
else:
decl.addf(" $1 = $2;$n", [s.loc.r, value])
decl.addf(" $1 = $2;$n", [s.loc.snippet, value])
else:
decl.addf(" $1;$n", [s.loc.r])
decl.addf(" $1;$n", [s.loc.snippet])
p.module.s[cfsVars].add(decl)
if p.withinLoop > 0 and value == "":
@@ -721,13 +725,13 @@ proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var b
var decl: Rope = ""
let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
genGlobalVarDecl(p, vn, td, "", decl)
decl.add " " & $s.loc.r
decl.add " " & $s.loc.snippet
genCppVarForCtor(p, value, decl, didGenTemp)
if didGenTemp: return # generated in the caller
p.module.s[cfsVars].add decl
proc assignParam(p: BProc, s: PSym, retType: PType) =
assert(s.loc.r != "")
assert(s.loc.snippet != "")
scopeMangledParam(p, s)
proc fillProcLoc(m: BModule; n: PNode) =
@@ -780,15 +784,11 @@ $1define nimfr_(proc, file) \
TFrame FR_; \
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_);
$1define nimfrs_(proc, file, slots, length) \
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename;NI len;VarSlot s[slots];} FR_; \
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_);
$1define nimln_(n) \
FR_.line = n;
$1define nimln_(n) \
FR_.line = n;
$1define nimlf_(n, file) \
FR_.line = n; FR_.filename = file;
$1define nimlf_(n, file) \
FR_.line = n; FR_.filename = file;
"""
if p.module.s[cfsFrameDefines].len == 0:
@@ -852,7 +852,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
p.options.excl optStackTrace
p.flags.incl nimErrorFlagDisabled
var dest: TLoc = initLoc(locTemp, lib.path, OnStack)
dest.r = getTempName(m)
dest.snippet = getTempName(m)
appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
[getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)])
expr(p, lib.path, dest)
@@ -870,7 +870,7 @@ proc mangleDynLibProc(sym: PSym): Rope =
# we have to build this as a single rope in order not to trip the
# optimization in genInfixCall, see test tests/cpp/t8241.nim
if sfCompilerProc in sym.flags:
# NOTE: sym.loc.r is the external name!
# NOTE: sym.loc.snippet is the external name!
result = rope(sym.name.s)
else:
result = rope(strutils.`%`("Dl_$1_", $sym.id))
@@ -878,10 +878,10 @@ proc mangleDynLibProc(sym: PSym): Rope =
proc symInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex
let isCall = isGetProcAddr(lib)
var extname = sym.loc.r
var extname = sym.loc.snippet
if not isCall: loadDynamicLib(m, lib)
var tmp = mangleDynLibProc(sym)
sym.loc.r = tmp # from now on we only need the internal name
sym.loc.snippet = tmp # from now on we only need the internal name
sym.typ.sym = nil # generate a new name
inc(m.labels, 2)
if isCall:
@@ -908,24 +908,24 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
appcg(m, m.s[cfsDynLibInit],
"\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
[tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
m.s[cfsVars].addf("$2 $1;$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
proc varInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex
var extname = sym.loc.r
var extname = sym.loc.snippet
loadDynamicLib(m, lib)
incl(sym.loc.flags, lfIndirect)
var tmp = mangleDynLibProc(sym)
sym.loc.r = tmp # from now on we only need the internal name
sym.loc.snippet = tmp # from now on we only need the internal name
inc(m.labels, 2)
appcg(m, m.s[cfsDynLibInit],
"$1 = ($2*) #nimGetProcAddr($3, $4);$n",
[tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
m.s[cfsVars].addf("$2* $1;$n",
[sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
[sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
proc symInDynamicLibPartial(m: BModule, sym: PSym) =
sym.loc.r = mangleDynLibProc(sym)
sym.loc.snippet = mangleDynLibProc(sym)
sym.typ.sym = nil # generate a new name
proc cgsymImpl(m: BModule; sym: PSym) {.inline.} =
@@ -948,7 +948,7 @@ proc cgsymValue(m: BModule, name: string): Rope =
cgsymImpl m, sym
else:
rawMessage(m.config, errGenerated, "system module needs: " & name)
result = sym.loc.r
result = sym.loc.snippet
if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}:
result.addActualSuffixForHCR(m.module, sym)
@@ -1215,7 +1215,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
else:
# declare the result symbol:
assignLocalVar(p, resNode)
assert(res.loc.r != "")
assert(res.loc.snippet != "")
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
allPathsAsgnResult(p, procBody) == InitSkippable:
# In an ideal world the codegen could rely on injectdestructors doing its job properly
@@ -1227,7 +1227,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
elif sfConstructor in prc.flags:
resNode.sym.loc.flags.incl lfIndirect
fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
prc.loc.r = getTypeDesc(m, resNode.sym.loc.t, dkVar)
prc.loc.snippet = getTypeDesc(m, resNode.sym.loc.t, dkVar)
else:
fillResult(p.config, resNode, prc.typ)
assignParam(p, res, prc.typ.returnType)
@@ -1292,7 +1292,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
m.s[cfsProcs].add(generatedProc)
if isReloadable(m, prc):
m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n",
[prc.loc.r, prc.loc.r & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
[prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
result = (sfCompileToCpp in m.module.flags and
@@ -1352,7 +1352,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
# the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded)
if isReloadable(q, prc):
q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n",
[prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
[prc.loc.snippet, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
else:
symInDynamicLibPartial(m, prc)
elif prc.typ.callConv == ccInline:
@@ -1369,8 +1369,8 @@ proc genProcNoForward(m: BModule, prc: PSym) =
#elif {sfExportc, sfImportc} * prc.flags == {}:
# # reset name to restore consistency in case of hashing collisions:
# echo "resetting ", prc.id, " by ", m.module.name.s
# prc.loc.r = nil
# prc.loc.r = mangleName(m, prc)
# prc.loc.snippet = nil
# prc.loc.snippet = mangleName(m, prc)
genProcPrototype(m, prc)
genProcAux(m, prc)
elif sfImportc notin prc.flags:
@@ -1382,7 +1382,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
if isReloadable(m, prc) and prc.id notin m.declaredProtos and
q != nil and q.module.id != m.module.id:
m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
[prc.loc.r, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
[prc.loc.snippet, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
genProcPrototype(m, prc)
if q != nil and not containsOrIncl(q.declaredThings, prc.id):
# make sure there is a "prototype" in the external module
@@ -1435,7 +1435,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
return
if sym.owner.id != m.module.id:
# else we already have the symbol generated!
assert(sym.loc.r != "")
assert(sym.loc.snippet != "")
incl(m.declaredThings, sym.id)
if sfThread in sym.flags:
declareThreadVar(m, sym, true)
@@ -1449,9 +1449,9 @@ proc genVarPrototype(m: BModule, n: PNode) =
if sfRegister in sym.flags: m.s[cfsVars].add(" register")
if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile")
if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS")
m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
m.s[cfsVars].addf(" $1;$n", [sym.loc.snippet])
if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)])
proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
@@ -1484,15 +1484,13 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope =
## Returns a mangled module name.
result = ""
result.add mangleModuleName(conf, filename).mangle
result = mangleModuleName(conf, filename).mangle
proc getSomeNameForModule(m: BModule): Rope =
## Returns a mangled module name.
assert m.module.kind == skModule
assert m.module.owner.kind == skPackage
result = ""
result.add mangleModuleName(m.g.config, m.filename).mangle
result = mangleModuleName(m.g.config, m.filename).mangle
proc getSomeInitName(m: BModule, suffix: string): Rope =
if not m.hcrOn:
@@ -1850,11 +1848,11 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R
var extname = prefix & sym
var tmp = mangleDynLibProc(prc)
prc.loc.r = tmp
prc.loc.snippet = tmp
prc.typ.sym = nil
if not containsOrIncl(m.declaredThings, prc.id):
m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, dkVar)])
m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.snippet, getTypeDesc(m, prc.loc.t, dkVar)])
result = "\t$1 = ($2) $3($4, $5);$n" %
[tmp, getTypeDesc(m, prc.typ, dkVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
@@ -1939,9 +1937,6 @@ proc genInitCode(m: BModule) =
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
prc.add(deinitFrame(m.initProc))
elif m.config.exc == excGoto:
if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
prc.addf("}$N", [])
@@ -2090,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))
@@ -2222,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)
@@ -2240,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)
@@ -2257,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.r = "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]
@@ -88,7 +88,7 @@ type
options*: TOptions # options that should be used for code
# generation; this is the same as prc.options
# unless prc == nil
optionsStack*: seq[TOptions]
optionsStack*: seq[(TOptions, TNoteKinds)]
module*: BModule # used to prevent excessive parameter passing
withinLoop*: int # > 0 if we are within a loop
splitDecls*: int # > 0 if we are in some context for C++ that
@@ -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

@@ -55,7 +55,7 @@ proc methodCall*(n: PNode; conf: ConfigRef): PNode =
# replace ordinary method by dispatcher method:
let disp = getDispatcher(result[0].sym)
if disp != nil:
result[0].typ = disp.typ
result[0].typ() = disp.typ
result[0].sym = disp
# change the arguments to up/downcasts to fit the dispatcher's parameters:
for i in 1..<result.len:
@@ -133,7 +133,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
disp.ast = copyTree(s.ast)
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
disp.loc.r = ""
disp.loc.snippet = ""
if s.typ.returnType != nil:
if disp.ast.len > resultPos:
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)

View File

@@ -311,12 +311,12 @@ proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode =
let curExc = ctx.newCurExcAccess()
curExc.info = info
let nilnode = newNode(nkNilLit)
nilnode.typ = curExc.typ
nilnode.typ() = curExc.typ
result = newTree(nkAsgn, curExc, nilnode)
proc newOr(g: ModuleGraph, a, b: PNode): PNode {.inline.} =
result = newTree(nkCall, newSymNode(g.getSysMagic(a.info, "or", mOr)), a, b)
result.typ = g.getSysType(a.info, tyBool)
result.typ() = g.getSysType(a.info, tyBool)
result.info = a.info
proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
@@ -334,7 +334,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
newSymNode(g.getSysMagic(c.info, "of", mOf)),
g.callCodegenProc("getCurrentException"),
c[i])
nextCond.typ = ctx.g.getSysType(c.info, tyBool)
nextCond.typ() = ctx.g.getSysType(c.info, tyBool)
nextCond.info = c.info
if cond.isNil:
@@ -444,11 +444,11 @@ proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode =
proc newNotCall(g: ModuleGraph; e: PNode): PNode =
result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e)
result.typ = g.getSysType(e.info, tyBool)
result.typ() = g.getSysType(e.info, tyBool)
proc boolLit(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
result = newIntLit(g, info, ord value)
result.typ = getSysType(g, info, tyBool)
result.typ() = getSysType(g, info, tyBool)
proc captureVar(c: var Ctx, s: PSym) =
if c.varStates.getOrDefault(s.itemId) != localRequiresLifting:
@@ -486,7 +486,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
result = newNodeI(nkStmtListExpr, n.info)
if n.typ.isNil: internalError(ctx.g.config, "lowerStmtListExprs: constr typ.isNil")
result.typ = n.typ
result.typ() = n.typ
for i in 0..<n.len:
case n[i].kind
@@ -513,7 +513,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
let isExpr = not isEmptyType(n.typ)
if isExpr:
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
tmp = ctx.newTempVar(n.typ, result)
else:
result = newNodeI(nkStmtList, n.info)
@@ -578,7 +578,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if isExpr:
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
let tmp = ctx.newTempVar(n.typ, result)
n[0] = ctx.convertExprBodyToAsgn(n[0], tmp)
@@ -609,7 +609,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if isExpr:
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
let tmp = ctx.newTempVar(n.typ, result)
if n[0].kind == nkStmtListExpr:
@@ -646,7 +646,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if isExpr:
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
else:
result = newNodeI(nkStmtList, n.info)
@@ -732,7 +732,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if ns:
needsSplit = true
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
let (st, ex) = exprToStmtList(n[^1])
result.add(st)
n[^1] = ex
@@ -802,7 +802,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if ns:
needsSplit = true
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
let (st, ex) = exprToStmtList(n[0])
result.add(st)
n[0] = ex
@@ -814,10 +814,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if ns:
needsSplit = true
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
let (st, ex) = exprToStmtList(n[1])
n.transitionSonsKind(nkBlockStmt)
n.typ = nil
n.typ() = nil
n[1] = st
result.add(n)
result.add(ex)
@@ -838,9 +838,9 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
# raise
let curExc = ctx.newCurExcAccess()
let nilnode = newNode(nkNilLit)
nilnode.typ = curExc.typ
nilnode.typ() = curExc.typ
let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
cmp.typ = ctx.g.getSysType(info, tyBool)
cmp.typ() = ctx.g.getSysType(info, tyBool)
let retStmt =
if ctx.nearestFinally == 0:
@@ -1185,11 +1185,11 @@ proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: P
proc createExceptionTable(ctx: var Ctx): PNode {.inline.} =
result = newNodeI(nkBracket, ctx.fn.info)
result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.idgen, ctx.fn)
result.typ() = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.idgen, ctx.fn)
for i in ctx.exceptionTable:
let elem = newIntNode(nkIntLit, i)
elem.typ = ctx.g.getSysType(ctx.fn.info, tyInt16)
elem.typ() = ctx.g.getSysType(ctx.fn.info, tyInt16)
result.add(elem)
proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
@@ -1212,7 +1212,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
let getNextState = newTree(nkBracketExpr,
ctx.createExceptionTable(),
ctx.newStateAccess())
getNextState.typ = intTyp
getNextState.typ() = intTyp
# :state = exceptionTable[:state]
result.add(ctx.newStateAssgn(getNextState))
@@ -1223,7 +1223,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
ctx.g.getSysMagic(info, "==", mEqI).newSymNode(),
ctx.newStateAccess(),
newIntTypeNode(0, intTyp))
cond.typ = boolTyp
cond.typ() = boolTyp
let raiseStmt = newTree(nkRaiseStmt, ctx.g.emptyNode)
let ifBranch = newTree(nkElifBranch, cond, raiseStmt)
@@ -1236,7 +1236,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
newIntTypeNode(0, intTyp),
ctx.newStateAccess())
cond.typ = boolTyp
cond.typ() = boolTyp
let asgn = newTree(nkAsgn, ctx.newUnrollFinallyAccess(info), cond)
result.add(asgn)
@@ -1247,12 +1247,12 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
ctx.newStateAccess(),
newIntTypeNode(0, intTyp))
cond.typ = boolTyp
cond.typ() = boolTyp
let negateState = newTree(nkCall,
ctx.g.getSysMagic(info, "-", mUnaryMinusI).newSymNode,
ctx.newStateAccess())
negateState.typ = intTyp
negateState.typ() = intTyp
let ifBranch = newTree(nkElifBranch, cond, ctx.newStateAssgn(negateState))
let ifStmt = newTree(nkIfStmt, ifBranch)

View File

@@ -24,7 +24,7 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils]
import
msgs, options, nversion, condsyms, extccomp, platform,
wordrecg, nimblecmd, lineinfos, pathutils
@@ -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
@@ -248,6 +249,7 @@ const
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
template warningOptionNoop(switch: string) =
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
@@ -303,6 +305,12 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
else:
result = false
localError(conf, info, errInvalidExceptionSystem % arg)
of "experimental":
try:
result = conf.features.contains parseEnum[Feature](arg)
except ValueError:
result = false
localError(conf, info, errInvalidFeatureButXFound % arg)
else:
result = false
invalidCmdLineOption(conf, passCmd1, switch, info)
@@ -912,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

@@ -11,7 +11,7 @@
## for details. Note this is a first implementation and only the "Concept matching"
## section has been implemented.
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types
import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
import std/intsets
@@ -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
@@ -309,7 +368,7 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
# error was reported earlier.
result = false
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool =
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType): bool =
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
@@ -318,7 +377,9 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; i
## `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:
@@ -328,16 +389,16 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; i
dest = existingBinding(m, dest)
if dest == nil or dest.kind != tyGenericParam: break
if dest != nil:
bindings.idTablePut(a, dest)
bindings.put(a, dest)
when logBindings: echo "A bind ", a, " ", dest
else:
bindings.idTablePut(a, b)
bindings.put(a, b)
when logBindings: echo "B bind ", a, " ", b
# we have a match, so bind 'arg' itself to 'concpt':
bindings.idTablePut(concpt, arg)
bindings.put(concpt, arg)
# invocation != nil means we have a non-atomic concept:
if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1:
# bind even more generic parameters
assert invocation.kind == tyGenericInvocation
for i in FirstGenericParamAt ..< invocation.kidsLen:
bindings.idTablePut(invocation[i], arg[i])
bindings.put(invocation[i], arg[i])

View File

@@ -166,4 +166,8 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasWarnStdPrefix")
defineSymbol("nimHasVtables")
defineSymbol("nimHasGenericsOpenSym2")
defineSymbol("nimHasGenericsOpenSym3")
defineSymbol("nimHasJsNoLambdaLifting")
defineSymbol("nimHasDefaultFloatRoundtrip")
defineSymbol("nimHasXorSet")

View File

@@ -115,7 +115,7 @@ proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, s
proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex =
let invalid = rstast.FileIndex(-1)
result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid)
result = d.nimToRstFid.getOrDefault(fileIndex, invalid)
if result == invalid:
let fname = toFullPath(d.conf, fileIndex)
result = addFilename(d.sharedState, fname)
@@ -830,8 +830,8 @@ proc getName(n: PNode): string =
of nkAccQuoted:
result = "`"
for i in 0..<n.len: result.add(getName(n[i]))
result = "`"
of nkOpenSymChoice, nkClosedSymChoice:
result.add('`')
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = getName(n[0])
else:
result = ""
@@ -849,7 +849,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
var r = ""
for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
result = getIdent(cache, r)
of nkOpenSymChoice, nkClosedSymChoice:
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = getNameIdent(cache, n[0])
else:
result = nil
@@ -863,7 +863,7 @@ proc getRstName(n: PNode): PRstNode =
of nkAccQuoted:
result = getRstName(n[0])
for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
of nkOpenSymChoice, nkClosedSymChoice:
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = getRstName(n[0])
else:
result = nil
@@ -1206,7 +1206,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
var param = %{"name": %($genericParam)}
if genericParam.sym.typ.len > 0:
param["types"] = newJArray()
param["types"].add %($genericParam.sym.typ.elementType)
param["types"] = %($genericParam.sym.typ.elementType)
result.json["signature"]["genericParams"].add param
if optGenIndex in d.conf.globalOptions:
genItem(d, n, nameNode, k, kForceExport)
@@ -1320,7 +1320,7 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id
if t.startsWith("ref "): t = substr(t, 4)
effects[i] = newIdentNode(getIdent(cache, t), n.info)
# set the type so that the following analysis doesn't screw up:
effects[i].typ = real[i].typ
effects[i].typ() = real[i].typ
result = newTreeI(nkExprColonExpr, n.info,
newIdentNode(getIdent(cache, $effectType), n.info), effects)

View File

@@ -275,7 +275,7 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
# the nkPar node:
if n.isNil:
result = newNode(nkTupleConstr)
result.typ = typ
result.typ() = typ
if typ.n.isNil:
internalError(conf, "cannot unpack unnamed tuple")
unpackObjectAdd(conf, x, typ.n, result)
@@ -298,7 +298,7 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
if n.isNil:
result = newNode(nkBracket)
result.typ = typ
result.typ() = typ
newSeq(result.sons, lengthOrd(conf, typ).toInt)
else:
result = n
@@ -319,7 +319,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
template aw(k, v, field: untyped): untyped =
if n.isNil:
result = newNode(k)
result.typ = typ
result.typ() = typ
else:
# check we have the right field:
result = n
@@ -333,12 +333,12 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
template setNil() =
if n.isNil:
result = newNode(nkNilLit)
result.typ = typ
result.typ() = typ
else:
reset n[]
result = n
result[] = TNode(kind: nkNilLit)
result.typ = typ
result.typ() = typ
template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
template awf(kind, v: untyped): untyped = aw(kind, v, floatVal)
@@ -427,7 +427,7 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
# cast through a pointer needs a new inner object:
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
else: x.copyTree
y.typ = x.typ
y.typ() = x.typ
result = unpack(conf, a, destTyp, y)
dealloc a
@@ -481,7 +481,7 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
if aTyp.isNil:
internalAssert conf, i+1 < fntyp.len
aTyp = fntyp[i+1]
args[i+start].typ = aTyp
args[i+start].typ() = aTyp
sig[i] = mapType(conf, aTyp)
if sig[i].isNil: globalError(conf, info, "cannot map FFI type")

View File

@@ -33,6 +33,19 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
let x = param
if x.kind == nkArgList:
for y in items(x): result.add(y)
elif nfDefaultRefsParam in x.flags:
# value of default param needs to be evaluated like template body
# if it contains other template params
var res: PNode
if isAtom(x):
res = newNodeI(nkPar, x.info)
evalTemplateAux(x, actual, c, res)
if res.len == 1: res = res[0]
else:
res = copyNode(x)
for i in 0..<x.safeLen:
evalTemplateAux(x[i], actual, c, res)
result.add res
else:
result.add copyTree(x)
@@ -51,7 +64,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
if x == nil:
x = copySym(s, c.idgen)
# sem'check needs to set the owner properly later, see bug #9476
x.owner = nil # c.genSymOwner
setOwner(x, nil) # c.genSymOwner
#if x.kind == skParam and x.owner.kind == skModule:
# internalAssert c.config, false
idTablePut(c.mapping, s, x)
@@ -169,7 +182,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
d.add newSymNode(sym, info)
result.add d
result.add res
result.typ = res.typ
result.typ() = res.typ
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
conf: ConfigRef;

View File

@@ -125,7 +125,7 @@ proc expandDefault(t: PType; info: TLineInfo): PNode =
of tyString:
result = newZero(t, info, nkStrLit)
of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass,
tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
result = newZero(t, info, nkEmpty) # bug indicator

View File

@@ -63,7 +63,7 @@ type
# Configuration settings for various compilers.
# When adding new compilers, the cmake sources could be a good reference:
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
# https://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
template compiler(name, settings: untyped): untyped =
proc name: TInfoCC {.compileTime.} = settings
@@ -162,7 +162,11 @@ compiler vcc:
linkerExe: "cl",
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
includeCmd: " /I",
linkDirCmd: " /LIBPATH:",
# HACK: we call `cl` so we have to pass `/link` for linker options,
# but users may still want to pass arguments to `cl` (see #14221)
# to deal with this, we add `/link` before each `/LIBPATH`,
# the linker ignores extra `/link`s since it's an unrecognized argument
linkDirCmd: " /link /LIBPATH:",
linkLibCmd: " $1.lib",
debug: " /RTC1 /Z7 ",
pic: "",
@@ -777,7 +781,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
# https://blog.molecular-matters.com/2017/05/09/deleting-pdb-files-locked-by-visual-studio/
# and a bit about the .pdb format in case that is ever needed:
# https://github.com/crosire/blink
# http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
# https://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
if conf.hcrOn and isVSCompatible(conf):
let t = now()
let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
@@ -1000,10 +1004,11 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
# works out of the box with `hashMainCompilationParams`.
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
type BuildCache = object
cacheVersion: string
outputFile: string
outputLastModificationTime: string
compile: seq[(string, string)]
link: seq[string]
linkcmd: string
@@ -1047,6 +1052,8 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
bcache.depfiles.add (path, $secureHashFile(path))
bcache.nimexe = hashNimExe()
if fileExists(bcache.outputFile):
bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
@@ -1067,6 +1074,8 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
# xxx optimize by returning false if stdin input was the same
for (file, hash) in bcache.depfiles:
if $secureHashFile(file) != hash: return true
if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
return true
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
var bcache: BuildCache = default(BuildCache)
@@ -1083,7 +1092,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
"jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
[outputCurrent, output, jsonFile.string])
var cmds: TStringSeq = default(TStringSeq)
var prettyCmds: TStringSeq= default(TStringSeq)
var prettyCmds: TStringSeq = default(TStringSeq)
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
for (name, cmd) in bcache.compile:
cmds.add cmd

View File

@@ -1104,7 +1104,7 @@ proc settype(n: PNode): PType =
proc buildOf(it, loc: PNode; o: Operators): PNode =
var s = newNodeI(nkCurly, it.info, it.len-1)
s.typ = settype(loc)
s.typ() = settype(loc)
for i in 0..<it.len-1: s[i] = it[i]
result = newNodeI(nkCall, it.info, 3)
result[0] = newSymNode(o.opContains)
@@ -1165,8 +1165,12 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
if check[1].kind == nkCurly:
result = copyTree(check)
if access.kind == nkDotExpr:
# change the access to the discriminator field access
var a = copyTree(access)
# set field name to discriminator field name
a[1] = check[2]
# set discriminator field type: important for `neg`
a.typ() = check[2].typ
result[2] = a
# 'access.kind != nkDotExpr' can happen for object constructors
# which we don't check yet

View File

@@ -414,7 +414,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
p.bitsize = s.bitsize
p.alignment = s.alignment
p.externalName = toLitId(s.loc.r, m)
p.externalName = toLitId(s.loc.snippet, m)
p.locFlags = s.loc.flags
c.addMissing s.typ
p.typ = s.typ.storeType(c, m)
@@ -837,8 +837,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
of nkSym:
result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].soperand))
if result.typ == nil and nfOpenSym notin result.flags:
result.typ = result.sym.typ
if result.typ == nil:
result.typ() = result.sym.typ
of externIntLit:
result.intVal = g[thisModule].fromDisk.numbers[n.litId]
of nkStrLit..nkTripleStrLit:
@@ -851,8 +851,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
assert n2.kind == nkNone
transitionNoneToSym(result)
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand))
if result.typ == nil and nfOpenSym notin result.flags:
result.typ = result.sym.typ
if result.typ == nil:
result.typ() = result.sym.typ
else:
for n0 in sonsReadonly(tree, n):
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
@@ -942,10 +942,10 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
result.guard = loadSym(c, g, si, s.guard)
result.bitsize = s.bitsize
result.alignment = s.alignment
result.owner = loadSym(c, g, si, s.owner)
setOwner(result, loadSym(c, g, si, s.owner))
let externalName = g[si].fromDisk.strings[s.externalName]
if externalName != "":
result.loc.r = rope externalName
result.loc.snippet = externalName
result.loc.flags = s.locFlags
result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom)
@@ -998,7 +998,7 @@ proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
t: PackedType; si, item: int32; result: PType) =
result.sym = loadSym(c, g, si, t.sym)
result.owner = loadSym(c, g, si, t.owner)
setOwner(result, loadSym(c, g, si, t.owner))
when false:
for op, item in pairs t.attachedOps:
result.attachedOps[op] = loadSym(c, g, si, item)
@@ -1062,7 +1062,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
name: getIdent(cache, splitFile(filename).name),
info: newLineInfo(fileIdx, 1, 1),
position: int(fileIdx))
m.module.owner = getPackage(conf, cache, fileIdx)
setOwner(m.module, getPackage(conf, cache, fileIdx))
m.module.flags = m.fromDisk.moduleFlags
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;

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,7 +246,10 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
result = createModuleAliasImpl(realModule.name)
if importHidden:
result.options.incl optImportHidden
c.unusedImports.add((result, n.info))
let moduleIdent = if n.kind 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
@@ -287,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
@@ -334,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)
@@ -371,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:
@@ -386,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

@@ -317,8 +317,9 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
]#
result = dest.kind != nkSym
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
if c.graph.config.selectedGC == gcOrc:
proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags:
# add cyclic flag, but not to sink calls, which IsExplicitSink generates
let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
if cyclicType(c.graph, t):
result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
@@ -331,7 +332,7 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) =
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
else:
let xenv = genBuiltin(c.graph, c.idgen, mAccessEnv, "accessEnv", dest)
xenv.typ = getSysType(c.graph, dest.info, tyPointer)
xenv.typ() = getSysType(c.graph, dest.info, tyPointer)
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
@@ -407,7 +408,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode =
proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
result = newNodeI(nkCall, info)
result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault)))
result.typ = t
result.typ() = t
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
# generate: (let tmp = v; reset(v); tmp)
@@ -464,7 +465,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
var newCall = newTreeIT(nkCall, src.info, src.typ,
newSymNode(op),
src)
c.finishCopy(newCall, n, isFromSink = true)
c.finishCopy(newCall, n, {}, isFromSink = true)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
newCall
@@ -473,7 +474,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
c.finishCopy(m, n, isFromSink = true)
c.finishCopy(m, n, {}, isFromSink = true)
result.add m
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
message(c.graph.config, n.info, hintPerformance,
@@ -501,7 +502,7 @@ proc containsConstSeq(n: PNode): bool =
return true
result = false
case n.kind
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
result = containsConstSeq(n[1])
of nkObjConstr, nkClosure:
for i in 1..<n.len:
@@ -761,7 +762,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
let tmp = c.getTemp(s, n[0].typ, n.info)
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
m.add p(n[0], c, s, normal)
c.finishCopy(m, n[0], isFromSink = false)
c.finishCopy(m, n[0], {}, isFromSink = false)
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
var toDisarm = n[0]
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
@@ -801,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
@@ -821,14 +814,17 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
# allow conversions from owned to unowned via this little hack:
let nTyp = n[1].typ
n[1].typ = n.typ
n[1].typ() = n.typ
result[1] = p(n[1], c, s, sinkArg)
result[1].typ = nTyp
result[1].typ() = nTyp
else:
result[1] = p(n[1], c, s, sinkArg)
elif n.kind in {nkObjDownConv, nkObjUpConv}:
result = copyTree(n)
result[0] = p(n[0], c, s, sinkArg)
elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}:
result = copyTree(n)
result[1] = p(n[1], c, s, sinkArg)
elif n.typ == nil:
# 'raise X' can be part of a 'case' expression. Deal with it here:
result = p(n, c, s, normal)
@@ -882,19 +878,14 @@ 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
elif c.inSpawn > 0:
c.inSpawn.dec
let parameters = n[0].typ
# bug #23907; skips tyGenericInst for generic callbacks
let parameters = if n[0].typ != nil: n[0].typ.skipTypes(abstractInst) else: n[0].typ
let L = if parameters != nil: parameters.signatureLen else: 0
when false:
@@ -904,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:
@@ -1017,9 +1014,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
# allow conversions from owned to unowned via this little hack:
let nTyp = n[1].typ
n[1].typ = n.typ
n[1].typ() = n.typ
result[1] = p(n[1], c, s, mode)
result[1].typ = nTyp
result[1].typ() = nTyp
else:
result[1] = p(n[1], c, s, mode)
@@ -1169,7 +1166,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
result = c.genCopy(dest, ri, flags)
dec c.inEnsureMove, isEnsureMove
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, isFromSink = false)
c.finishCopy(result, dest, flags, isFromSink = false)
of nkBracket:
# array constructor
if ri.len > 0 and isDangerousSeq(ri.typ):
@@ -1177,7 +1174,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
result = c.genCopy(dest, ri, flags)
dec c.inEnsureMove, isEnsureMove
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, isFromSink = false)
c.finishCopy(result, dest, flags, isFromSink = false)
else:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
@@ -1198,7 +1195,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
result = c.genCopy(dest, ri, flags)
dec c.inEnsureMove, isEnsureMove
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, isFromSink = false)
c.finishCopy(result, dest, flags, isFromSink = false)
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
@@ -1218,7 +1215,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
result = c.genCopy(dest, ri, flags)
dec c.inEnsureMove, isEnsureMove
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, isFromSink = false)
c.finishCopy(result, dest, flags, isFromSink = false)
when false:
proc computeUninit(c: var Con) =

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

@@ -33,7 +33,7 @@ import
nversion, msgs, idents, types,
ropes, wordrecg, renderer,
cgmeth, lowerings, sighashes, modulegraphs, lineinfos,
transf, injectdestructors, sourcemap, astmsgs, backendpragmas,
transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas,
mangleutils
import pipelineutils
@@ -103,7 +103,7 @@ type
prc: PSym
globals, locals, body: Rope
options: TOptions
optionsStack: seq[TOptions]
optionsStack: seq[(TOptions, TNoteKinds)]
module: BModule
g: PGlobals
beforeRetNeeded: bool
@@ -195,7 +195,7 @@ proc mapType(typ: PType): TJSTypeKind =
of tyPointer:
# treat a tyPointer like a typed pointer to an array of bytes
result = etyBaseIndex
of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent:
of tyRange, tyDistinct, tyOrdinal, tyError, tyLent:
# tyLent is no-op as JS has pass-by-reference semantics
result = mapType(skipModifier t)
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
@@ -246,7 +246,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
for chr in name:
if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
return false
result = s.loc.r
result = s.loc.snippet
if result == "":
if s.kind == skField and s.name.s.validJsName:
result = rope(s.name.s)
@@ -277,7 +277,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
else:
result.add("_")
result.add(rope(s.id))
s.loc.r = result
s.loc.snippet = result
proc escapeJSString(s: string): string =
result = newStringOfCap(s.len + s.len shr 2)
@@ -611,6 +611,17 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
r.res = frmt % [a, tmp]
r.kind = resExpr
proc genBreakState(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes = default(TCompRes)
# mangle `:state` properly somehow
if n.kind == nkClosure:
gen(p, n[1], a)
r.res = "(($1).HEX3Astate < 0)" % [rdLoc(a)]
else:
gen(p, n, a)
r.res = "((($1.ClE_0).HEX3Astate) < 0)" % [rdLoc(a)]
r.kind = resExpr
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
var
x, y: TCompRes = default(TCompRes)
@@ -981,7 +992,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# if isJsObject(throwObj.typ):
if isImportedException(throwObj.typ, p.config):
orExpr.addf("lastJSError instanceof $1",
[throwObj.typ.sym.loc.r])
[throwObj.typ.sym.loc.snippet])
else:
orExpr.addf("isObj(lastJSError.m_type, $1)",
[genTypeInfo(p, throwObj.typ)])
@@ -991,8 +1002,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# If some branch requires a local alias introduce it here. This is needed
# since JS cannot do ``catch x as y``.
if excAlias != nil:
excAlias.sym.loc.r = mangleName(p.module, excAlias.sym)
lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r)
excAlias.sym.loc.snippet = mangleName(p.module, excAlias.sym)
lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.snippet)
gen(p, n[i][^1], a)
moveInto(p, a, r)
lineF(p, "}$n", [])
@@ -1098,14 +1109,19 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
lineF(p, "break;$n", [])
of nkElse:
if transferRange:
lineF(p, "else{$n", [])
if n.len == 2: # a dangling else for a case statement
transferRange = false
lineF(p, "switch ($1) {$n", [cond.rdLoc])
lineF(p, "default: $n", [])
else:
lineF(p, "else{$n", [])
else:
lineF(p, "default: $n", [])
p.nested:
gen(p, it[0], stmt)
moveInto(p, stmt, r)
if transferRange:
lineF(p, "}$n", [])
lineF(p, "}$n", [])
else:
lineF(p, "break;$n", [])
else: internalError(p.config, it.info, "jsgen.genCaseStmt")
@@ -1218,7 +1234,7 @@ proc generateHeader(p: PProc, prc: PSym): Rope =
# to keep it simple
let env = prc.ast[paramsPos].lastSon
assert env.kind == nkSym, "env is missing"
env.sym.loc.r = "this"
env.sym.loc.snippet = "this"
for i in 1..<typ.n.len:
assert(typ.n[i].kind == nkSym)
@@ -1360,8 +1376,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
else:
if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
var f = b[1].sym
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
r.res = makeJSString($f.loc.r)
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
r.res = makeJSString($f.loc.snippet)
internalAssert p.config, a.typ != etyBaseIndex
r.address = a.res
r.kind = resExpr
@@ -1388,8 +1404,8 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
else:
if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess")
var f = n[1].sym
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
r.res = "$1.$2" % [r.res, f.loc.r]
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
r.res = "$1.$2" % [r.res, f.loc.snippet]
mkTemp(1)
r.kind = resExpr
@@ -1409,11 +1425,11 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
# Field symbol
var field = accessExpr[1].sym
internalAssert p.config, field.kind == skField
if field.loc.r == "": field.loc.r = mangleName(p.module, field)
if field.loc.snippet == "": field.loc.snippet = mangleName(p.module, field)
# Discriminant symbol
let disc = checkExpr[2].sym
internalAssert p.config, disc.kind == skField
if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc)
if disc.loc.snippet == "": disc.loc.snippet = mangleName(p.module, disc)
var setx: TCompRes = default(TCompRes)
gen(p, checkExpr[1], setx)
@@ -1430,16 +1446,16 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen
let msg = genFieldDefect(p.config, field.name.s, disc)
lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n",
setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===",
setx.res, tmp, disc.loc.snippet, if negCheck: "!==" else: "===",
makeJSString(msg), genTypeInfo(p, disc.typ))
if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex:
r.typ = etyBaseIndex
r.res = makeJSString($field.loc.r)
r.res = makeJSString($field.loc.snippet)
r.address = tmp
else:
r.typ = etyNone
r.res = "$1.$2" % [tmp, field.loc.r]
r.res = "$1.$2" % [tmp, field.loc.snippet]
r.kind = resExpr
proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
@@ -1506,10 +1522,10 @@ template isIndirect(x: PSym): bool =
proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
let s = n.sym
if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3")
if s.loc.snippet == "": internalError(p.config, n.info, "genAddr: 3")
case s.kind
of skParam:
r.res = s.loc.r
r.res = s.loc.snippet
r.address = ""
r.typ = etyNone
of skVar, skLet, skResult:
@@ -1523,15 +1539,15 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
# make addr() a no-op:
r.typ = etyNone
if isIndirect(s):
r.res = s.loc.r & "[0]"
r.res = s.loc.snippet & "[0]"
else:
r.res = s.loc.r
r.res = s.loc.snippet
r.address = ""
elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
# for ease of code generation, we do not distinguish between
# sfAddrTaken and sfGlobal.
r.typ = etyBaseIndex
r.address = s.loc.r
r.address = s.loc.snippet
r.res = rope("0")
else:
# 'var openArray' for instance produces an 'addr' but this is harmless:
@@ -1569,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:
@@ -1624,7 +1633,7 @@ proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s)
if owner.prc == s.owner:
if not owner.generatedParamCopies.containsOrIncl(s.id):
let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)]
let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.snippet, genTypeInfo(p, s.typ)]
owner.locals.add(owner.indentLine(copy))
return
owner = owner.up
@@ -1635,7 +1644,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
var s = n.sym
case s.kind
of skVar, skLet, skParam, skTemp, skResult, skForVar:
if s.loc.r == "":
if s.loc.snippet == "":
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
if sfCompileTime in s.flags:
genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info))
@@ -1646,29 +1655,29 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
r.typ = etyBaseIndex
if {sfAddrTaken, sfGlobal} * s.flags != {}:
if isIndirect(s):
r.address = "$1[0][0]" % [s.loc.r]
r.res = "$1[0][1]" % [s.loc.r]
r.address = "$1[0][0]" % [s.loc.snippet]
r.res = "$1[0][1]" % [s.loc.snippet]
else:
r.address = "$1[0]" % [s.loc.r]
r.res = "$1[1]" % [s.loc.r]
r.address = "$1[0]" % [s.loc.snippet]
r.res = "$1[1]" % [s.loc.snippet]
else:
r.address = s.loc.r
r.res = s.loc.r & "_Idx"
r.address = s.loc.snippet
r.res = s.loc.snippet & "_Idx"
elif isIndirect(s):
r.res = "$1[0]" % [s.loc.r]
r.res = "$1[0]" % [s.loc.snippet]
else:
r.res = s.loc.r
r.res = s.loc.snippet
of skConst:
genConstant(p, s)
if s.loc.r == "":
if s.loc.snippet == "":
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
r.res = s.loc.r
r.res = s.loc.snippet
of skProc, skFunc, skConverter, skMethod, skIterator:
if sfCompileTime in s.flags:
localError(p.config, n.info, "request to generate code for .compileTime proc: " &
s.name.s)
discard mangleName(p.module, s)
r.res = s.loc.r
r.res = s.loc.snippet
if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or
{sfImportc, sfInfixCall} * s.flags != {}:
discard
@@ -1680,13 +1689,13 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
else:
genProcForSymIfNeeded(p, s)
else:
if s.loc.r == "":
if s.loc.snippet == "":
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
if mapType(p, s.typ) == etyBaseIndex:
r.address = s.loc.r
r.res = s.loc.r & "_Idx"
r.address = s.loc.snippet
r.res = s.loc.snippet & "_Idx"
else:
r.res = s.loc.r
r.res = s.loc.snippet
r.kind = resVal
proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
@@ -1828,9 +1837,9 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
# don't call '$' here for efficiency:
let f = n[0].sym
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
if sfInfixCall in f.flags:
let pat = $n[0].sym.loc.r
let pat = $n[0].sym.loc.snippet
internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@'}):
var typ = skipTypes(n[0].typ, abstractInst)
@@ -1945,7 +1954,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar:
result = putToSeq("0", indirect)
of tyInt, tyUInt:
if $t.sym.loc.r == "bigint":
if $t.sym.loc.snippet == "bigint":
result = putToSeq("0n", indirect)
else:
result = putToSeq("0", indirect)
@@ -2067,28 +2076,28 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
if a.typ == etyBaseIndex:
if targetBaseIndex:
line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
[returnType, v.loc.r, a.address, a.res]))
[returnType, v.loc.snippet, a.address, a.res]))
else:
if isIndirect(v):
line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
[returnType, v.loc.r, a.address, a.res]))
[returnType, v.loc.snippet, a.address, a.res]))
else:
line(p, runtimeFormat(varCode & " = [$3, $4];$n",
[returnType, v.loc.r, a.address, a.res]))
[returnType, v.loc.snippet, a.address, a.res]))
else:
if targetBaseIndex:
let tmp = p.getTemp
lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
[tmp, a.res, v.loc.r])
[tmp, a.res, v.loc.snippet])
else:
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res]))
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, a.res]))
return
else:
s = a.res
if isIndirect(v):
line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s]))
line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.snippet, s]))
else:
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s]))
line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, s]))
if useReloadingGuard or useGlobalPragmas:
dec p.extraIndent
@@ -2442,6 +2451,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)")
of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
of mXorSet: binaryExpr(p, n, r, "SetXor", "SetXor($1, $2)")
of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
of mInSet:
@@ -2563,17 +2573,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
let val = it[1]
gen(p, val, a)
var f = it[0].sym
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id)
let typ = val.typ.skipTypes(abstractInst)
if a.typ == etyBaseIndex:
initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res])
initList.addf("$#: [$#, $#]", [f.loc.snippet, a.address, a.res])
else:
if not needsNoCopy(p, val):
useMagic(p, "nimCopy")
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
initList.addf("$#: $#", [f.loc.r, a.res])
initList.addf("$#: $#", [f.loc.snippet, a.res])
let t = skipTypes(n.typ, abstractInst + skipPtrs)
createObjInitList(p, t, fieldIDs, initList)
r.res = ("{$1}") % [initList]
@@ -2632,7 +2642,13 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
let src = skipTypes(n[0].typ, abstractVarRange)
let dest = skipTypes(n.typ, abstractVarRange)
if optRangeCheck notin p.options:
return
if optJsBigInt64 in p.config.globalOptions and
dest.kind in {tyUInt..tyUInt32, tyInt..tyInt32} and
src.kind in {tyInt64, tyUInt64}:
# conversions to Number are kept
r.res = "Number($1)" % [r.res]
else:
discard
elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures:
if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res]
@@ -2813,9 +2829,9 @@ proc genPragma(p: PProc, n: PNode) =
case whichPragma(it)
of wEmit: genAsmOrEmitStmt(p, it[1])
of wPush:
processPushBackendOption(p.optionsStack, p.options, n, i+1)
processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
of wPop:
processPopBackendOption(p.optionsStack, p.options)
processPopBackendOption(p.config, p.optionsStack, p.options)
else: discard
proc genCast(p: PProc, n: PNode, r: var TCompRes) =
@@ -2992,7 +3008,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
of nkLambdaKinds:
let s = n[namePos].sym
discard mangleName(p.module, s)
r.res = s.loc.r
r.res = s.loc.snippet
if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard
elif not p.g.generatedSyms.containsOrIncl(s.id):
p.locals.add(genProc(p, s))
@@ -3043,16 +3059,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
of nkGotoState, nkState:
globalError(p.config, n.info, "not implemented")
of nkBreakState:
var a: TCompRes = default(TCompRes)
if n[0].kind == nkClosure:
gen(p, n[0][1], a)
let sym = n[0][1].typ[0].n[0].sym
r.res = "(($1).$2 < 0)" % [rdLoc(a), mangleName(p.module, sym)]
else:
gen(p, n[0], a)
let sym = n[0].typ[0].n[0].sym
r.res = "((($1.ClE_0).$2) < 0)" % [rdLoc(a), mangleName(p.module, sym)]
r.kind = resExpr
genBreakState(p, n[0], r)
of nkPragmaBlock: gen(p, n.lastSon, r)
of nkComesFrom:
discard "XXX to implement for better stack traces"

View File

@@ -175,6 +175,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
#echo "produced environment: ", param.id, " for ", routine.id
proc getEnvParam*(routine: PSym): PSym =
if routine.ast.isNil: return nil
let params = routine.ast[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
@@ -283,8 +284,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
localError(g.config, n.info,
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
" Consider using a <ref $2> which can be captured.") %
[s.name.s, typeToString(s.typ), g.config$s.info])
" Consider using a <ref T> which can be captured.") %
[s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
[s.name.s, owner.name.s, $owner.typ.callConv])
@@ -419,6 +420,12 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
#echo "adding closure to ", fn.name.s
proc iterEnvHasUpField(g: ModuleGraph, iter: PSym): bool =
let cp = getEnvParam(iter)
doAssert(cp != nil, "Env param not present in iter")
let upField = lookupInRecord(cp.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
upField != nil
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
case n.kind
of nkSym:
@@ -437,7 +444,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
let body = transformBody(c.graph, c.idgen, s, {useCache})
detectCapturedVars(body, s, c)
let ow = s.skipGenericOwner
let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
let innerClosure = innerProc and s.typ.callConv == ccClosure and (not s.isIterator or iterEnvHasUpField(c.graph, s))
let interested = interestingVar(s)
if ow == owner:
if owner.isIterator:
@@ -602,7 +609,7 @@ proc rawClosureCreation(owner: PSym;
let unowned = c.unownedEnvVars[owner.id]
assert unowned != nil
let env2 = copyTree(env)
env2.typ = unowned.typ
env2.typ() = unowned.typ
result.add newAsgnStmt(unowned, env2, env.info)
createTypeBoundOpsLL(d.graph, unowned.typ, env.info, d.idgen, owner)
@@ -642,16 +649,27 @@ proc finishClosureCreation(owner: PSym; d: var DetectionPass; c: LiftingPass;
res.add newAsgnStmt(unowned, nilLit, info)
createTypeBoundOpsLL(d.graph, unowned.typ, info, d.idgen, owner)
proc closureCreationForIter(iter: PNode;
proc getUpForIter(g: ModuleGraph; owner, iterOwner: PSym, expectedUpTyp: PType): PNode =
var p = getHiddenParam(g, owner)
var res = p.newSymNode
while res.typ.skipTypes({tyOwned, tyRef, tyPtr}) != expectedUpTyp:
let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
if upField == nil:
return nil
p = upField
res = rawIndirectAccess(res, upField, p.info)
res
proc closureCreationForIter(owner: PSym, iter: PNode;
d: var DetectionPass; c: var LiftingPass): PNode =
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
let owner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
let iterOwner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, iterOwner, iter.info)
incl(v.flags, sfShadowed)
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
var vnode: PNode
if owner.isIterator:
let it = getHiddenParam(d.graph, owner)
if iterOwner.isIterator:
let it = getHiddenParam(d.graph, iterOwner)
addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
vnode = indirectAccess(newSymNode(it), v, v.info)
else:
@@ -660,12 +678,14 @@ proc closureCreationForIter(iter: PNode;
addVar(vs, vnode)
result.add(vs)
result.add genCreateEnv(vnode)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, iterOwner)
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
if upField != nil:
let u = setupEnvVar(owner, d, c, iter.info)
if u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == upField.typ.skipTypes({tyOwned, tyRef, tyPtr}):
let expectedUpTyp = upField.typ.skipTypes({tyOwned, tyRef, tyPtr})
let u = if iterOwner == owner: setupEnvVar(iterOwner, d, c, iter.info)
else: getUpForIter(d.graph, owner, iterOwner, expectedUpTyp)
if u != nil and u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == expectedUpTyp:
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
u, iter.info))
else:
@@ -699,7 +719,7 @@ proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
let available = getHiddenParam(d.graph, owner)
result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
elif s.isIterator:
result = closureCreationForIter(n, d, c)
result = closureCreationForIter(owner, n, d, c)
elif s.skipGenericOwner == owner:
# direct dependency, so use the outer's env variable:
result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
@@ -766,7 +786,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
let oldInContainer = c.inContainer
c.inContainer = 0
let m = newSymNode(n[namePos].sym)
m.typ = n.typ
m.typ() = n.typ
result = liftCapturedVars(m, owner, d, c)
c.inContainer = oldInContainer
of nkHiddenStdConv:

82
compiler/layeredtable.nim Normal file
View File

@@ -0,0 +1,82 @@
import std/tables
import ast
type
LayeredIdTableObj* {.acyclic.} = object
## stack of type binding contexts implemented as a linked list
topLayer*: TypeMapping
## the mappings on the current layer
nextLayer*: ref LayeredIdTableObj
## the parent type binding context, possibly `nil`
previousLen*: int
## total length of the bindings up to the parent layer,
## used to track if new bindings were added
const useRef = not defined(gcDestructors)
# implementation detail, only arc/orc doesn't cause issues when
# using LayeredIdTable as an object and not a ref
when useRef:
type LayeredIdTable* = ref LayeredIdTableObj
else:
type LayeredIdTable* = LayeredIdTableObj
proc initLayeredTypeMap*(pt: sink TypeMapping = initTypeMapping()): LayeredIdTable =
result = LayeredIdTable(topLayer: pt, nextLayer: nil)
proc shallowCopy*(pt: LayeredIdTable): LayeredIdTable {.inline.} =
## copies only the type bindings of the current layer, but not any parent layers,
## useful for write-only bindings
result = LayeredIdTable(topLayer: pt.topLayer, nextLayer: pt.nextLayer, previousLen: pt.previousLen)
proc currentLen*(pt: LayeredIdTable): int =
## the sum of the cached total binding count of the parents and
## the current binding count, just used to track if bindings were added
pt.previousLen + pt.topLayer.len
proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable =
result = LayeredIdTable(topLayer: initTable[ItemId, PType](), previousLen: pt.currentLen)
when useRef:
result.nextLayer = pt
else:
new(result.nextLayer)
result.nextLayer[] = pt
proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
when useRef:
pt = pt.nextLayer
else:
when defined(gcDestructors):
pt = pt.nextLayer[]
else:
# workaround refc
let tmp = pt.nextLayer[]
pt = tmp
proc lookup(typeMap: ref LayeredIdTableObj, key: ItemId): PType =
result = nil
var tm = typeMap
while tm != nil:
result = getOrDefault(tm.topLayer, key)
if result != nil: return
tm = tm.nextLayer
template lookup*(typeMap: ref LayeredIdTableObj, key: PType): PType =
## recursively looks up binding of `key` in all parent layers
lookup(typeMap, key.itemId)
when not useRef:
proc lookup(typeMap: LayeredIdTableObj, key: ItemId): PType {.inline.} =
result = getOrDefault(typeMap.topLayer, key)
if result == nil and typeMap.nextLayer != nil:
result = lookup(typeMap.nextLayer, key)
template lookup*(typeMap: LayeredIdTableObj, key: PType): PType =
lookup(typeMap, key.itemId)
proc put(typeMap: var LayeredIdTable, key: ItemId, value: PType) {.inline.} =
typeMap.topLayer[key] = value
template put*(typeMap: var LayeredIdTable, key, value: PType) =
## binds `key` to `value` only in current layer
put(typeMap, key.itemId, value)

View File

@@ -49,7 +49,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
result = newNodeI(nkBracketExpr, a.info, 2)
result[0] = a
result[1] = i
result.typ = elemType
result.typ() = elemType
proc destructorOverridden(g: ModuleGraph; t: PType): bool =
let op = getAttachedOp(g, t, attachedDestructor)
@@ -68,7 +68,7 @@ proc dotField(x: PNode, f: PSym): PNode =
else:
result[0] = x
result[1] = newSymNode(f, x.info)
result.typ = f.typ
result.typ() = f.typ
proc newAsgnStmt(le, ri: PNode): PNode =
result = newNodeI(nkAsgn, le.info, 2)
@@ -88,7 +88,7 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add newAsgnStmt(x, y)
elif c.kind == attachedDestructor and c.addMemReset:
let call = genBuiltin(c, mDefault, "default", x)
call.typ = t
call.typ() = t
body.add newAsgnStmt(x, call)
elif c.kind == attachedWasMoved:
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
@@ -105,7 +105,7 @@ proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
result = newNodeI(nkWhileStmt, c.info, 2)
let cmp = genBuiltin(c, mLtI, "<", i)
cmp.add genLen(c.g, dest)
cmp.typ = getSysType(c.g, c.info, tyBool)
cmp.typ() = getSysType(c.g, c.info, tyBool)
result[0] = cmp
result[1] = newNodeI(nkStmtList, c.info)
@@ -127,10 +127,10 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
dotExpr.add newSymNode(field)
let offsetOf = genBuiltin(c, mOffsetOf, "offsetof", dotExpr)
offsetOf.typ = intType
offsetOf.typ() = intType
let minusExpr = genBuiltin(c, mSubI, "-", castExpr1)
minusExpr.typ = intType
minusExpr.typ() = intType
minusExpr.add offsetOf
let objPtr = makePtrType(objType.owner, objType, c.idgen)
@@ -224,10 +224,16 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
if t.baseClass != nil:
let obj = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
obj.add newNodeI(nkEmpty, c.info)
obj.add x
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, obj, y)
let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
dest.add newNodeI(nkEmpty, c.info)
dest.add x
var src = y
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
src.add newNodeI(nkEmpty, c.info)
src.add y
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
@@ -259,7 +265,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
# because the wasMoved(dest) call would zero out src, if dest aliases src.
var cond = newTree(nkCall, newSymNode(c.g.getSysMagic(c.info, "==", mEqRef)),
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
cond.typ = getSysType(c.g, x.info, tyBool)
cond.typ() = getSysType(c.g, x.info, tyBool)
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
temp.typ = x.typ
@@ -290,7 +296,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
result = newIntLit(g, info, ord value)
result.typ = getSysType(g, info, tyBool)
result.typ() = getSysType(g, info, tyBool)
proc getCycleParam(c: TLiftCtx): PNode =
assert c.kind in {attachedAsgn, attachedDup}
@@ -539,18 +545,18 @@ proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
# don't call genAddr(c, x) here:
result = genBuiltin(c, mNewSeq, "newSeq", x)
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
lenCall.typ = getSysType(c.g, x.info, tyInt)
lenCall.typ() = getSysType(c.g, x.info, tyInt)
result.add lenCall
proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
let lenCall = genBuiltin(c, mLengthStr, "len", y)
lenCall.typ = getSysType(c.g, x.info, tyInt)
lenCall.typ() = getSysType(c.g, x.info, tyInt)
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
result.add lenCall
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
lenCall.typ = getSysType(c.g, x.info, tyInt)
lenCall.typ() = getSysType(c.g, x.info, tyInt)
var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
op = instantiateGeneric(c, op, t, t)
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
@@ -573,7 +579,7 @@ proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
)
cond.typ = getSysType(c.g, c.info, tyBool)
cond.typ() = getSysType(c.g, c.info, tyBool)
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
@@ -714,7 +720,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if isFinal(elemType):
addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
alignOf.typ = getSysType(c.g, c.info, tyInt)
alignOf.typ() = getSysType(c.g, c.info, tyInt)
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf)
else:
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
@@ -724,7 +730,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if isCyclic:
if isFinal(elemType):
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
typInfo.typ = getSysType(c.g, c.info, tyPointer)
typInfo.typ() = getSysType(c.g, c.info, tyPointer)
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
else:
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
@@ -732,7 +738,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x)
else:
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
cond.typ = getSysType(c.g, x.info, tyBool)
cond.typ() = getSysType(c.g, x.info, tyBool)
case c.kind
of attachedSink:
@@ -759,7 +765,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if isCyclic:
if isFinal(elemType):
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
typInfo.typ = getSysType(c.g, c.info, tyPointer)
typInfo.typ() = getSysType(c.g, c.info, tyPointer)
body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y)
else:
# If the ref is polymorphic we have to account for this
@@ -780,7 +786,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
## Closures are really like refs except they always use a virtual destructor
## and we need to do the refcounting only on the ref field which we call 'xenv':
let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
xenv.typ = getSysType(c.g, c.info, tyPointer)
xenv.typ() = getSysType(c.g, c.info, tyPointer)
let isCyclic = c.g.config.selectedGC == gcOrc
let tmp =
@@ -796,7 +802,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if isCyclic: "nimDecRefIsLastCyclicDyn"
else: "nimDecRefIsLast"
let cond = callCodegenProc(c.g, decRefProc, c.info, tmp)
cond.typ = getSysType(c.g, x.info, tyBool)
cond.typ() = getSysType(c.g, x.info, tyBool)
case c.kind
of attachedSink:
@@ -808,7 +814,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add newAsgnStmt(x, y)
of attachedAsgn:
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
yenv.typ = getSysType(c.g, c.info, tyPointer)
yenv.typ() = getSysType(c.g, c.info, tyPointer)
if isCyclic:
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
body.add newAsgnStmt(x, y)
@@ -820,7 +826,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add newAsgnStmt(x, y)
of attachedDup:
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
yenv.typ = getSysType(c.g, c.info, tyPointer)
yenv.typ() = getSysType(c.g, c.info, tyPointer)
if isCyclic:
body.add newAsgnStmt(x, y)
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
@@ -872,7 +878,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if isFinal(elemType):
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
alignOf.typ = getSysType(c.g, c.info, tyInt)
alignOf.typ() = getSysType(c.g, c.info, tyInt)
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf)
else:
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
@@ -895,14 +901,14 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# a big problem is that we don't know the environment's type here, so we
# have to go through some indirection; we delegate this to the codegen:
let call = newNodeI(nkCall, c.info, 2)
call.typ = t
call.typ() = t
call[0] = newSymNode(createMagic(c.g, c.idgen, "deepCopy", mDeepCopy))
call[1] = y
body.add newAsgnStmt(x, call)
elif (optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
xx.typ = getSysType(c.g, c.info, tyPointer)
xx.typ() = getSysType(c.g, c.info, tyPointer)
case c.kind
of attachedSink:
# we 'nil' y out afterwards so we *need* to take over its reference
@@ -911,13 +917,13 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add newAsgnStmt(x, y)
of attachedAsgn:
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
yy.typ = getSysType(c.g, c.info, tyPointer)
yy.typ() = getSysType(c.g, c.info, tyPointer)
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
body.add newAsgnStmt(x, y)
of attachedDup:
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
yy.typ = getSysType(c.g, c.info, tyPointer)
yy.typ() = getSysType(c.g, c.info, tyPointer)
body.add newAsgnStmt(x, y)
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
of attachedDestructor:
@@ -932,7 +938,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
xx.typ = getSysType(c.g, c.info, tyPointer)
xx.typ() = getSysType(c.g, c.info, tyPointer)
var actions = newNodeI(nkStmtList, c.info)
#discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
@@ -1040,7 +1046,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
else:
discard "cannot copy openArray"
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
@@ -1146,8 +1152,8 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y)
xx.typ = getSysType(c.g, c.info, tyPointer)
yy.typ = xx.typ
xx.typ() = getSysType(c.g, c.info, tyPointer)
yy.typ() = xx.typ
body.add newAsgnStmt(xx, yy)
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
@@ -1270,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

@@ -32,12 +32,12 @@ proc interestingVar(s: PSym): bool {.inline.} =
proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode =
let field = addUniqueField(c.objType, s, c.cache, c.idgen)
var deref = newNodeI(nkHiddenDeref, info)
deref.typ = c.objType
deref.typ() = c.objType
deref.add(newSymNode(c.partialParam, info))
result = newNodeI(nkDotExpr, info)
result.add(deref)
result.add(newSymNode(field))
result.typ = field.typ
result.typ() = field.typ
proc liftLocals(n: PNode; i: int; c: var Ctx) =
let it = n[i]

View File

@@ -92,8 +92,9 @@ type
warnStmtListLambda = "StmtListLambda",
warnBareExcept = "BareExcept",
warnImplicitDefaultValue = "ImplicitDefaultValue",
warnGenericsIgnoredInjection = "GenericsIgnoredInjection",
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] = [
@@ -198,8 +198,9 @@ const
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
warnBareExcept: "$1",
warnImplicitDefaultValue: "$1",
warnGenericsIgnoredInjection: "$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
@@ -268,6 +268,7 @@ const
NotesVerbosity* = computeNotesVerbosity()
errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
errFloatToString* = "cannot convert '$1' to '$2'"
type
TFileInfo* = object

View File

@@ -12,7 +12,7 @@
import std/strutils
from std/sugar import dup
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages, modulegraphs
export packages
const
@@ -97,7 +97,7 @@ template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind)
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
hintName in ctx.config.notes and # ignore if name checks are not requested
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
sym.kind != skResult and # ignore `result`
sym.kind != skTemp and # ignore temporary variables created by the compiler
@@ -138,7 +138,7 @@ template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
## Check symbol uses match their definition's style.
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
hintName in ctx.config.notes and # ignore if name checks are not requested
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
sym.kind != skTemp and # ignore temporary variables created by the compiler
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
@@ -154,5 +154,5 @@ template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragma
## Note: This only applies to builtin pragmas, not user pragmas.
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
hintName in ctx.config.notes and # ignore if name checks are not requested
(sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)): # ignore foreign packages
checkPragmaUseImpl(ctx.config, info, w, pragmaName)

View File

@@ -68,6 +68,7 @@ when not declared(readLineFromStdin):
# fallback implementation:
proc readLineFromStdin(prompt: string, line: var string): bool =
stdout.write(prompt)
stdout.flushFile()
result = readLine(stdin, line)
if not result:
stdout.write("\n")

View File

@@ -50,7 +50,7 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
case x.kind
of nkIdent: id.add(x.ident.s)
of nkSym: id.add(x.sym.name.s)
of nkSymChoices:
of nkSymChoices, nkOpenSym:
if x[0].kind == nkSym:
id.add(x[0].sym.name.s)
else:
@@ -63,6 +63,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
result = n[0].sym.name
else:
handleError(n, origin)
of nkOpenSym:
result = considerQuotedIdent(c, n[0], origin)
else:
handleError(n, origin)
@@ -219,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)
@@ -229,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):
@@ -250,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
@@ -561,23 +564,33 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
var amb: bool = false
discard errorUseQualifier(c, info, s, amb)
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
proc ambiguousIdentifierMsg*(candidates: seq[PSym], prefix = "use one of", indent = 0): string =
result = ""
for i in 0 ..< indent:
result.add(' ')
result.add "ambiguous identifier: '" & candidates[0].name.s & "'"
var i = 0
for candidate in candidates:
if i == 0: err.add " -- $1 the following:\n" % prefix
else: err.add "\n"
err.add " " & candidate.owner.name.s & "." & candidate.name.s
err.add ": " & typeToString(candidate.typ)
if i == 0: result.add " -- $1 the following:\n" % prefix
else: result.add "\n"
for i in 0 ..< indent:
result.add(' ')
result.add " " & candidate.owner.name.s & "." & candidate.name.s
result.add ": " & typeToString(candidate.typ)
inc i
localError(c.config, info, errGenerated, err)
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates))
proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string =
var candidates = newSeq[PSym](choices.len)
let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate"
for i, n in choices:
candidates[i] = n.sym
errorUseQualifier(c, info, candidates, prefix)
result = ambiguousIdentifierMsg(candidates, prefix, indent)
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices))
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
var err: string
@@ -630,9 +643,10 @@ type
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind]): seq[PSym] =
result = searchInScopesFilterBy(c, ident, filter)
if result.len == 0:
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
includePureEnum = false): seq[PSym] =
result = selectFromScopesElseAll(c, ident, filter)
if skEnumField in filter and (result.len == 0 or includePureEnum):
result.add allPureEnumFields(c, ident)
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
@@ -665,24 +679,33 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
c.isAmbiguous = amb
of nkSym:
result = n.sym
of nkOpenSym:
result = qualifiedLookUp(c, n[0], flags)
of nkDotExpr:
result = nil
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
if m != nil and m.kind == skModule:
var ident: PIdent = nil
if n[1].kind == nkIdent:
ident = n[1].ident
elif n[1].kind == nkAccQuoted:
if n[1].kind == nkAccQuoted:
ident = considerQuotedIdent(c, n[1])
else:
# this includes sym and symchoice nodes, but since we are looking in
# a module, it shouldn't matter what was captured
ident = n[1].getPIdent
if ident != nil:
if m == c.module:
result = strTableGet(c.topLevelScope.symbols, ident)
var ti: TIdentIter = default(TIdentIter)
result = initIdentIter(ti, c.topLevelScope.symbols, ident)
if result != nil and nextIdentIter(ti, c.topLevelScope.symbols) != nil:
# another symbol exists with same name
c.isAmbiguous = true
else:
var amb: bool = false
if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
var amb: bool = false
result = errorUseQualifier(c, n.info, m, amb)
else:
result = someSym(c.graph, m, ident)
result = someSymAmb(c.graph, m, ident, amb)
if amb: c.isAmbiguous = true
if result == nil and checkUndeclared in flags:
result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
elif n[1].kind == nkSym:
@@ -701,6 +724,10 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
if result != nil and result.kind == skStub: loadStub(result)
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if n.kind == nkOpenSym:
# maybe the logic in semexprs should be mirrored here instead
# for now it only seems this is called for `pickSym` in `getTypeIdent`
return initOverloadIter(o, c, n[0])
o.importIdx = -1
o.marked = initIntSet()
case n.kind

View File

@@ -174,12 +174,12 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
# returns a[].field as a node
assert field.kind == skField
var deref = newNodeI(nkHiddenDeref, info)
deref.typ = a.typ.skipTypes(abstractInst)[0]
deref.typ() = a.typ.skipTypes(abstractInst)[0]
deref.add a
result = newNodeI(nkDotExpr, info)
result.add deref
result.add newSymNode(field)
result.typ = field.typ
result.typ() = field.typ
proc rawDirectAccess*(obj, field: PSym): PNode =
# returns a.field as a node
@@ -187,7 +187,7 @@ proc rawDirectAccess*(obj, field: PSym): PNode =
result = newNodeI(nkDotExpr, field.info)
result.add newSymNode(obj)
result.add newSymNode(field)
result.typ = field.typ
result.typ() = field.typ
proc lookupInRecord(n: PNode, id: ItemId): PSym =
result = nil
@@ -250,12 +250,12 @@ proc newDotExpr*(obj, b: PSym): PNode =
assert field != nil, b.name.s
result.add newSymNode(obj)
result.add newSymNode(field)
result.typ = field.typ
result.typ() = field.typ
proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
# returns a[].b as a node
var deref = newNodeI(nkHiddenDeref, info)
deref.typ = a.typ.skipTypes(abstractInst).elementType
deref.typ() = a.typ.skipTypes(abstractInst).elementType
var t = deref.typ.skipTypes(abstractInst)
var field: PSym
while true:
@@ -273,12 +273,12 @@ proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
result = newNodeI(nkDotExpr, info)
result.add deref
result.add newSymNode(field)
result.typ = field.typ
result.typ() = field.typ
proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
# returns a[].b as a node
var deref = newNodeI(nkHiddenDeref, info)
deref.typ = a.typ.skipTypes(abstractInst).elementType
deref.typ() = a.typ.skipTypes(abstractInst).elementType
var t = deref.typ.skipTypes(abstractInst)
var field: PSym
let bb = getIdent(cache, b)
@@ -297,7 +297,7 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): P
result = newNodeI(nkDotExpr, info)
result.add deref
result.add newSymNode(field)
result.typ = field.typ
result.typ() = field.typ
proc getFieldFromObj*(t: PType; v: PSym): PSym =
assert v.kind != skField
@@ -320,7 +320,7 @@ proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
result = newNodeI(nkAddr, n.info, 1)
result[0] = n
result.typ = newType(typeKind, idgen, n.typ.owner)
result.typ() = newType(typeKind, idgen, n.typ.owner)
result.typ.rawAddSon(n.typ)
proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
@@ -344,18 +344,18 @@ proc callCodegenProc*(g: ModuleGraph; name: string;
if optionalArgs != nil:
for i in 1..<optionalArgs.len-2:
result.add optionalArgs[i]
result.typ = sym.typ.returnType
result.typ() = sym.typ.returnType
proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
result = nkIntLit.newIntNode(value)
result.typ = getSysType(g, info, tyInt)
result.typ() = getSysType(g, info, tyInt)
proc genHigh*(g: ModuleGraph; n: PNode): PNode =
if skipTypes(n.typ, abstractVar).kind == tyArray:
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar))))
else:
result = newNodeI(nkCall, n.info, 2)
result.typ = getSysType(g, n.info, tyInt)
result.typ() = getSysType(g, n.info, tyInt)
result[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
result[1] = n
@@ -364,7 +364,7 @@ proc genLen*(g: ModuleGraph; n: PNode): PNode =
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1))
else:
result = newNodeI(nkCall, n.info, 2)
result.typ = getSysType(g, n.info, tyInt)
result.typ() = getSysType(g, n.info, tyInt)
result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
result[1] = n

View File

@@ -166,4 +166,4 @@ proc makeAddr*(n: PNode; idgen: IdGenerator): PNode =
result = n
else:
result = newTree(nkHiddenAddr, n)
result.typ = makePtrType(n.typ, idgen)
result.typ() = makePtrType(n.typ, idgen)

View File

@@ -11,7 +11,7 @@
## represents a complete Nim project. Single modules can either be kept in RAM
## or stored in a rod-file.
import std/[intsets, tables, hashes, strtabs, algorithm]
import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
import ../dist/checksums/src/checksums/md5
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
import ic / [packed_ast, ic]
@@ -88,6 +88,7 @@ type
suggestMode*: bool # whether we are in nimsuggest mode or not.
invalidTransitiveClosure: bool
interactive*: bool
withinSystem*: bool # in system.nim or a module imported by system.nim
inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
# first module that included it
importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
@@ -274,6 +275,25 @@ proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
else:
result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym =
let importHidden = optImportHidden in m.options
if isCachedModule(g, m):
result = nil
for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden):
if result == nil:
# set result to the first symbol
result = s
else:
# another symbol found
amb = true
break
else:
var ti: TIdentIter = default(TIdentIter)
result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
# another symbol exists with same name
amb = true
proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
result = someSym(g, g.systemModule, name)
@@ -309,7 +329,7 @@ proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
t.inst = result
assert result != nil
proc resolveAttachedOp(g: ModuleGraph; t: var LazySym): PSym =
proc resolveAttachedOp*(g: ModuleGraph; t: var LazySym): PSym =
result = t.sym
if result == nil:
result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
@@ -452,6 +472,49 @@ proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic):
proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym =
result = createMagic(g, g.idgen, name, m)
proc uniqueModuleName*(conf: ConfigRef; m: PSym): string =
## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
## so that it is useful as a C identifier snippet.
let fid = FileIndex(m.position)
let path = AbsoluteFile toFullPath(conf, fid)
var isLib = false
var rel = ""
if path.string.startsWith(conf.libpath.string):
isLib = true
rel = relativeTo(path, conf.libpath).string
else:
rel = relativeTo(path, conf.projectPath).string
if not isLib and not belongsToProjectPackage(conf, m):
# special handlings for nimble packages
when DirSep == '\\':
let rel2 = replace(rel, '\\', '/')
else:
let rel2 = rel
const pkgs2 = "pkgs2/"
var start = rel2.find(pkgs2)
if start >= 0:
start += pkgs2.len
start += skipUntil(rel2, {'/'}, start)
if start+1 < rel2.len:
rel = "pkg/" & rel2[start+1..<rel.len] # strips paths
let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
result = newStringOfCap(trunc)
for i in 0..<trunc:
let c = rel[i]
case c
of 'a'..'z', '0'..'9':
result.add c
of {os.DirSep, os.AltSep}:
result.add 'Z' # because it looks a bit like '/'
of '.':
result.add 'O' # a circle
else:
# We mangle upper letters too so that there cannot
# be clashes with our special meanings of 'Z' and 'O'
result.addInt ord(c)
proc registerModule*(g: ModuleGraph; m: PSym) =
assert m != nil
assert m.kind == skModule
@@ -463,7 +526,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
setLen(g.packed.pm, m.position + 1)
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position))))
uniqueName: rope(uniqueModuleName(g.config, m)))
initStrTables(g, m)
proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
@@ -615,7 +678,6 @@ proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
if m != nil:
g.suggestSymbols.del(fileIdx)
g.suggestErrors.del(fileIdx)
g.resetForBackend
incl m.flags, sfDirty
proc unmarkAllDirty*(g: ModuleGraph) =
@@ -678,8 +740,6 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
proc configComplete*(g: ModuleGraph) =
rememberStartupConfig(g.startupPackedConfig, g.config)
from std/strutils import repeat, `%`
proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
let conf = graph.config
let isNimscript = conf.isDefined("nimscript")

View File

@@ -25,7 +25,7 @@ template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
let packSym = getPackage(graph, fileIdx)
result.owner = packSym
setOwner(result, packSym)
result.position = int fileIdx
proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =

View File

@@ -629,7 +629,7 @@ proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "
message(conf, info, warnDeprecated, msg)
proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return
writeContext(conf, info)
liMessage(conf, info, errInternal, errMsg, doAbort, info2)
@@ -669,31 +669,6 @@ template listMsg(title, r) =
proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string =
## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
## so that it is useful as a C identifier snippet.
let path = AbsoluteFile toFullPath(conf, fid)
let rel =
if path.string.startsWith(conf.libpath.string):
relativeTo(path, conf.libpath).string
else:
relativeTo(path, conf.projectPath).string
let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
result = newStringOfCap(trunc)
for i in 0..<trunc:
let c = rel[i]
case c
of 'a'..'z':
result.add c
of {os.DirSep, os.AltSep}:
result.add 'Z' # because it looks a bit like '/'
of '.':
result.add 'O' # a circle
else:
# We mangle upper letters and digits too so that there cannot
# be clashes with our special meanings of 'Z' and 'O'
result.addInt ord(c)
proc genSuccessX*(conf: ConfigRef) =
let mem =
when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"

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

View File

@@ -919,7 +919,7 @@ proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
newSymNode(op, r.info),
l,
r)
result.typ = newType(tyBool, ctx.idgen, nil)
result.typ() = newType(tyBool, ctx.idgen, nil)
proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
var cache = newIdentCache()
@@ -929,7 +929,7 @@ proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
result = nkPrefix.newTree(
newSymNode(op, node.info),
node)
result.typ = newType(tyBool, ctx.idgen, nil)
result.typ() = newType(tyBool, ctx.idgen, nil)
proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
infix(ctx, l, r, mEqRef)

View File

@@ -4,12 +4,13 @@ hint[XDeclaredButNotUsed]:off
define:booting
define:nimcore
define:nimPreviewFloatRoundtrip
define:nimPreviewSlimSystem
define:nimPreviewCstringConversion
define:nimPreviewProcConversion
define:nimPreviewRangeDefault
define:nimPreviewNonVarDestructor
define:nimPreviewCheckedClose
define:nimPreviewAsmSemSymbol
threads:off
#import:"$projectpath/testability"

View File

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

View File

@@ -84,7 +84,7 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
elemType = settype[0]
first = firstOrd(conf, elemType).toInt64
result = newNodeI(nkCurly, info)
result.typ = settype
result.typ() = settype
result.info = info
e = 0
while e < s.len * ElemSize:
@@ -101,7 +101,7 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
result.add aa
else:
n = newNodeI(nkRange, info)
n.typ = elemType
n.typ() = elemType
n.add aa
let bb = newIntTypeNode(b + first, elemType)
bb.info = info

View File

@@ -204,6 +204,7 @@ type
nkModuleRef # for .rod file support: A (moduleId, itemId) pair
nkReplayAction # for .rod file support: A replay action
nkNilRodNode # for .rod file support: a 'nil' PNode
nkOpenSym # container for captured sym that can be overriden by local symbols
const
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,

View File

@@ -25,7 +25,7 @@ const
useEffectSystem* = true
useWriteTracking* = false
hasFFI* = defined(nimHasLibFFI)
copyrightYear* = "2024"
copyrightYear* = "2025"
nimEnableCovariance* = defined(nimEnableCovariance)
@@ -179,7 +179,6 @@ const
cmdCtags, cmdBuildindex}
type
NimVer* = tuple[major: int, minor: int, patch: int]
TStringSeq* = seq[string]
TGCMode* = enum # the selected GC
gcUnselected = "unselected"
@@ -226,8 +225,11 @@ type
strictDefs,
strictCaseObjects,
inferGenericTypes,
genericsOpenSym,
openSym, # remove nfDisabledOpenSym when this is default
# alternative to above:
genericsOpenSym
vtables
typeBoundOps
LegacyFeature* = enum
allowSemcheckedAstModification,
@@ -368,7 +370,6 @@ type
arguments*: string ## the arguments to be passed to the program that
## should be run
ideCmd*: IdeCmd
oldNewlines*: bool
cCompiler*: TSystemCC # the used compiler
modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs
cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline
@@ -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
@@ -395,8 +397,7 @@ type
outDir*: AbsoluteDir
jsonBuildFile*: AbsoluteFile
prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
nimStdlibVersion*: NimVer
dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef
dllOverrides*, moduleOverrides*, cfileSpecificOptions*: StringTableRef
projectName*: string # holds a name like 'nim'
projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
projectFull*: AbsoluteFile # projectPath/projectName
@@ -408,7 +409,6 @@ type
commandArgs*: seq[string] # any arguments after the main command
commandLine*: string
extraCmds*: seq[string] # for writeJsonBuildInstructions
keepComments*: bool # whether the parser needs to keep comments
implicitImports*: seq[string] # modules that are to be implicitly imported
implicitIncludes*: seq[string] # modules that are to be implicitly included
docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
@@ -449,16 +449,6 @@ type
clientProcessId*: int
proc parseNimVersion*(a: string): NimVer =
# could be moved somewhere reusable
result = default(NimVer)
if a.len > 0:
let b = a.split(".")
assert b.len == 3, a
template fn(i) = result[i] = b[i].parseInt # could be optimized if needed
fn(0)
fn(1)
fn(2)
proc assignIfDefault*[T](result: var T, val: T, def = default(T)) =
## if `result` was already assigned to a value (that wasn't `def`), this is a noop.
@@ -592,7 +582,6 @@ proc newConfigRef*(): ConfigRef =
command: "", # the main command (e.g. cc, check, scan, etc)
commandArgs: @[], # any arguments after the main command
commandLine: "",
keepComments: true, # whether the parser needs to keep comments
implicitImports: @[], # modules that are to be implicitly imported
implicitIncludes: @[], # modules that are to be implicitly included
docSeeSrcUrl: "",
@@ -610,6 +599,7 @@ proc newConfigRef*(): ConfigRef =
arguments: "",
suggestMaxResults: 10_000,
maxLoopIterationsVM: 10_000_000,
maxCallDepthVM: 2_000,
vmProfileData: newProfileData(),
spellSuggestMax: spellSuggestSecretSauce,
currentConfigDir: ""
@@ -633,12 +623,6 @@ proc newPartialConfigRef*(): ConfigRef =
proc cppDefine*(c: ConfigRef; define: string) =
c.cppDefines.incl define
proc getStdlibVersion*(conf: ConfigRef): NimVer =
if conf.nimStdlibVersion == (0,0,0):
let s = conf.symbols.getOrDefault("nimVersion", "")
conf.nimStdlibVersion = s.parseNimVersion
result = conf.nimStdlibVersion
proc isDefined*(conf: ConfigRef; symbol: string): bool =
if conf.symbols.hasKey(symbol):
result = true

View File

@@ -51,3 +51,11 @@ func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
## See Also:
## * `modulegraphs.belongsToStdlib`
conf.mainPackageId == sym.getPackageId
func belongsToProjectPackageMaybeNil*(conf: ConfigRef, sym: PSym): bool =
## Return whether the symbol belongs to the project's package.
## Returns `false` if `sym` is nil.
##
## See Also:
## * `modulegraphs.belongsToStdlib`
sym != nil and conf.mainPackageId == sym.getPackageId

View File

@@ -266,7 +266,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
{tyOpenArray, tyTuple, tyObject}:
result = isAssignable(owner, n[1])
elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct):
elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
# types that are equal modulo distinction preserve l-value:
result = isAssignable(owner, n[1])
of nkHiddenDeref:
@@ -283,8 +283,15 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isAssignable(owner, n[0])
of nkCallKinds:
# builtin slice keeps lvalue-ness:
if getMagic(n) in {mArrGet, mSlice}:
let m = getMagic(n)
if m == mSlice:
# builtin slice keeps l-value-ness
# except for pointers because slice dereferences
if n[1].typ.kind == tyPtr:
result = arLValue
else:
result = isAssignable(owner, n[1])
elif m == mArrGet:
result = isAssignable(owner, n[1])
elif n.typ != nil:
case n.typ.kind

View File

@@ -1401,7 +1401,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode =
result = primarySuffix(p, result, baseInd, mode)
proc binaryNot(p: var Parser; a: PNode): PNode =
if p.tok.tokType == tkNot:
if p.tok.tokType == tkNot and p.tok.indent < 0:
let notOpr = newIdentNodeP(p.tok.ident, p)
getTok(p)
optInd(p, notOpr)

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

@@ -47,9 +47,8 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
of NonePass:
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
) =
proc processImplicitImports*(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator) =
# XXX fixme this should actually be relative to the config file!
let relativeTo = toFullPath(graph.config, m.info)
for module in items(implicits):
@@ -64,7 +63,7 @@ proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind
if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
break
proc prePass(c: PContext; n: PNode) =
proc prePass*(c: PContext; n: PNode) =
for son in n:
if son.kind == nkPragma:
for s in son:

View File

@@ -156,16 +156,16 @@ proc pragmaEnsures(c: PContext, n: PNode) =
proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
# special cases to improve performance:
if extname == "$1":
s.loc.r = rope(s.name.s)
s.loc.snippet = rope(s.name.s)
elif '$' notin extname:
s.loc.r = rope(extname)
s.loc.snippet = rope(extname)
else:
try:
s.loc.r = rope(extname % s.name.s)
s.loc.snippet = rope(extname % s.name.s)
except ValueError:
localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
when hasFFI:
s.cname = $s.loc.r
s.cname = $s.loc.snippet
proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
@@ -370,7 +370,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
proc processNote(c: PContext, n: PNode) =
template handleNote(enumVals, notes) =
let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
if x != errUnknown:
if x != errUnknown:
nk = TNoteKind(x)
let x = c.semConstBoolExpr(c, n[1])
n[1] = x
@@ -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:
@@ -722,12 +725,12 @@ proc processPragma(c: PContext, n: PNode, i: int) =
proc pragmaRaisesOrTags(c: PContext, n: PNode) =
proc processExc(c: PContext, x: PNode) =
if c.hasUnresolvedArgs(c, x):
x.typ = makeTypeFromExpr(c, x)
x.typ() = makeTypeFromExpr(c, x)
else:
var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
if t.kind notin {tyObject, tyOr}:
localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
x.typ = t
x.typ() = t
if n.kind in nkPragmaCallKinds and n.len == 2:
let it = n[1]
@@ -800,13 +803,14 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode =
var callNode: PNode
if n.kind in {nkIdent, nkSym}:
case n.kind
of nkIdentKinds:
# pragma -> pragma()
callNode = newTree(nkCall, n)
elif n.kind == nkExprColonExpr:
of nkExprColonExpr:
# pragma: arg -> pragma(arg)
callNode = newTree(nkCall, n[0], n[1])
elif n.kind in nkPragmaCallKinds:
of nkPragmaCallKinds - {nkExprColonExpr}:
callNode = n
else:
invalidPragma(c, n)
@@ -1014,7 +1018,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
incl(sym.loc.flags, lfHeader)
incl(sym.loc.flags, lfNoDecl)
# implies nodecl, because otherwise header would not make sense
if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
of wNoSideEffect:
noVal(c, it)
if sym != nil:
@@ -1036,7 +1040,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
incl(sym.flags, sfGeneratedOp)
of wNosinks:
noVal(c, it)
incl(sym.flags, sfNosinks)
incl(sym.flags, sfWasForwarded)
of wDynlib:
processDynLib(c, it, sym)
of wCompilerProc, wCore:
@@ -1343,6 +1347,16 @@ proc mergePragmas(n, pragmas: PNode) =
else:
for p in pragmas: n[pragmasPos].add p
proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) =
if n[pragmasPos].kind == nkEmpty:
n[pragmasPos] = newNodeI(nkPragma, n.info)
for p in pragmas:
let prag = whichPragma(p)
if prag in validPragmas:
let copy = copyTree(p)
overwriteLineInfo copy, n.info
n[pragmasPos].add copy
proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
validPragmas: TSpecialWords) =
if sym != nil and sym.kind != skModule:
@@ -1356,7 +1370,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
internalError(c.config, info, "implicitPragmas")
inc i
popInfoContext(c.config)
if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o)
if sym.kind in routineKinds and sym.ast != nil:
mergeValidPragmas(sym.ast, o, validPragmas)
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
localError(c.config, info, ".dynlib requires .exportc")
@@ -1365,7 +1380,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
sfImportc in sym.flags and lib != nil:
incl(sym.loc.flags, lfDynamicLib)
addToLib(lib, sym)
if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
if n == nil: return false

View File

@@ -0,0 +1,54 @@
import pragmas, options, ast, trees, lineinfos, idents, wordrecg
import std/assertions
import renderer
proc processNote(config: ConfigRef, n: PNode) =
template handleNote(enumVals, notes) =
let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
assert x != errUnknown
assert n[1].kind == nkIntLit
nk = TNoteKind(x)
if n[1].intVal != 0: incl(notes, nk)
else: excl(notes, nk)
var nk: TNoteKind
case whichKeyword(n[0][0].ident)
of wHint: handleNote(hintMin .. hintMax, config.notes)
of wWarning: handleNote(warnMin .. warnMax, config.notes)
of wWarningAsError: handleNote(warnMin .. warnMax, config.warningAsErrors)
of wHintAsError: handleNote(hintMin .. hintMax, config.warningAsErrors)
else: discard
proc pushBackendOption(optionsStack: var seq[(TOptions, TNoteKinds)], options: TOptions, notes: TNoteKinds) =
optionsStack.add (options, notes)
proc popBackendOption(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
let entry = optionsStack[^1]
options = entry[0]
config.notes = entry[1]
optionsStack.setLen(optionsStack.len-1)
proc processPushBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions,
n: PNode, start: int) =
pushBackendOption(optionsStack, options, config.notes)
for i in start..<n.len:
let it = n[i]
if it.kind in nkPragmaCallKinds and it.len == 2:
if it[0].kind == nkBracketExpr and
it[0].len == 2 and
it[0][1].kind == nkIdent and it[0][0].kind == nkIdent:
processNote(config, it)
elif it[1].kind == nkIntLit:
let sw = whichPragma(it[0])
let opts = pragmaToOptions(sw)
if opts != {}:
if it[1].intVal != 0:
options.incl opts
else:
options.excl opts
template processPopBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
popBackendOption(config, optionsStack, options)

View File

@@ -442,6 +442,11 @@ proc atom(g: TSrcGen; n: PNode): string =
result = $n.floatVal & "\'f64"
else:
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
of nkFloat128Lit:
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
result = $n.floatVal & "\'f128"
else:
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128"
of nkNilLit: result = "nil"
of nkType:
if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
@@ -514,6 +519,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
of nkClosedSymChoice, nkOpenSymChoice:
if n.len > 0: result += lsub(g, n[0])
of nkOpenSym: result = lsub(g, n[0])
of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
of nkTupleClassTy: result = len("tuple")
of nkDotExpr: result = lsons(g, n) + 1
@@ -1013,7 +1019,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
proc skipHiddenNodes(n: PNode): PNode =
result = n
while result != nil:
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1:
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1:
result = result[1]
elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
result.len > 0:
@@ -1275,6 +1281,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
else:
gsub(g, n, 0)
of nkOpenSym: gsub(g, n, 0)
of nkPar, nkClosure:
put(g, tkParLe, "(")
gcomma(g, n, c)
@@ -1304,10 +1311,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkCustomLit, n[0].strVal)
gsub(g, n, 1)
else:
gsub(g, n, 0)
for i in 0..<n.len-1:
gsub(g, n, i)
put(g, tkDot, ".")
assert n.len == 2, $n.len
accentedName(g, n[1])
if n.len > 1:
accentedName(g, n[^1])
of nkBind:
putWithSpace(g, tkBind, "bind")
gsub(g, n, 0)

View File

@@ -322,6 +322,14 @@ proc intersects(s1, s2: IntSet): bool =
if s2.contains(a):
return true
proc hasPushOrPopPragma(n: DepN): bool =
# Checks if the tree node has some pragmas that do not
# play well with reordering, like the push/pop pragma
# no crossing for push/pop barrier
let a = n.pnode
result = a.kind == nkPragma and a[0].kind == nkIdent and
(a[0].ident.s == "push" or a[0].ident.s == "pop")
proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
# Build a dependency graph
result = newSeqOfCap[DepN](deps.len)
@@ -363,6 +371,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
for dep in deps[i][0]:
if dep in declares:
ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
elif hasPushOrPopPragma(nj):
# Every node that comes after a push/pop pragma must
# depend on it; vice versa
if j < i:
ni.kids.add nj
else:
nj.kids.add ni
else:
for d in declares:
if uses.contains(d):
@@ -403,18 +418,7 @@ proc getStrongComponents(g: var DepG): seq[seq[DepN]] =
if v.idx < 0:
strongConnect(v, idx, s, result)
proc hasForbiddenPragma(n: PNode): bool =
# Checks if the tree node has some pragmas that do not
# play well with reordering, like the push/pop pragma
result = false
for a in n:
if a.kind == nkPragma and a[0].kind == nkIdent and
a[0].ident.s == "push":
return true
proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
if n.hasForbiddenPragma:
return n
var includedFiles = initIntSet()
let mpath = toFullPath(graph.config, module.fileIdx)
let n = expandIncludes(graph, module, n, mpath,

View File

@@ -18,7 +18,7 @@ import
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
lowerings, plugins/active, lineinfos, int128,
isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs,
extccomp
extccomp, layeredtable
import vtables
import std/[strtabs, math, tables, intsets, strutils, packedsets]
@@ -89,6 +89,11 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
changeType(c, x, formal, check=true)
result = arg
result = skipHiddenSubConv(result, c.graph, c.idgen)
# mark inserted converter as used:
var a = result
if a.kind == nkHiddenDeref: a = a[0]
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
markUsed(c, a.info, a[0].sym)
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
@@ -97,13 +102,13 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
renderTree(arg, {renderNoComments}))
# error correction:
result = copyTree(arg)
result.typ = formal
result.typ() = formal
elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum:
# Pick the right 'sym' from the sym choice by looking at 'formal' type:
result = nil
for ch in arg:
if sameType(ch.typ, formal):
return getConstExpr(c.module, ch, c.idgen, c.graph)
return ch
typeMismatch(c.config, info, formal, arg.typ, arg)
else:
result = indexTypesMatch(c, formal, arg.typ, arg)
@@ -111,7 +116,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
typeMismatch(c.config, info, formal, arg.typ, arg)
# error correction:
result = copyTree(arg)
result.typ = formal
result.typ() = formal
else:
result = fitNodePostMatch(c, formal, result)
@@ -251,7 +256,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
# when there is a nested proc inside a template, semtmpl
# will assign a wrong owner during the first pass over the
# template; we must fix it here: see #909
result.owner = getCurrOwner(c)
setOwner(result, getCurrOwner(c))
else:
result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
if find(result.name.s, '`') >= 0:
@@ -465,7 +470,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
renderTree(result, {renderNoComments}))
result = newSymNode(errorSym(c, result))
else:
result.typ = makeTypeDesc(c, typ)
result.typ() = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info)
else:
if s.ast[genericParamsPos] != nil and retType.isMetaType:
@@ -473,7 +478,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
# e.g. template foo(T: typedesc): seq[T]
# We will instantiate the return type here, because
# we now know the supplied arguments
var paramTypes = initTypeMapping()
var paramTypes = initLayeredTypeMap()
for param, value in genericParamsInMacroCall(s, call):
var givenType = value.typ
# the sym nodes used for the supplied generic arguments for
@@ -481,7 +486,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
# in this case, get the type directly from the sym
if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
givenType = value.sym.typ
idTablePut(paramTypes, param.typ, givenType)
put(paramTypes, param.typ, givenType)
retType = generateTypeInstance(c, paramTypes,
macroResult.info, retType)
@@ -489,15 +494,29 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
if retType.kind == tyVoid:
result = semStmt(c, result, flags)
else:
result = semExpr(c, result, flags, expectedType)
result = semExpr(c, result, flags)
result = fitNode(c, retType, result, result.info)
#globalError(s.info, errInvalidParamKindX, typeToString(s.typ.returnType))
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"
errFloatToString = "cannot convert '$1' to '$2'"
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
@@ -522,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)
@@ -608,7 +629,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, ch
newNodeIT(nkType, recNode.info, asgnType)
)
asgnExpr.flags.incl nfSkipFieldChecking
asgnExpr.typ = recNode.typ
asgnExpr.typ() = recNode.typ
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
else:
raiseAssert "unreachable"
@@ -630,7 +651,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
if checkDefault: # don't add defaults when checking whether a case branch has default fields
return
defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
defaultValue.typ = discriminator.typ
defaultValue.typ() = discriminator.typ
selectedBranch = recNode.pickCaseBranchIndex defaultValue
defaultValue.flags.incl nfSkipFieldChecking
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
@@ -643,7 +664,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
elif recType.kind in {tyObject, tyArray, tyTuple}:
let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
if asgnExpr != nil:
asgnExpr.typ = recNode.typ
asgnExpr.typ() = recNode.typ
asgnExpr.flags.incl nfSkipFieldChecking
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
else:
@@ -651,41 +672,48 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
if aTypSkip.kind == tyObject:
case aTypSkip.kind
of tyObject:
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
if child.len > 0:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
asgnExpr.typ = aTyp
asgnExpr.typ() = aTyp
asgnExpr.sons.add child
result = semExpr(c, asgnExpr)
else:
result = nil
elif aTypSkip.kind == tyArray:
of tyArray:
let child = defaultNodeField(c, a, aTypSkip[1], checkDefault)
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
result.typ() = aTyp
else:
result = nil
elif aTypSkip.kind == tyTuple:
of tyTuple:
var hasDefault = false
if aTypSkip.n != nil:
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
if hasDefault and children.len > 0:
result = newNodeI(nkTupleConstr, a.info)
result.typ = aTyp
result.typ() = aTyp
result.sons.add children
result = semExpr(c, result)
else:
result = nil
else:
result = nil
of tyRange:
if c.graph.config.isDefined("nimPreviewRangeDefault"):
result = firstRange(c.config, aTypSkip)
else:
result = nil
else:
result = nil
@@ -722,6 +750,8 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
result.semOverloadedCall = semOverloadedCall
result.semInferredLambda = semInferredLambda
result.semGenerateInstance = generateInstance
result.instantiateOnlyProcType = instantiateOnlyProcType
result.fitDefaultNode = fitDefaultNode
result.semTypeNode = semTypeNode
result.instTypeBoundOp = sigmatch.instTypeBoundOp
result.hasUnresolvedArgs = hasUnresolvedArgs
@@ -842,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
@@ -246,7 +307,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
candidates.add(getProcHeader(c.config, err.sym, prefer))
candidates.addDeclaredLocMaybe(c.config, err.sym)
candidates.add("\n")
let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam}
let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches
var argList = n
if isGenericMismatch and n[0].kind == nkBracketExpr:
argList = n[0]
let nArg =
if err.firstMismatch.arg < argList.len:
argList[err.firstMismatch.arg]
else:
nil
let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
if n.len > 1:
if verboseTypeMismatch notin c.config.legacyFeatures:
@@ -269,6 +339,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
of kMissingParam:
candidates.add(" missing parameter: " & nameParam)
candidates.add "\n"
of kExtraGenericParam:
candidates.add(" extra generic param given")
candidates.add "\n"
of kMissingGenericParam:
candidates.add(" missing generic parameter: " & nameParam)
candidates.add "\n"
of kVarNeeded:
doAssert nArg != nil
doAssert err.firstMismatch.formal != nil
@@ -278,6 +354,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
candidates.add "\n"
of kTypeMismatch:
doAssert nArg != nil
if nArg.kind in nkSymChoices:
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
let wanted = err.firstMismatch.formal.typ
doAssert err.firstMismatch.formal != nil
doAssert wanted != nil
@@ -292,9 +370,39 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
effectProblem(wanted, got, candidates, c)
candidates.add "\n"
of kGenericParamTypeMismatch:
let pos = err.firstMismatch.arg
doAssert n[0].kind == nkBracketExpr and pos < n[0].len
let arg = n[0][pos]
doAssert arg != nil
var wanted = err.firstMismatch.formal.typ
if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints:
wanted = wanted.genericConstraint
let got = arg.typ.skipTypes({tyTypeDesc})
doAssert err.firstMismatch.formal != nil
doAssert wanted != nil
doAssert got != nil
candidates.add " generic parameter mismatch, expected "
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
candidates.add " but got '"
candidates.add renderTree(arg)
candidates.add "' of type: "
candidates.addTypeDeclVerboseMaybe(c.config, got)
if nArg.kind in nkSymChoices:
candidates.add "\n"
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
if got != nil and got.kind == tyProc and wanted.kind == tyProc:
# These are proc mismatches so,
# add the extra explict detail of the mismatch
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
if got != nil:
effectProblem(wanted, got, candidates, c)
candidates.add "\n"
of kUnknown: discard "do not break 'nim check'"
else:
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
if err.firstMismatch.kind in genericParamMismatches:
candidates.add(" in generic parameters")
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
case err.firstMismatch.kind
of kUnknownNamedParam:
@@ -306,9 +414,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param")
of kExtraArg: candidates.add("\n extra argument given")
of kMissingParam: candidates.add("\n missing parameter: " & nameParam)
of kTypeMismatch, kVarNeeded:
of kExtraGenericParam:
candidates.add("\n extra generic param given")
of kMissingGenericParam:
candidates.add("\n missing generic parameter: " & nameParam)
of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded:
doAssert nArg != nil
let wanted = err.firstMismatch.formal.typ
var wanted = err.firstMismatch.formal.typ
if isGenericMismatch and wanted.kind == tyGenericParam and
wanted.genericParamHasConstraints:
wanted = wanted.genericConstraint
doAssert err.firstMismatch.formal != nil
candidates.add("\n required type for " & nameParam & ": ")
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
@@ -319,8 +434,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
else:
candidates.add renderTree(nArg)
candidates.add "' is of type: "
let got = nArg.typ
var got = nArg.typ
if isGenericMismatch: got = got.skipTypes({tyTypeDesc})
candidates.addTypeDeclVerboseMaybe(c.config, got)
if nArg.kind in nkSymChoices:
candidates.add "\n"
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
doAssert wanted != nil
if got != nil:
if got.kind == tyProc and wanted.kind == tyProc:
@@ -331,8 +450,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
of kUnknown: discard "do not break 'nim check'"
candidates.add "\n"
if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and
n.kind == nkCommand:
if err.firstMismatch.arg == 1 and nArg != nil and
nArg.kind == nkTupleConstr and n.kind == nkCommand:
maybeWrongSpace = true
for diag in err.diagnostics:
candidates.add(diag & "\n")
@@ -409,23 +528,6 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
result.add("\n" & errExpectedPosition & "\n" & candidates)
localError(c.config, n.info, result)
proc bracketNotFoundError(c: PContext; n: PNode) =
var errors: CandidateErrors = @[]
var o: TOverloadIter = default(TOverloadIter)
let headSymbol = n[0]
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind in routineKinds:
errors.add(CandidateError(sym: symx,
firstMismatch: MismatchInfo(),
diagnostics: @[],
enabled: false))
symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0:
localError(c.config, n.info, "could not resolve: " & $n)
else:
notFoundError(c, n, errors)
proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
result = ""
if c.compilesContextId > 0:
@@ -518,7 +620,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
if overloadsState == csEmpty and result.state == csEmpty:
if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
result.state = csNoMatch
if efNoDiagnostics in flags:
if c.inGenericContext > 0 and nfExprCall in n.flags:
# untyped expression calls end up here, see #24099
return
# xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
@@ -554,6 +657,39 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
getProcHeader(c.config, alt.calleeSym),
args])
proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
var errors: CandidateErrors = @[]
let headSymbol = n[0]
block:
# we build a closed symchoice of all `[]` overloads for their errors,
# except add a custom error for the magics which always match
var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c))
var o: TOverloadIter = default(TOverloadIter)
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind in routineKinds:
if symx.magic in {mArrGet, mArrPut}:
errors.add(CandidateError(sym: symx,
firstMismatch: MismatchInfo(),
diagnostics: @[],
enabled: false))
else:
choice.add newSymNode(symx, headSymbol.info)
symx = nextOverloadIter(o, c, headSymbol)
n[0] = choice
# copied from semOverloadedCallAnalyzeEffects, might be overkill:
const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}
let filter =
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
baseFilter + {skIterator}
else: baseFilter
# this will add the errors:
var r = resolveOverloads(c, n, n, filter, flags, errors, true)
if errors.len == 0:
localError(c.config, n.info, "could not resolve: " & $n)
else:
notFoundError(c, n, errors)
proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
let a = if a.kind == nkHiddenDeref: a[0] else: a
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
@@ -561,7 +697,7 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
if s.isGenericRoutineStrict:
let finalCallee = generateInstance(c, s, x.bindings, a.info)
a[0].sym = finalCallee
a[0].typ = finalCallee.typ
a[0].typ() = finalCallee.typ
#a.typ = finalCallee.typ.returnType
proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
@@ -570,6 +706,15 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
for i in 1..<n.len:
instGenericConvertersArg(c, n[i], x)
proc markConvertersUsed*(c: PContext, n: PNode) =
assert n.kind in nkCallKinds
for i in 1..<n.len:
var a = n[i]
if a == nil: continue
if a.kind == nkHiddenDeref: a = a[0]
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
markUsed(c, a.info, a[0].sym)
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
var m = newCandidate(c, f)
result = paramTypesMatch(m, f, a, arg, nil)
@@ -587,15 +732,15 @@ proc inferWithMetatype(c: PContext, formal: PType,
# This almost exactly replicates the steps taken by the compiler during
# param matching. It performs an embarrassing amount of back-and-forth
# type jugling, but it's the price to pay for consistency and correctness
result.typ = generateTypeInstance(c, m.bindings, arg.info,
result.typ() = generateTypeInstance(c, m.bindings, arg.info,
formal.skipTypes({tyCompositeTypeClass}))
else:
typeMismatch(c.config, arg.info, formal, arg.typ, arg)
# error correction:
result = copyTree(arg)
result.typ = formal
result.typ() = formal
proc updateDefaultParams(call: PNode) =
proc updateDefaultParams(c: PContext, call: PNode) =
# In generic procs, the default parameter may be unique for each
# instantiation (see tlateboundgenericparams).
# After a call is resolved, we need to re-assign any default value
@@ -605,8 +750,18 @@ proc updateDefaultParams(call: PNode) =
let calleeParams = call[0].sym.typ.n
for i in 1..<call.len:
if nfDefaultParam in call[i].flags:
let def = calleeParams[i].sym.ast
let formal = calleeParams[i].sym
let def = formal.ast
if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
# mirrored with sigmatch:
if def.kind == nkEmpty:
# The default param value is set to empty in `instantiateProcType`
# when the type of the default expression doesn't match the type
# of the instantiated proc param:
pushInfoContext(c.config, call.info, call[0].sym.detailedInfo)
typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast)
popInfoContext(c.config)
def.typ() = errorType(c)
call[i] = def
proc getCallLineInfo(n: PNode): TLineInfo =
@@ -663,7 +818,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
if t[i] == nil or u[i] == nil: return
stackPut(t[i], u[i])
of tyGenericParam:
let prebound = x.bindings.idTableGet(t)
let prebound = x.bindings.lookup(t)
if prebound != nil:
continue # Skip param, already bound
@@ -675,7 +830,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
discard
# update bindings
for i in 0 ..< flatUnbound.len():
x.bindings.idTablePut(flatUnbound[i], flatBound[i])
x.bindings.put(flatUnbound[i], flatBound[i])
proc semResolvedCall(c: PContext, x: var TCandidate,
n: PNode, flags: TExprFlags;
@@ -686,12 +841,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
markUsed(c, info, finalCallee)
onUse(info, finalCallee)
assert finalCallee.ast != nil
if x.hasFauxMatch:
if x.matchedErrorType:
result = x.call
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
result.typ = newTypeS(x.fauxMatch, c)
if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor
if containsGenericType(result.typ):
result.typ() = newTypeS(tyError, c)
incl result.typ.flags, tfCheckedForDestructor
return
let gp = finalCallee.ast[genericParamsPos]
if gp.isGenericParams:
@@ -717,16 +872,18 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
# this node will be used in template substitution,
# pretend this is an untyped node and let regular sem handle the type
# to prevent problems where a generic parameter is treated as a value
tn.typ = nil
tn.typ() = nil
x.call.add tn
else:
internalAssert c.config, false
result = x.call
instGenericConvertersSons(c, result, x)
markConvertersUsed(c, result)
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
result.typ = finalCallee.typ.returnType
updateDefaultParams(result)
if finalCallee.magic notin {mArrGet, mArrPut}:
result.typ() = finalCallee.typ.returnType
updateDefaultParams(c, result)
proc canDeref(n: PNode): bool {.inline.} =
result = n.len >= 2 and (let t = n[1].typ;
@@ -734,7 +891,7 @@ proc canDeref(n: PNode): bool {.inline.} =
proc tryDeref(n: PNode): PNode =
result = newNodeI(nkHiddenDeref, n.info)
result.typ = n.typ.skipTypes(abstractInst)[0]
result.typ() = n.typ.skipTypes(abstractInst)[0]
result.add n
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
@@ -751,9 +908,9 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
candidates)
result = semResolvedCall(c, r, n, flags, expectedType)
else:
if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil:
if c.inGenericContext > 0 and c.matchedConcept == nil:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, result.copyTree)
result.typ() = makeTypeFromExpr(c, result.copyTree)
elif efExplain notin flags:
# repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again)
@@ -768,22 +925,22 @@ proc explicitGenericInstError(c: PContext; n: PNode): PNode =
localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n))
result = n
proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
proc explicitGenericSym(c: PContext, n: PNode, s: PSym, errors: var CandidateErrors, doError: bool): PNode =
if s.kind in {skTemplate, skMacro}:
internalError c.config, n.info, "cannot get explicitly instantiated symbol of " &
(if s.kind == skTemplate: "template" else: "macro")
# binding has to stay 'nil' for this to work!
var m = newCandidate(c, s, nil)
for i in 1..<n.len:
let formal = s.ast[genericParamsPos][i-1].typ
var arg = n[i].typ
# try transforming the argument into a static one before feeding it into
# typeRel
if formal.kind == tyStatic and arg.kind != tyStatic:
let evaluated = c.semTryConstExpr(c, n[i], n[i].typ)
if evaluated != nil:
arg = newTypeS(tyStatic, c, son = evaluated.typ)
arg.n = evaluated
let tm = typeRel(m, formal, arg)
if tm in {isNone, isConvertible}: return nil
matchGenericParams(m, n, s)
if m.state != csMatch:
# state is csMatch only if *all* generic params were matched,
# including implicit parameters
if doError:
errors.add(CandidateError(
sym: s,
firstMismatch: m.firstMismatch,
diagnostics: m.diagnostics))
return nil
var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved
let info = getCallLineInfo(n)
@@ -802,46 +959,57 @@ proc setGenericParams(c: PContext, n, expectedParams: PNode) =
nil
e = semExprWithType(c, n[i], expectedType = constraint)
if e.typ == nil:
n[i].typ = errorType(c)
n[i].typ() = errorType(c)
else:
n[i].typ = e.typ.skipTypes({tyTypeDesc})
n[i].typ() = e.typ.skipTypes({tyTypeDesc})
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool): PNode =
assert n.kind == nkBracketExpr
setGenericParams(c, n, s.ast[genericParamsPos])
var s = s
var a = n[0]
var errors: CandidateErrors = @[]
if a.kind == nkSym:
# common case; check the only candidate has the right
# number of generic type parameters:
if s.ast[genericParamsPos].safeLen != n.len-1:
let expected = s.ast[genericParamsPos].safeLen
localError(c.config, getCallLineInfo(n), errGenerated, "cannot instantiate: '" & renderTree(n) &
"'; got " & $(n.len-1) & " typeof(s) but expected " & $expected)
return n
result = explicitGenericSym(c, n, s)
if result == nil: result = explicitGenericInstError(c, n)
result = explicitGenericSym(c, n, s, errors, doError)
if result == nil:
if c.inGenericContext > 0:
# same as in semOverloadedCall, make expression untyped,
# may have failed match due to unresolved types
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
elif doError:
notFoundError(c, n, errors)
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
# choose the generic proc with the proper number of type parameters.
# XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
# It's good enough for now.
result = newNodeI(a.kind, getCallLineInfo(n))
for i in 0..<a.len:
var candidate = a[i].sym
if candidate.kind in {skProc, skMethod, skConverter,
skFunc, skIterator}:
# it suffices that the candidate has the proper number of generic
# type parameters:
if candidate.ast[genericParamsPos].safeLen == n.len-1:
let x = explicitGenericSym(c, n, candidate)
if x != nil: result.add(x)
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 == 1 and a.kind == nkClosedSymChoice:
result = result[0]
elif result.len == 0: result = explicitGenericInstError(c, n)
# candidateCount != 1: return explicitGenericInstError(c, n)
if result.len == 0:
result = nil
if doError:
notFoundError(c, n, errors)
else:
result = explicitGenericInstError(c, n)
# probably unreachable: we are trying to instantiate `a` which is not
# a sym/symchoice
if doError:
result = explicitGenericInstError(c, n)
else:
result = nil
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] =
# Searches for the fn in the symbol table. If the parameter lists are suitable

View File

@@ -15,8 +15,8 @@ when defined(nimPreviewSlimSystem):
import std/assertions
import
options, ast, astalgo, msgs, idents, renderer,
magicsys, vmdef, modulegraphs, lineinfos, pathutils
options, ast, msgs, idents, renderer,
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable
import ic / ic
@@ -73,9 +73,9 @@ type
efNoUndeclared, efIsDotCall, efCannotBeDotCall,
# Use this if undeclared identifiers should not raise an error during
# overload resolution.
efNoDiagnostics,
efTypeAllowed # typeAllowed will be called after
efWantNoDefaults
efIgnoreDefaults # var statements without initialization
efAllowSymChoice # symchoice node should not be resolved
TExprFlags* = set[TExprFlag]
@@ -136,9 +136,13 @@ type
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.}
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
semInferredLambda*: proc(c: PContext, pt: LayeredIdTable, n: PNode): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: LayeredIdTable,
info: TLineInfo): PSym
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
@@ -168,6 +172,7 @@ type
inUncheckedAssignSection*: int
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
inTypeofContext*: int
TBorrowState* = enum
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
@@ -191,29 +196,29 @@ proc getIntLitType*(c: PContext; literal: PNode): PType =
proc setIntLitType*(c: PContext; result: PNode) =
let i = result.intVal
case c.config.target.intSize
of 8: result.typ = getIntLitType(c, result)
of 8: result.typ() = getIntLitType(c, result)
of 4:
if i >= low(int32) and i <= high(int32):
result.typ = getIntLitType(c, result)
result.typ() = getIntLitType(c, result)
else:
result.typ = getSysType(c.graph, result.info, tyInt64)
result.typ() = getSysType(c.graph, result.info, tyInt64)
of 2:
if i >= low(int16) and i <= high(int16):
result.typ = getIntLitType(c, result)
result.typ() = getIntLitType(c, result)
elif i >= low(int32) and i <= high(int32):
result.typ = getSysType(c.graph, result.info, tyInt32)
result.typ() = getSysType(c.graph, result.info, tyInt32)
else:
result.typ = getSysType(c.graph, result.info, tyInt64)
result.typ() = getSysType(c.graph, result.info, tyInt64)
of 1:
# 8 bit CPUs are insane ...
if i >= low(int8) and i <= high(int8):
result.typ = getIntLitType(c, result)
result.typ() = getIntLitType(c, result)
elif i >= low(int16) and i <= high(int16):
result.typ = getSysType(c.graph, result.info, tyInt16)
result.typ() = getSysType(c.graph, result.info, tyInt16)
elif i >= low(int32) and i <= high(int32):
result.typ = getSysType(c.graph, result.info, tyInt32)
result.typ() = getSysType(c.graph, result.info, tyInt32)
else:
result.typ = getSysType(c.graph, result.info, tyInt64)
result.typ() = getSysType(c.graph, result.info, tyInt64)
else:
internalError(c.config, result.info, "invalid int size")
@@ -445,7 +450,7 @@ when false:
proc makeStaticExpr*(c: PContext, n: PNode): PNode =
result = newNodeI(nkStaticExpr, n.info)
result.sons = @[n]
result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ
result.typ() = if n.typ != nil and n.typ.kind == tyStatic: n.typ
else: newTypeS(tyStatic, c, n.typ)
proc makeAndType*(c: PContext, t1, t2: PType): PType =
@@ -504,7 +509,7 @@ proc errorType*(c: PContext): PType =
proc errorNode*(c: PContext, n: PNode): PNode =
result = newNodeI(nkEmpty, n.info)
result.typ = errorType(c)
result.typ() = errorType(c)
# These mimic localError
template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, msg: TMsgKind, arg: string): PNode =
@@ -525,10 +530,11 @@ template localErrorNode*(c: PContext, n: PNode, arg: string): PNode =
liMessage(c.config, n2.info, errGenerated, arg, doNothing, instLoc())
errorNode(c, n2)
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
dest.kind = kind
dest.owner = getCurrOwner(c)
dest.size = - 1
when false:
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
dest.kind = kind
dest.owner = getCurrOwner(c)
dest.size = - 1
proc makeRangeType*(c: PContext; first, last: BiggestInt;
info: TLineInfo; intType: PType = nil): PType =
@@ -559,7 +565,7 @@ proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym =
proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode =
result = newSymNode(symFromType(c, t, info), info)
result.typ = makeTypeDesc(c, t)
result.typ() = makeTypeDesc(c, t)
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,15 @@ type
replaceByFieldName: bool
c: PContext
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)
@@ -72,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)
@@ -148,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

@@ -38,7 +38,7 @@ proc newIntNodeT*(intVal: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph):
# original type was 'int', not a distinct int etc.
if n.typ.kind == tyInt:
# access cache for the int lit type
result.typ = getIntLitTypeG(g, result, idgen)
result.typ() = getIntLitTypeG(g, result, idgen)
result.info = n.info
proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
@@ -46,12 +46,12 @@ proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
result = newFloatNode(nkFloat32Lit, floatVal)
else:
result = newFloatNode(nkFloatLit, floatVal)
result.typ = n.typ
result.typ() = n.typ
result.info = n.info
proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
result = newStrNode(nkStrLit, strVal)
result.typ = n.typ
result.typ() = n.typ
result.info = n.info
proc getConstExpr*(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
@@ -302,6 +302,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
of mMinusSet:
result = nimsets.diffSets(g.config, a, b)
result.info = n.info
of mXorSet:
result = nimsets.symdiffSets(g.config, a, b)
result.info = n.info
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
of mInSet: result = newIntNodeT(toInt128(ord(inSet(a, b))), n, idgen, g)
of mRepr:
@@ -316,7 +319,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g)
of mArrToSeq:
result = copyTree(a)
result.typ = n.typ
result.typ() = n.typ
of mCompileOption:
result = newIntNodeT(toInt128(ord(commands.testCompileOption(g.config, a.getStr, n.info))), n, idgen, g)
of mCompileOptionArg:
@@ -411,7 +414,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
result = newIntNodeT(toInt128(a.getOrdValue != 0), n, idgen, g)
of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`?
result = a
result.typ = n.typ
result.typ() = n.typ
else:
raiseAssert $srcTyp.kind
of tyInt..tyInt64, tyUInt..tyUInt64:
@@ -420,14 +423,17 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g)
of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
var val = a.getOrdValue
if check: rangeCheck(n, val, g)
result = newIntNodeT(val, n, idgen, g)
if dstTyp.kind in {tyUInt..tyUInt64}:
result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g)
result.transitionIntKind(nkUIntLit)
else:
if check: rangeCheck(n, val, g)
result = newIntNodeT(val, n, idgen, g)
else:
result = a
result.typ = n.typ
if check and result.kind in {nkCharLit..nkUInt64Lit}:
result.typ() = n.typ
if check and result.kind in {nkCharLit..nkUInt64Lit} and
dstTyp.kind notin {tyUInt..tyUInt64}:
rangeCheck(n, getInt(result), g)
of tyFloat..tyFloat64:
case srcTyp.kind
@@ -435,12 +441,12 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
result = newFloatNodeT(toFloat64(getOrdValue(a)), n, g)
else:
result = a
result.typ = n.typ
result.typ() = n.typ
of tyOpenArray, tyVarargs, tyProc, tyPointer:
result = nil
else:
result = a
result.typ = n.typ
result.typ() = n.typ
proc getArrayConstr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
if n.kind == nkBracket:
@@ -511,10 +517,10 @@ proc foldConStrStr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode =
result = newSymNode(s, info)
if s.typ.kind != tyTypeDesc:
result.typ = newType(tyTypeDesc, idgen, s.owner)
result.typ() = newType(tyTypeDesc, idgen, s.owner)
result.typ.addSonSkipIntLit(s.typ, idgen)
else:
result.typ = s.typ
result.typ() = s.typ
proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
result = nil
@@ -583,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" %
@@ -633,7 +639,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
if s.typ.kind == tyStatic:
if s.typ.n != nil and tfUnresolved notin s.typ.flags:
result = s.typ.n
result.typ = s.typ.base
result.typ() = s.typ.base
elif s.typ.isIntLit:
result = s.typ.n
else:
@@ -746,7 +752,9 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
if a == nil: return
if leValueConv(n[1], a) and leValueConv(a, n[2]):
result = a # a <= x and x <= b
result.typ = n.typ
result.typ() = n.typ
elif n.typ.kind in {tyUInt..tyUInt64}:
discard "don't check uints"
else:
localError(g.config, n.info,
"conversion from $1 to $2 is invalid" %
@@ -755,7 +763,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
var a = getConstExpr(m, n[0], idgen, g)
if a == nil: return
result = a
result.typ = n.typ
result.typ() = n.typ
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var a = getConstExpr(m, n[1], idgen, g)
if a == nil: return
@@ -772,7 +780,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
not (n.typ.kind == tyProc and a.typ.kind == tyProc):
# we allow compile-time 'cast' for pointer types:
result = a
result.typ = n.typ
result.typ() = n.typ
of nkBracketExpr: result = foldArrayAccess(m, n, idgen, g)
of nkDotExpr: result = foldFieldAccess(m, n, idgen, g)
of nkCheckedFieldExpr:

View File

@@ -61,6 +61,7 @@ template canOpenSym(s): bool =
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
ctx: var GenericCtx; flags: TSemGenericFlags,
isAmbiguous: bool,
fromDotExpr=false): PNode =
result = nil
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
@@ -72,9 +73,15 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
result.transitionSonsKind(nkClosedSymChoice)
else:
result = symChoice(c, n, s, scOpen)
if result.kind == nkSym and canOpenSym(result.sym):
result.flags.incl nfOpenSym
result.typ = nil
if canOpenSym(s):
if openSym in c.features:
if result.kind == nkSym:
result = newOpenSym(result)
else:
result.typ() = nil
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
case s.kind
of skUnknown:
# Introduced in this pass! Leave it as an identifier.
@@ -98,13 +105,28 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
if s.typ != nil and s.typ.kind == tyStatic:
if s.typ.n != nil:
result = s.typ.n
elif c.inGenericContext > 0 and withinConcept notin flags:
# don't leave generic param as identifier node in generic type,
# sigmatch will try to instantiate generic type AST without all params
# fine to give a symbol node a generic type here since
# we are in a generic context and `prepareNode` will be called
result = newSymNodeTypeDesc(s, c.idgen, n.info)
if canOpenSym(result.sym):
if openSym in c.features:
result = newOpenSym(result)
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
else:
result = n
else:
result = newSymNodeTypeDesc(s, c.idgen, n.info)
if canOpenSym(result.sym):
result.flags.incl nfOpenSym
result.typ = nil
if openSym in c.features:
result = newOpenSym(result)
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
onUse(n.info, s)
of skParam:
result = n
@@ -112,18 +134,41 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
of skType:
if (s.typ != nil) and
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
if isAmbiguous:
# ambiguous types should be symchoices since lookup behaves
# differently for them in regular expressions
maybeDotChoice(c, n, s, fromDotExpr)
return
result = newSymNodeTypeDesc(s, c.idgen, n.info)
if canOpenSym(result.sym):
result.flags.incl nfOpenSym
result.typ = nil
if openSym in c.features:
result = newOpenSym(result)
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
elif c.inGenericContext > 0 and withinConcept notin flags:
# don't leave generic param as identifier node in generic type,
# sigmatch will try to instantiate generic type AST without all params
# fine to give a symbol node a generic type here since
# we are in a generic context and `prepareNode` will be called
result = newSymNodeTypeDesc(s, c.idgen, n.info)
if canOpenSym(result.sym):
if openSym in c.features:
result = newOpenSym(result)
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
else:
result = n
onUse(n.info, s)
else:
result = newSymNode(s, n.info)
if canOpenSym(result.sym):
result.flags.incl nfOpenSym
result.typ = nil
if openSym in c.features:
result = newOpenSym(result)
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
onUse(n.info, s)
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
@@ -145,7 +190,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
elif s.isMixedIn:
result = symChoice(c, n, s, scForceOpen)
else:
result = semGenericStmtSymbol(c, n, s, ctx, flags)
result = semGenericStmtSymbol(c, n, s, ctx, flags, amb)
# else: leave as nkIdent
proc newDot(n, b: PNode): PNode =
@@ -161,10 +206,11 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule}
c.isAmbiguous = false
var s = qualifiedLookUp(c, n, luf)
if s != nil:
isMacro = s.kind in {skTemplate, skMacro}
result = semGenericStmtSymbol(c, n, s, ctx, flags)
result = semGenericStmtSymbol(c, n, s, ctx, flags, c.isAmbiguous)
else:
n[0] = semGenericStmt(c, n[0], flags, ctx)
result = n
@@ -172,30 +218,27 @@ 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}
if withinBind in flags or s.id in ctx.toBind:
if s.kind == skType: # don't put types in sym choice
result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true))
var ambig = false
if candidates.len > 1:
let s2 = searchInScopes(c, ident, ambig)
result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags,
isAmbiguous = ambig, fromDotExpr = true))
else:
result = newDot(result, symChoice(c, n, s, scClosed))
elif s.isMixedIn:
result = newDot(result, symChoice(c, n, s, scForceOpen))
else:
var ambig = false
if s.kind == skType and candidates.len > 1:
var ambig = false
let s2 = searchInScopes(c, ident, ambig)
if ambig:
# this is a type conversion like a.T where T is ambiguous with
# other types or routines
# in regular code, this never considers a type conversion and
# skips to routine overloading
# so symchoices are used which behave similarly with type symbols
result = newDot(result, symChoice(c, n, s, scForceOpen))
return
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
discard searchInScopes(c, ident, ambig)
let syms = semGenericStmtSymbol(c, n, s, ctx, flags,
isAmbiguous = ambig, fromDotExpr = true)
result = newDot(result, syms)
proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
@@ -262,7 +305,9 @@ proc semGenericStmt(c: PContext, n: PNode,
# check if it is an expression macro:
checkMinSonsLen(n, 1, c.config)
let fn = n[0]
c.isAmbiguous = false
var s = qualifiedLookUp(c, fn, {})
let ambig = c.isAmbiguous
if s == nil and
{withinMixin, withinConcept}*flags == {} and
fn.kind in {nkIdent, nkAccQuoted} and
@@ -315,7 +360,12 @@ proc semGenericStmt(c: PContext, n: PNode,
of skType:
# bad hack for generics:
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
if ambig:
# ambiguous types should be symchoices since lookup behaves
# differently for them in regular expressions
result[0] = sc
else:
result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
onUse(fn.info, s)
first = 1
else:
@@ -563,9 +613,27 @@ proc semGenericStmt(c: PContext, n: PNode,
else:
body = getBody(c.graph, s)
else: body = n[bodyPos]
n[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
let bodyFlags = if n.kind == nkTemplateDef: flags + {withinMixin} else: flags
n[bodyPos] = semGenericStmtScope(c, body, bodyFlags, ctx)
closeScope(c)
of nkPragma, nkPragmaExpr: discard
of nkPragmaExpr:
result[1] = semGenericStmt(c, n[1], flags, ctx)
of nkPragma:
for i in 0 ..< n.len:
let x = n[i]
let prag = whichPragma(x)
if x.kind in nkPragmaCallKinds:
# process each child individually to prevent untyped macros/templates
# from instantiating
# if pragma is language-level pragma, skip name node:
let start = ord(prag != wInvalid)
for j in start ..< x.len:
# treat as mixin context for user pragmas & macro args
x[j] = semGenericStmt(c, x[j], flags+{withinMixin}, ctx)
elif prag == wInvalid:
# only sem if not a language-level pragma
# treat as mixin context for user pragmas & macro args
result[i] = semGenericStmt(c, x, flags+{withinMixin}, ctx)
of nkExprColonExpr, nkExprEqExpr:
checkMinSonsLen(n, 2, c.config)
result[1] = semGenericStmt(c, n[1], flags, ctx)

View File

@@ -34,7 +34,7 @@ proc pushProcCon*(c: PContext; owner: PSym) =
const
errCannotInstantiateX = "cannot instantiate: '$1'"
iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PSym =
iterator instantiateGenericParamList(c: PContext, n: PNode, pt: LayeredIdTable): PSym =
internalAssert c.config, n.kind == nkGenericParams
for a in n.items:
internalAssert c.config, a.kind == nkSym
@@ -43,7 +43,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PS
let symKind = if q.typ.kind == tyStatic: skConst else: skType
var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info)
s.flags.incl {sfUsed, sfFromGeneric}
var t = idTableGet(pt, q.typ)
var t = lookup(pt, q.typ)
if t == nil:
if tfRetType in q.typ.flags:
# keep the generic type and allow the return type to be bound
@@ -53,7 +53,9 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PS
if q.typ.kind != tyCompositeTypeClass:
localError(c.config, a.info, errCannotInstantiateX % s.name.s)
t = errorType(c)
elif t.kind in {tyGenericParam, tyConcept}:
elif t.kind in {tyGenericParam, tyConcept, tyFromExpr} or
# generic body types are accepted as typedesc arguments
(t.kind == tyGenericBody and q.typ.kind != tyTypeDesc):
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
t = errorType(c)
elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or
@@ -109,7 +111,7 @@ proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var SymMappi
elif s.owner == nil or s.owner.kind == skPackage:
#echo "copied this ", s.name.s
x = copySym(s, c.idgen)
x.owner = owner
setOwner(x, owner)
idTablePut(symMap, s, x)
n.sym = x
else:
@@ -220,7 +222,7 @@ proc referencesAnotherParam(n: PNode, p: PSym): bool =
if referencesAnotherParam(n[i], p): return true
return false
proc instantiateProcType(c: PContext, pt: TypeMapping,
proc instantiateProcType(c: PContext, pt: LayeredIdTable,
prc: PSym, info: TLineInfo) =
# XXX: Instantiates a generic proc signature, while at the same
# time adding the instantiated proc params into the current scope.
@@ -237,7 +239,7 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
# will need to use openScope, addDecl, etc.
#addDecl(c, prc)
pushInfoContext(c.config, info)
var typeMap = initLayeredTypeMap(pt)
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
var cl = initTypeVars(c, typeMap, info, nil)
var result = instCopyType(cl, prc.typ)
let originalParams = result.n
@@ -253,9 +255,15 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
var typeToFit = resulti
let needsStaticSkipping = resulti.kind == tyFromExpr
let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags
if resulti.kind == tyFromExpr:
resulti.flags.incl tfNonConstExpr
result[i] = replaceTypeVarsT(cl, resulti)
if needsStaticSkipping:
result[i] = result[i].skipTypes({tyStatic})
if needsTypeDescSkipping:
result[i] = result[i].skipTypes({tyTypeDesc})
typeToFit = result[i]
# ...otherwise, we use the instantiated type in `fitNode`
if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
@@ -265,7 +273,7 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
internalAssert c.config, originalParams[i].kind == nkSym
let oldParam = originalParams[i].sym
let param = copySym(oldParam, c.idgen)
param.owner = prc
setOwner(param, prc)
param.typ = result[i]
# The default value is instantiated and fitted against the final
@@ -273,9 +281,10 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
# call head symbol, because this leads to infinite recursion.
if oldParam.ast != nil:
var def = oldParam.ast.copyTree
if def.kind in nkCallKinds:
for i in 1..<def.len:
def[i] = replaceTypeVarsN(cl, def[i], 1)
if def.typ.kind == tyFromExpr:
def.typ.flags.incl tfNonConstExpr
if not isIntLit(def.typ):
def = prepareNode(cl, def)
# allow symchoice since node will be fit later
# although expectedType should cover it
@@ -292,6 +301,8 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
# the user calls an explicit instantiation of the proc (this is
# the only way the default value might be inserted).
param.ast = errorNode(c, def)
# we know the node is empty, we need the actual type for error message
param.ast.typ() = def.typ
else:
param.ast = fitNodePostMatch(c, typeToFit, converted)
param.typ = result[i]
@@ -315,6 +326,22 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
prc.typ = result
popInfoContext(c.config)
proc instantiateOnlyProcType(c: PContext, pt: LayeredIdTable, prc: PSym, info: TLineInfo): PType =
# instantiates only the type of a given proc symbol
# used by sigmatch for explicit generics
# wouldn't be needed if sigmatch could handle complex cases,
# examples are in texplicitgenerics
# might be buggy, see rest of generateInstance if problems occur
let fakeSym = copySym(prc, c.idgen)
incl(fakeSym.flags, sfFromGeneric)
fakeSym.instantiatedFrom = prc
openScope(c)
for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt):
addDecl(c, s)
instantiateProcType(c, pt, fakeSym, info)
closeScope(c)
result = fakeSym.typ
proc fillMixinScope(c: PContext) =
var p = c.p
while p != nil:
@@ -335,7 +362,7 @@ proc getLocalPassC(c: PContext, s: PSym): string =
for p in n:
extractPassc(p)
proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
proc generateInstance(c: PContext, fn: PSym, pt: LayeredIdTable,
info: TLineInfo): PSym =
## Generates a new instance of a generic procedure.
## The `pt` parameter is a type-unsafe mapping table used to link generic
@@ -346,7 +373,11 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
if c.instCounter > 50:
globalError(c.config, info, "generic instantiation too nested")
inc c.instCounter
defer: dec c.instCounter
let currentTypeofContext = c.inTypeofContext
c.inTypeofContext = 0
defer:
dec c.instCounter
c.inTypeofContext = currentTypeofContext
# careful! we copy the whole AST including the possibly nil body!
var n = copyTree(fn.ast)
# NOTE: for access of private fields within generics from a different module
@@ -364,9 +395,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
let passc = getLocalPassC(c, producer)
if passc != "": #pass the local compiler options to the consumer module too
extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex))
result.owner = c.module
setOwner(result, c.module)
else:
result.owner = fn
setOwner(result, fn)
result.ast = n
pushOwner(c, result)

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)
@@ -50,23 +93,27 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
case n.kind
of nkObjConstr:
let x = t.skipTypes(abstractPtrs)
n.typ = t
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
n.typ() = t
for i in 0..<n.len:
if i >= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i
else: annotateType(n[i], x[i], conf)
elif x.kind == tyProc and x.callConv == ccClosure:
n.typ = t
n.typ() = t
elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples
if n.kind == nkTupleConstr:
let
@@ -87,39 +134,39 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
globalError(conf, n.info, "Incorrectly generated tuple constr")
n[] = bracketExpr[]
n.typ = t
n.typ() = t
else:
globalError(conf, n.info, "() must have a tuple type")
of nkBracket:
if x.kind in {tyArray, tySequence, tyOpenArray}:
n.typ = t
n.typ() = t
for m in n: annotateType(m, x.elemType, conf)
else:
globalError(conf, n.info, "[] must have some form of array type")
of nkCurly:
if x.kind in {tySet}:
n.typ = t
n.typ() = t
for m in n: annotateType(m, x.elemType, conf)
else:
globalError(conf, n.info, "{} must have the set type")
of nkFloatLit..nkFloat128Lit:
if x.kind in {tyFloat..tyFloat128}:
n.typ = t
n.typ() = t
else:
globalError(conf, n.info, "float literal must have some float type")
of nkCharLit..nkUInt64Lit:
if x.kind in {tyInt..tyUInt64, tyBool, tyChar, tyEnum}:
n.typ = t
n.typ() = t
else:
globalError(conf, n.info, "integer literal must have some int type")
of nkStrLit..nkTripleStrLit:
if x.kind in {tyString, tyCstring}:
n.typ = t
n.typ() = t
else:
globalError(conf, n.info, "string literal must be of some string type")
of nkNilLit:
if x.kind in NilableTypes+{tyString, tySequence}:
n.typ = t
n.typ() = t
else:
globalError(conf, n.info, "nil literal must be of some pointer type")
else: discard

View File

@@ -18,7 +18,7 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
let typ = result[1].typ # new(x)
if typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyRef and typ.skipTypes({tyGenericInst, tyAlias, tySink})[0].kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[1].info, typ))
asgnExpr.typ = typ
asgnExpr.typ() = typ
var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
while true:
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false)
@@ -38,7 +38,7 @@ proc semAddr(c: PContext; n: PNode): PNode =
if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}:
localError(c.config, n.info, errExprHasNoAddress)
result.add x
result.typ = makePtrType(c, x.typ)
result.typ() = makePtrType(c, x.typ)
proc semTypeOf(c: PContext; n: PNode): PNode =
var m = BiggestInt 1 # typeOfIter
@@ -49,26 +49,50 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
else:
m = mode.intVal
result = newNodeI(nkTypeOfExpr, n.info)
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
result.add typExpr
result.typ = makeTypeDesc(c, typExpr.typ)
if typExpr.typ.kind == tyFromExpr:
typExpr.typ.flags.incl tfNonConstExpr
result.typ() = makeTypeDesc(c, typExpr.typ)
type
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags, afterOverloading = false): PNode
proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
result = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: result.add(n[i])
result = semSubscript(c, result, flags)
result = semSubscript(c, result, flags, afterOverloading = true)
if result.isNil:
let x = copyTree(n)
x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
bracketNotFoundError(c, x)
#localError(c.config, n.info, "could not resolve: " & $n)
result = errorNode(c, n)
if c.inGenericContext > 0:
for i in 0..<n.len:
let a = n[i]
if a.typ != nil and a.typ.kind in {tyGenericParam, tyFromExpr}:
# expression is compiled early in a generic body
result = semGenericStmt(c, x)
result.typ() = makeTypeFromExpr(c, copyTree(result))
result.typ.flags.incl tfNonConstExpr
return
let s = # extract sym from first arg
if n.len > 1:
if n[1].kind == nkSym: n[1].sym
elif n[1].kind in nkSymChoices + {nkOpenSym} and n[1].len != 0:
n[1][0].sym
else: nil
else: nil
if s != nil and s.kind in routineKinds:
# this is a failed generic instantiation
# semSubscript should already error but this is better for cascading errors
result = explicitGenericInstError(c, n)
else:
bracketNotFoundError(c, x, flags)
result = errorNode(c, n)
proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
# rewrite `[]=`(a, i, x) back to ``a[i] = x``.
@@ -176,15 +200,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
let preferStr = traitCall[2].strVal
prefer = parseEnum[TPreferedDesc](preferStr)
result = newStrNode(nkStrLit, operand.typeToString(prefer))
result.typ = getSysType(c.graph, traitCall[1].info, tyString)
result.typ() = getSysType(c.graph, traitCall[1].info, tyString)
result.info = traitCall.info
of "name", "$":
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
result.typ = getSysType(c.graph, traitCall[1].info, tyString)
result.typ() = getSysType(c.graph, traitCall[1].info, tyString)
result.info = traitCall.info
of "arity":
result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
result.typ = newType(tyInt, c.idgen, context)
result.typ() = newType(tyInt, c.idgen, context)
result.info = traitCall.info
of "genericHead":
var arg = operand
@@ -224,8 +248,9 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
of "rangeBase":
# return the base type of a range type
var arg = operand.skipTypes({tyGenericInst})
assert arg.kind == tyRange
result = getTypeDescNode(c, arg.base, operand.owner, traitCall.info)
if arg.kind == tyRange:
arg = arg.base
result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
of "isCyclic":
var operand = operand.skipTypes({tyGenericInst})
let isCyclic = canFormAcycle(c.graph, operand)
@@ -253,7 +278,7 @@ proc semOrd(c: PContext, n: PNode): PNode =
discard
else:
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(parType, preferDesc))
result.typ = errorType(c)
result.typ() = errorType(c)
proc semBindSym(c: PContext, n: PNode): PNode =
result = copyNode(n)
@@ -369,7 +394,7 @@ proc semOf(c: PContext, n: PNode): PNode =
message(c.config, n.info, hintConditionAlwaysTrue, renderTree(n))
result = newIntNode(nkIntLit, 1)
result.info = n.info
result.typ = getSysType(c.graph, n.info, tyBool)
result.typ() = getSysType(c.graph, n.info, tyBool)
return result
elif diff == high(int):
if commonSuperclass(a, b) == nil:
@@ -378,10 +403,10 @@ proc semOf(c: PContext, n: PNode): PNode =
message(c.config, n.info, hintConditionAlwaysFalse, renderTree(n))
result = newIntNode(nkIntLit, 0)
result.info = n.info
result.typ = getSysType(c.graph, n.info, tyBool)
result.typ() = getSysType(c.graph, n.info, tyBool)
else:
localError(c.config, n.info, "'of' takes 2 arguments")
n.typ = getSysType(c.graph, n.info, tyBool)
n.typ() = getSysType(c.graph, n.info, tyBool)
result = n
proc semUnown(c: PContext; n: PNode): PNode =
@@ -416,9 +441,9 @@ proc semUnown(c: PContext; n: PNode): PNode =
result = t
result = copyTree(n[1])
result.typ = unownedType(c, result.typ)
result.typ() = unownedType(c, result.typ)
# little hack for injectdestructors.nim (see bug #11350):
#result[0].typ = nil
#result[0].typ() = nil
proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym =
# We need to do 2 things: Replace n.typ which is a 'ref T' by a 'var T' type.
@@ -428,7 +453,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
proc transform(c: PContext; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode =
result = shallowCopy(n)
if sameTypeOrNil(n.typ, old):
result.typ = fresh
result.typ() = fresh
if n.kind == nkSym and n.sym == oldParam:
result.sym = newParam
for i in 0 ..< safeLen(n):
@@ -439,7 +464,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
result = copySym(orig, c.idgen)
result.info = info
result.flags.incl sfFromGeneric
result.owner = orig
setOwner(result, orig)
let origParamType = orig.typ.firstParamType
let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen)
let oldParam = orig.typ.n[1].sym
@@ -510,32 +535,34 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
discard "already turned this one into a finalizer"
else:
if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move
fin.owner = fin.instantiatedFrom
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
selfSymNode.typ = fin.typ.firstParamType
wrapperSym.flags.incl sfUsed
setOwner(fin, fin.instantiatedFrom)
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)
transFormedSym.owner = 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 =
@@ -587,7 +614,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mTypeTrait: result = semTypeTraits(c, n)
of mAstToStr:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph)
result.typ = getSysType(c.graph, n.info, tyString)
result.typ() = getSysType(c.graph, n.info, tyString)
of mInstantiationInfo: result = semInstantiationInfo(c, n)
of mOrd: result = semOrd(c, n)
of mOf: result = semOf(c, n)
@@ -600,7 +627,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result = semDynamicBindSym(c, n)
of mProcCall:
result = n
result.typ = n[1].typ
result.typ() = n[1].typ
of mDotDot:
result = n
of mPlugin:
@@ -643,7 +670,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result[0] = newSymNode(op)
if op.typ.len == 3:
let boolLit = newIntLit(c.graph, n.info, 1)
boolLit.typ = getSysType(c.graph, n.info, tyBool)
boolLit.typ() = getSysType(c.graph, n.info, tyBool)
result.add boolLit
of mWasMoved:
result = n
@@ -685,7 +712,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result = n
if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and
expectedType.kind == tySequence and result.typ.elementType.kind == tyEmpty:
result.typ = expectedType # type inference for empty sequence # bug #21377
result.typ() = expectedType # type inference for empty sequence # bug #21377
of mEnsureMove:
result = n
if n[1].kind in {nkStmtListExpr, nkBlockExpr,

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
@@ -188,7 +188,7 @@ proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode,
newNodeIT(nkType, constrCtx.initExpr.info, asgnType)
)
asgnExpr.flags.incl nfSkipFieldChecking
asgnExpr.typ = recTyp
asgnExpr.typ() = recTyp
defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr)
proc collectBranchFields(c: PContext, n: PNode, discriminatorVal: PNode,
@@ -387,10 +387,13 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
if e != nil:
result.status = initFull
elif field.ast != nil:
result.status = initUnknown
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
if efIgnoreDefaults notin flags:
result.status = initUnknown
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
else:
result.status = initNone
else:
if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass
if {efWantNoDefaults, efIgnoreDefaults} * flags == {}: # cannot compute defaults at the typeRightPass
let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault)
if defaultExpr != nil:
result.status = initUnknown
@@ -443,7 +446,7 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
assert objType != nil
if objType.kind == tyObject:
var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults})
if constrCtx.missingFields.len > 0:
localError(c.config, info,
"The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)])
@@ -462,17 +465,20 @@ 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:
t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned})
if optOwnedRefs in c.config.globalOptions:
result.typ = makeVarType(c, result.typ, tyOwned)
result.typ() = makeVarType(c, result.typ, tyOwned)
# we have to watch out, there are also 'owned proc' types that can be used
# multiple times as long as they don't have closures.
result.typ.flags.incl tfHasOwned
@@ -515,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

@@ -407,9 +407,9 @@ proc transformSlices(g: ModuleGraph; idgen: IdGenerator; n: PNode): PNode =
result = copyNode(n)
var typ = newType(tyOpenArray, idgen, result.typ.owner)
typ.add result.typ.elementType
result.typ = typ
result.typ() = typ
let opSlice = newSymNode(createMagic(g, idgen, "slice", mSlice))
opSlice.typ = getSysType(g, n.info, tyInt)
opSlice.typ() = getSysType(g, n.info, tyInt)
result.add opSlice
result.add n[1]
let slice = n[2].skipStmtList

View File

@@ -11,7 +11,7 @@ import
ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, options, guards, lineinfos, semfold, semdata,
modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
semstrictfuncs, suggestsymdb
semstrictfuncs, suggestsymdb, pushpoppragmas
import std/[tables, intsets, strutils, sequtils]
@@ -85,6 +85,7 @@ type
isInnerProc: bool
inEnforcedNoSideEffects: bool
currOptions: TOptions
optionsStack: seq[(TOptions, TNoteKinds)]
config: ConfigRef
graph: ModuleGraph
c: PContext
@@ -123,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)
@@ -192,7 +194,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
let dot = newNodeI(nkDotExpr, n.info, 2)
dot[0] = n[0]
dot[1] = newSymNode(g)
dot.typ = g.typ
dot.typ() = g.typ
for L in a.locked:
#if a.guards.sameSubexprs(dot, L): return
if guards.sameTree(dot, L): return
@@ -211,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
@@ -410,7 +413,7 @@ proc throws(tracked, n, orig: PNode) =
if n.typ == nil or n.typ.kind != tyError:
if orig != nil:
let x = copyTree(orig)
x.typ = n.typ
x.typ() = n.typ
tracked.add x
else:
tracked.add n
@@ -425,12 +428,12 @@ proc excType(g: ModuleGraph; n: PNode): PType =
proc createRaise(g: ModuleGraph; n: PNode): PNode =
result = newNode(nkType)
result.typ = getEbase(g, n.info)
result.typ() = getEbase(g, n.info)
if not n.isNil: result.info = n.info
proc createTag(g: ModuleGraph; n: PNode): PNode =
result = newNode(nkType)
result.typ = g.sysTypeFromName(n.info, "RootEffect")
result.typ() = g.sysTypeFromName(n.info, "RootEffect")
if not n.isNil: result.info = n.info
proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
@@ -615,9 +618,16 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
for i in 0..<n.len:
var it = n[i]
let pragma = whichPragma(it)
if pragma == wEffects:
case pragma
of wEffects:
# list the computed effects up to here:
listEffects(tracked)
of wPush:
processPushBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions, n, i+1)
of wPop:
processPopBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions)
else:
discard
template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
@@ -1063,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
@@ -1202,7 +1212,7 @@ proc track(tracked: PEffects, n: PNode) =
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
tracked.owner.flags.incl sfInjectDestructors
# bug #15038: ensure consistency
if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ
if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ() = n.sym.typ
of nkHiddenAddr, nkAddr:
if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym) and
n.typ.kind notin {tyVar, tyLent}:
@@ -1293,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?
@@ -1594,7 +1609,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; c: PContext): TEffects
result = TEffects(exc: effects[exceptionEffects], tags: effects[tagEffects],
forbids: effects[forbiddenEffects], owner: s, ownerModule: s.getModule,
init: @[], locked: @[], graph: g, config: g.config, c: c,
currentBlock: 1
currentBlock: 1, optionsStack: @[(g.config.options, g.config.notes)]
)
result.guards.s = @[]
result.guards.g = g
@@ -1630,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)
@@ -1642,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

@@ -112,11 +112,11 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
dec(c.p.nestedLoopCounter)
closeScope(c)
if n[1].typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
result.typ() = c.enforceVoidContext
elif efInTypeof in flags:
result.typ = n[1].typ
result.typ() = n[1].typ
elif implicitlyDiscardable(n[1]):
result[1].typ = c.enforceVoidContext
result[1].typ() = c.enforceVoidContext
proc semProc(c: PContext, n: PNode): PNode
@@ -135,7 +135,7 @@ const
skipForDiscardable = {nkStmtList, nkStmtListExpr,
nkOfBranch, nkElse, nkFinally, nkExceptBranch,
nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr,
nkHiddenStdConv, nkHiddenDeref}
nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref}
proc implicitlyDiscardable(n: PNode): bool =
# same traversal as endsInNoReturn
@@ -172,7 +172,7 @@ proc implicitlyDiscardable(n: PNode): bool =
of nkElse:
branch[0]
else:
raiseAssert "Malformed `case` statement in endsInNoReturn"
raiseAssert "Malformed `case` statement in implicitlyDiscardable"
# all branches are discardable
result = true
of nkTryStmt:
@@ -190,18 +190,19 @@ proc implicitlyDiscardable(n: PNode): bool =
else:
result = false
proc endsInNoReturn(n: PNode, returningNode: var PNode): bool =
proc endsInNoReturn(n: PNode, returningNode: var PNode; discardableCheck = false): bool =
## check if expr ends the block like raising or call of noreturn procs do
result = false # assume it does return
template checkBranch(branch) =
if not endsInNoReturn(branch, returningNode):
if not endsInNoReturn(branch, returningNode, discardableCheck):
# proved a branch returns
return false
var it = n
# skip these beforehand, no special handling needed
while it.kind in skipForDiscardable and it.len > 0:
let skips = if discardableCheck: skipForDiscardable else: skipForDiscardable-{nkBlockExpr, nkBlockStmt}
while it.kind in skips and it.len > 0:
it = it.lastSon
case it.kind
@@ -245,7 +246,7 @@ proc endsInNoReturn(n: PNode, returningNode: var PNode): bool =
var lastIndex = it.len - 1
if it[lastIndex].kind == nkFinally:
# if finally is noreturn, then the entire statement is noreturn
if endsInNoReturn(it[lastIndex][^1], returningNode):
if endsInNoReturn(it[lastIndex][^1], returningNode, discardableCheck):
return true
dec lastIndex
for i in 1 .. lastIndex:
@@ -274,7 +275,7 @@ proc fixNilType(c: PContext; n: PNode) =
elif n.kind in {nkStmtList, nkStmtListExpr}:
n.transitionSonsKind(nkStmtList)
for it in n: fixNilType(c, it)
n.typ = nil
n.typ() = nil
proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
if c.matchedConcept != nil or efInTypeof in flags: return
@@ -291,7 +292,7 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
else:
# Ignore noreturn procs since they don't have a type
var n = result
if result.endsInNoReturn(n):
if result.endsInNoReturn(n, discardableCheck = true):
return
var s = "expression '" & $n & "' is of type '" &
@@ -330,14 +331,14 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil):
for it in n: discardCheck(c, it.lastSon, flags)
result.transitionSonsKind(nkIfStmt)
# propagate any enforced VoidContext:
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
if typ == c.enforceVoidContext: result.typ() = c.enforceVoidContext
else:
for it in n:
let j = it.len-1
if not endsInNoReturn(it[j]):
it[j] = fitNode(c, typ, it[j], it[j].info)
result.transitionSonsKind(nkIfExpr)
result.typ = typ
result.typ() = typ
proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
var check = initIntSet()
@@ -437,7 +438,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
discardCheck(c, n[0], flags)
for i in 1..<n.len: discardCheck(c, n[i].lastSon, flags)
if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
result.typ() = c.enforceVoidContext
else:
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
if not endsInNoReturn(n[0]):
@@ -447,7 +448,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
let j = it.len-1
if not endsInNoReturn(it[j]):
it[j] = fitNode(c, typ, it[j], it[j].info)
result.typ = typ
result.typ() = typ
proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
result = fitNode(c, typ, n, n.info)
@@ -456,7 +457,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
if r1.kind in {nkCharLit..nkUInt64Lit} and typ.skipTypes(abstractRange).kind in {tyFloat..tyFloat128}:
result = newFloatNode(nkFloatLit, BiggestFloat r1.intVal)
result.info = n.info
result.typ = typ
result.typ() = typ
if not floatRangeCheck(result.floatVal, typ):
localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)])
elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring:
@@ -491,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) =
@@ -604,32 +594,59 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
obj.n.add newSymNode(field)
n[0] = makeDeref x
n[1] = newSymNode(field)
n.typ = field.typ
n.typ() = field.typ
else:
localError(c.config, n.info, "implicit object field construction " &
"requires a .partial object, but got " & typeToString(obj))
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 =
# make sure it's not a normal pragma, and calls an identifier
# considerQuotedIdent below will fail on non-identifiers
result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds
result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds+{nkDotExpr}
if result:
# make sure it's not a user pragma
let ident = considerQuotedIdent(c, key)
result = strTableGet(c.userPragmas, ident) == nil
if key.kind != nkDotExpr:
let ident = considerQuotedIdent(c, key)
result = strTableGet(c.userPragmas, ident) == nil
if result:
# make sure it's not a custom pragma
var amb = false
let sym = searchInScopes(c, ident, amb)
let sym = qualifiedLookUp(c, key, {})
result = sym == nil or sfCustomPragma notin sym.flags
proc copyExcept(n: PNode, i: int): PNode =
@@ -828,6 +845,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var typFlags: TTypeAllowedFlags = {}
var def: PNode = c.graph.emptyNode
if typ != nil and typ.kind == tyRange and
c.graph.config.isDefined("nimPreviewRangeDefault") and
a[^1].kind == nkEmpty:
a[^1] = firstRange(c.config, typ)
if a[^1].kind != nkEmpty:
def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
@@ -902,7 +924,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if sfGenSym notin v.flags:
if not isDiscardUnderscore(v): addInterfaceDecl(c, v)
else:
if v.owner == nil: v.owner = c.p.owner
if v.owner == nil: setOwner(v, c.p.owner)
when oKeepVariableNames:
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
else:
@@ -979,6 +1001,7 @@ proc semConst(c: PContext, n: PNode): PNode =
var typFlags: TTypeAllowedFlags = {}
# don't evaluate here since the type compatibility check below may add a converter
openScope(c)
var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
@@ -1005,6 +1028,7 @@ proc semConst(c: PContext, n: PNode): PNode =
if c.matchedConcept != nil:
typFlags.incl taConcept
typeAllowedCheck(c, a.info, typ, skConst, typFlags)
closeScope(c)
if a.kind == nkVarTuple:
# generate new section from tuple unpacking and embed it into this one
@@ -1018,7 +1042,7 @@ proc semConst(c: PContext, n: PNode): PNode =
when defined(nimsuggest):
v.hasUserSpecifiedType = hasUserSpecifiedType
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
elif v.owner == nil: setOwner(v, getCurrOwner(c))
styleCheckDef(c, v)
onDef(a[j].info, v)
@@ -1089,7 +1113,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
v.typ = iter[i]
n[0][i] = newSymNode(v)
if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
elif v.owner == nil: setOwner(v, getCurrOwner(c))
else:
var v = symForVar(c, n[0])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
@@ -1099,7 +1123,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
v.typ = iterBase
n[0] = newSymNode(v)
if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
elif v.owner == nil: setOwner(v, getCurrOwner(c))
else:
localError(c.config, n.info, errWrongNumberOfVariables)
elif n.len-2 != iterAfterVarLent.len:
@@ -1133,7 +1157,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
v.typ = iter[i][j]
n[i][j] = newSymNode(v)
if not isDiscardUnderscore(v): addDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
elif v.owner == nil: setOwner(v, getCurrOwner(c))
else:
var v = symForVar(c, n[i])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
@@ -1148,7 +1172,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
n[i] = newSymNode(v)
if sfGenSym notin v.flags:
if not isDiscardUnderscore(v): addDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
elif v.owner == nil: setOwner(v, getCurrOwner(c))
inc(c.p.nestedLoopCounter)
let oldBreakInLoop = c.p.breakInLoop
c.p.breakInLoop = true
@@ -1223,7 +1247,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
toResolve.add n[0]
var errors: CandidateErrors = @[]
var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoDiagnostics},
var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoUndeclared},
errors, false)
if r.state == csMatch:
var match = r.calleeSym
@@ -1237,8 +1261,6 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, flags)
of skTemplate: result = semTemplateExpr(c, toExpand, match, flags)
else: result = errorNode(c, n[0])
elif r.state == csNoMatch:
result = errorNode(c, n[0])
else:
result = errorNode(c, n[0])
if result.kind == nkEmpty:
@@ -1285,9 +1307,9 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = semForVars(c, n, flags)
# propagate any enforced VoidContext:
if n[^1].typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
result.typ() = c.enforceVoidContext
elif efInTypeof in flags:
result.typ = result.lastSon.typ
result.typ() = result.lastSon.typ
closeScope(c)
proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
@@ -1367,14 +1389,14 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil
for i in 1..<n.len: discardCheck(c, n[i].lastSon, flags)
# propagate any enforced VoidContext:
if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
result.typ() = c.enforceVoidContext
else:
for i in 1..<n.len:
var it = n[i]
let j = it.len-1
if not endsInNoReturn(it[j]):
it[j] = fitNode(c, typ, it[j], it[j].info)
result.typ = typ
result.typ() = typ
proc semRaise(c: PContext, n: PNode): PNode =
result = n
@@ -1469,7 +1491,7 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
s = typsym
# add it here, so that recursive types are possible:
if sfGenSym notin s.flags: addInterfaceDecl(c, s)
elif s.owner == nil: s.owner = getCurrOwner(c)
elif s.owner == nil: setOwner(s, getCurrOwner(c))
if name.kind == nkPragmaExpr:
if name[0].kind == nkPostfix:
@@ -1692,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]
@@ -1752,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:
@@ -1784,10 +1819,6 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
checkForMetaFields(c, baseType.n, hasError)
if not hasError:
checkConstructedType(c.config, s.info, s.typ)
# fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name:
if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c):
incl(s.flags, sfGenSym)
#instAllTypeBoundOp(c, n.info)
@@ -1967,13 +1998,13 @@ proc semProcAnnotation(c: PContext, prc: PNode;
return result
proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
proc semInferredLambda(c: PContext, pt: LayeredIdTable, n: PNode): PNode =
## used for resolving 'auto' in lambdas based on their callsite
var n = n
let original = n[namePos].sym
let s = original #copySym(original, false)
#incl(s.flags, sfFromGeneric)
#s.owner = original
#s.owner() = original
n = replaceTypesInBody(c, pt, n, original)
result = n
@@ -1988,7 +2019,7 @@ proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
tyFromExpr}+tyTypeClasses:
localError(c.config, params[i].info, "cannot infer type of parameter: " &
params[i].sym.name.s)
#params[i].sym.owner = s
#params[i].sym.owner() = s
openScope(c)
pushOwner(c, s)
addParams(c, params, skProc)
@@ -2000,7 +2031,7 @@ proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
popOwner(c)
closeScope(c)
if optOwnedRefs in c.config.globalOptions and result.typ != nil:
result.typ = makeVarType(c, result.typ, tyOwned)
result.typ() = makeVarType(c, result.typ, tyOwned)
# alternative variant (not quite working):
# var prc = arg[0].sym
# let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
@@ -2033,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:
@@ -2074,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,
@@ -2090,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
@@ -2114,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,
@@ -2215,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() & ")")
@@ -2307,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:
@@ -2357,10 +2401,11 @@ 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
s.owner = c.getCurrOwner
setOwner(s, c.getCurrOwner)
else:
# Highlighting needs to be done early so the position for
# name isn't changed (see taccent_highlight). We don't want to check if this is the
@@ -2606,9 +2651,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
c.patterns.add(s)
if isAnon:
n.transitionSonsKind(nkLambda)
result.typ = s.typ
result.typ() = s.typ
if optOwnedRefs in c.config.globalOptions:
result.typ = makeVarType(c, result.typ, tyOwned)
result.typ() = makeVarType(c, result.typ, tyOwned)
elif isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure:
localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid")
@@ -2627,7 +2672,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
# gensym'ed iterator?
if n[namePos].kind == nkSym:
# gensym'ed iterators might need to become closure iterators:
n[namePos].sym.owner = getCurrOwner(c)
setOwner(n[namePos].sym, getCurrOwner(c))
n[namePos].sym.transitionRoutineSymKind(skIterator)
result = semProcAux(c, n, skIterator, iteratorPragmas)
# bug #7093: if after a macro transformation we don't have an
@@ -2645,10 +2690,10 @@ 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)
result.typ() = makeVarType(c, result.typ, tyOwned)
result.typ.callConv = ccClosure
proc semProc(c: PContext, n: PNode): PNode =
@@ -2733,20 +2778,23 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) =
proc evalInclude(c: PContext, n: PNode): PNode =
result = newNodeI(nkStmtList, n.info)
result.add n
template checkAs(it: PNode) =
if it.kind == nkInfix and it.len == 3:
let op = it[0].getPIdent
if op != nil and op.id == ord(wAs):
localError(c.config, it.info, "Cannot use '" & it[0].renderTree & "' in 'include'.")
for i in 0..<n.len:
var imp: PNode
let it = n[i]
if it.kind == nkInfix and it.len == 3 and it[0].ident.s != "/":
localError(c.config, it.info, "Cannot use '" & it[0].ident.s & "' in 'include'.")
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
let sep = it[0]
let dir = it[1]
imp = newNodeI(nkInfix, it.info)
imp.add sep
imp.add dir
imp.add sep # dummy entry, replaced in the loop
for x in it[2]:
imp[2] = x
checkAs(it)
if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
let lastPos = it.len - 1
var imp = copyNode(it)
newSons(imp, it.len)
for i in 0 ..< lastPos: imp[i] = it[i]
imp[lastPos] = imp[0] # dummy entry, replaced in the loop
for x in it[lastPos]:
checkAs(x)
imp[lastPos] = x
incMod(c, n, imp, result)
else:
incMod(c, n, it, result)
@@ -2776,7 +2824,7 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
n[1] = semExpr(c, n[1], expectedType = expectedType)
dec c.inUncheckedAssignSection, inUncheckedAssignSection
result = n
result.typ = n[1].typ
result.typ() = n[1].typ
for i in 0..<pragmaList.len:
case whichPragma(pragmaList[i])
of wLine: setInfoRecursive(result, pragmaList[i].info)
@@ -2857,18 +2905,18 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
let verdict = semConstExpr(c, n[i])
if verdict == nil or verdict.kind != nkIntLit or verdict.intVal == 0:
localError(c.config, result.info, "concept predicate failed")
of tyUnknown: continue
of tyFromExpr: continue
else: discard
if n[i].typ == c.enforceVoidContext: #or usesResult(n[i]):
voidContext = true
n.typ = c.enforceVoidContext
n.typ() = c.enforceVoidContext
if i == last and (n.len == 1 or ({efWantValue, efInTypeof} * flags != {})):
n.typ = n[i].typ
n.typ() = n[i].typ
if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr)
elif i != last or voidContext:
discardCheck(c, n[i], flags)
else:
n.typ = n[i].typ
n.typ() = n[i].typ
if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr)
var m = n[i]
while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates

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
@@ -218,63 +223,98 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
if k == skParam and c.inTemplateHeader > 0:
local.flags.incl sfTemplateParam
proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode =
incl(s.flags, sfUsed)
# bug #12885; ideally sem'checking is performed again afterwards marking
# the symbol as used properly, but the nfSem mechanism currently prevents
# that from happening, so we mark the module as used here already:
markOwnerModuleAsUsed(c, s)
markOwnerModuleAsUsed(c.c, s)
# we do not call onUse here, as the identifier is not really
# resolved here. We will fixup the used identifiers later.
case s.kind
of skUnknown:
# Introduced in this pass! Leave it as an identifier.
result = n
of OverloadableSyms-{skTemplate,skMacro}:
result = symChoice(c, n, s, scOpen, isField)
of skTemplate, skMacro:
result = symChoice(c, n, s, scOpen, isField)
if result.kind == nkSym:
# template/macro symbols might need to be semchecked again
# prepareOperand etc don't do this without setting the type to nil
result.typ = nil
of OverloadableSyms:
result = symChoice(c.c, n, s, scOpen, isField)
if not isField and result.kind in {nkSym, nkOpenSymChoice}:
if openSym in c.c.features:
if result.kind == nkSym:
result = newOpenSym(result)
else:
result.typ() = nil
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
of skGenericParam:
if isField and sfGenSym in s.flags: result = n
else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
else:
result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
if not isField and s.owner != c.owner:
if openSym in c.c.features:
result = newOpenSym(result)
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
of skParam:
result = n
of skType:
if isField and sfGenSym in s.flags: result = n
else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
else:
if isAmbiguous:
# ambiguous types should be symchoices since lookup behaves
# differently for them in regular expressions
result = symChoice(c.c, n, s, scOpen, isField)
else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
if not isField and not (s.owner == c.owner and
s.typ != nil and s.typ.kind == tyGenericParam) and
result.kind in {nkSym, nkOpenSymChoice}:
if openSym in c.c.features:
if result.kind == nkSym:
result = newOpenSym(result)
else:
result.typ() = nil
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
else:
if isField and sfGenSym in s.flags: result = n
else: result = newSymNode(s, n.info)
else:
result = newSymNode(s, n.info)
if not isField:
if openSym in c.c.features:
result = newOpenSym(result)
else:
result.flags.incl nfDisabledOpenSym
result.typ() = nil
# Issue #12832
when defined(nimsuggest):
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false)
# field access (dot expr) will be handled by builtinFieldAccess
if not isField:
styleCheckUse(c, n.info, s)
styleCheckUse(c.c, n.info, s)
proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode =
result = n
if n.kind == nkIdent:
let s = qualifiedLookUp(c.c, n, {})
if s != nil:
if s.owner == c.owner and s.kind == skParam:
if s.owner == c.owner and (s.kind == skParam or
(sfGenSym in s.flags and not explicitInject)):
incl(s.flags, sfUsed)
result = newSymNode(s, n.info)
onUse(n.info, s)
else:
for i in 0..<n.safeLen:
result[i] = semRoutineInTemplName(c, n[i])
result[i] = semRoutineInTemplName(c, n[i], explicitInject)
proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
result = n
checkSonsLen(n, bodyPos + 1, c.c.config)
if n.kind notin nkLambdaKinds:
# routines default to 'inject':
if symBinding(n[pragmasPos]) == spGenSym:
let binding = symBinding(n[pragmasPos])
if binding == spGenSym:
let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
if not hasParam:
var s = newGenSym(k, ident, c)
@@ -286,7 +326,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
else:
n[namePos] = ident
else:
n[namePos] = semRoutineInTemplName(c, n[namePos])
n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject)
# open scope for parameters
openScope(c)
for i in patternPos..paramsPos-1:
@@ -343,6 +383,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
case n.kind
of nkIdent:
if n.ident.id in c.toInject: return n
c.c.isAmbiguous = false
let s = qualifiedLookUp(c.c, n, {})
if s != nil:
if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags:
@@ -360,9 +401,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = newSymNode(s, n.info)
onUse(n.info, s)
else:
if s.kind in {skType, skVar, skLet, skConst}:
if s.kind in {skVar, skLet, skConst}:
discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
result = semTemplSymbol(c.c, n, s, c.noGenSym > 0)
result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
of nkBind:
result = semTemplBody(c, n[0])
of nkBindStmt:
@@ -499,13 +540,20 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
of nkConverterDef:
result = semRoutineInTemplBody(c, n, skConverter)
of nkPragmaExpr:
result[0] = semTemplBody(c, n[0])
result = semTemplBodySons(c, n)
of nkPostfix:
result[1] = semTemplBody(c, n[1])
of nkPragma:
for x in n:
if x.kind == nkExprColonExpr:
x[1] = semTemplBody(c, x[1])
for i in 0 ..< n.len:
let x = n[i]
let prag = whichPragma(x)
if prag == wInvalid:
# only sem if not a language-level pragma
result[i] = semTemplBody(c, x)
elif x.kind in nkPragmaCallKinds:
# is pragma, but value still needs to be checked
for j in 1 ..< x.len:
x[j] = semTemplBody(c, x[j])
of nkBracketExpr:
if n.typ == nil:
# if a[b] is nested inside a typed expression, don't convert it
@@ -556,6 +604,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
of nkDotExpr, nkAccQuoted:
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
# so we use the generic code for nkDotExpr too
c.c.isAmbiguous = false
let s = qualifiedLookUp(c.c, n, {})
if s != nil:
# mirror the nkIdent case
@@ -570,9 +619,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
elif contains(c.toMixin, s.name.id):
return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
else:
if s.kind in {skType, skVar, skLet, skConst}:
if s.kind in {skVar, skLet, skConst}:
discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
return semTemplSymbol(c.c, n, s, c.noGenSym > 0)
return semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
if n.kind == nkDotExpr:
result = n
result[0] = semTemplBody(c, n[0])
@@ -647,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:
@@ -656,6 +708,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
pushOwner(c, s)
openScope(c)
n[namePos] = newSymNode(s)
s.ast = n # for implicitPragmas to use
pragmaCallable(c, s, n, templatePragmas)
implicitPragmas(c, s, n.info, templatePragmas)
@@ -706,6 +759,17 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
c: c,
owner: s
)
# handle default params:
for i in 1..<s.typ.n.len:
let param = s.typ.n[i].sym
if param.ast != nil:
# param default values need to be treated like template body:
if sfDirty in s.flags:
param.ast = semTemplBodyDirty(ctx, param.ast)
else:
param.ast = semTemplBody(ctx, param.ast)
if param.ast.referencesAnotherParam(s):
param.ast.flags.incl nfDefaultRefsParam
if sfDirty in s.flags:
n[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos])
else:
@@ -715,11 +779,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
closeScope(c)
popOwner(c)
# set the symbol AST after pragmas, at least. This stops pragma that have
# been pushed (implicit) to be explicitly added to the template definition
# and misapplied to the body. see #18113
s.ast = n
if sfCustomPragma in s.flags:
if n[bodyPos].kind != nkEmpty:
localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)

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:
@@ -475,6 +501,13 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
let t = semTypeNode(c, it, nil)
addSonSkipIntLitChecked(c, result, t, it, c.idgen)
proc firstRange(config: ConfigRef, t: PType): PNode =
if t.skipModifier().kind in tyFloat..tyFloat64:
result = newFloatNode(nkFloatLit, firstFloat(t))
else:
result = newIntNode(nkIntLit, firstOrd(config, t))
result.typ() = t
proc semTuple(c: PContext, n: PNode, prev: PType): PType =
var typ: PType
result = newOrPrevType(tyTuple, prev, c)
@@ -487,12 +520,18 @@ 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:
a[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ))
a[^1].typ = typ
a[^1] = firstRange(c.config, typ)
hasDefaultField = true
else:
localError(c.config, a.info, errTypeExpected)
@@ -580,9 +619,14 @@ proc semBranchRange(c: PContext, n, a, b: PNode, covered: var Int128): PNode =
let bc = semConstExpr(c, b)
if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}:
localError(c.config, b.info, "range of string is invalid")
let at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
let bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
var at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
var bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
# the calls to fitNode may introduce calls to converters
# mirrored with semCaseBranch for single elements
if at.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
at = semConstExpr(c, at)
if bt.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
bt = semConstExpr(c, bt)
result = newNodeI(nkRange, a.info)
result.add(at)
result.add(bt)
@@ -613,6 +657,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
var b = branch[i]
if b.kind == nkRange:
branch[i] = b
# same check as in semBranchRange for exhaustiveness
covered = covered + getOrdValue(b[1]) + 1 - getOrdValue(b[0])
elif isRange(b):
branch[i] = semCaseBranchRange(c, n, b, covered)
else:
@@ -628,8 +674,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
checkMinSonsLen(n, 1, c.config)
var tmp = fitNode(c, n[0].typ, r, r.info)
# the call to fitNode may introduce a call to a converter
if tmp.kind == nkHiddenCallConv or
(tmp.kind == nkHiddenStdConv and n[0].typ.kind == tyCstring):
# mirrored with semBranchRange
if tmp.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
tmp = semConstExpr(c, tmp)
branch[i] = skipConv(tmp)
inc(covered)
@@ -783,6 +829,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
of nkRecWhen:
var a = copyTree(n)
var branch: PNode = nil # the branch to take
var cannotResolve = false # no branch should be taken
for i in 0..<a.len:
var it = a[i]
if it == nil: illFormedAst(n, c.config)
@@ -800,24 +847,30 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
let e = semExprWithType(c, it[0], {efDetermineType})
if e.typ.kind == tyFromExpr:
it[0] = makeStaticExpr(c, e)
cannotResolve = true
else:
it[0] = forceBool(c, e)
let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
if val == nil or val.kind != nkIntLit:
cannotResolve = true
elif not cannotResolve and val.intVal != 0 and branch == nil:
branch = it[1]
of nkElse:
checkSonsLen(it, 1, c.config)
if branch == nil: branch = it[0]
if branch == nil and not cannotResolve: branch = it[0]
idx = 0
else: illFormedAst(n, c.config)
if c.inGenericContext > 0:
if c.inGenericContext > 0 and cannotResolve:
# use a new check intset here for each branch:
var newCheck: IntSet = check
var newPos = pos
var newf = newNodeI(nkRecList, n.info)
semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields)
it[idx] = if newf.len == 1: newf[0] else: newf
if c.inGenericContext > 0:
father.add a
elif branch != nil:
if branch != nil:
semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields)
elif cannotResolve:
father.add a
elif father.kind in {nkElse, nkOfBranch}:
father.add newNodeI(nkRecList, n.info)
of nkRecCase:
@@ -838,16 +891,22 @@ 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)
else:
typ = semTypeNode(c, n[^2], nil)
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
n[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ))
n[^1].typ = typ
n[^1] = firstRange(c.config, typ)
hasDefaultField = true
propagateToOwner(rectype, typ)
var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
@@ -864,8 +923,8 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
f.options = c.config.options
if fieldOwner != nil and
{sfImportc, sfExportc} * fieldOwner.flags != {} and
not hasCaseFields and f.loc.r == "":
f.loc.r = rope(f.name.s)
not hasCaseFields and f.loc.snippet == "":
f.loc.snippet = rope(f.name.s)
f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags
inc(pos)
if containsOrIncl(check, f.name.id):
@@ -1077,7 +1136,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
if sfGenSym in param.flags:
# bug #XXX, fix the gensym'ed parameters owner:
if param.owner == nil:
param.owner = getCurrOwner(c)
setOwner(param, getCurrOwner(c))
else: addDecl(c, param)
template shouldHaveMeta(t) =
@@ -1166,11 +1225,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType[i] = t
result = paramType
of tyAlias, tyOwned, tySink:
of tyAlias, tyOwned:
result = recurse(paramType.base)
of tySequence, tySet, tyArray, tyOpenArray,
tyVar, tyLent, tyPtr, tyRef, tyProc:
tyVar, tyLent, tyPtr, tyRef, tyProc, tySink:
# XXX: this is a bit strange, but proc(s: seq)
# produces tySequence(tyGenericParam, tyNone).
# This also seems to be true when creating aliases
@@ -1277,7 +1336,7 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
result = semTypeNode(c, n[0], nil)
constraint = semNodeKindConstraints(n, c.config, 1)
elif n.kind == nkCall and
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and
considerQuotedIdent(c, n[0]).s == "{}":
result = semTypeNode(c, n[1], nil)
constraint = semNodeKindConstraints(n, c.config, 2)
@@ -1307,6 +1366,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
result = newProcType(c, n.info, prev)
var check = initIntSet()
var counter = 0
template isCurrentlyGeneric: bool =
# genericParams might update as implicit generic params are added
genericParams != nil and genericParams.len > 0
for i in 1..<n.len:
var a = n[i]
@@ -1327,7 +1389,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
hasDefault = a[^1].kind != nkEmpty
if hasType:
let isGeneric = isCurrentlyGeneric()
inc c.inGenericContext, ord(isGeneric)
typ = semParamType(c, a[^2], constraint)
dec c.inGenericContext, ord(isGeneric)
# TODO: Disallow typed/untyped in procs in the compiler/stdlib
if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped):
if not isMagic(getCurrOwner(c)):
@@ -1347,15 +1412,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
"either use ';' (semicolon) or explicitly write each default value")
message(c.config, a.info, warnImplicitDefaultValue, msg)
block determineType:
var defTyp = typ
if genericParams != nil and genericParams.len > 0:
defTyp = nil
def = semGenericStmt(c, def)
if hasUnresolvedArgs(c, def):
def.typ = makeTypeFromExpr(c, def.copyTree)
var canBeVoid = false
if kind == skTemplate:
if typ != nil and typ.kind == tyUntyped:
# don't do any typechecking or assign a type for
# `untyped` parameter default value
break determineType
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, defTyp)
elif hasUnresolvedArgs(c, def):
# template default value depends on other parameter
# don't do any typechecking
def.typ() = makeTypeFromExpr(c, def.copyTree)
break determineType
elif typ != nil and typ.kind == tyTyped:
canBeVoid = true
let isGeneric = isCurrentlyGeneric()
inc c.inGenericContext, ord(isGeneric)
if canBeVoid:
def = semExpr(c, def, {efDetermineType, efAllowSymChoice}, typ)
else:
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ)
dec c.inGenericContext, ord(isGeneric)
if def.referencesAnotherParam(getCurrOwner(c)):
def.flags.incl nfDefaultRefsParam
@@ -1374,7 +1450,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
typ.flags.incl tfCheckedForDestructor
else:
elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void
# if def.typ != nil and def.typ.kind != tyNone:
# example code that triggers it:
# proc sort[T](cmp: proc(a, b: T): int = cmp)
@@ -1427,11 +1503,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
onDef(a[j].info, arg)
a[j] = newSymNode(arg)
var r: PType =
if n[0].kind != nkEmpty:
semTypeNode(c, n[0], nil)
else:
nil
var r: PType = nil
if n[0].kind != nkEmpty:
let isGeneric = isCurrentlyGeneric()
inc c.inGenericContext, ord(isGeneric)
r = semTypeNode(c, n[0], nil)
dec c.inGenericContext, ord(isGeneric)
if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped:
# XXX: To implement the proposed change in the warning, just
@@ -1482,9 +1559,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
# XXX This rather hacky way keeps 'tflatmap' compiling:
if tfHasMeta notin oldFlags:
result.flags.excl tfHasMeta
result.n.typ = r
result.n.typ() = r
if genericParams != nil and genericParams.len > 0:
if isCurrentlyGeneric():
for n in genericParams:
if {sfUsed, sfAnon} * n.sym.flags == {}:
result.flags.incl tfUnresolved
@@ -1499,8 +1576,8 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
n[i] = semStmt(c, n[i], {})
if n.len > 0:
result = semTypeNode(c, n[^1], prev)
n.typ = result
n[^1].typ = result
n.typ() = result
n[^1].typ() = result
else:
result = nil
@@ -1513,15 +1590,15 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
if n[0].kind notin {nkEmpty, nkSym}:
addDecl(c, newSymS(skLabel, n[0], c))
result = semStmtListType(c, n[1], prev)
n[1].typ = result
n.typ = result
n[1].typ() = result
n.typ() = result
closeScope(c)
c.p.breakInLoop = oldBreakInLoop
dec(c.p.nestedBlockCounter)
proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
result = semTypeNode(c, n, nil)
n.typ = makeTypeDesc(c, result)
n.typ() = makeTypeDesc(c, result)
proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): bool =
var
@@ -1594,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)
@@ -1641,7 +1723,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
recomputeFieldPositions(tx, tx.n, position)
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody} and prev != nil:
if prev != nil and (prev.kind == tyGenericBody or
typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}):
result = newTypeS(tyAlias, c)
result.rawAddSon typeExpr
result.sym = prev.sym
@@ -1679,6 +1762,10 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
# unnecessary new type creation
let alias = maybeAliasType(c, result, prev)
if alias != nil: result = alias
elif n.typ.kind == tyFromExpr and c.inGenericContext > 0:
# sometimes not possible to distinguish type from value in generic body,
# for example `T.Foo`, so both are handled under `tyFromExpr`
result = n.typ
else:
localError(c.config, n.info, "expected type, but got: " & n.renderTree)
result = errorType(c)
@@ -1764,12 +1851,15 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
discard "builtin pragma"
else:
trySuggestPragmas(c, key)
let ident = considerQuotedIdent(c, key)
if strTableGet(c.userPragmas, ident) != nil:
let ident =
if key.kind in nkIdentKinds:
considerQuotedIdent(c, key)
else:
nil
if ident != nil and strTableGet(c.userPragmas, ident) != nil:
discard "User-defined pragma"
else:
var amb = false
let sym = searchInScopes(c, ident, amb)
let sym = qualifiedLookUp(c, key, {})
# XXX: What to do here if amb is true?
if sym != nil and sfCustomPragma in sym.flags:
discard "Custom user pragma"
@@ -1786,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
@@ -1836,10 +1926,14 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let t = semExprWithType(c, n, {efInTypeof})
closeScope(c)
fixupTypeOf(c, prev, t)
result = t.typ
if result.kind == tyFromExpr:
result.flags.incl tfNonConstExpr
proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
@@ -1850,10 +1944,14 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
else:
m = mode.intVal
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
closeScope(c)
fixupTypeOf(c, prev, t)
result = t.typ
if result.kind == tyFromExpr:
result.flags.incl tfNonConstExpr
proc semTypeIdent(c: PContext, n: PNode): PSym =
if n.kind == nkSym:
@@ -1917,7 +2015,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
n.transitionNoneToSym()
n.sym = result
n.info = oldInfo
n.typ = result.typ
n.typ() = result.typ
else:
localError(c.config, n.info, "identifier expected")
result = errorSym(c, n)
@@ -1941,11 +2039,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkTupleConstr: result = semAnonTuple(c, n, prev)
of nkCallKinds:
let x = n[0]
let ident = case x.kind
of nkIdent: x.ident
of nkSym: x.sym.name
of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name
else: nil
let ident = x.getPIdent
if ident != nil and ident.s == "[]":
let b = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: b.add(n[i])
@@ -2035,20 +2129,21 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
elif op.id == ord(wType):
checkSonsLen(n, 2, c.config)
result = semTypeOf(c, n[1], prev)
elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf:
elif op.s == "typeof" and (
(n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or
(n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)):
result = semTypeOf2(c, n, prev)
elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
result = semTypeExpr(c, n[1], prev)
else:
if c.inGenericContext > 0 and n.kind == nkCall:
let n = semGenericStmt(c, n)
result = makeTypeFromExpr(c, n.copyTree)
else:
result = semTypeExpr(c, n, prev)
result = semTypeExpr(c, n, prev)
of nkWhenStmt:
var whenResult = semWhen(c, n, false)
if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType)
result = semTypeNode(c, whenResult, prev)
if whenResult.kind == nkWhenStmt:
result = whenResult.typ
else:
result = semTypeNode(c, whenResult, prev)
of nkBracketExpr:
checkMinSonsLen(n, 2, c.config)
var head = n[0]
@@ -2093,6 +2188,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mRef: result = semAnyRef(c, n, tyRef, prev)
of mPtr: result = semAnyRef(c, n, tyPtr, prev)
of mTuple: result = semTuple(c, n, prev)
of mBuiltinType:
case s.name.s
of "lent": result = semAnyRef(c, n, tyLent, prev)
of "sink": result = semAnyRef(c, n, tySink, prev)
of "owned": result = semAnyRef(c, n, tyOwned, prev)
else: result = semGeneric(c, n, s, prev)
else: result = semGeneric(c, n, s, prev)
of nkDotExpr:
let typeExpr = semExpr(c, n)
@@ -2122,7 +2223,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
if s.kind != skError: localError(c.config, n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
internalAssert c.config, s.typ.base.kind != tyNone
result = s.typ.base
elif prev == nil:
result = s.typ
@@ -2146,7 +2247,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
if s.kind == skType:
s.typ
else:
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
internalAssert c.config, s.typ.base.kind != tyNone
s.typ.base
let alias = maybeAliasType(c, t, prev)
if alias != nil:
@@ -2207,12 +2308,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkType: result = n.typ
of nkStmtListType: result = semStmtListType(c, n, prev)
of nkBlockType: result = semBlockType(c, n, prev)
of nkOpenSym: result = semTypeNode(c, n[0], prev)
else:
result = semTypeExpr(c, n, prev)
when false:
localError(c.config, n.info, "type expected, but got: " & renderTree(n))
result = newOrPrevType(tyError, prev, c)
n.typ = result
n.typ() = result
dec c.inTypeContext
proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
@@ -2359,7 +2461,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
else:
# the following line fixes ``TV2*[T:SomeNumber=TR] = array[0..1, T]``
# from manyloc/named_argument_bug/triengine:
def.typ = def.typ.skipTypes({tyTypeDesc})
def.typ() = def.typ.skipTypes({tyTypeDesc})
if not containsGenericType(def.typ):
def = fitNode(c, typ, def, def.info)

View File

@@ -12,7 +12,7 @@
import std / tables
import ast, astalgo, msgs, types, magicsys, semdata, renderer, options,
lineinfos, modulegraphs
lineinfos, modulegraphs, layeredtable
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -65,10 +65,6 @@ proc cacheTypeInst(c: PContext; inst: PType) =
addToGenericCache(c, gt.sym, inst)
type
LayeredIdTable* {.acyclic.} = ref object
topLayer*: TypeMapping
nextLayer*: LayeredIdTable
TReplTypeVars* = object
c*: PContext
typeMap*: LayeredIdTable # map PType to PType
@@ -84,27 +80,12 @@ 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
proc initLayeredTypeMap*(pt: sink TypeMapping): LayeredIdTable =
result = LayeredIdTable()
result.topLayer = pt
proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable =
result = LayeredIdTable(nextLayer: cl.typeMap, topLayer: initTable[ItemId, PType]())
proc lookup(typeMap: LayeredIdTable, key: PType): PType =
result = nil
var tm = typeMap
while tm != nil:
result = getOrDefault(tm.topLayer, key.itemId)
if result != nil: return
tm = tm.nextLayer
template put(typeMap: LayeredIdTable, key, value: PType) =
typeMap.topLayer[key.itemId] = value
result = newTypeMapLayer(cl.typeMap)
template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code
when false:
@@ -114,28 +95,90 @@ 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 =
proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
## instantiates a given generic expression, not a type node
if n.kind == nkSym and n.sym.kind == skType and
n.sym.typ != nil and n.sym.typ.kind == tyGenericBody:
# generic body types are allowed as user expressions, see #24090
return n
let t = replaceTypeVarsT(cl, n.typ)
if t != nil and t.kind == tyStatic and t.n != nil:
return if tfUnresolved in t.flags: prepareNode(cl, t.n)
else: t.n
result = copyNode(n)
result.typ = t
result.typ() = t
if result.kind == nkSym:
result.sym =
if n.typ != nil and n.typ == n.sym.typ:
replaceTypeVarsS(cl, n.sym, result.typ)
else:
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
let isCall = result.kind in nkCallKinds
for i in 0..<n.safeLen:
# XXX HACK: ``f(a, b)``, avoid to instantiate `f`
if isCall and i == 0: result.add(n[i])
else: result.add(prepareNode(cl, n[i]))
# we need to avoid trying to instantiate nodes that can have uninstantiated
# types, like generic proc symbols or raw generic type symbols
case n.kind
of nkSymChoices:
# don't try to instantiate symchoice symbols, they can be
# generic procs which the compiler will think are uninstantiated
# because their type will contain uninstantiated params
for i in 0..<n.len:
result.add(n[i])
of nkCallKinds:
# don't try to instantiate call names since they may be generic proc syms
# also bracket expressions can turn into calls with symchoice [] and
# we need to not instantiate the Generic in Generic[int]
# exception exists for the call name being a dot expression since
# dot expressions need their LHS instantiated
assert n.len != 0
# avoid instantiating generic proc symbols, refine condition if needed:
let ignoreFirst = n[0].kind notin {nkDotExpr, nkBracketExpr} + nkCallKinds
let name = n[0].getPIdent
let ignoreSecond = name != nil and name.s == "[]" and n.len > 1 and
# generic type instantiation:
((n[1].typ != nil and n[1].typ.kind == tyTypeDesc) or
# generic proc instantiation:
(n[1].kind == nkSym and n[1].sym.isGenericRoutineStrict))
if ignoreFirst:
result.add(n[0])
else:
result.add(prepareNode(cl, n[0]))
if n.len > 1:
if ignoreSecond:
result.add(n[1])
else:
result.add(prepareNode(cl, n[1]))
for i in 2..<n.len:
result.add(prepareNode(cl, n[i]))
of nkBracketExpr:
# don't instantiate Generic body type in expression like Generic[T]
# exception exists for the call name being a dot expression since
# dot expressions need their LHS instantiated
assert n.len != 0
let ignoreFirst = n[0].kind != nkDotExpr and
# generic type instantiation:
((n[0].typ != nil and n[0].typ.kind == tyTypeDesc) or
# generic proc instantiation:
(n[0].kind == nkSym and n[0].sym.isGenericRoutineStrict))
if ignoreFirst:
result.add(n[0])
else:
result.add(prepareNode(cl, n[0]))
for i in 1..<n.len:
result.add(prepareNode(cl, n[i]))
of nkDotExpr:
# don't try to instantiate RHS of dot expression, it can outright be
# undeclared, but definitely instantiate LHS
assert n.len >= 2
result.add(prepareNode(cl, n[0]))
result.add(n[1])
for i in 2..<n.len:
result.add(prepareNode(cl, n[i]))
else:
for i in 0..<n.safeLen:
result.add(prepareNode(cl, n[i]))
proc isTypeParam(n: PNode): bool =
# XXX: generic params should use skGenericParam instead of skType
@@ -218,7 +261,10 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
if n == nil: return
result = copyNode(n)
if n.typ != nil:
result.typ = replaceTypeVarsT(cl, n.typ)
if n.typ.kind == tyFromExpr:
# type of node should not be evaluated as a static value
n.typ.flags.incl tfNonConstExpr
result.typ() = replaceTypeVarsT(cl, n.typ)
checkMetaInvariants(cl, result.typ)
case n.kind
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
@@ -230,7 +276,13 @@ 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.typ.kind == tyVoid:
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
result = newNodeI(nkRecList, n.info)
of nkRecWhen:
@@ -292,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
@@ -311,7 +363,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
result = copySym(s, cl.c.idgen)
incl(result.flags, sfFromGeneric)
#idTablePut(cl.symMap, s, result)
result.owner = s.owner
setOwner(result, s.owner)
result.typ = t
if result.kind != skType:
result.ast = replaceTypeVarsN(cl, s.ast)
@@ -429,12 +481,12 @@ 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
cl.typeMap = cl.typeMap.nextLayer
setToPreviousLayer(cl.typeMap)
# This is actually wrong: tgeneric_closure fails with this line:
#newbody.callConv = body.callConv
@@ -526,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
@@ -569,6 +621,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result.kind = tyUserTypeClassInst
of tyGenericBody:
if cl.allowMetaTypes: return
localError(
cl.c.config,
cl.info,
@@ -587,13 +640,18 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
assert t.n.typ != t
var n = prepareNode(cl, t.n)
if n.kind != nkEmpty:
n = cl.c.semConstExpr(cl.c, n)
if tfNonConstExpr in t.flags:
n = cl.c.semExprWithType(cl.c, n, flags = {efInTypeof})
else:
n = cl.c.semConstExpr(cl.c, n)
if n.typ.kind == tyTypeDesc:
# XXX: sometimes, chained typedescs enter here.
# It may be worth investigating why this is happening,
# because it may cause other bugs elsewhere.
result = n.typ.skipTypes({tyTypeDesc})
# result = n.typ.base
elif tfNonConstExpr in t.flags:
result = n.typ
else:
if n.typ.kind != tyStatic and n.kind != nkType:
# XXX: In the future, semConstExpr should
@@ -619,8 +677,31 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
elif t.elementType.kind != tyNone:
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.elementType))
of tyUserTypeClass, tyStatic:
of tyUserTypeClass:
result = t
of tyStatic:
if cl.c.matchedConcept != nil:
# allow concepts to not instantiate statics for now
# they can't always infer them
return
if not containsGenericType(t) and (t.n == nil or t.n.kind in nkLiterals):
# no need to instantiate
return
bailout()
result = instCopyType(cl, t)
cl.localCache[t.itemId] = result
for i in FirstGenericParamAt..<result.kidsLen:
var r = result[i]
if r != nil:
r = replaceTypeVarsT(cl, r)
result[i] = r
propagateToOwner(result, r)
result.n = replaceTypeVarsN(cl, result.n)
if not cl.allowMetaTypes and result.n != nil and
result.base.kind != tyNone:
result.n = cl.c.semConstExpr(cl.c, result.n)
result.n.typ() = result.base
of tyGenericInst, tyUserTypeClassInst:
bailout()
@@ -631,23 +712,27 @@ 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:
if resulti.kind == tyGenericBody:
if resulti.kind == tyGenericBody and not cl.allowMetaTypes:
localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info,
"cannot instantiate '" &
typeToString(result[i], preferDesc) &
"' inside of type definition: '" &
t.owner.name.s & "'; Maybe generic arguments are missing?")
var r = replaceTypeVarsT(cl, 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})
@@ -696,16 +781,24 @@ proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo;
localCache: initTypeMapping(), typeMap: typeMap,
info: info, c: p, owner: owner)
proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
proc replaceTypesInBody*(p: PContext, pt: LayeredIdTable, n: PNode;
owner: PSym, allowMetaTypes = false,
fromStaticExpr = false, expectedType: PType = nil): PNode =
var typeMap = initLayeredTypeMap(pt)
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
var cl = initTypeVars(p, typeMap, n.info, owner)
cl.allowMetaTypes = allowMetaTypes
pushInfoContext(p.config, n.info)
result = replaceTypeVarsN(cl, n, expectedType = expectedType)
popInfoContext(p.config)
proc prepareTypesInBody*(p: PContext, pt: LayeredIdTable, n: PNode;
owner: PSym = nil): PNode =
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
var cl = initTypeVars(p, typeMap, n.info, owner)
pushInfoContext(p.config, n.info)
result = prepareNode(cl, n)
popInfoContext(p.config)
when false:
# deadcode
proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
@@ -733,13 +826,13 @@ proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) =
inc currPosition
else: discard "cannot happen"
proc generateTypeInstance*(p: PContext, pt: TypeMapping, info: TLineInfo,
proc generateTypeInstance*(p: PContext, pt: LayeredIdTable, info: TLineInfo,
t: PType): PType =
# Given `t` like Foo[T]
# pt: Table with type mappings: T -> int
# Desired result: Foo[int]
# proc (x: T = 0); T -> int ----> proc (x: int = 0)
var typeMap = initLayeredTypeMap(pt)
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
var cl = initTypeVars(p, typeMap, info, nil)
pushInfoContext(p.config, info)
result = replaceTypeVarsT(cl, t)
@@ -749,15 +842,15 @@ proc generateTypeInstance*(p: PContext, pt: TypeMapping, info: TLineInfo,
var position = 0
recomputeFieldPositions(objType, objType.n, position)
proc prepareMetatypeForSigmatch*(p: PContext, pt: TypeMapping, info: TLineInfo,
proc prepareMetatypeForSigmatch*(p: PContext, pt: LayeredIdTable, info: TLineInfo,
t: PType): PType =
var typeMap = initLayeredTypeMap(pt)
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
var cl = initTypeVars(p, typeMap, info, nil)
cl.allowMetaTypes = true
pushInfoContext(p.config, info)
result = replaceTypeVarsT(cl, t)
popInfoContext(p.config)
template generateTypeInstance*(p: PContext, pt: TypeMapping, arg: PNode,
template generateTypeInstance*(p: PContext, pt: LayeredIdTable, arg: PNode,
t: PType): untyped =
generateTypeInstance(p, pt, arg.info, t)

View File

@@ -55,6 +55,8 @@ proc hashSym(c: var MD5Context, s: PSym) =
c &= it.name.s
c &= "."
it = it.owner
c &= "#"
c &= s.disamb
proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) =
if sfAnon in s.flags or s.kind == skGenericParam:
@@ -69,6 +71,8 @@ proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) =
c &= it.name.s
c &= "."
it = it.owner
c &= "#"
c &= s.disamb
proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: ConfigRef) =
if n == nil:
@@ -154,9 +158,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
# is actually safe without an infinite recursion check:
if t.sym != nil:
if {sfCompilerProc} * t.sym.flags != {}:
doAssert t.sym.loc.r != ""
doAssert t.sym.loc.snippet != ""
# The user has set a specific name for this type
c &= t.sym.loc.r
c &= t.sym.loc.snippet
elif CoOwnerSig in flags:
c.hashTypeSym(t.sym, conf)
else:

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@ proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNod
arg.sym.typ.kind notin {tyVar, tySink, tyOwned}:
# Watch out: cannot do this inference for procs with forward
# declarations.
if {sfWasForwarded, sfNosinks} * owner.flags == {}:
if sfWasForwarded notin owner.flags:
let argType = arg.sym.typ
let sinkType = newType(tySink, idgen, owner)
@@ -43,9 +43,9 @@ proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNod
#message(config, arg.info, warnUser,
# ("turned '$1' to a sink parameter") % [$arg])
#echo config $ arg.info, " turned into a sink parameter ", arg.sym.name.s
elif {sfWasForwarded, sfNosinks} * arg.sym.flags == {}:
elif sfWasForwarded notin arg.sym.flags:
# we only report every potential 'sink' parameter only once:
incl arg.sym.flags, sfNosinks
incl arg.sym.flags, sfWasForwarded
message(config, arg.info, hintPerformance,
"could not turn '$1' to a sink parameter" % [arg.sym.name.s])
#echo config $ arg.info, " candidate for a sink parameter here"

View File

@@ -385,6 +385,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
accum.maxAlign = 1
computeObjectOffsetsFoldFunction(conf, typ.n, true, accum)
else:
if typ.baseClass == nil and lacksMTypeField(typ) and typ.n.len == 1 and
typ.n[0].kind == nkSym and
typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
# a dummy field is generated for an object with a single field
# with an UncheckedArray type
assert accum.offset == 0
accum.offset = 1
computeObjectOffsetsFoldFunction(conf, typ.n, false, accum)
let paddingAtEnd = int16(accum.finish())
if typ.sym != nil and
@@ -470,7 +477,7 @@ template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
if size >= 0:
let res = newIntNode(nkIntLit, size)
res.info = node.info
res.typ = node.typ
res.typ() = node.typ
res
else:
fallback
@@ -484,7 +491,7 @@ template foldAlignOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
if align >= 0:
let res = newIntNode(nkIntLit, align)
res.info = node.info
res.typ = node.typ
res.typ() = node.typ
res
else:
fallback
@@ -512,7 +519,7 @@ template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
if offset >= 0:
let tmp = newIntNode(nkIntLit, offset)
tmp.info = node.info
tmp.typ = node.typ
tmp.typ() = node.typ
tmp
else:
fallback

View File

@@ -16,7 +16,7 @@ from trees import getMagic, getRoot
proc callProc(a: PNode): PNode =
result = newNodeI(nkCall, a.info)
result.add a
result.typ = a.typ.returnType
result.typ() = a.typ.returnType
# we have 4 cases to consider:
# - a void proc --> nothing to do
@@ -117,7 +117,7 @@ proc castToVoidPointer(g: ModuleGraph, n: PNode, fvField: PNode): PNode =
result = newNodeI(nkCast, fvField.info)
result.add newNodeI(nkEmpty, fvField.info)
result.add fvField
result.typ = ptrType
result.typ() = ptrType
proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
varSection, varInit, call, barrier, fv: PNode;
@@ -200,7 +200,7 @@ proc createCastExpr(argsParam: PSym; objType: PType; idgen: IdGenerator): PNode
result = newNodeI(nkCast, argsParam.info)
result.add newNodeI(nkEmpty, argsParam.info)
result.add newSymNode(argsParam)
result.typ = newType(tyPtr, idgen, objType.owner)
result.typ() = newType(tyPtr, idgen, objType.owner)
result.typ.rawAddSon(objType)
template checkMagicProcs(g: ModuleGraph, n: PNode, formal: PNode) =
@@ -266,9 +266,9 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
if argType.kind in {tyVarargs, tyOpenArray}:
# important special case: we always create a zero-copy slice:
let slice = newNodeI(nkCall, n.info, 4)
slice.typ = n.typ
slice.typ() = n.typ
slice[0] = newSymNode(createMagic(g, idgen, "slice", mSlice))
slice[0].typ = getSysType(g, n.info, tyInt) # fake type
slice[0].typ() = getSysType(g, n.info, tyInt) # fake type
var fieldB = newSym(skField, tmpName, idgen, objType.owner, n.info, g.config.options)
fieldB.typ = getSysType(g, n.info, tyInt)
discard objType.addField(fieldB, g.cache, idgen)

View File

@@ -56,7 +56,6 @@ type
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
deferDetected, tooEarly: bool
isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields)
inAddr: bool
flags: TransformFlags
graph: ModuleGraph
idgen: IdGenerator
@@ -100,12 +99,19 @@ proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
let owner = getCurrOwner(c)
result = newSymNode(r)
proc transform(c: PTransf, n: PNode): PNode
proc transform(c: PTransf, n: PNode, noConstFold = false): PNode
proc transformSons(c: PTransf, n: PNode): PNode =
proc transformSons(c: PTransf, n: PNode, noConstFold = false): PNode =
result = newTransNode(n)
for i in 0..<n.len:
result[i] = transform(c, n[i])
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)
@@ -176,7 +182,7 @@ proc freshVar(c: PTransf; v: PSym): PNode =
let owner = getCurrOwner(c)
var newVar = copySym(v, c.idgen)
incl(newVar.flags, sfFromGeneric)
newVar.owner = owner
setOwner(newVar, owner)
result = newSymNode(newVar)
proc transformVarSection(c: PTransf, v: PNode): PNode =
@@ -357,7 +363,7 @@ proc transformAsgn(c: PTransf, n: PNode): PNode =
# given tuple type
newTupleConstr[i] = def[0]
newTupleConstr.typ = rhs.typ
newTupleConstr.typ() = rhs.typ
let asgnNode = newTransNode(nkAsgn, n.info, 2)
asgnNode[0] = transform(c, n[0])
@@ -367,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``
@@ -401,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)
@@ -409,15 +429,9 @@ proc transformYield(c: PTransf, n: PNode): PNode =
result.add transform(c, v)
for i in 0..<c.transCon.forStmt.len - 2:
let lhs = c.transCon.forStmt[i]
let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
result.add(asgnTo(lhs, rhs))
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)
@@ -430,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)
@@ -469,8 +484,8 @@ proc transformYield(c: PTransf, n: PNode): PNode =
result.add(introduceNewLocalVars(c, c.transCon.forLoopBody))
c.isIntroducingNewLocalVars = false
proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
result = transformSons(c, n)
proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds, isAddr = false): PNode =
result = transformSons(c, n, noConstFold = isAddr)
# inlining of 'var openarray' iterators; bug #19977
if n.typ.kind != tyOpenArray and (c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags): return
var n = result
@@ -482,9 +497,9 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
n[0][0] = m[0]
result = n[0]
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
result.typ = n.typ
result.typ() = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
result.typ() = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var m = n[0][1]
if m.kind in kinds:
@@ -492,15 +507,25 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
n[0][1] = m[0]
result = n[0]
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
result.typ = n.typ
result.typ() = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
result.typ() = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
else:
if n[0].kind in kinds:
if n[0].kind in kinds and
not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and
n[0][0].typ.skipTypes(abstractVar).kind == tyTuple
) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and
n.typ.kind == tyVar and
n.typ.skipTypes(abstractVar).kind == tyOpenArray and
n[0][0].typ.skipTypes(abstractVar).kind == tyString) 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
result = n[0][0]
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
result.typ = n.typ
result.typ() = n.typ
proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
## Converts 'prc' into '(thunk, nil)' so that it's compatible with
@@ -559,7 +584,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
else:
result = transform(c, n[1])
#result = transformSons(c, n)
result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen)
result.typ() = takeType(n.typ, n[1].typ, c.graph, c.idgen)
#echo n.info, " came here and produced ", typeToString(result.typ),
# " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
of tyCstring:
@@ -587,7 +612,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
result[0] = transform(c, n[1])
else:
result = transform(c, n[1])
result.typ = n.typ
result.typ() = n.typ
else:
result = transformSons(c, n)
of tyObject:
@@ -600,7 +625,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
result[0] = transform(c, n[1])
else:
result = transform(c, n[1])
result.typ = n.typ
result.typ() = n.typ
of tyGenericParam, tyOrdinal:
result = transform(c, n[1])
# happens sometimes for generated assignments, etc.
@@ -624,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:
@@ -632,9 +663,11 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
case arg.kind
of nkEmpty..nkNilLit:
result = paDirectMapping
of nkDotExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr:
of nkDotExpr, nkDerefExpr, nkHiddenDeref:
result = putArgInto(arg[0], formal)
#if result == paViaIndirection: result = paFastAsgn
of nkAddr, nkHiddenAddr:
result = putArgInto(arg[0], formal)
if result == paViaIndirection: result = paFastAsgn
of nkCurly, nkBracket:
for i in 0..<arg.len:
if putArgInto(arg[i], formal) != paDirectMapping:
@@ -788,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))
@@ -818,7 +851,7 @@ proc transformCase(c: PTransf, n: PNode): PNode =
# as an expr
let kind = if n.typ != nil: nkIfExpr else: nkIfStmt
ifs = newTransNode(kind, it.info, 0)
ifs.typ = n.typ
ifs.typ() = n.typ
ifs.add(e)
of nkElse:
if ifs == nil: result.add(e)
@@ -842,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
@@ -926,7 +968,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode =
let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
convNode[0] = newNodeI(nkEmpty, n.info)
convNode[1] = excCall
convNode.typ = excTypeNode.typ.toRef(c.idgen)
convNode.typ() = excTypeNode.typ.toRef(c.idgen)
# -> let exc = ...
let identDefs = newTransNode(nkIdentDefs, n[1].info, 3)
identDefs[0] = n[0][2]
@@ -983,12 +1025,12 @@ proc transformDerefBlock(c: PTransf, n: PNode): PNode =
# We transform (block: x)[] to (block: x[])
let e0 = n[0]
result = shallowCopy(e0)
result.typ = n.typ
result.typ() = n.typ
for i in 0 ..< e0.len - 1:
result[i] = e0[i]
result[e0.len-1] = newTreeIT(nkHiddenDeref, n.info, n.typ, e0[e0.len-1])
proc transform(c: PTransf, n: PNode): PNode =
proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
when false:
var oldDeferAnchor: PNode
if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
@@ -1054,13 +1096,8 @@ proc transform(c: PTransf, n: PNode): PNode =
of nkBreakStmt: result = transformBreak(c, n)
of nkCallKinds:
result = transformCall(c, n)
of nkHiddenAddr:
result = transformAddrDeref(c, n, {nkHiddenDeref})
of nkAddr:
let oldInAddr = c.inAddr
c.inAddr = true
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref})
c.inAddr = oldInAddr
of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}, isAddr = true)
of nkDerefExpr:
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
of nkHiddenDeref:
@@ -1072,6 +1109,9 @@ proc transform(c: PTransf, n: PNode): 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:
@@ -1140,7 +1180,7 @@ proc transform(c: PTransf, n: PNode): PNode =
let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
n.typ != nil and
n.typ.kind == tyPointer
if not exprIsPointerCast and not c.inAddr:
if not exprIsPointerCast and not noConstFold:
var cnst = getConstExpr(c.module, result, c.idgen, c.graph)
# we inline constants if they are not complex constants:
if cnst != nil and not dontInlineConstant(n, cnst):
@@ -1190,7 +1230,7 @@ proc liftDeferAux(n: PNode) =
tryStmt.add deferPart
n[i] = tryStmt
n.sons.setLen(i+1)
n.typ = tryStmt.typ
n.typ() = tryStmt.typ
goOn = true
break
for i in 0..n.safeLen-1:

View File

@@ -147,6 +147,13 @@ proc whichPragma*(n: PNode): TSpecialWord =
of nkCast: return wCast
of nkClosedSymChoice, nkOpenSymChoice:
return whichPragma(key[0])
of nkBracketExpr:
if n.kind notin nkPragmaCallKinds: return wInvalid
result = whichPragma(key[0])
if result notin {wHint, wHintAsError, wWarning, wWarningAsError}:
# note bracket pragmas, see processNote
result = wInvalid
return
else: return wInvalid
if result in nonPragmaWordsLow..nonPragmaWordsHigh:
result = wInvalid

View File

@@ -200,7 +200,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = typeAllowedNode(marker, t.n, kind, c, flags)
of tyEmpty:
if kind in {skVar, skLet}: result = t
of tyProxy:
of tyError:
# for now same as error node; we say it's a valid type as it should
# prevent cascading errors:
result = nil
@@ -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
@@ -232,7 +235,11 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
if result: return
if not containsOrIncl(marker, t.id):
case t.kind
of tyGenericInst, tyGenericBody, tyAlias, tySink, tyInferred:
of tyGenericBody:
# treat as atomic, containsUnresolvedType wants always false,
# containsGenericType always gives true
discard
of tyGenericInst, tyAlias, tySink, tyInferred:
result = iterOverTypeAux(marker, skipModifier(t), iter, closure)
else:
for a in t.kids:
@@ -748,6 +755,16 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if tfThread in t.flags:
addSep(prag)
prag.add("gcsafe")
var effectsOfStr = ""
for i, a in t.paramTypes:
let j = paramTypeToNodeIndex(i)
if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags:
addSep(effectsOfStr)
effectsOfStr.add(t.n[j].sym.name.s)
if effectsOfStr != "":
addSep(prag)
prag.add("effectsOf: ")
prag.add(effectsOfStr)
if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
let effects = t.owner.typ.n[0]
if effects.kind == nkEffectList and effects.len == effectListLen:
@@ -776,7 +793,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
case t.kind
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyError:
result = Zero
of tySet, tyVar: result = firstOrd(conf, t.elementType)
of tyArray: result = firstOrd(conf, t.indexType)
@@ -909,7 +926,7 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 =
result = lastOrd(conf, skipModifier(t))
of tyUserTypeClasses:
result = lastOrd(conf, last(t))
of tyProxy: result = Zero
of tyError: result = Zero
of tyOrdinal:
if t.hasElementType: result = lastOrd(conf, skipModifier(t))
else:
@@ -984,6 +1001,8 @@ type
AllowCommonBase
PickyCAliases # be picky about the distinction between 'cint' and 'int32'
IgnoreFlags # used for borrowed functions and methods; ignores the tfVarIsPtr flag
PickyBackendAliases # be picky about different aliases
IgnoreRangeShallow
TTypeCmpFlags* = set[TTypeCmpFlag]
@@ -1185,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
@@ -1212,26 +1233,40 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
inc c.recCheck
else:
if containsOrIncl(c, a, b): return true
template maybeSkipRange(x: set[TTypeKind]): set[TTypeKind] =
if IgnoreRangeShallow in c.flags:
x + {tyRange}
else:
x
template withoutShallowFlags(body) =
let oldFlags = c.flags
c.flags.excl IgnoreRangeShallow
body
c.flags = oldFlags
if x == y: return true
var a = skipTypes(x, {tyAlias})
let aliasSkipSet = maybeSkipRange({tyAlias})
var a = skipTypes(x, aliasSkipSet)
while a.kind == tyUserTypeClass and tfResolved in a.flags:
a = skipTypes(a.last, {tyAlias})
var b = skipTypes(y, {tyAlias})
a = skipTypes(a.last, aliasSkipSet)
var b = skipTypes(y, aliasSkipSet)
while b.kind == tyUserTypeClass and tfResolved in b.flags:
b = skipTypes(b.last, {tyAlias})
b = skipTypes(b.last, aliasSkipSet)
assert(a != nil)
assert(b != nil)
if a.kind != b.kind:
case c.cmp
of dcEq: return false
of dcEqIgnoreDistinct:
a = a.skipTypes({tyDistinct, tyGenericInst})
b = b.skipTypes({tyDistinct, tyGenericInst})
if a.kind != b.kind: return false
of dcEqOrDistinctOf:
a = a.skipTypes({tyDistinct, tyGenericInst})
if a.kind != b.kind: return false
case c.cmp
of dcEq:
if a.kind != b.kind: return false
of dcEqIgnoreDistinct:
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
a = a.skipTypes(distinctSkipSet)
b = b.skipTypes(distinctSkipSet)
if a.kind != b.kind: return false
of dcEqOrDistinctOf:
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
a = a.skipTypes(distinctSkipSet)
if a.kind != b.kind: return false
#[
The following code should not run in the case either side is an generic alias,
@@ -1239,14 +1274,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
objects ie `type A[T] = SomeObject`
]#
# this is required by tunique_type but makes no sense really:
if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
if c.cmp == dcEq and x.kind == tyGenericInst and
IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
let
lhs = x.skipGenericAlias
rhs = y.skipGenericAlias
if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen:
return false
for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
if not sameTypeAux(ff, aa, c): return false
withoutShallowFlags:
for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
if not sameTypeAux(ff, aa, c): return false
return true
case a.kind
@@ -1260,6 +1297,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
let symFlagsB = if b.sym != nil: b.sym.flags else: {}
if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
result = symFlagsA == symFlagsB
elif result and PickyBackendAliases in c.flags:
let symFlagsA = if a.sym != nil: a.sym.flags else: {}
let symFlagsB = if b.sym != nil: b.sym.flags else: {}
if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
result = a.id == b.id
of tyStatic, tyFromExpr:
result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
@@ -1267,15 +1309,34 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
cycleCheck()
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
of tyObject:
ifFastObjectTypeCheckFailed(a, b):
cycleCheck()
result = sameObjectStructures(a, b, c) and sameFlags(a, b)
result = sameFlags(a, b)
if result:
ifFastObjectTypeCheckFailed(a, b):
cycleCheck()
# should be generic, and belong to the same generic head type:
assert a.typeInst != nil, "generic object " & $a & " has no typeInst"
assert b.typeInst != nil, "generic object " & $b & " has no typeInst"
if result:
withoutShallowFlags:
# this is required because of generic `ref object`s,
# the value of their dereferences are not wrapped in `tyGenericInst`,
# so we need to check the generic parameters here
for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
if not sameTypeAux(ff, aa, c): return false
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:
@@ -1284,8 +1345,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
of tyError:
result = b.kind == tyError
of tyTuple:
cycleCheck()
result = sameTuple(a, b, c) and sameFlags(a, b)
withoutShallowFlags:
cycleCheck()
result = sameTuple(a, b, c) and sameFlags(a, b)
of tyTypeDesc:
if c.cmp == dcEqIgnoreDistinct: result = false
elif ExactTypeDescValues in c.flags:
@@ -1309,7 +1371,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
tyAnd, tyOr, tyNot, tyAnything, tyOwned:
cycleCheck()
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
result = sameChildrenAux(a, b, c)
withoutShallowFlags:
result = sameChildrenAux(a, b, c)
if result and IgnoreFlags notin c.flags:
if IgnoreTupleFields in c.flags:
result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam}
@@ -1322,8 +1385,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n))
of tyRange:
cycleCheck()
result = sameTypeOrNilAux(a.elementType, b.elementType, c) and
sameValue(a.n[0], b.n[0]) and
result = sameTypeOrNilAux(a.elementType, b.elementType, c)
if result and IgnoreRangeShallow notin c.flags:
result = sameValue(a.n[0], b.n[0]) and
sameValue(a.n[1], b.n[1])
of tyAlias, tyInferred, tyIterable:
cycleCheck()
@@ -1333,8 +1397,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
# The type system must distinguish between `T[int] = object #[empty]#`
# and `T[float] = object #[empty]#`!
cycleCheck()
for ff, aa in underspecifiedPairs(a, b, 1, -1):
if not sameTypeAux(ff, aa, c): return false
withoutShallowFlags:
for ff, aa in underspecifiedPairs(a, b, 1, -1):
if not sameTypeAux(ff, aa, c): return false
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
of tyNone: result = false
of tyConcept:
@@ -1346,6 +1411,19 @@ proc sameBackendType*(x, y: PType): bool =
c.cmp = dcEqIgnoreDistinct
result = sameTypeAux(x, y, c)
proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
var c = initSameTypeClosure()
c.flags.incl IgnoreTupleFields
c.flags.incl IgnoreRangeShallow
c.cmp = dcEqIgnoreDistinct
result = sameTypeAux(x, y, c)
proc sameBackendTypePickyAliases*(x, y: PType): bool =
var c = initSameTypeClosure()
c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases}
c.cmp = dcEqIgnoreDistinct
result = sameTypeAux(x, y, c)
proc compareTypes*(x, y: PType,
cmp: TDistinctCompare = dcEq,
flags: TTypeCmpFlags = {}): bool =
@@ -1406,6 +1484,8 @@ proc commonSuperclass*(a, b: PType): PType =
return t
y = y.baseClass
proc lacksMTypeField*(typ: PType): bool {.inline.} =
(typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
include sizealignoffsetimpl
@@ -1442,6 +1522,23 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil)
proc containsUnresolvedTypeIter(t: PType, closure: RootRef): bool =
if tfUnresolved in t.flags: return true
case t.kind
of tyStatic:
return t.n == nil
of tyTypeDesc:
if t.base.kind == tyNone: return true
if containsUnresolvedTypeIter(t.base, closure): return true
return false
of tyGenericInvocation, tyGenericParam, tyFromExpr, tyAnything:
return true
else:
return false
proc containsUnresolvedType*(t: PType): bool =
result = iterOverType(t, containsUnresolvedTypeIter, nil)
proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
if t.kind == tyDistinct:
result = t.elementType
@@ -1611,7 +1708,7 @@ proc skipHidden*(n: PNode): PNode =
proc skipConvTakeType*(n: PNode): PNode =
result = n.skipConv
result.typ = n.typ
result.typ() = n.typ
proc isEmptyContainer*(t: PType): bool =
case t.kind
@@ -1651,7 +1748,7 @@ proc skipHiddenSubConv*(n: PNode; g: ModuleGraph; idgen: IdGenerator): PNode =
result = n
else:
result = copyTree(result)
result.typ = dest
result.typ() = dest
else:
result = n
@@ -1725,6 +1822,13 @@ proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, co
of efTagsIllegal:
msg.add "\n.notTag catched an illegal effect"
proc typeNameAndDesc*(t: PType): string =
result = typeToString(t)
let desc = typeToString(t, preferDesc)
if result != desc:
result.add(" = ")
result.add(desc)
proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) =
if formal.kind != tyError and actual.kind != tyError:
let actualStr = typeToString(actual)
@@ -1855,5 +1959,124 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
else:
result = false
proc lacksMTypeField*(typ: PType): bool {.inline.} =
(typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
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

@@ -106,6 +106,7 @@ type
unanalysableMutation: bool
inAsgnSource, inConstructor, inNoSideEffectSection: int
inConditional, inLoop: int
inConvHasDestructor: int
owner: PSym
g: ModuleGraph
@@ -427,16 +428,28 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
# primitive literals including the empty are harmless:
discard
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv:
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
destMightOwn(c, dest, n[1])
of nkConv:
if hasDestructor(n.typ):
inc c.inConvHasDestructor
destMightOwn(c, dest, n[1])
dec c.inConvHasDestructor
else:
destMightOwn(c, dest, n[1])
of nkIfStmt, nkIfExpr:
for i in 0..<n.len:
inc c.inConditional
destMightOwn(c, dest, n[i].lastSon)
dec c.inConditional
of nkCaseStmt:
for i in 1..<n.len:
inc c.inConditional
destMightOwn(c, dest, n[i].lastSon)
dec c.inConditional
of nkStmtList, nkStmtListExpr:
if n.len > 0:
@@ -481,7 +494,7 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
of nkCallKinds:
if n.typ != nil:
if hasDestructor(n.typ):
if hasDestructor(n.typ) or c.inConvHasDestructor > 0:
# calls do construct, what we construct must be destroyed,
# so dest cannot be a cursor:
dest.flags.incl ownsData
@@ -489,8 +502,17 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
# we know the result is derived from the first argument:
var roots: seq[(PSym, int)] = @[]
allRoots(n[1], roots, RootEscapes)
for r in roots:
connect(c, dest.sym, r[0], n[1].info)
if roots.len == 0 and c.inConditional > 0:
# when in a conditional expression,
# to ensure that the first argument isn't outlived
# by the lvalue, we need find the root, otherwise
# it is probably a local temporary
# (e.g. a return value from a call),
# we should prevent cursorfication
dest.flags.incl preventCursor
else:
for r in roots:
connect(c, dest.sym, r[0], n[1].info)
else:
let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone

View File

@@ -202,7 +202,7 @@ proc copyValue(src: PNode): PNode =
return src
result = newNode(src.kind)
result.info = src.info
result.typ = src.typ
result.typ() = src.typ
result.flags = src.flags * PersistentNodeFlags
result.comment = src.comment
when defined(useNodeIds):
@@ -507,7 +507,7 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
setLen(node.sons, newLen)
if oldLen < newLen:
for i in oldLen..<newLen:
node[i] = getNullValue(typ.elementType, info, c.config)
node[i] = getNullValue(c, typ.elementType, info, c.config)
const
errNilAccess = "attempt to access a nil address"
@@ -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
@@ -609,7 +612,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcYldVal: assert false
of opcAsgnInt:
decodeB(rkInt)
regs[ra].intVal = regs[rb].intVal
if regs[rb].kind == rkInt:
regs[ra].intVal = regs[rb].intVal
else:
stackTrace(c, tos, pc, "opcAsgnInt: got " & $regs[rb].kind)
of opcAsgnFloat:
decodeB(rkFloat)
regs[ra].floatVal = regs[rb].floatVal
@@ -639,6 +645,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
regs[ra].intVal = cast[int](regs[rb].node.intVal)
of rkNodeAddr:
regs[ra].intVal = cast[int](regs[rb].nodeAddr)
of rkRegisterAddr:
regs[ra].intVal = cast[int](regs[rb].regAddr)
of rkInt:
regs[ra].intVal = regs[rb].intVal
else:
@@ -674,16 +682,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
else:
assert regs[rb].kind == rkNode
let nb = regs[rb].node
case nb.kind
of nkCharLit..nkUInt64Lit:
ensureKind(rkInt)
regs[ra].intVal = nb.intVal
of nkFloatLit..nkFloat64Lit:
ensureKind(rkFloat)
regs[ra].floatVal = nb.floatVal
if nb == nil:
stackTrace(c, tos, pc, errNilAccess)
else:
ensureKind(rkNode)
regs[ra].node = nb
case nb.kind
of nkCharLit..nkUInt64Lit:
ensureKind(rkInt)
regs[ra].intVal = nb.intVal
of nkFloatLit..nkFloat64Lit:
ensureKind(rkFloat)
regs[ra].floatVal = nb.floatVal
else:
ensureKind(rkNode)
regs[ra].node = nb
of opcSlice:
# A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits
# nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken.
@@ -848,25 +859,30 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcLdObj:
# a = b.c
decodeBC(rkNode)
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
case src.kind
of nkEmpty..nkNilLit:
# for nkPtrLit, this could be supported in the future, use something like:
# derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
# where we compute the offset in bytes for field rc
stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
of nkObjConstr:
let n = src[rc + 1].skipColon
regs[ra].node = n
of nkTupleConstr:
let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
src[rc]
else:
src[rc].skipColon
regs[ra].node = n
if rb >= regs.len or regs[rb].kind == rkNone or
(regs[rb].kind == rkNode and regs[rb].node == nil) or
(regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil):
stackTrace(c, tos, pc, errNilAccess)
else:
let n = src[rc]
regs[ra].node = n
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
case src.kind
of nkEmpty..nkNilLit:
# for nkPtrLit, this could be supported in the future, use something like:
# derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
# where we compute the offset in bytes for field rc
stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
of nkObjConstr:
let n = src[rc + 1].skipColon
regs[ra].node = n
of nkTupleConstr:
let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
src[rc]
else:
src[rc].skipColon
regs[ra].node = n
else:
let n = src[rc]
regs[ra].node = n
of opcLdObjAddr:
# a = addr(b.c)
decodeBC(rkNodeAddr)
@@ -892,8 +908,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
stackTrace(c, tos, pc, errNilAccess)
elif dest[shiftedRb].kind == nkExprColonExpr:
writeField(dest[shiftedRb][1], regs[rc])
dest[shiftedRb][1].flags.incl nfSkipFieldChecking
else:
writeField(dest[shiftedRb], regs[rc])
dest[shiftedRb].flags.incl nfSkipFieldChecking
of opcWrStrIdx:
decodeBC(rkNode)
let idx = regs[rb].intVal.int
@@ -1261,6 +1279,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
createSet(regs[ra])
move(regs[ra].node.sons,
nimsets.diffSets(c.config, regs[rb].node, regs[rc].node).sons)
of opcXorSet:
decodeBC(rkNode)
createSet(regs[ra])
move(regs[ra].node.sons,
nimsets.symdiffSets(c.config, regs[rb].node, regs[rc].node).sons)
of opcConcatStr:
decodeBC(rkNode)
createStr regs[ra]
@@ -1361,7 +1384,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
else:
internalError(c.config, c.debug[pc], "opcParseFloat: Incorrectly created openarray")
else:
regs[ra].intVal = parseBiggestFloat(regs[ra].node.strVal, rcAddr.floatVal)
regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal, rcAddr.floatVal)
of opcRangeChck:
let rb = instr.regB
@@ -1376,7 +1399,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let rb = instr.regB
let rc = instr.regC
let bb = regs[rb].node
if bb.kind == nkNilLit:
stackTrace(c, tos, pc, "attempt to call nil closure")
let isClosure = bb.kind == nkTupleConstr
if isClosure and bb[0].kind == nkNilLit:
stackTrace(c, tos, pc, "attempt to call nil closure")
let prc = if not isClosure: bb.sym else: bb[0].sym
if prc.offset < -1:
# it's a callback:
@@ -1416,11 +1443,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
newSeq(newFrame.slots, prc.offset+ord(isClosure))
if not isEmptyType(prc.typ.returnType):
putIntoReg(newFrame.slots[0], getNullValue(prc.typ.returnType, prc.info, c.config))
putIntoReg(newFrame.slots[0], getNullValue(c, prc.typ.returnType, prc.info, c.config))
for i in 1..rc-1:
newFrame.slots[i] = regs[rb+i]
if isClosure:
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'
@@ -1513,10 +1548,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
# Set the `name` field of the exception
var exceptionNameNode = newStrNode(nkStrLit, c.currentExceptionA.typ.sym.name.s)
if c.currentExceptionA[2].kind == nkExprColonExpr:
exceptionNameNode.typ = c.currentExceptionA[2][1].typ
exceptionNameNode.typ() = c.currentExceptionA[2][1].typ
c.currentExceptionA[2][1] = exceptionNameNode
else:
exceptionNameNode.typ = c.currentExceptionA[2].typ
exceptionNameNode.typ() = c.currentExceptionA[2].typ
c.currentExceptionA[2] = exceptionNameNode
c.exceptionInstr = pc
@@ -1549,7 +1584,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNew:
ensureKind(rkNode)
let typ = c.types[instr.regBx - wordExcess]
regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config)
regs[ra].node.flags.incl nfIsRef
of opcNewSeq:
let typ = c.types[instr.regBx - wordExcess]
@@ -1558,10 +1593,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let instr2 = c.code[pc]
let count = regs[instr2.regA].intVal.int
regs[ra].node = newNodeI(nkBracket, c.debug[pc])
regs[ra].node.typ = typ
regs[ra].node.typ() = typ
newSeq(regs[ra].node.sons, count)
for i in 0..<count:
regs[ra].node[i] = getNullValue(typ.elementType, c.debug[pc], c.config)
regs[ra].node[i] = getNullValue(c, typ.elementType, c.debug[pc], c.config)
of opcNewStr:
decodeB(rkNode)
regs[ra].node = newNodeI(nkStrLit, c.debug[pc])
@@ -1573,7 +1608,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcLdNull:
ensureKind(rkNode)
let typ = c.types[instr.regBx - wordExcess]
regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config)
# opcLdNull really is the gist of the VM's problems: should it load
# a fresh null to regs[ra].node or to regs[ra].node[]? This really
# depends on whether regs[ra] represents the variable itself or whether
@@ -1973,7 +2008,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
else:
internalAssert c.config, false
regs[ra].node.info = n.info
regs[ra].node.typ = n.typ
regs[ra].node.typ() = n.typ
of opcNCopyLineInfo:
decodeB(rkNode)
regs[ra].node.info = regs[rb].node.info
@@ -2053,7 +2088,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
ensureKind(rkNode)
regs[ra].node = temp
regs[ra].node.info = c.debug[pc]
regs[ra].node.typ = typ
regs[ra].node.typ() = typ
of opcConv:
let rb = instr.regB
inc pc
@@ -2287,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
@@ -2302,7 +2338,7 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
# setup parameters:
if not isEmptyType(sym.typ.returnType) or sym.kind == skMacro:
putIntoReg(tos.slots[0], getNullValue(sym.typ.returnType, sym.info, c.config))
putIntoReg(tos.slots[0], getNullValue(c, sym.typ.returnType, sym.info, c.config))
# XXX We could perform some type checking here.
for i in 0..<sym.typ.paramsLen:
putIntoReg(tos.slots[i+1], args[i])
@@ -2313,9 +2349,17 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
localError(c.config, sym.info,
"NimScript: attempt to call non-routine: " & sym.name.s)
proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode =
result = newNodeI(nkEmpty, n.info)
result.typ() = newType(tyError, idgen, owner)
result.typ.flags.incl tfCheckedForDestructor
proc evalStmt*(c: PCtx, n: PNode) =
let n = transformExpr(c.graph, c.idgen, c.module, n)
let start = genStmt(c, n)
if c.cannotEval:
c.cannotEval = false
return
# execute new instructions; this redundant opcEof check saves us lots
# of allocations in 'execute':
if c.code[start].opcode != opcEof:
@@ -2326,7 +2370,10 @@ proc evalExpr*(c: PCtx, n: PNode): PNode =
# `nim --eval:"expr"` might've used it at some point for idetools; could
# be revived for nimsuggest
let n = transformExpr(c.graph, c.idgen, c.module, n)
c.cannotEval = false
let start = genExpr(c, n)
if c.cannotEval:
return errorNode(c.idgen, c.module, n)
assert c.code[start].opcode != opcEof
result = execute(c, start)
@@ -2379,7 +2426,10 @@ proc evalConstExprAux(module: PSym; idgen: IdGenerator;
var c = PCtx g.vm
let oldMode = c.mode
c.mode = mode
c.cannotEval = false
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
if c.cannotEval:
return errorNode(idgen, prc, n)
if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
assert c.code[start].opcode != opcEof
when debugEchoCode: c.echoCode start
@@ -2436,7 +2486,7 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
var n = x
if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n[1]
n.flags.incl nfIsRef
n.typ = x.typ
n.typ() = x.typ
result = TFullReg(kind: rkNode, node: n)
iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
@@ -2450,11 +2500,6 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
# to prevent endless recursion in macro instantiation
const evalMacroLimit = 1000
#proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode =
# result = newNodeI(nkEmpty, n.info)
# result.typ = newType(tyError, idgen, owner)
# result.typ.flags.incl tfCheckedForDestructor
proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstCounter: ref int;
n, nOrig: PNode, sym: PSym): PNode =
#if g.config.errorCounter > 0: return errorNode(idgen, module, n)
@@ -2478,7 +2523,10 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC
c.comesFromHeuristic.line = 0'u16
c.callsite = nOrig
c.templInstCounter = templInstCounter
c.cannotEval = false
let start = genProc(c, sym)
if c.cannotEval:
return errorNode(idgen, module, n)
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
let maxSlots = sym.offset

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