Compare commits

..

504 Commits

Author SHA1 Message Date
narimiran
336dc9d827 more removing of failing packages 2026-05-22 11:22:54 +02:00
narimiran
3fe329dbd1 remove failing packages 2026-05-22 10:02:48 +02:00
Juan Carlos
9ef34c3589 Fix Bisect (#25218)
- Nim requires `SSL_library_init`, OpenSSL 3.x removed
`SSL_library_init`, Windows defaults to OpenSSL 3.x, then install
OpenSSL 1.x on Windows.
- Keep `jiro4989/setup-nim-action` at `v1`, because `v2` uses a YAML
"hardcoded" matrix of Nim versions, but this Bisect "dynamically" finds
the Nim version with a bug, therefore we cant hardcode Nim versions in
the YAML, the Bisect programmatically installs required Nim versions as
it goes bisecting commit-by-commit.
- Update `actions/checkout` from `v4` to `v5`.
- Add support for Nim `2.2.4`.

@ringabout

(cherry picked from commit 1ef81f4190)
2026-05-22 09:24:05 +02:00
dependabot[bot]
fda07a76fd update actions' versions 2026-05-22 09:19:52 +02:00
ringabout
19dc02e1bb updates to macos-15 (#25278)
ref https://github.com/actions/runner-images/issues/13046

(cherry picked from commit d5549a3c65)
2026-05-22 09:15:01 +02:00
Andreas Rumpf
0a6f1b44dc fixes DOS via malformed HTTP protocol (#25793)
refs https://github.com/nim-lang/Nim/pull/25568

(cherry picked from commit f0077a12b2)
2026-05-22 08:55:39 +02:00
Miran
a837ee7800 replace outdated macos-13 runner (#25155)
(cherry picked from commit c49fb5ac5f)
2025-09-17 08:59:09 +02:00
ringabout
2fbb793f4e fixes #25173; SinglyLinkedList.remove broken / AssertionDefect (#25175)
fixes #25173

(cherry picked from commit 51a9ada043)
2025-09-16 20:13:12 +02:00
ringabout
edeac84e47 fixes #24725; Invalid =sink generated for pure inheritable object (#24726)
fixes #24725

`lacksMTypeField` doesn't take the base types into consideration. And
for ` {.inheritable, pure.}`, it shouldn't generate a `m_type` field.

(cherry picked from commit e449813c61)
2025-08-05 08:33:50 +02:00
Juan M Gómez
d2b8c3c0d5 Bumps nimble 0.20.1 (#25062)
(cherry picked from commit cd806f9dbe)
2025-07-19 11:49:25 +02:00
ringabout
10564f25b3 fixes #24719; improves order of destruction (#25060)
fixes #24719

(cherry picked from commit 8e57a9f623)
2025-07-19 08:22:53 +02:00
narimiran
fbc7e54e95 revert Atlas commit to the one compatible with 2.0.x 2025-07-14 09:48:48 +02:00
Juan M Gómez
7f3dbd5656 Updates nimble commit (#25036)
(cherry picked from commit 370ee61f6d)
2025-07-08 16:23:45 +02:00
ringabout
71f3e100b6 provides a $ function for Path (#23617)
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 4e7c70fd7d)
2025-07-02 09:59:38 +02:00
ringabout
d2cb099e44 fixes #24881; build_all.sh koch tools fails to build atlas (#24884)
fixes #24881

To test: `nim c koch.nim` + delete the `dist` directory

(cherry picked from commit af9219ada7)
2025-07-01 14:23:58 +02:00
Miran
7e51e48cac update the tooling versions (#24878)
(cherry picked from commit 11e4bd668c)
2025-06-30 14:10:33 +02:00
ringabout
19c89622ca fixes #23564; hasCustomPragma skips alises types (#24994)
fixes #23564

perhaps handle generic aliases (tyGenericInst for aliases types) if
needed

(cherry picked from commit 7e6fa9e2d6)
2025-06-27 13:47:24 +02:00
Jacek Sieka
b1d9af4d5f Ensure that gc interface remains non-raising (#25006)
GC_fullCollect in particular has an annoying `Exception` effect

(cherry picked from commit aba9361510)
2025-06-18 16:15:42 +02:00
metagn
c467532484 don't set sym of generic param type value to generic param sym (#24995)
fixes #23713

`linkTo` normally sets the sym of the type as well as the type of the
sym, but this is not wanted for custom pragmas as it would look up the
definition of the generic param and not the definition of its value. I
don't see a practical use for this either.

(cherry picked from commit 7701b3c7e6)
2025-06-16 11:17:19 +02:00
Eugene Kabanov
114d0b8bdc Fix FreeBSD getThreadId() should use different syscall definition for 64bit platforms. (#24977)
(cherry picked from commit 7a53db6874)
2025-06-06 08:33:38 +02:00
Alfred Morgan
f8ce7ddd6d Patch 24922 (#24923)
(cherry picked from commit b61a614e8a)
2025-06-03 07:38:25 +02:00
Andreas Rumpf
cd6dbbd7f3 fixes #4851 [backport] (#24954)
(cherry picked from commit 1e602490e9)
2025-05-19 17:54:43 +02:00
narimiran
acac0bc1f2 bump NimVersion to 2.0.17 2025-04-22 16:26:30 +02:00
narimiran
82f3a57612 bump NimVersion to 2.0.16 2025-04-21 19:10:44 +02:00
metagn
7e589bcbe4 add bit type overloads of $ and repr (#24865)
fixes #24864

(cherry picked from commit 97d819a251)
2025-04-14 10:56:41 +02:00
metagn
dbed9310ba ignore typeof in closure iterators (#24861)
fixes #24859

(cherry picked from commit f58cd51fc4)
2025-04-14 10:54:56 +02:00
ringabout
9524edec60 fixes #24850; macro-generated if/else and when/else statements have m… (#24852)
…ismatched indentation with repr

fixes #24850

(cherry picked from commit 29a2e25d1e)
2025-04-09 07:57:52 +02:00
metagn
574db65396 make fillObjectFields recur over base type (#24854)
fixes #24847

Object constructors call `fillObjectFields` when a field inside the
constructor does not have a location, however when the field is from a
base type this does not process it. Now `fillObjectFields` also calls
itself for the base type to fix this but not sure if this is a good
solution as `fillObjectFields` is used in other places too.

(cherry picked from commit a625fab098)
2025-04-09 07:57:39 +02:00
ringabout
e2bf3a6f70 bump to windows 2025 (#24853)
(cherry picked from commit 052ceca3c1)
2025-04-09 07:55:26 +02:00
Miran
6f2dbc105e test stint more thoroughly (#24832)
(cherry picked from commit 10c9ebad93)
2025-04-09 07:55:20 +02:00
ringabout
63151568b4 fixes #24801; Invalid C codegen generated when destroying distinct seq types (#24835)
fixes #24801

Because distinct `seq` types match `proc `=destroy`*[T](x: var T)
{.inline, magic: "Destroy".}`. But the Nim compiler generates lifted seq
types for corresponding distinct types. So we skip the address for
distinct types.

Related to https://github.com/nim-lang/Nim/pull/22207 I had a hard time
finding the other place where generic destructors get replaced by
attachedDestructors

(cherry picked from commit 4352fa2ef0)
2025-04-04 10:18:00 +02:00
ringabout
4610c2b314 fixes #10625; setjmp on linux mangles ebp leading to early collection (#24787)
fixes #10625

(cherry picked from commit 7c5d005510)
2025-03-25 09:45:23 +01:00
ringabout
d01002d8f8 fixes #24770; Thread local not registed as GC root when =destroy exists (#24776)
fixes #24770

e.g. `seq[(ObjectWithDestructors, string)]`/ For refc, a seq with
elements that have destructors will have `hasAsgn` flags. The flag is
the criteria whether a seq is thought as `containsGarbageCollectedRef`.
i.e. whether to `registerTraverseProc` for the type.
The culprit seems to be that `searchTypeForAux` doesn't consider the
element type of sequence, even it contains a string that should belong
to `GarbageCollectedRef`.

With this PR:

It now generates

```
nimRegisterThreadLocalMarker(TM__mSF73dT1lSI7DG58StKHLQ_5);
```

in refc

(cherry picked from commit dfa482e292)
2025-03-13 12:24:29 +01:00
ringabout
bc2fa6fe32 fixes #24147; Copy hook causes an incompatible-pointer-types (#24149)
fixes #24147

(cherry picked from commit 5c843d3d60)
2025-03-11 08:58:37 +01:00
ringabout
41637db18f fixes ORC memory leaks; marks hooks with optQuirky (#24701)
closes https://github.com/nim-lang/Nim/pull/24686
closes #24693

```nim
# v.nim
import std/[json]

var test: seq[string]
var testData: JsonNode
try:
  ## Fails
  testData = parseJson("""[{"id": 1"}, {"id": "2"}]""")

  ## Works
  # testdata = parseJson("""[{"id": "1"}, {"id": "2"}]""")

  ## Fails
  # let stream = newStringStream("""[{"id": 1"}, {"id": "2"}]""")
  # testData = parseJson(stream, "input", false, false)
  # stream.close()

except:
  testData = %* []
for t in testData:
  test.add(t["id"].getStr())
echo $test
```

With this PR:

```
==66425== LEAK SUMMARY:
==66425==    definitely lost: 0 bytes in 0 blocks
==66425==    indirectly lost: 0 bytes in 0 blocks
==66425==      possibly lost: 0 bytes in 0 blocks
==66425==    still reachable: 16,512 bytes in 2 blocks
==66425==         suppressed: 0 bytes in 0 blocks
==66425== Reachable blocks (those to which a pointer was found) are not shown.
==66425== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==66425==
==66425== For lists of detected and suppressed errors, rerun with: -s
==66425== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
```

(cherry picked from commit f0b5bf359e)
2025-02-20 10:26:12 +01:00
ringabout
b7eefc31d8 implements quirky for functions (#24700)
ref https://github.com/nim-lang/Nim/pull/24686

With this PR

```nim
import std/streams

proc foo() =
  var name = newStringStream("2r2")
  raise newException(ValueError, "sh")

try:
  foo()
except:
 discard

echo 123
```
this example no longer leaks

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 510ac84518)
2025-02-18 17:51:21 +01:00
metagn
5df3bf467e retry thttpclient_ssl twice (#24467)
Flaky on linux_amd64

(cherry picked from commit 652edb229a)
2025-02-17 21:16:14 +01:00
metagn
ac44139084 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-02-17 21:16:13 +01:00
metagn
ac882ea696 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-02-17 17:11:42 +01:00
ringabout
56f9559c69 adds a ubuntu 24.04 matrix with gcc 14 for tests (#23673)
ref https://forum.nim-lang.org/t/11587

(cherry picked from commit 6336d2681b)
2025-02-17 15:37:45 +01:00
ringabout
c08c32a1ed fixes #5901 #21211; don't fold cast function types because of gcc 14 (#23683)
follow up https://github.com/nim-lang/Nim/pull/6265

fixes #5901
fixes #21211

It causes many problems with gcc14 if we fold the cast function types.
Let's check what it will break

(cherry picked from commit 2d1533f34f)
2025-02-17 13:25:35 +01:00
narimiran
03491904a0 update CI to Ubuntu 24.04 2025-02-17 09:27:37 +01:00
metagn
57f84c5376 track introduced locals in vmgen for eval check (#24674)
fixes #8758, fixes #10828, fixes #12172, fixes #21610, fixes #23803,
fixes #24633, fixes #24634, succeeds #24085

We simply track the symbol ID of every traversed `var`/`let` definition
in `vmgen`, then these symbols are always considered evaluable in the
current `vmgen` context. The set of symbols is reset before every
generation, but both tests worked properly without doing this including
the nested `const`, so maybe it's already done in some way I'm not
seeing.

(cherry picked from commit a5cc33c1d3)
2025-02-17 08:23:51 +01:00
ringabout
1df4debbf1 fixes bugs on the Nim manual (#24669)
ref https://en.cppreference.com/w/cpp/error/exception/what

> Pointer to a null-terminated string with explanatory information. The
pointer is guaranteed to be valid at least until the exception object
from which it is obtained is destroyed, or until a non-const member
function on the exception object is called.

The pointer is only valid before `CStdException as e` is destroyed

Old examples are broken on macOS arm64

```
/Users/blue/Desktop/nimony/test4.nim(38) test4
/Users/blue/Desktop/nimony/test4.nim(26) fn
/Users/blue/.choosenim/toolchains/nim-#devel/lib/std/assertions.nim(41) failedAssertImpl
/Users/blue/.choosenim/toolchains/nim-#devel/lib/std/assertions.nim(36) raiseAssert
/Users/blue/.choosenim/toolchains/nim-#devel/lib/system/fatal.nim(53) sysFatal
Error: unhandled exception: /Users/blue/Desktop/nimony/test4.nim(26, 3) `$b == "foo2"`  [AssertionDefect]
```

(cherry picked from commit e6f6c369ff)
2025-02-10 17:11:50 +01:00
metagn
1ff69eae17 don't mark captured field sym in template as fully used (#24660)
fixes #24657

(cherry picked from commit 647c6687f1)
2025-01-31 09:36:33 +01:00
metagn
4fa122eb44 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-27 09:30:11 +01:00
Tomohiro
53782b1404 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:51:04 +01:00
ringabout
cafe1284ac 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:13:03 +01:00
ringabout
e2ae7d5f43 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:12:26 +01:00
metagn
772ebe6715 disable sfml test on osx (#24615)
Tried installing sfml 2 in #24614 but didn't work

(cherry picked from commit d83ff81695)
2025-01-22 09:56:31 +01:00
ringabout
32c99651fc ci: update to ubuntu 22.04 (#24608)
(cherry picked from commit 41c447b5f4)
2025-01-22 06:58:51 +01:00
metagn
2eac7941ff 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-22 06:56:48 +01:00
metagn
cedcb7881d 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-22 06:45:40 +01:00
narimiran
117b913458 bump NimVersion to 2.0.15 2025-01-22 06:39:41 +01:00
narimiran
bf4de6a394 bump NimVersion to 2.0.14 2024-12-23 08:07:41 +01:00
ringabout
86f1a2785a remove zippy data from tarballs (#24551)
fixes https://github.com/nim-lang/nightlies/issues/95

(cherry picked from commit 63c884038d)
2024-12-23 06:59:22 +01:00
ringabout
c7d057d7f9 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)
2024-12-23 06:58:25 +01:00
Juan M Gómez
8f44c40b6b Adds skipParentCfg back. Bump nimble to a commit where it doesnt rely in the parent config (#24545)
(cherry picked from commit 8ce58fab26)
2024-12-20 15:51:57 +01:00
Juan M Gómez
76935b4c34 (cherry picked from commit 556f217b4c) 2024-12-20 15:49:40 +01:00
Juan M Gómez
30abe60cda Bumps nimble v0.16.4 (#24437)
(cherry picked from commit be4d19e562)
2024-12-20 15:48:29 +01:00
ringabout
14dfabb230 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)
2024-12-20 07:52:00 +01:00
ringabout
5fb4662ab1 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)
2024-12-20 05:33:40 +01:00
metagn
535556875e 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)`).

(cherry picked from commit b0e6d28782)
2024-12-20 05:30:27 +01:00
metagn
95fa7f0f12 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.

(cherry picked from commit 1fbb67ffe9)
2024-12-20 05:21:18 +01:00
bptato
6bcb078476 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)
2024-12-17 15:58:34 +01:00
metagn
5c58e7d201 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)
2024-12-17 15:58:33 +01:00
metagn
5327498547 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)
2024-12-17 14:38:42 +01:00
ringabout
d84d41dc95 fixes #24371; incorrect importc wrapper incompatible with gcc 14 on Windows (#24388)
fixes #24371

(cherry picked from commit 74df699ff1)
2024-12-17 14:38:32 +01:00
ringabout
693b35b59f fixes #23545; C compiler error when default initializing an object field function (#24375)
fixes #23545

(cherry picked from commit 815bbf0e73)
2024-12-17 14:38:23 +01:00
bptato
c1bb144fdc 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)
2024-12-17 14:35:25 +01:00
ringabout
1f38c3cea8 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)
2024-12-17 14:34:27 +01:00
metagn
f45ca4fdf4 only generate first field for default value of union (#24303)
fixes #20653

(cherry picked from commit 6df050d6d2)
2024-12-16 17:42:19 +01:00
metagn
5cbc7a6d1b 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

(cherry picked from commit 24e5b21c90)
2024-12-16 15:11:47 +01:00
metagn
56e7c75e03 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.

(cherry picked from commit 7de4ace949)
2024-12-16 15:11:03 +01:00
metagn
d51236e9cc fix string literal assignment with different lengths on ARC (#24083)
fixes #24080

(cherry picked from commit cd22560af5)
2024-12-16 15:10:58 +01:00
metagn
4d0d848235 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`.

(cherry picked from commit bf865fa75a)
2024-12-16 15:10:46 +01:00
ringabout
8859f1ddf7 fixes #24034; fixes lent types after taking implicit address (#24035)
fixes #24034

(cherry picked from commit 11ead19bc1)
2024-12-16 15:09:57 +01:00
metagn
0e2b34ce35 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.

(cherry picked from commit 04da0a6028)
2024-12-16 15:09:00 +01:00
metagn
ba516c8eb5 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?

(cherry picked from commit 1befb8d4a3)
2024-12-16 15:07:47 +01:00
metagn
009a5b0684 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.

(cherry picked from commit 31ee75f10e)
2024-12-16 15:06:10 +01:00
SirOlaf
c786415eef 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.

(cherry picked from commit f765898a75)
2024-12-16 15:06:00 +01:00
Yuriy Glukhov
33dc54c573 Check for nil in cstringArrayToSeq (#23747)
This fixes crashes in some specific network configurations (as
`cstringArrayToSeq` is used extensively in `nativesockets`).

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 2c83f94544)
2024-12-16 15:03:09 +01:00
Marius Andra
6a1d1a8c2b fixes 12381, HttpClient socket handle leak (#23575)
## Bug

Fixes https://github.com/nim-lang/Nim/issues/12381 - HttpClient socket
handle leak

To replicate the bug, run the following code in a loop:

```nim
import httpclient
while true:
    echo "New loop"
    var client = newHttpClient(timeout = 1000)
    try:
        let response = client.request("http://10.44.0.4/bla", httpMethod = HttpPost, body = "boo")
        echo "HTTP " & $response.status
    except CatchableError as e:
        echo "Error sending logs: " & $e.msg
    finally:
        echo "Finally"
        client.close()
```

Note the IP address as the hostname. I'm directly connecting to a
plausible local IP, but one that does not resolve, as I have everything
under 10.4.x.x.

The output looks like this to me:

```
New loop
Error sending logs: Operation timed out
Finally
New loop
Error sending logs: Operation timed out
Finally
New loop
...
```

In Nim 2.0.4, running the code above leaks the socket:

<img width="944" alt="Screenshot 2024-05-05 at 22 00 13"
src="https://github.com/nim-lang/Nim/assets/53387/ddac67db-d7df-45e6-b7a5-3d42f79775ea">

## Fix

With the added line of code, each old socket is cleanly removed:

<img width="938" alt="Screenshot 2024-05-05 at 21 54 18"
src="https://github.com/nim-lang/Nim/assets/53387/5b0b4b2d-d4f0-4e74-a9cf-74aec0c50d2e">

I believe the line below, `closeUnusedFds(ord(domain))` was supposed to
clean up the failed connection attempts, but it failed to do so for the
last one, assuming it succeeded. Yet it didn't. This fix makes sure
failed connections are closed immediately.

## Tests

I don't have a test with this PR. When testing locally, the
`connect(lastFd, ..)` call on line 2032 blocks for ~75 seconds, ignoring
the http timeout. I fear any test I could add would either 1) take way
too long, 2) one day run in an environment where my randomly chosen IP
is real, yielding in weird flakes.

The only bug i can imagine is if running `lastFd.close()` twice is a bad
idea. I tested by actually running it twice, and... no crash/op? So
seems safe? I'm hoping the CI run will be green, and this will be
enough. However I'm happy to take feedback on how I should test this,
and do the necessary changes.

~Edit: looks like a test does fail, so moving to a draft while I figure
this out.~ Attempt 2 fixed it.

(cherry picked from commit e6f66e4d13)
2024-12-16 15:02:56 +01:00
ringabout
0c426e7875 fixes #23295; don't expand constants for complex structures (#23297)
fixes #23295

(cherry picked from commit 39f2df1972)
2024-12-16 15:02:23 +01:00
ringabout
4e1b5ee702 fixes #18104; tranform one liner var decl before templates expansion (#23294)
fixes #18104

(cherry picked from commit 1e9a3c438b)
2024-12-16 15:01:45 +01:00
ringabout
3a334e09ea fixes #23233; Regression when using generic type with Table/OrderedTable (#23235)
fixes #23233

(cherry picked from commit 720021908d)
2024-12-16 14:59:51 +01:00
narimiran
dbe9c724a0 Revert "Bumps nimble v0.16.4 (#24437)"
This reverts commit 1778b8354a.
2024-12-16 14:02:04 +01:00
narimiran
9620d206b9 Revert "(cherry picked from commit 556f217b4c)"
This reverts commit 9e5cdc43a6.
2024-12-16 14:01:53 +01:00
Juan M Gómez
9e5cdc43a6 (cherry picked from commit 556f217b4c) 2024-12-16 08:07:55 +01:00
Juan M Gómez
1778b8354a Bumps nimble v0.16.4 (#24437)
(cherry picked from commit be4d19e562)
2024-12-13 19:22:23 +01:00
ringabout
f6f72722bb 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)
2024-12-12 12:27:10 +01:00
metagn
90993aeff6 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)
2024-12-09 09:11:30 +01:00
ringabout
15e5ddc675 fixes #24504; fixes ensureMove for refs (#24505)
fixes #24504

(cherry picked from commit d0288d3b57)
2024-12-08 19:18:30 +01:00
narimiran
9f43f9fa6e bump NimVersion to 2.0.13 2024-12-08 19:18:30 +01:00
narimiran
ce7c6f4f33 bump NimVersion to 2.0.12 2024-10-31 10:28:16 +01:00
ringabout
b0074121ec fixes #24379; better error messages for ill-formed type symbols from macros (#24380)
fixes #24379

(cherry picked from commit d61897459d)
2024-10-29 18:03:00 +01:00
narimiran
3713994ef1 two 2.0-specific fixes 2024-10-28 14:18:56 +01:00
metagn
75bd2d0688 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)
(cherry picked from commit 7ad7ee03e5c0adb6832cbae10a62de7b68ef6fa5)
2024-10-28 09:57:30 +01:00
ringabout
eedfcbeb30 fixes #22389; fixes #19840; don't fold paths containing addr (#23807)
fixes #22389;
fixes #19840

(cherry picked from commit 5c5e7a9b6e)
(cherry picked from commit 00e39185f1d59597d17b69bbccf8879e3427f928)
2024-10-28 09:56:55 +01:00
ringabout
fe72db98c1 fixes addr/hiddenAddr in strictdefs (#23477)
(cherry picked from commit 9b378296f6)
(cherry picked from commit 744b241e4b7cb8c8d9e21e8a7f078d17d9ef90d4)
2024-10-28 09:55:43 +01:00
ringabout
a70b4712fc 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)
2024-10-28 09:17:53 +01:00
ringabout
76d834c182 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)
2024-10-24 15:12:48 +02:00
metagn
72fcda1b35 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)
2024-10-23 08:22:05 +02:00
ringabout
fd9d2f5b82 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)
2024-10-23 08:12:50 +02:00
metagn
d3f7fb3100 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)
2024-10-23 08:12:50 +02:00
metagn
4acc7a5e18 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)
2024-10-23 08:12:50 +02:00
narimiran
e334aad202 cowstrings and ssostrings packages require Nim 2.2 2024-10-11 17:08:15 +02:00
Miran
67d7a7c124 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)
2024-10-11 15:22:53 +02:00
metagn
206bbbd940 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)
2024-10-11 14:19:38 +02:00
Juan M Gómez
cc4c9251f0 Bumps nimble to v0.16.2 (#24283)
(cherry picked from commit af23bc2941)
2024-10-11 14:18:16 +02:00
Miran
8754469f49 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)
2024-10-11 10:18:40 +02:00
metagn
e2ef322754 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)
2024-10-09 07:48:30 +02:00
metagn
4fe17a2d87 delay markUsed for converters until call is resolved (#24243)
fixes #24241

(cherry picked from commit 09043f409f)
2024-10-08 16:32:20 +02:00
Ryan McConnell
cc887c23f4 fixes 23823; array static overload - again (#23824)
#23823

(cherry picked from commit 22ba5abd63)
2024-10-05 18:42:55 +02:00
Ryan McConnell
acd09cec43 fixes #23755; array static inference during overload resolution (#23760)
---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 27abcdd57f)
2024-10-05 18:42:54 +02:00
ringabout
c8094176f1 fixes #24173; always bundle checksums (#24189)
fixes #24173

`cloneDependency` always has its logic to use existing deps

(cherry picked from commit 62a5bb4d0a)
2024-10-05 07:54:20 +02:00
Ryan McConnell
3c66401dee Changing generic weight of tyGenericParam (#22143)
This is in reference to a [feature
request](https://github.com/nim-lang/Nim/issues/22142) that I posted.

I'm making this PR to demonstrate the suggested change and expect that
this should be scrutinized

---------

Co-authored-by: Bung <crc32@qq.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 74fa8ed59a)
2024-10-04 10:01:48 +02:00
narimiran
ff46fcfd24 bump NimVersion to 2.0.11 2024-10-04 08:16:27 +02:00
narimiran
e941ee15be bump NimVersion to 2.0.10 2024-09-30 19:18:52 +02:00
metagn
5f119f0cdf 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.

(cherry picked from commit febc58e036)
2024-09-30 18:58:01 +02:00
metagn
0636bd2a04 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.

(cherry picked from commit 7974a2208c)
2024-09-30 18:29:19 +02:00
metagn
2499336fef 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.

(cherry picked from commit 2a48182288)
2024-09-30 18:24:24 +02:00
ringabout
1ba16876f1 fixes #24167; {.push deprecated.} for templates (#24170)
fixes #24167

(cherry picked from commit 3b85c1a2e9)
2024-09-28 08:18:28 +02:00
metagn
3a180df0d8 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.

(cherry picked from commit a1777200c1)
2024-09-28 08:18:04 +02:00
metagn
9d65507d20 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.

(cherry picked from commit b9de2bb4f3)
2024-09-23 17:24:59 +02:00
Ryan McConnell
78003e9f7b 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>
(cherry picked from commit 37dba853c9)
2024-09-23 16:41:38 +02:00
metagn
27381cc602 make genericsOpenSym work at instantiation time, new behavior in openSym (#24111)
alternative to #24101

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.

(cherry picked from commit 0c3573e4a0)
2024-09-18 19:36:30 +02:00
metagn
dd9cb0425c 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.

(cherry picked from commit 680a13a142)
2024-09-18 19:27:51 +02:00
metagn
894eee03a5 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.

(cherry picked from commit 1660ddf98a)
2024-09-18 19:25:18 +02:00
ringabout
4b8b8fb9d7 fixes for 32bit system (#23980)
(cherry picked from commit a4dff1a03e)
2024-09-17 13:43:10 +02:00
metagn
3214174f06 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.

(cherry picked from commit 770f8d5513)
2024-09-16 16:29:37 +02:00
Andreas Rumpf
8bb9823fde fixes #23445; fixes #23418 [backport] (#23699)
(cherry picked from commit 56c95758b2)
2024-09-16 12:30:27 +02:00
SirOlaf
40ee3a950e Fix #22826: Don't skip generic instances in type comparison (#22828)
Close #22826

I am not sure why this code skips generic insts, so letting CI tell me.
Update: It has told me nothing. Maybe someone knows during review.

Issue itself seems to be that the generic instance is skipped thus it
ends up being just `float` which makes it use the wrong generic instance
of the proc because it matches the one in cache

---------

Co-authored-by: SirOlaf <>
(cherry picked from commit c13c48500b)
2024-09-16 12:30:27 +02:00
narimiran
74c4b23e0d backport ast.nim refactorings 2024-09-16 12:30:27 +02:00
Juan M Gómez
7180ce6faa bumps nimble to 0.16.1 (#24102)
(cherry picked from commit 6c35a36043)
2024-09-16 10:00:41 +02:00
metagn
fd1d008cce 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.

(cherry picked from commit 6d362e0ffe)
2024-09-16 10:00:18 +02:00
ringabout
8bc8347f02 bump checksums (#23975)
ref https://github.com/nim-lang/checksums/pull/16
ref https://github.com/nim-lang/Nim/pull/23970

(cherry picked from commit 0ffc0493a3)
2024-09-13 14:22:54 +02:00
ringabout
33bef682dd 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>
(cherry picked from commit 4ef06a5cc5)
2024-09-13 13:43:53 +02:00
ringabout
bfd4b9b7a4 fixes #23945; type checking for whenvm expresssions (#23970)
fixes #23945

(cherry picked from commit dda638c1ba)
2024-09-13 13:43:45 +02:00
metagn
7d2fe8086f 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.

(cherry picked from commit 34719cad9d)
2024-09-13 13:43:38 +02:00
metagn
a78834c26c 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.

(cherry picked from commit 58813a3b2e)
2024-09-13 13:43:29 +02:00
metagn
7cbf0ee53c 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.

(cherry picked from commit f7c11a8978)
2024-09-13 13:43:23 +02:00
metagn
f86f2928a7 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?

(cherry picked from commit 995081b56a)
2024-09-13 13:43:06 +02:00
ringabout
5f09bf11de fixes #23954; uint8 > 8 bit at compile-time (#23955)
fixes #23954

(cherry picked from commit 298ada3412)
2024-09-13 13:43:00 +02:00
Mark Leyva
25ce25bc42 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`.

(cherry picked from commit c5b206d4ac)
2024-09-13 13:42:36 +02:00
ringabout
7503bed717 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

(cherry picked from commit f0e1eef65e)
2024-09-13 11:10:00 +02:00
ringabout
b7041b250c fixes #23894; succ/pred shouldn't raise OverflowDefect for unsigned integers (#23895)
fixes #23894

keeps it consistent with `inc`

(cherry picked from commit bd063113ec)
2024-09-13 11:09:45 +02:00
Buldram
06d00dfcf6 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.

(cherry picked from commit 925dc5c131)
2024-09-13 11:09:35 +02:00
metagn
baa8af92af 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.

(cherry picked from commit 97f5474545)
2024-09-13 11:09:24 +02:00
ringabout
f09b612f64 fixes #20865; fixes #20987; Missing bounds check in array slicing (#23814)
fixes #20865
fixes #20987

(cherry picked from commit e53a2ed19b)
2024-09-13 11:09:04 +02:00
Mark Leyva
7d0cdfc79d fixes #5091; Ensure we don't wait on an exited process on Linux (#23743)
Fixes #5091.

Ensure we don't wait on an exited process on Linux

(cherry picked from commit 288d5c4ac3)
2024-09-13 11:00:07 +02:00
lit
ae117bd7bc fixes #23732, os.sleep(-1) now returns immediately (#23734)
fixes #23732

(cherry picked from commit 2a658c64d8)
2024-09-13 10:59:57 +02:00
ringabout
b79e603702 ref #20653; fixes chronos empty case branches (#23706)
ref #20653

```nim
  Error* = object
    case kind*: ErrorType
    of ErrorA:
      discard
    of ErrorB:
      discard
```
For an object variants without fields, it shouldn't generate empty
brackets for default values since there are no fields at all in case
branches.

(cherry picked from commit 948bb38335)
2024-09-13 10:56:05 +02:00
c-blake
da2ba43dca Fix non-exported memfiles.setFileSize to be able to shrink files on posix via memfiles.resize (#23717)
Fix non-exported `setFileSize` to take optional `oldSize` to (on posix)
shrink differently than it grows (`ftruncate` not `posix_fallocate`)
since it makes sense to assume the higher address space has already been
allocated there and include the old file size in the `proc resize` call.
Also, do not even try `setFileSize` in the first place unless the `open`
itself works by moving the call into the `if newFileSize != -1` branch.

Just cosmetics, also improve some old 2011 comments, note a logic diff
for callers using both `mappedSize` & `newFileSize` from windows branch
in case someone wants to fix that & simplify code formatting a little.

(cherry picked from commit 8037bbe327)
2024-09-13 10:55:55 +02:00
ringabout
b5ef14ceda fixes #22798; Duplicate libraries linker warning (i.e., '-lm') on macOS (#23292)
fixes #22798

Per
https://stackoverflow.com/questions/33675638/gcc-link-the-math-library-by-default-in-c-on-mac-os-x
and
https://stackoverflow.com/questions/30694042/c-std-library-dont-appear-to-be-linked-in-object-file

> There's no separate math library on OSX. While a lot of systems ship
functions in the standard C math.h header in a separate math library,
OSX does not do that, it's part of the libSystem library, which is
always linked in.

required by https://github.com/nim-lang/Nim/pull/23290

(cherry picked from commit de4c7dfdd9)
2024-09-13 10:24:01 +02:00
ringabout
dc62ee00df fixes #23635; tasks.toTask Doesn't Expect a Dot Expression (#23641)
fixes #23635

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit cc5ce72376)
2024-09-13 10:22:36 +02:00
ringabout
b6a8dcd922 fixes #22852; fixes #23435; fixes #23645; SIGSEGV when slicing string or seq[T] with index out of range (#23279)
follow up https://github.com/nim-lang/Nim/pull/23013

fixes #22852
fixes #23435
fixes #23645

reports rangeDefect correctly

```nim
/workspaces/Nim/test9.nim(1) test9
/workspaces/Nim/lib/system/indices.nim(116) []
/workspaces/Nim/lib/system/fatal.nim(53) sysFatal
Error: unhandled exception: value out of range: -2 notin 0 .. 9223372036854775807 [RangeDefect]
```

(cherry picked from commit c615828ccb)
2024-09-13 10:22:30 +02:00
Alexander Kernozhitsky
37965bd591 Handle arbitrarily long symlink target in expandSymlinks() (#23650)
For now, `expandSymlinks()` can handle only symlinks with lengths up to
1024.

We can improve this logic and retry inside a loop with increasing
lengths until we succeed.

The same approach is used in
[Go](377646589d/src/os/file_unix.go (L446)),
[Rust](785eb65377/library/std/src/sys/pal/unix/fs.rs (L1700))
and [Nim's
`getCurrentDir()`](https://github.com/nim-lang/Nim/blob/devel/lib/std/private/ospaths2.nim#L877),
so maybe it's a good idea to use the same logic in `expandSymlinks()`
also.

(cherry picked from commit 3bda5fc840)
2024-09-13 10:22:20 +02:00
ringabout
c2b4d8a968 fixes reifiedOpenArray; nkHiddenStdConv is PathKinds1 not PathKinds0 (#23633)
(cherry picked from commit 5cd141cebb)
2024-09-13 10:22:11 +02:00
ringabout
8d254a5945 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

(cherry picked from commit c10f84b9d7)
2024-09-08 08:42:53 +02:00
ringabout
4856beae70 fixes #23973; fixes #23974; Memory corruption with lent and ORC (#23981)
fixes #23973;
fixes #23974

(cherry picked from commit 26107e931c)
2024-08-31 13:51:52 +02:00
ringabout
568a637f0f 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

(cherry picked from commit 648f82c2ed)
2024-08-31 13:50:28 +02:00
ringabout
8b421d2203 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

(cherry picked from commit b7a275da1d)
2024-08-31 13:49:36 +02:00
Ryan McConnell
a5237a5053 Overload resultion with generic variables an inheritance (#23870)
The test case diff is self explanatory

(cherry picked from commit c1f91c26a5)
2024-08-31 13:49:16 +02:00
metagn
d0a8637872 fix noreturn/implicit discard check logic (#23681)
fixes #10440, fixes #13871, fixes #14665, fixes #19672, fixes #23677

The false positive in #23677 was caused by behavior in
`implicitlyDiscardable` where only the last node of `if`/`case`/`try`
etc expressions were considered, as in the final node of the final
branch (in this case `else`). To fix this we use the same iteration in
`implicitlyDiscardable` that we use in `endsInNoReturn`, with the
difference that for an `if`/`case`/`try` statement to be implicitly
discardable, all of its branches must be implicitly discardable.
`noreturn` calls are also considered implicitly discardable for this
reason, otherwise stuff like `if true: discardableCall() else: error()`
doesn't compile.

However `endsInNoReturn` also had bugs, one where `finally` was
considered in noreturn checking when it shouldn't, another where only
`nkIfStmt` was checked and not `nkIfExpr`, and the node given for the
error message was bad. So `endsInNoReturn` now skips over
`skipForDiscardable` which no longer contains
`nkIfStmt`/`nkCaseStmt`/`nkTryStmt`, stores the first encountered
returning node in a var parameter for the error message, and handles
`finally` and `nkIfExpr`.

Fixing #23677 already broke a line in `syncio` so some package code
might be affected.

(cherry picked from commit 42e8472ca6)
2024-08-31 13:45:23 +02:00
metagn
7821aa94eb remove bad type inference behavior for enum identifiers (#23588)
refs
https://github.com/nim-lang/Nim/issues/23586#issuecomment-2102113750

In #20091 a bad kind of type inference was mistakenly left in where if
an identifier `abc` had an expected type of an enum type `Enum`, and
`Enum` had a member called `abc`, the identifier would change to be that
enum member. This causes bugs where a local symbol can have the same
name as an enum member but have a different value. I had assumed this
behavior was removed since but it wasn't, and CI seems to pass having it
removed.

A separate PR needs to be made for the 2.0 branch because these lines
were moved around during a refactoring in #23123 which is not in 2.0.

(cherry picked from commit c101490a0c)
2024-08-31 13:44:58 +02:00
metagn
b1e5dd605a fix wrong subtype relation in tuples & infer some conversions (#23228)
fixes #18125

Previously a tuple type like `(T, int)` would match an expected tuple
type `(U, int)` if `T` is a subtype of `U`. This is wrong since the
codegen does not handle type conversions of individual tuple elements in
a type conversion of an entire tuple. For this reason the compiler
already does not accept `(float, int)` for a matched type `(int, int)`,
however the code that checked for which relations are unacceptable
checked for `< isSubtype` rather than `<= isSubtype`, so subtypes were
not included in the unacceptable relations.

Update: Now only considered unacceptable when inheritance is used, as in
[`paramTypesMatch`](3379d26629/compiler/sigmatch.nim (L2252-L2254)).
Ideally subtype relations that don't need conversions, like `nil`,
`seq[empty]`, `range[0..5]` etc would be their own relation
`isConcreteSubtype` (which would also allow us to differentiate with
`openArray[T]`), but this is too big of a refactor for now.

To compensate for this making things like `let x: (Parent, int) =
(Child(), 0)` not compile (they would crash codegen before anyway but
should still work in principle), type inference for tuple constructors
is updated such that they call `fitNode` on the fields and their
expected types, so a type conversion is generated for the individual
subtype element.

(cherry picked from commit cfd69bad1a)
2024-08-31 13:44:37 +02:00
Ryan McConnell
87ab9b8ac2 typRel and sumGeneric adjustments (#23137)
Filling in some more logic in `typeRel` that I came across when poking
the compiler in another PR. Some of the cases where `typeRel` returns an
"incorrect" result are actually common, but `sumGeneric` ends up
breaking the tie correctly. There isn't anything wrong with that
necessarily, but I assume that it's preferred these functions behave
just as well in isolation as they do when integrated.

I will be following up this description with specific examples.

(cherry picked from commit ccc7c45d71)
2024-08-31 13:41:38 +02:00
metagn
c18e599f53 ambiguous identifier resolution (#23123)
fixes #23002, fixes #22841, refs comments in #23097

When an identifier is ambiguous in scope (i.e. multiple imports contain
symbols with the same name), attempt resolving it through type inference
(by creating a symchoice). To do this efficiently, `qualifiedLookUp` had
to be broken up so that `semExpr` can access the ambiguous candidates
directly (now obtained directly via `lookUpCandidates`).

This fixes the linked issues, but an example like:

```nim
let on = 123

{.warning[ProveInit]: on.}
```

will still fail, since `on` is unambiguously the local `let` symbol here
(this is also true for `proc on` but `proc` symbols generate symchoices
anyway).

Type symbols are not considered to not confuse the type inference. This
includes the change in sigmatch, up to this point symchoices with
nonoverloadable symbols could be created, they just wouldn't be
considered during disambiguation. Now every proper symbol except types
are considered in disambiguation, so the correct symbols must be picked
during the creation of the symchoice node. I remember there being a
violating case of this in the compiler, but this was very likely fixed
by excluding type symbols as CI seems to have found no issues.

The pure enum ambiguity test was disabled because ambiguous pure enums
now behave like overloadable enums with this behavior, so we get a
longer error message for `echo amb` like `type mismatch: got <MyEnum |
OtherEnum> but expected T`

(cherry picked from commit b280100499)
2024-08-31 12:53:43 +02:00
Ryan McConnell
d1aa568de3 Param match relax (#23033)
---------

Co-authored-by: Nikolay Nikolov <nickysn@gmail.com>
Co-authored-by: Pylgos <43234674+Pylgos@users.noreply.github.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
Co-authored-by: Jason Beetham <beefers331@gmail.com>
(cherry picked from commit 94f7e9683f)
2024-08-31 12:47:25 +02:00
ringabout
352c82af89 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

(cherry picked from commit eed9cb0d3f)
2024-08-29 06:31:14 +02:00
ringabout
22149422ee fixes LineTooLong hints on old compilers (#22412)
* fixes LineTooLong hints on old compilers

* fixes config/nim.cfg

(cherry picked from commit bf5d173bc6)
2024-08-29 06:23:13 +02:00
ringabout
d6f7625626 disable presto (#23958)
(cherry picked from commit 06b25bd2c4)
2024-08-19 09:25:29 +02:00
narimiran
3e8dfde2f9 Revert "bump NimVersion to 2.0.10"
This reverts commit 92e505577e.
2024-08-19 09:25:03 +02:00
ringabout
3f183f6769 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

(cherry picked from commit b215ec3735)
2024-08-19 09:23:43 +02:00
Alexander Kernozhitsky
1100f550dc [backport] fixes #23796; remove extra indirection for args in importc'ed functions in cpp (#23800)
fixes #23796

(cherry picked from commit 1dcc364cd2)
2024-08-19 09:23:35 +02:00
Juan M Gómez
02eb396f45 bump nimble to include the fix to nimble dump (#23918)
(cherry picked from commit 6126a0bf46)
2024-08-14 11:55:57 +02:00
metagn
796aa78562 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
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`.

(cherry picked from commit 0c890ff9a7)
2024-08-14 09:36:57 +02:00
metagn
1cc1451ca3 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.

(cherry picked from commit a64aa51fe9)
2024-08-14 09:36:56 +02:00
metagn
dd37bb88fe 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.

(cherry picked from commit 469a6044c0)
2024-08-14 09:36:56 +02:00
metagn
a88bce6106 adapt semOpAux to opt-in symchoices (#23750)
fixes #23749, refs #22716

`semIndirectOp` is used here because of the callback expressions, in
this case `db.getProc(...)`, and `semIndirectOp` calls `semOpAux` to
type its arguments before overloading starts. Hence it can opt in to
symchoices since overloading will resolve them.

(cherry picked from commit 948fc29bb2)
2024-08-14 09:08:41 +02:00
metagn
8a4ee2b84f make expressions opt in to symchoices (#22716)
refs #22605

Sym choice nodes are now only allowed to pass through semchecking if
contexts ask for them to (with `efAllowSymChoice`). Otherwise they are
resolved or treated as ambiguous. The contexts that can receive
symchoices in this PR are:

* Call operands and addresses and emulations of such, which will subject
them to overload resolution which will resolve them or fail.
* Type conversion operands only for routine symchoices for type
disambiguation syntax (like `(proc (x: int): int)(foo)`), which will
resolve them or fail.
* Proc parameter default values both at the declaration and during
generic instantiation, which undergo type narrowing and so will resolve
them or fail.

This means unless these contexts mess up sym choice nodes should never
leave the semchecking stage. This serves as a blueprint for future
improvements to intermediate symbol resolution.

Some tangential changes are also in this PR:

1. The `AmbiguousEnum` hint is removed, it was always disabled by
default and since #22606 it only started getting emitted after the
symchoice was soundly resolved.
2. Proc setter syntax (`a.b = c` becoming `` `b=`(a, c) ``) used to
fully type check the RHS before passing the transformed call node to
proc overloading. Now it just passes the original node directly so proc
overloading can deal with its typechecking.

(cherry picked from commit 5f9038a5d7)
2024-08-14 09:08:41 +02:00
SirOlaf
7b834b94da 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.

(cherry picked from commit 881fbb8f81)
2024-08-13 15:31:16 +02:00
SirOlaf
179ae267e9 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.

(cherry picked from commit fd1e62a7e2)
2024-08-13 15:31:16 +02:00
ringabout
5d872321b3 make -d:debugHeapLinks compile again (#23126)
I have made `realloc` absorb unused adjacent memory, which improves the
performance. I'm investigating whether `deallocOsPages` can be used to
improve memory comsumption.

(cherry picked from commit 53855a9fa3)
2024-08-13 15:23:19 +02:00
Andreas Rumpf
ff1881a4c1 fixes #22510 (#23100)
(cherry picked from commit 69d0b73d66)
2024-08-13 15:22:06 +02:00
Juan M Gómez
702f8342ff bump nimble to 0.16.0 (#23883)
(cherry picked from commit ccf90f5bcb)
2024-08-01 13:33:05 +02:00
narimiran
92e505577e bump NimVersion to 2.0.10 2024-07-17 14:44:53 +02:00
metagn
1ce954a0cf 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.

(cherry picked from commit cd946084ab)
2024-07-16 13:00:15 +02:00
ringabout
76e6130f64 patches for #23129 (#23198)
fixes it in the normal situation

(cherry picked from commit 30cb6826c0)
2024-07-09 20:09:41 +02:00
ringabout
83455cf1d5 fixes #23129; fixes generated hooks raise unlisted Exception, which never raise (#23195)
fixes #23129

(cherry picked from commit 62c5b8b287)
2024-07-09 20:09:41 +02:00
ringabout
be99f2fed8 fixes #22286; enforce Non-var T destructors by nimPreviewNonVarDestructor (#22975)
fixes #22286
ref https://forum.nim-lang.org/t/10642

For backwards compatibilities, we might need to keep the changes under a
preview compiler flag. Let's see how many packags it break.

**TODO** in the following PRs

- [ ] Turn the `var T` destructors warning into an error with
`nimPreviewNonVarDestructor`

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 379299a5ac)
2024-07-09 20:09:41 +02:00
Juan M Gómez
cbcf6c5be6 Bumps nimble so the next Nim release includes the latest changes (#23808)
(cherry picked from commit 14f86b3965)
2024-07-09 08:19:26 +02:00
SirOlaf
e4db9bffba Adjust the correct chunk's free space in allocator (#23795)
Fixes #23788

(cherry picked from commit 3f5016f60e)
2024-07-09 08:19:16 +02:00
Alexander Kernozhitsky
ecc7e3d41d fixes #23790; roll back instCounter properly in case of exceptions (#23802)
fixes #23790

(cherry picked from commit 841d30a213)
2024-07-08 11:17:49 +02:00
narimiran
22896b3a95 bump NimVersion to 2.0.9 2024-07-08 11:17:49 +02:00
David Krause
eaf0e7ff60 Update mimetypes.nim; added avif & avifs (#23786)
Added avif and avifs to mimetypes
2024-07-03 22:47:42 +02:00
narimiran
5935c3bfa9 bump NimVersion to 2.0.8 2024-07-02 17:05:59 +02:00
ringabout
2114414099 fixes #19977; rework inlining of 'var openarray' iterators for C++ (#23258)
fixes #19977

(cherry picked from commit f7c6e04cfb)
2024-07-02 06:06:13 +02:00
Alexander Kernozhitsky
3788aa0a99 [backport] fixes #23748; do not skip materializing temporaries for proc arguments (#23769)
fixes #23748

(cherry picked from commit 4202b606b1)
2024-06-30 18:54:32 +02:00
Alexander Kernozhitsky
e98c98b46c Comment out flaky test in tests/stdlib/thttpclient (#23772)
```
$ curl -v http://example.com/404 |& grep 'HTTP/1.1'
> GET /404 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
```

So, the test with http://example.com/404 should be disabled, I think.

(cherry picked from commit c88894bf76)
2024-06-30 07:28:27 +02:00
narimiran
51723bace8 getTemp has a different signature in Nim 2.0.x 2024-06-29 13:03:12 +02:00
Juan Carlos
a90e687d26 Document move limitations (#23763)
- See
https://github.com/nim-lang/Nim/issues/23759#issuecomment-2192123783

(cherry picked from commit 179897e55f)
2024-06-29 12:57:47 +02:00
Juan M Gómez
4b42170022 Bumps nimble to entryPoints commit (#23766)
(cherry picked from commit 9f74baa49d)
2024-06-29 12:57:17 +02:00
ringabout
2eff34f08a fixes commit hashes (#23520)
(cherry picked from commit 1185b93c6d)
2024-06-29 12:57:02 +02:00
ringabout
ba51e7c4d8 bundle atlas with sat (#23375)
pending https://github.com/nim-lang/atlas/pull/119
pending AtlasStableCommit updates

(cherry picked from commit 2a7ddcab2d)
2024-06-29 12:55:30 +02:00
ringabout
d259099ef0 fixes #23759; rework move for refc (#23764)
fixes #23759

(cherry picked from commit 56ed4e0bb9)
2024-06-29 12:38:52 +02:00
ringabout
915cd5b28b fixes #9940; genericAssign does not take care of the importC variables in refc [backport] (#23761)
fixes #9940

(cherry picked from commit 828cd58d8a)
2024-06-26 18:50:37 +02:00
Andreas Rumpf
16bbffcb77 fixes #23725; Size computations work better when they are correct (#23758)
[backport]

(cherry picked from commit 8096fa45bd)
2024-06-26 08:15:51 +02:00
ringabout
73af7c60a1 nrvo for embedded importc'ed types (#23708)
(cherry picked from commit 0b5a938f57)
2024-06-25 17:42:36 +02:00
narimiran
7bdcaada13 don't use unknown proc 2024-06-24 11:16:56 +02:00
ringabout
e77e129a05 fixes #23742; setLen(0) no longer allocates memory for uninitialized strs/seqs for refc (#23745)
fixes #23742

Before my PR, `setLen(0)` doesn't free buffer if `s != nil`, but it
allocated unnecessary memory for `strs`. This PR rectifies this
behavior. `setLen(0)` no longer allocates memory for uninitialized
strs/seqs

(cherry picked from commit 2bef08774f)
2024-06-24 08:29:11 +02:00
ringabout
d0b3b7e1a7 [backport] fixes #23711; C code contains backtick`gensym (#23716)
fixes #23711

(cherry picked from commit 646bd99d46)
2024-06-24 08:28:29 +02:00
metagn
2735fd2bf5 ignore uninstantiated static on match to base type [backport:2.0] (#23731)
fixes #23730

Since #23188 the compiler errors when matching a type variable to an
uninstantiated static value. However sometimes an uninstantiated static
value is given even when only a type match is being performed to the
base type of the static type, in the given issue this case is:

```nim
proc foo[T: SomeInteger](x: T): int = int(x)
proc bar(x: static int): array[foo(x), int] = discard
discard bar(123)
```

To deal with this issue we only error when matching against a type
variable constrained to `static`.

Not sure if the `q.typ.kind == tyGenericParam and
q.typ.genericConstraint == tyStatic` check is necessary, the code above
for deciding whether the variable becomes `skConst` doesn't use it.

(cherry picked from commit 128090c593)
2024-06-24 08:26:49 +02:00
Andreas Rumpf
8d1f03e1da fixes a long standing bug with varargs type inference [backport] (#23720)
(cherry picked from commit 5996b12355)
2024-06-24 08:26:19 +02:00
ringabout
d83eb88eb9 disable dnsclient because it is fragile (#23728)
```
  Unhandled exception: /home/runner/work/Nim/Nim/pkgstemp/dnsclient/tests/test1.nim(28, 3) `rr.strings == @["dnsclient.nim"]`  [AssertionDefect]
  [FAILED] query TXT
  [OK] query MX
  [OK] query CNAME
  [OK] query SRV
  Error: execution of an external program failed: '/home/runner/work/Nim/Nim/pkgstemp/dnsclient/tests/test1'
         Tip: 2 messages have been suppressed, use --verbose to show them.
  tools.nim(36)            doCmd

      Error:  Execution failed with exit code 1
          ... Command: /home/runner/work/Nim/Nim/bin/nim c --noNimblePath -d:NimblePkgVersion=0.3.4 --hints:off -r --path:. /home/runner/work/Nim/Nim/pkgstemp/dnsclient/tests/test1
```

(cherry picked from commit c58b6e8df8)
2024-06-24 08:25:27 +02:00
narimiran
10907cc4a7 bump NimVersion to 2.0.7 2024-06-24 08:24:34 +02:00
narimiran
c00e8e71e0 bump NimVersion to 2.0.6 2024-06-16 19:01:20 +02:00
narimiran
a073e225c8 Revert "fixes #22510 (#23100)"
This reverts commit d6bc8699b9.
2024-06-16 19:01:01 +02:00
Andreas Rumpf
140d641443 fixes #22927; no test case extractable [backport] (#23707)
(cherry picked from commit 3770236bee)
2024-06-13 11:08:08 +02:00
ringabout
cc779a42ce remove pkg "pylib" (#23691)
https://github.com/Yardanico/nimpylib is 404 now
(cherry picked from commit 09b5ed251e)
2024-06-11 14:07:27 +02:00
ringabout
26a4b137c6 [backport] fixes #23690; SIGSEGV with object variants and RTTI (#23703)
fixes #23690

```nim
dest.`:state` = src.`:state`
var :tmp_553651276 = dest.e1.a
`=wasMoved`(dest.e1.a)
dest.e1.a.kind = src.e1.a.kind
case dest.e1.a.kind
of 0:
  dest.e1.a.a = src.e1.a.a
of 1:
  `=copy`(dest.e1.a.c, src.e1.a.c)
case :tmp_553651276.kind
of 0:
of 1:
  `=destroy`(:tmp_553651276.c)
```
`dest.e1.a.kind = src.e1.a.kind` changes the discrimant but it fails to
clear the memory of `dest.e1.a`. Before using hooks for copying, we need
to clear the dest, e.g. `=wasMoved(dest.e1.a.c)`.

```nim
dest.`:state` = src.`:state`
var :tmp_553651276 = dest.e1.a
`=wasMoved`(dest.e1.a)
dest.e1.a.kind = src.e1.a.kind
case dest.e1.a.kind
of 0:
  `=wasMoved`(dest.e1.a.a)
  dest.e1.a.a = src.e1.a.a
  `=wasMoved`(dest.e1.a.b)
of 1:
  `=wasMoved`(dest.e1.a.c)
  `=copy`(dest.e1.a.c, src.e1.a.c)
case :tmp_553651276.kind
of 0:
of 1:
  `=destroy`(:tmp_553651276.c)
```

(cherry picked from commit 262ff648aa)
2024-06-11 14:04:29 +02:00
Juan M Gómez
d7544ec3db [Backport 2.0] Nimsuggest handle unknown file (#23701) 2024-06-10 17:14:16 +02:00
narimiran
767a901267 Revert "Fix #22826: Don't skip generic instances in type comparison (#22828)"
This reverts commit 5e20e935dd.
2024-06-07 16:30:32 +02:00
Andreas Rumpf
36f8cccda4 fixes #23354; [backport] (#23685)
(cherry picked from commit 7039b8b5bc)
2024-06-07 15:34:34 +02:00
Andreas Rumpf
d6bc8699b9 fixes #22510 (#23100)
(cherry picked from commit 69d0b73d66)
2024-06-06 16:14:57 +02:00
ringabout
7db4376cda fixes openarray hoist with gcc 14 (#23647)
blocks https://github.com/nim-lang/Nim/pull/23673

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 17475fc5d3)
2024-06-04 10:13:14 +02:00
ringabout
a85c635da8 fixes#23665; rework spawn with gcc 14 and fixes other tests (#23660)
fixes #23665

(cherry picked from commit 08f1eac8ac)
2024-06-04 07:38:38 +02:00
Miran
3e18c48cda [backport] test more packages (#23671)
These packages are some of the dependencies of Nimbus with shorter
testing times.

(cherry picked from commit d22e8f7f82)
2024-06-03 15:06:37 +02:00
ringabout
3455623500 rework ctypes with gcc 14 (#23636)
(cherry picked from commit 4bd1cf2376)
2024-06-03 08:25:55 +02:00
Juan M Gómez
8daac1b061 [Backport 2.0] Updates nimble (#23643) 2024-06-02 15:16:04 +02:00
ringabout
7071e3a080 closes #15778; adds a test case (#23613)
closes #15778

(cherry picked from commit c08356865d)
2024-05-24 15:57:30 +02:00
ringabout
98075688f3 fixes openarray views default values in JS (#23607)
(cherry picked from commit 0fcd838fd9)
2024-05-24 15:57:15 +02:00
ringabout
5abf9a1d77 fixes #23440; fixes destruction for temporary object subclass (#23452)
fixes #23440

(cherry picked from commit f175c81079)
2024-05-24 11:18:18 +02:00
metagn
ab053fd56a remove HEAD arraymancer dependency requirement in package CI (#23509)
Was introduced to handle a break in #23392, according to
https://github.com/nim-lang/Nim/pull/23503#issuecomment-2057266475
should not be needed anymore

(cherry picked from commit 49e1ca0b3e)
2024-05-24 08:28:08 +02:00
lit
598de35d45 doc(format): ospaths2,strutils: followup #23560 (#23629)
followup #23560

(cherry picked from commit b838d3ece1)
2024-05-23 13:55:24 +02:00
lit
a88b275bc3 doc(format): system.nim: doc of hostCPU for loongarch64 (#23621)
In doc, `loongarch64` used to be written as `'"loongarch64"'`

since it's [supported](https://github.com/nim-lang/Nim/pull/19223)

(cherry picked from commit b3b26b2e56)
2024-05-23 13:55:24 +02:00
PHO
e57b207ff8 Support NetBSD/aarch64 (#23616)
I could trivially port Nim to NetBSD/aarch64 because it already
supported NetBSD and aarch64. I only needed to generate `c_code` for
this combination.

(cherry picked from commit 0ba932132e)
2024-05-23 13:55:23 +02:00
ringabout
b9951e8c87 fixes lifting subtype calling parent's hooks (#23612)
ref https://forum.nim-lang.org/t/11587

Tested with `gcc version 14.0.1 20240412` locally

(cherry picked from commit 2c8551556e)
2024-05-23 13:55:23 +02:00
ringabout
9ad0ada8e4 adds Nim-related mimetypes back (#23589)
ref https://github.com/nim-lang/Nim/pull/23226

(cherry picked from commit 1eb9aac2f7)
2024-05-23 09:02:25 +02:00
lit
30eb2b394e Improve strutils.rsplit doc, proc and iterator have oppose result order. (#23570)
[`rsplit
iterator`](https://nim-lang.org/docs/strutils.html#rsplit.i,string,char,int)
yields substring in reversed order,

while [`proc
rsplit`](https://nim-lang.org/docs/strutils.html#rsplit%2Cstring%2Cchar%2Cint)'s
order is not reversed, but its doc only declare ```
The same as the rsplit iterator, but is a func that returns a sequence
of substrings.
```

(cherry picked from commit 2e3777d6f3)
2024-05-23 09:02:19 +02:00
ringabout
6a1694b28b fixes #23552; Invalid codegen when trying to mannualy delete distinct seq (#23558)
fixes #23552

(cherry picked from commit 2995a0318b)
2024-05-23 09:01:58 +02:00
Antonis Geralis
8f5451832e Add a note about the sideeffect pragma (#23543)
(cherry picked from commit 63398b11f5)
2024-05-23 08:59:31 +02:00
Angel Ezquerra
cd65b5e5f8 Add Complex version of almostEqual function (#23549)
This adds a version of `almostEqual` (which was already available for
floats) thata works with `Complex[SomeFloat]`.

Proof that this is needed is that the first thing that the complex.nim
runnable examples block did before this commit was define (an
incomplete) `almostEqual` function that worked with complex values.

(cherry picked from commit d8e1504ed1)
2024-05-23 08:59:23 +02:00
ringabout
80a6005f55 fixes #22409; don't check style for enumFieldSymChoice in the function (#23580)
fixes #22409

(cherry picked from commit 1ad4e80060)
2024-05-23 08:59:09 +02:00
lit
cd72be29c6 fixes #23442, fix for FileId under Windows (#23444)
See according issue:

Details:
<https://github.com/nim-lang/Nim/issues/23442#issuecomment-2021763669>

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 6cc783f7f3)
2024-05-23 08:57:35 +02:00
ringabout
1425654e9d fixes #23556; typeinfo.extendSeq generates random values in ORC (#23557)
fixes #23556

It should somehow handle default fields in the future

(cherry picked from commit 36bf3fa47b)
2024-05-23 08:57:27 +02:00
lit
1463d811fb Update unicode.nim: cmpRunesIgnoreCase: fix doc format (#23560)
Its doc used to render wrongly where `>` is considered as quote block:

![image](https://github.com/nim-lang/Nim/assets/97860435/4aeda257-3231-42a5-9dd9-0052950a160e)

(cherry picked from commit d772186b2d)
2024-05-23 08:57:19 +02:00
ringabout
d372a0030d fixes #23419; internal error with void in generic array instantiation (#23550)
fixes #23419

`void` is only supported as fields of objects/tuples. It shouldn't allow
void in the array.

I didn't merge it with taField because that flag is also used for
tyLent, which is allowed in the fields of other types.

(cherry picked from commit 185e06c923)
2024-05-23 08:56:53 +02:00
ringabout
a6e729ed5d fixes #23321; Error: internal error: openArrayLoc: ref array[0..0, int] (#23548)
fixes #23321

In the function `mapType`, ptrs (tyPtr, tyVar, tyLent, tyRef)
are mapped into ctPtrToArray, the dereference of which is skipped
in the `genref`. We need to skip these ptrs in the function
`genOpenArraySlice`.

(cherry picked from commit d09c3c0f58)
2024-05-22 09:39:02 +02:00
yojiyama7
c5f2d84ac7 fix typo: "As can been seen" to "As can be seen" (#23544)
(cherry picked from commit 47594eb909)
2024-05-22 09:38:57 +02:00
ringabout
24e5e9875c fixes #23531; fixes invalid meta type accepted in the object fields (#23532)
fixes #23531
fixes #19546
fixes #6982

(cherry picked from commit f682dabf71)
2024-05-22 09:38:27 +02:00
ringabout
4e977767a7 fixes #23536; Stack trace with wrong line number when the proc called inside for loop (#23540)
fixes #23536

(cherry picked from commit 0b0f185bd1)
2024-05-22 09:28:43 +02:00
ringabout
df589bcce1 fixes #23522; fixes pre-existing wrong type for iter in liftIterSym (#23538)
fixes #23522

(cherry picked from commit 407c0cb64a)
2024-05-22 09:28:21 +02:00
ringabout
e690ff35a2 fixes #23525; an 'emit' pragma cannot be pushed (#23537)
fixes #23525

(cherry picked from commit 4601bb0255)
2024-05-22 09:24:12 +02:00
ringabout
e3f4c3d417 fixes #23524; global variables cannot be analysed when injecting move (#23529)
fixes #23524

```nim
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
  ...
  result = n.kind == nkSym and n.sym.owner == owner and
    {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
    (n.sym.kind != skParam or isSinkParam(n.sym))
```
In `isAnalysableFieldAccess`, globals, cursors are already rejected

(cherry picked from commit cd3cf3a20e)
2024-05-22 09:23:43 +02:00
Nikolay Nikolov
efcb6b4b11 * fix for the debug line info code generation (#23488)
Previously, in certain cases, the compiler would generate debug info for
the correct line number, but for the wrong .nim source file.

(cherry picked from commit 7e3bac9235)
2024-05-21 18:51:04 +02:00
José Paulo
5eee3e06fc fix #23518 - <expr> is crashes nimsuggest (#23523)
This solution should resolve the nimsuggest crash issue. However,
perhaps the problem is in the parser?

fix #23518

(cherry picked from commit 60af04635f)
2024-05-21 18:51:04 +02:00
ringabout
dbbd2c8002 fixes #23505; fixes injectdestructors errors on transformed addr (deref) refs (#23507)
fixes #23505

(cherry picked from commit acd4c8a353)
2024-05-21 18:51:04 +02:00
ringabout
59e49ded2d fixes #23494; Wrong type in object construction error message (#23504)
fixes #23494

(cherry picked from commit 20698b8057)
2024-05-21 18:51:03 +02:00
Pouriya Jamshidi
4bf12a086d fix JSON deep copy description (#23495)
Hi,

This is a tiny change, fixing the error in the documentation of JSON's
deep copy proc.

(cherry picked from commit 1bd0955218)
2024-05-21 18:51:03 +02:00
metagn
b1bfba9a31 stop gensym identifiers hijacking routine decl names in templates (#23392)
fixes #23326

In a routine declaration node in a template, if the routine is marked as
`gensym`, the compiler adds it as a new symbol to a preliminary scope of
the template. If it's not marked as gensym, then it searches the
preliminary scope of the template for the name of the routine, then when
it matches a template parameter or a gensym identifier, the compiler
replaces the name node with a symbol node of the found symbol.

This makes sense for the template parameter since it has to be replaced
later, but not really for the gensym identifier, as it doesn't allow us
to inject a routine with the same name as an identifier previously
declared as gensym (the problem in #23326 is when this is in another
`when` branch).

However this is the only channel to reuse a gensym symbol in a
declaration, so maybe removing it has side effects. For example if we
have:

```nim
proc foo(x: int) {.gensym.} = discard
proc foo(x: float) {.gensym.} = discard
```

it will not behave the same as

```nim
proc foo(x: int) {.gensym.} = discard
proc foo(x: float) = discard
```

behaved previously, which maybe allowed overloading over the gensym'd
symbols.

A note to the "undeclared identifier" error message has also been added
for a potential error code that implicitly depended on the old behavior
might give, namely ``undeclared identifier: 'abc`gensym123'``, which
happens when in a template an identifier is first declared gensym in
code that doesn't compile, then as a routine which injects by default,
then the identifier is used.

(cherry picked from commit 73b0b0d31c)
2024-05-21 18:51:03 +02:00
lit
a798356838 Update encodings.nim, fix open with bad arg raising no EncodingError (#23481)
On POSIX, `std/encodings` uses iconv, and `iconv_open` returns
`(iconv_t) -1` on failure, not `NULL`

(cherry picked from commit c23d6a3cb9)
2024-05-21 18:51:02 +02:00
lit
1d7170f0ed Update syncio.nim, fixes "open by FileHandle" doesn't work on Windows (#23456)
## Reprodution
if on Windows:
```Nim
when defined(windows):
  var file: File
  let succ = file.open(<aFileHandle>)
```
then `succ` will be false.

If tested, it can be found to fail with errno `22` and message: `Invalid
argument`

## Problem
After some investigations and tests,
I found it's due to the `mode` argument for `fdopen`.

Currently `NoInheritFlag`(`'N'` in Windows) is added to `mode` arg
passed to `_fdopen`, but if referring to
[Windows `_fdopen`
doc](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fdopen-wfdopen?view=msvc-170),
you'll find there is no `'N'` describled. That's `'N'` is not accepted
by `_fdopen`.

Therefore, the demo above will fail.

## In Addition
To begin with, technologically speaking, when opening with a
`fileHandle`(or called `fd`), there is no concept of fd-inheritable as
`fd` is opened already.

In POSIX, `NoInheritFlag` is defined as `e`.

It's pointed out in [POSIX `open`
man-doc](https://www.man7.org/linux/man-pages/man3/fopen.3.html) that
`e` in mode is ignored for fdopen(),

which means `e` for `fdopen()` is not wanted, just allowed.

Therefore, better to also not pass `e` to `fdopen`

---

In all, that's this PR.

(cherry picked from commit dee55f587f)
2024-05-21 18:35:22 +02:00
ringabout
eaa8c7ca7a fixes #23422; card regression (#23437)
fixes #23422

ref https://github.com/nim-lang/Nim/issues/20997
https://github.com/nim-lang/Nim/pull/21165

The function `cardSet` is used for large sets that are stored in the
form of arrays. It shouldn't be passed as a pointer

(cherry picked from commit 4b6a9e4add)
2024-05-21 18:35:04 +02:00
ringabout
5f8e08031d fixes #23429; rework --verbosity with warnings/hints (#23441)
fixes #23429

(cherry picked from commit a24990bd8c)
2024-05-21 14:52:30 +02:00
Gianmarco
c2a14cb3fc Fix compile time errors when using tables on 8/16-bits systems. (#23450)
Refer to the discussion in #23439.

(cherry picked from commit afc30a3b93)
2024-05-21 14:52:30 +02:00
arkanoid87
b3a62931cd Update manual.md (#23393)
adding link to generic == for tuples in Open and Closed symbols example

(cherry picked from commit cbf48a253f)
2024-05-21 14:52:29 +02:00
soonsouth
b213da9937 chore: fix some typos (#23412)
Signed-off-by: soonsouth <cuibuwei@163.com>
(cherry picked from commit b387bc49b5)
2024-05-21 14:52:29 +02:00
ringabout
6eb3e3134e closes #22846; adds a test case (#23374)
closes #22846

(cherry picked from commit 6ebad30e7a)
2024-05-21 14:19:42 +02:00
ringabout
1e2d98a3e0 closes #15751; adds a test case (#23372)
closes #15751

(cherry picked from commit a2584c779b)
2024-05-21 14:19:35 +02:00
ringabout
55b5068d20 closes #10219; adds a test case (#23370)
closes #10219

(cherry picked from commit d373d304ff)
2024-05-21 14:19:25 +02:00
Juan M Gómez
c17e48c9c5 makes nimsuggest listen on localhost by default (#23351)
(cherry picked from commit 2081da3207)
2024-05-21 14:19:19 +02:00
ringabout
11713ef018 remove obselete doc with nimrtl (#23358)
since nimrtl.dll is created with `--threads:on`

(cherry picked from commit a619434904)
2024-05-21 14:19:04 +02:00
Juan M Gómez
b522d1fe24 Dont mangle when targeting cpp (#23335)
Unfortunately we cant trick the debugger when targeting C++ so this one
also needs to wait for our own debugger adapter.

(cherry picked from commit 90fe1b340f)
2024-05-21 14:18:54 +02:00
autumngray
dd94d98110 Fix nimsuggest highlight for import statements (#23263)
Currently, I don't have syntax highlighting (+ no/wrong
jump-to-definition) for some import statement forms, namely:

- `import module/name/with/(slashes)`
- `import (mod) as alias`
- `import basemod/[ (sub1), (sub2) ]`

With this patch, highlight/def will work for the regions indicated by
parentheses.

(cherry picked from commit 15577043e8)
2024-05-21 14:17:58 +02:00
Juan M Gómez
2f399807cb [Backport 2.0 ] Skips generic owner when mangling instances (#23600) 2024-05-14 15:03:26 +02:00
metagn
5cecd81a05 fix semFinishOperands for bracket expressions [backport:2.0] (#23571)
fixes #23568, fixes #23310

In #23091 `semFinishOperands` was changed to not be called for `mArrGet`
and `mArrPut`, presumably in preparation for #23188 (not sure why it was
needed in #23091, maybe they got mixed together), since the compiler
handles these later and needs the first argument to not be completely
"typed" since brackets can serve as explicit generic instantiations in
which case the first argument would have to be an unresolved generic
proc (not accepted by `finishOperand`).

In this PR we just make it so `mArrGet` and `mArrPut` specifically skip
calling `finishOperand` on the first argument. This way the generic
arguments in the explicit instantiation get typed, but not the
unresolved generic proc.

(cherry picked from commit 09bd9d0b19)
2024-05-08 18:41:43 +02:00
narimiran
526e48b2ed handle the weirdness of the type of the type keyword 2024-05-03 07:35:06 +02:00
metagn
a16f126614 add switch, warning, and bind support for new generic injection behavior (#23102)
refs #23091, especially post merge comments

Unsure if `experimental` and `bind` are the perfect constructs to use
but they seem to get the job done here. Symbol nodes do not get marked
`nfOpenSym` if the `bind` statement is used for their symbol, and
`nfOpenSym` nodes do not get replaced by new local symbols if the
experimental switch is not enabled in the local context (meaning it also
works with `push experimental`). However this incurs a warning as the
fact that the node is marked `nfOpenSym` means we did not `bind` it, so
we might want to do that or turn on the experimental switch if we didn't
intend to bind it.

The experimental switch name is arbitrary and could be changed.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 4b1a841707)
2024-05-03 07:32:19 +02:00
metagn
8b815cc857 delay resolved procvar check for proc params + acknowledge unresolved statics (#23188)
fixes #23186

As explained in #23186, generics can transform `genericProc[int]` into a
call `` `[]`(genericProc, int) `` which causes a problem when
`genericProc` is resemmed, since it is not a resolved generic proc. `[]`
needs unresolved generic procs since `mArrGet` also handles explicit
generic instantiations, so delay the resolved generic proc check to
`semFinishOperands` which is intentionally not called for `mArrGet`.

The root issue for
[t6137](https://github.com/nim-lang/Nim/blob/devel/tests/generics/t6137.nim)
is also fixed (because this change breaks it otherwise), the compiler
doesn't consider the possibility that an assigned generic param can be
an unresolved static value (note the line `if t.kind == tyStatic: s.ast
= t.n` below the change in sigmatch), now it properly errors that it
couldn't instantiate it as it would for a type param. ~~The change in
semtypinst is just for symmetry with the code above it which also gives
a `cannot instantiate` error, it may or may not be necessary/correct.~~
Now removed, I don't think it was correct.

Still possible that this has unintended consequences.

(cherry picked from commit e8092a5470)
2024-05-02 12:17:23 +02:00
metagn
d43672833a allow replacing captured syms in macro calls in generics (#23091)
fixes #22605, separated from #22744

This marks symbol captures in macro calls in generic contexts as
`nfOpenSym`, which means if there is a new symbol in the local
instantiatied body during instantiation time, this symbol replaces the
captured symbol. We have to be careful not to consider symbols outside
of the instantiation body during instantiation, because this will leak
symbols from the instantiation context scope rather than the original
declaration scope. This is done by checking if the local context owner
(maybe should be the symbol of the proc currently getting instantiated
instead? not sure how to get this) is the same as or a parent owner of
the owner of the replacement candidate symbol.

This solution is distinct from the symchoice mechanisms which we
originally assumed had to be related, if this assumption was wrong it
would explain why this solution took so long to arrive at.

(cherry picked from commit 941659581a)
2024-05-02 10:02:06 +02:00
Juan M Gómez
fcb8461efa Fixes an issue where exported types werent being cgen with the exportc pragma (#23369)
(cherry picked from commit 78c834dd76)
2024-04-28 06:57:55 +02:00
ringabout
871cd4b9db fixes refc with non-var destructor; cancel warnings (#23156)
fixes https://forum.nim-lang.org/t/10807

(cherry picked from commit 35ec9c31bd)
2024-04-27 20:00:30 +02:00
ringabout
fa78d937dc fixes #22597; avoid side effects for call returning openArray types (#23257)
fixes #22597

```nim
proc autoToOpenArray*[T](s: Slice[T]): openArray[T] =
  echo "here twice"
  result = toOpenArray(s.p, s.first, s.last)
```
For functions returning openarray types, `fixupCall` creates a temporary
variable to store the return value: `let tmp = autoToOpenArray()`. But
`genOpenArrayConv` cannot handle openarray assignements with side
effects. It should have stored the right part of the assignment first
instead of calling the right part twice.

(cherry picked from commit d44b0b1869)
2024-04-27 20:00:30 +02:00
metagn
b302b3102e don't use previous bindings of auto for routine return types (#23207)
fixes #23200, fixes #18866

not turned to `tyUntyped`. This had the side effect that anything
previously bound to `tyAnything` in the proc type match was then bound
to the proc return type, which is wrong since we don't know the proc
return type even if we know the expected parameter types (`tyUntyped`
also [does not care about its previous bindings in
`typeRel`](ab4278d217/compiler/sigmatch.nim (L1059-L1061))
maybe for this reason).

Now we mark `tyAnything` return types for routines as `tfRetType` [as
done for other meta return
types](18b5fb256d/compiler/semtypes.nim (L1451)),
and ignore bindings to `tyAnything` + `tfRetType` types in `semtypinst`.
On top of this, we reset the type relation in `paramTypesMatch` only
after creating the instantiation (instead of trusting
`isInferred`/`isInferredConvertible` before creating the instantiation),
using the same mechanism that `isBothMetaConvertible` uses.

This fixes the issues as well as making the disabled t15386_2 test
introduced in #21065 work. As seen in the changes for the other tests,
the error messages give an obscure `proc (a: GenericParam): auto` now,
but it does give the correct error that the overload doesn't match
instead of matching the overload pre-emptively and expecting a specific
return type.

tsugar had to be changed due to #16906, which is the problem where
`void` is not inferred in the case where `result` was never touched.

(cherry picked from commit f46f26e79a)
2024-04-27 20:00:30 +02:00
ringabout
eb3aa70ffa fixes #22939; fixes #16890; push should but doesn't apply to importc … (#22944)
…var/let symbols

fixes #22939
fixes #16890

Besides, it was applied to let/const/var with pragmas, now it is
universally applied.

```nim
{.push exportc.}
proc foo =
  let bar = 12
  echo bar
{.pop.}
```

For example, the `bar` variable will be affected by `exportc`.

(cherry picked from commit cecaf9c56b)
2024-04-26 14:27:17 +02:00
ringabout
72a2fe5125 fixes broken nightlies; follow up #22544 (#22585)
ref https://github.com/nim-lang/nightlies/actions/runs/5970369118/job/16197865657

> /home/runner/work/nightlies/nightlies/nim/lib/pure/os.nim(678, 30) Error: getApplOpenBsd() can raise an unlisted exception: ref OSError

(cherry picked from commit 1fcb53cded)
2024-04-24 06:48:51 +02:00
narimiran
e582f1ccb3 Revert "Look up generic parameters when found inside semOverloadedCall, fixin… (#23054)"
This reverts commit 54bd380011.
2024-04-23 18:01:24 +02:00
narimiran
0e1a699937 Revert "Overloads passed to static proc parameters now convert to the desired… (#23063)"
This reverts commit 82e77a13f3.
2024-04-23 18:01:12 +02:00
bptato
494b5486ba Fix std/base64.decode out of bounds read (#23526)
inputLen may end up as 0 in the loop if the input string only includes
trailing characters. e.g. without the patch, decode(" ") would panic.

(cherry picked from commit 30cf570af9)
2024-04-23 06:57:55 +02:00
narimiran
33817f2c30 Revert "fixes #22923; fixes =dup issues (#23182)"
This reverts commit fbb9ce4d5c.
2024-04-23 06:57:09 +02:00
Gianmarco
f44c49c5bd Change unicode lookup tables to have int32 elements to support platforms where sizeof(int) < 4 (#23433)
Fixes an issue that comes up when using strutils.`%` or any other
strutils/strformat feature that uses the unicode lookup tables behind
the scenes, on systems where ints are than 32-bit wide.

Tested with:

```bash
./koch test cat lib
```

Refer to the discussion in #23125.

(cherry picked from commit 4c38569229)
2024-04-22 16:18:24 +02:00
Igor Sirotin
3dd5e11480 fix: use ErrorColor for hints marked as errors (#23430)
# Description

When using `--hintAsError`, we want some red color to appear in the
logs.
Same is already done for `warningAsError`.

# Cherry-picking to Nim 1.6

Would be nice to cherry-pick this and the `warningAsError` log highlight
to 1.6 branch, as it's used in status-desktop.

(cherry picked from commit 50c1e93a74)
2024-04-22 16:18:16 +02:00
ringabout
716ff93161 fixes #23401; prevents nrvo for cdecl procs (#23409)
fixes #23401

(cherry picked from commit f639cf063f)
2024-04-22 16:18:08 +02:00
metagn
1ac5aa951d propagate efWantStmt in semWhen (#23400)
fixes #23399

The new case introduced in #21657 is triggered by `efWantStmt` but the
`when` statement doesn't normally propagate this flag, so propagate it
when the `semCheck` param in `semWhen` is true which happens when the
`when` statement is `efWhenStmt` anyway.

(cherry picked from commit fb6c805568)
2024-04-22 16:17:56 +02:00
ringabout
d112874078 fixes #22166; adds sideeffects for close and setFilePos (#23380)
fixes #22166

(cherry picked from commit 1e20165a15)
2024-04-22 16:17:09 +02:00
ringabout
61da33f31b fixes #22284; fixes #22282; don't override original parameters of inferred lambdas (#23368)
fixes #22284
fixes #22282

```
Error: j(uRef, proc (config: F; sources: auto) {.raises: [].} = discard ) can raise an unlisted exception: Exception
```

The problem is that `n.typ.n` contains the effectList which shouldn't
appear in the parameter of a function defintion. We could not simply use
`n.typ.n` as `n[paramsPos]`. The effect lists should be stripped away
anyway.

(cherry picked from commit 320311182c)
2024-04-22 16:16:57 +02:00
ringabout
e15aa20059 fixes sink regression for ORC; ref #23354 (#23359)
ref #23354

The new move analyzer requires types that have the tfAsgn flag
(otherwise `lastRead` will return true); tfAsgn is included when the
destructor is not trival. But it should consider the assignement for
objects in this case because objects might have a trival destructors but
it's the assignement that matters when it is passed to sink parameters.

(cherry picked from commit 572b0b67ff)
2024-04-22 10:22:28 +02:00
ringabout
8d7b7ff861 fixes #13481; fixes #22708; disable using union objects in VM (#23362)
fixes #13481;
fixes #22708

Otherwise it gives implicit results or bad codegen

(cherry picked from commit 31d7554524)
2024-04-22 10:22:28 +02:00
Jacek Sieka
d389310bb9 strformat: detect format string errors at compile-time (#23356)
This also prevents unwanted `raises: [ValueError]` effects from bubbling
up from correct format strings which makes `fmt` broadly unusable with
`raises`.

The old runtime-based `formatValue` overloads are kept for
backwards-compatibility, should anyone be using runtime format strings.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit a1e41930f8)
2024-04-22 10:22:28 +02:00
ringabout
1ae6deda61 fixes #22723; skips tyUserTypeClasses in injectdestructors (#23341)
fixes #22723

(cherry picked from commit 6ce6cd4bb8)
2024-04-22 10:22:28 +02:00
ringabout
6d38eafda1 fixes #23304; uses snprintf instead of sprintf (#23322)
fixes #23304

(cherry picked from commit dfd778d056)
2024-04-22 10:22:28 +02:00
heterodoxic
845be91df5 assume a module's usage if it contains a passC/passL/compile pragma w… (#23323)
…hich conveys effects beyond its module scope for C/C++
codegen(suppresses current UnusedImport warning)

Just a minor inconvenience working in the area of C/C++ integration I
guess, but here we go:

I noticed receiving ```UnusedImport``` warnings for modules having only
```passC```/```passL```/```compile``` pragmas around. I gather the
compiler cannot actually infer those modules being unused as they *may*
have consequences for the whole build process (as they did in my simple
case).

Thus, I hereby suggest adding the `sfUsed` flag to the respective module
in order to suppress the compiler's warning.

I reckon other pragmas should be put into consideration as well: I will
keep up the investigation with PR followups.

(cherry picked from commit 9a46230335)
2024-04-22 10:22:27 +02:00
ringabout
fa003a00ee fixes regression #22909; don't optimize result init if statements can raise which corrupts the compiler (#23271)
fixes #22909
required by https://github.com/nim-lang/Nim/pull/23267

```nim
proc foo: string =
  assert false
  result = ""
```

In the function `foo`, `assert false` raises an exception, which can
cause `result` to be uninitialized if the default result initialization
is optimized out

(cherry picked from commit 7d9721007c)
2024-04-22 08:53:16 +02:00
ringabout
45f1b19942 clean up goto exceptions; remove the setjmp.h dep (#23259)
(cherry picked from commit e3350cbe6f)
2024-04-22 08:53:03 +02:00
ringabout
96917755cd fixes #23247; don't destroy openarray since it doesn't own the data (#23254)
fixes #23247
closes #23251 (which accounts for why the openarray type is lifted
because ops are lifted for openarray conversions)

related: https://github.com/nim-lang/Nim/pull/18713

It seems to me that openarray doesn't own the data, so it cannot destroy
itself. The same case should be applied to
https://github.com/nim-lang/Nim/issues/19435. It shouldn't be destroyed
even openarray can have a destructor. A cleanup will be followed for
https://github.com/nim-lang/Nim/pull/19723 if it makes sense.

According to https://github.com/nim-lang/Nim/pull/12073, it lifts
destructor for openarray when openarray is sunk into the function, when
means `sink openarray` owns the data and needs to destroy it. In other
cases, destructor shouldn't be lifted for `openarray` in the first place
and it shouldn't destroy the data if it doesn't own it.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 24a606902a)
2024-04-22 08:52:55 +02:00
Bung
8a7a776034 fix mime types data (#23226)
generated via https://github.com/bung87/mimetypes_gen

source data:
http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=co

(cherry picked from commit 01097fc1fc)
2024-04-20 09:44:31 +02:00
Angel Ezquerra
e33d96ad4e Make std/math classify work without --passc:-fast-math. (#23211)
By using the existing isNaN function we can make std/math's classify
function work even if `--passc:-fast-math` is used.

(cherry picked from commit 38f9ee0e58)
2024-04-20 09:44:31 +02:00
ringabout
5a2cd98867 fixes #22218; avoids cursor when copy is disabled (#23209)
fixes #22218

(cherry picked from commit b2f79df81c)
2024-04-20 09:44:31 +02:00
ringabout
a1b310d186 fixes #12334; keeps nkHiddenStdConv for cstring conversions (#23216)
fixes #12334

`nkHiddenStdConv` shouldn't be removed if the sources aren't literals,
viz. constant symbols.

(cherry picked from commit 3fb46fac32)
2024-04-20 09:44:31 +02:00
metagn
f97c0cced0 error on large integer types as array index range (#23229)
fixes #17163, refs #23204

Types that aren't `tyRange` and are bigger than 16 bits, so `int32`,
`uint64`, `int` etc, are disallowed as array index range types.
`tyRange` is excluded because the max array size is backend independent
(except for the specific size of `high(uint64)` which crashes the
compiler) and so there should still be an escape hatch for people who
want bigger arrays.

(cherry picked from commit 3ab8b6b2cf)
2024-04-20 09:44:31 +02:00
ringabout
2c964268f8 fixes #23223; prevents insert self-assignment (#23225)
fixes #23223

(cherry picked from commit 3379d26629)
2024-04-20 09:44:30 +02:00
ringabout
a715aeb716 fixes #23180; fixes #19805; prohibits invalid tuple unpacking code in for loop (#23185)
fixes #23180
fixes #19805

(cherry picked from commit ab4278d217)
2024-04-20 09:44:30 +02:00
ringabout
d57200bc6f fixes #15924; Tuple destructuring is broken with closure iterators (#23205)
fixes #15924

(cherry picked from commit 8484abc2e4)
2024-04-20 09:44:30 +02:00
ringabout
fbb9ce4d5c fixes #22923; fixes =dup issues (#23182)
fixes #22923

(cherry picked from commit 29ac3c9986)
2024-04-20 09:44:29 +02:00
metagn
76735147b3 trigger range check with new type inference on nkIntLit [backport:1.6] (#23179)
fixes #23177

`changeType` doesn't perform range checks to see if the expression fits
the new type [if the old type is the same as the new
type](62d8ca4306/compiler/semexprs.nim (L633)).
For `nkIntLit`, we previously set the type to the concrete base of the
expected type first, then call `changeType`, which works for things like
range types but not bare types of smaller bit size like `int8`. Now we
don't set the type (so the type is nil), and `changeType` performs the
range check when the type is unset (nil).

(cherry picked from commit 00be8f287a)
2024-04-19 18:31:38 +02:00
metagn
c2d297cc3c don't transform typed bracket exprs to [] calls in templates (#23175)
fixes #22775

It's pre-existing that [`prepareOperand` doesn't typecheck expressions
which have
types](a4f3bf3742/compiler/sigmatch.nim (L2444)).
Templates can take typed subscript expressions, transform them into
calls to `[]`, and then have this `[]` not be resolved later if the
expression is nested inside of a call argument, which leaks an untyped
expression past semantic analysis. To prevent this, don't transform any
typed subscript expressions into calls to `[]` in templates. Ditto for
curly subscripts (with `{}`) and assignments to subscripts and curly
subscripts (with `[]=` and `{}=`).

(cherry picked from commit 62d8ca4306)
2024-04-19 16:42:44 +02:00
Ryan McConnell
4886534ed3 Fixes #23172 (#23173)
#23172

(cherry picked from commit a4f3bf3742)
2024-04-19 16:42:34 +02:00
ringabout
2ed16bf2d4 fixes #23139; Cannot get repr of range type of enum (#23164)
fixes #23139

(cherry picked from commit 3dee1a3e4c)
2024-04-19 16:42:27 +02:00
ringabout
c3d043e980 fixes #23167; take nkOpenSymChoice into consideration caused by templates [backport] (#23168)
fixes #23167

(cherry picked from commit 4eaa3b028c)
2024-04-19 16:39:22 +02:00
Jacek Sieka
0cdca3a0cd Recommend hanging indent in NEP1 (#23105)
This PR modernises the NEP1 style guide to prefer hanging indent over
vertial alignment for long code statements while still allowing
alignment in legacy code.

The change is based on research and study of existing style guides for
both braced and indented languages that have seen wide adoption as well
as working with a large Nim codebase with several teams touching the
same code regularly.

The research was done as part of due diligence leading up to
[nph](https://github.com/arnetheduck/nph) which uses this style
throughout.

There are several reasons why hanging indent works well for
collaboration, good code practices and modern Nim features:

* as NEP1 itself points out, alignment causes unnecessary friction when
refactoring, adding/removing items to lists and otherwise improving code
style or due to the need for realignment - the new recommendation aligns
NEP1 with itself
* When collaborating, alignment leads to unnecessary git conflicts and
blame changes - with hanging indent, such conflicts are minimised.
* Vertical alignment pushes much of the code to the right where often
there is little space - when using modern features such as generics
where types may be composed of several (descriptively named) components,
there is simply no more room for parameters or comments
* The space to the left of the alignemnt cannot productively be used for
anything (unlike on the right, where comments may be placed)
* Double hanging indent maintaines visual separation between parameters
/ condition and the body that follows.

This may seem like a drastic change, but in reality, it is not:

* the most popular editor for Nim (vscode) already promotes this style
by default (if you press enter after `(`, it will jump to an indent on
the next line)
* although orthogonal to these changes, tools such as `nph` can be used
to reformat existing code should this be desired - when done in a single
commit, `git blame` is not lost and neither are exsting PRs (they can
simply be reformatted deterministically) - `nph` is also integrated with
vscode.
* It only affects long lines - ie most code remains unchanged

Examples of vertical alignment in the wild, for wildly successful
languages and formatters:

* [PEP-8](https://peps.python.org/pep-0008/#indentation)
*
[black](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#how-black-wraps-lines)
* [prettier](https://prettier.io/docs/en/)

The above examples are useful mainly to show that hanging-indent
_generally_ is no impediment to efficient code reading and on the whole
is an uncontroversial choice as befits the standard library.

(cherry picked from commit c4f98b7696)
2024-04-19 16:39:13 +02:00
Gianmarco
5e22f6ca58 Fix cmpRunesIgnoreCase on system where sizeof(int) < 4. Fixes #23125. (#23138)
Fixes an issue where importing the `strutils` module, or any other
importing the `strutils` module, ends up with a compile time error on
platforms where ints are less then 32-bit wide.

The fix follows the suggestions made in #23125.

(cherry picked from commit 15c7b76c66)
2024-04-19 16:37:28 +02:00
metagn
f8d538f4ad fix spurious indent and newlines in rendering of nkRecList (#23121)
Rendering of `nkRecList` produces an indent and adds a new line at the
end. However for things like case object `of`/`else` branches or `when`
branches this is already done, so this produces 2 indents and an extra
new line. Instead, just add an indent in the place where the indent that
`nkRecList` produces is needed, for the rendering of the final node of
`nkObjectTy`. There doesn't seem to be a need to add the newline.

Before:

```nim
case x*: bool
of true:
    y*: int

of false:
  nil
```

After:

```nim
case x*: bool
of true:
  y*: int
of false:
  nil
```

(cherry picked from commit fc49c6e3ba)
2024-04-19 16:37:23 +02:00
Jake Leahy
8efdc313a3 Don't crash for invalid toplevel parseStmt/Expr calls (#23089)
This code will crash `check`/`nimsuggest` since the `ra` register is
uninitialised

```nim
import macros

static:
  discard parseExpr("'")
```
Now it assigns an empty node so that it has something

Testament changes were so I could properly write a test. It would pass
even with a segfault since it could find the error

(cherry picked from commit db9d8003b0)
2024-04-19 16:37:16 +02:00
Jake Leahy
49cc3c5746 Mark macros.error as .noreturn. (#23081)
Closes #14329

Marks `macros.error` as `.noreturn` so that it can be used in
expressions. This also fixes the issue that occurred in #19659 where a
stmt that could be an expression (Due to having `discardable` procs at
the end of other branches) would believe a `noreturn` proc is returning
the same type e.g.
```nim
 proc bar(): int {.discardable.} = discard

if true: bar()
else: quit(0) # Says that quit is of type `int` and needs to be used/discarded except it actually has no return type
```

(cherry picked from commit b3b87f0f8a)
2024-04-19 16:35:26 +02:00
Jake Leahy
865a2f895e Allow parseAll to parse statements separated by semicolons (#23088)
Fixes the second issue listed in #9918.

Fixed by replacing the logic used in `parseAll` with just a continious
loop to `complexOrSimpleStmt` like what the [normal parser
does](https://github.com/nim-lang/Nim/blob/devel/compiler/passes.nim#L143-L146).
`complexOrSimpleStmt` [guarantees
progress](https://github.com/nim-lang/Nim/blob/devel/compiler/parser.nim#L2541)
so we don't need to check progress ourselves.

Also allows `nimpretty` to parse more valid Nim code such as
```nim
proc foo(); # Would complain about indention here
proc foo() =
  # ...
```

(cherry picked from commit 0bd4d80238)
2024-04-19 16:35:07 +02:00
Jason Beetham
82e77a13f3 Overloads passed to static proc parameters now convert to the desired… (#23063)
… type mirroring proc params

(cherry picked from commit 91efa49550)
2024-04-19 14:01:22 +02:00
Pylgos
fca71c4bb4 fixes #9381; Fix double evaluation of types in generic objects (#23072)
fixes https://github.com/nim-lang/Nim/issues/9381

(cherry picked from commit 1b7b0d69db)
2024-04-19 14:01:07 +02:00
ringabout
3d4ec68e54 fixes #23060; editDistance wrongly compare the length of rune strings (#23062)
fixes #23060

(cherry picked from commit 7e1ea50bc3)
2024-04-19 14:00:01 +02:00
Jason Beetham
54bd380011 Look up generic parameters when found inside semOverloadedCall, fixin… (#23054)
…g static procs

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 8cc3c774c8)
2024-04-19 13:59:54 +02:00
narimiran
55905604da Revert "fixes #22913; fixes #12985 differently push-ing pragma exportc genera… (#22941)"
This reverts commit d566d21730.
2024-04-19 08:12:35 +02:00
Jakub
fd64402e47 allow Nix builds by not calling git in isGitRepo for Nimble (#23515)
Because `isGitRepo()` call requires `/bin/sh` it will always fail when
building Nim in a Nix build sandbox, and the check doesn't even make
sense if Nix already provides Nimble source code.

Since for Nimble `allowBundled` is set to `true` this effectlvely does
not change behavior for normal builds, but does avoid ugly hacks when
building in Nix which lacks `/bin/sh` and fails to call `git`.

Reference:
*
https://github.com/status-im/nimbus-eth2/pull/6180#discussion_r1570237858

Signed-off-by: Jakub Sokołowski <jakub@status.im>
(cherry picked from commit d6823f4776)
2024-04-18 12:28:06 +02:00
ringabout
c696ec92db fixes #23001; give a better warning for PtrToCstringConv (#23005)
fixes #23001

(cherry picked from commit 7ea5aaaebb)
2024-04-18 10:34:01 +02:00
SirOlaf
4bc45bb6bc Fix endsInNoReturn for case statements (#23009)
While looking at the CI I noticed that there's a couple false positives
for `case` statements that cannot be checked for exhaustiveness since my
changes, this should resolve them.

---------

Co-authored-by: SirOlaf <>
(cherry picked from commit 9140f8e221)
2024-04-18 10:33:54 +02:00
ringabout
9f35ede1a8 fixes #22926; Different type inferred when setting a default value for an array field (#22999)
fixes #22926

(cherry picked from commit 96513b2506)
2024-04-18 10:33:47 +02:00
ringabout
51fa2fada1 fixes #22996; typeAllowedCheck for default fields (#22998)
fixes #22996

(cherry picked from commit 795aad4f2a)
2024-04-18 10:33:25 +02:00
John Viega
aed04c6405 fix: std/marshal unmarshaling of ref objects (#22983)
Fixes #16496

![Marshal doesn't properly unmarshal *most* ref objects; the exceptions
being nil
ones](https://github-production-user-asset-6210df.s3.amazonaws.com/4764481/285471431-a39ee2c5-5670-4b12-aa10-7a10ba6b5b96.gif)
Test case added.

Note that this test (t9754) does pass locally, but there are tons of
failures by default on OS X arm64, mostly around the bohem GC, so it's
pretty spammy, and could easily have missed something. If there are
better instructions please do let me know.

---------

Co-authored-by: John Viega <viega@Johns-MacBook-Pro.local>
Co-authored-by: John Viega <viega@Johns-MBP.localdomain>
Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
(cherry picked from commit 5b2fcabff5)
2024-04-18 10:33:14 +02:00
ringabout
d566d21730 fixes #22913; fixes #12985 differently push-ing pragma exportc genera… (#22941)
…tes invalid C identifiers

fixes #22913
fixes #12985 differently

`{.push.} now does not apply to generic instantiations`

(cherry picked from commit 5dafcf4957)
2024-04-18 10:30:56 +02:00
Derek
5c4c8c44dc let InotifyEvent type sizeof-able (#22958)
Since the `InotifyEvent`s are receive through `read()`, user need the
size of the type.

(cherry picked from commit 0f7488e20f)
2024-04-18 10:30:51 +02:00
握猫猫
47f1ab7b97 Fix OSError errorCode field is not assigned a value (#22954)
In this PR, the following changes were made:
1. Replaced `raise newException(OSError, osErrorMsg(errno))` in batches
with `raiseOSError(errcode)`.
2. Replaced `newException(OSError, osErrorMsg(errno))` in batches with
`newOSError(errcode)`.

There are still some places that have not been replaced. After checking,
they are not system errors in the traditional sense.

```nim
proc dlclose(lib: LibHandle) =
  raise newException(OSError, "dlclose not implemented on Nintendo Switch!")
```

```nim
if not fileExists(result) and not dirExists(result):
  # consider using: `raiseOSError(osLastError(), result)`
  raise newException(OSError, "file '" & result & "' does not exist")
```

```nim
proc paramStr*(i: int): string =
  raise newException(OSError, "paramStr is not implemented on Genode")
```

(cherry picked from commit 39fbd30513)
2024-04-18 10:30:46 +02:00
ringabout
710801d880 fixes #22932; treats closure iterators as pointers (#22934)
fixes #22932
follow up https://github.com/nim-lang/Nim/pull/21629

---------

Co-authored-by: Nickolay Bukreyev <SirNickolas@users.noreply.github.com>
(cherry picked from commit 0dc3513613)
2024-04-18 10:30:40 +02:00
ringabout
39d0eb3a58 fixes #22673; Cannot prove that result is initialized for a placehold… (#22915)
…er base method returning a lent

fixes #22673

(cherry picked from commit 2e070dfc76)
2024-04-18 10:30:34 +02:00
Jacek Sieka
71dde8f7cf reserve sysFatal for Defect (#22158)
Per manual, `panics:on` affects _only_ `Defect`:s - thus `sysFatal`
should not redirect any other exceptions.

Also, when `sysFatal` is used in `nimPanics` mode, it should use regular
exception handling pipeline to ensure exception hooks are called
consistently for all raised defects.

(cherry picked from commit 58c44312af)
2024-04-18 10:30:26 +02:00
ringabout
683a1213db fixes #22860; suppress AnyEnumConv warning when iterating over set (#22904)
fixes #22860

(cherry picked from commit af556841ac)
2024-04-18 10:30:16 +02:00
ringabout
3ae3050b80 fixes #22902; borrow from proc return type mismatch (#22908)
fixes #22902

(cherry picked from commit 95e5ad6927)
2024-04-18 10:30:09 +02:00
ringabout
bd829c093d fixes #22883; replace default(typeof( with reset; suppress `Unsaf… (#22895)
fixes #22883

…eDefault` warnings

avoid issues mentioned by https://forum.nim-lang.org namely, it
allocated unnecessary stack objects in the loop

```c
while (1)
{
tyObject_N__8DSNqSGSHBKOhI8CqSgAow T5_;
nimZeroMem((void *)(&T5_), sizeof(tyObject_N__8DSNqSGSHBKOhI8CqSgAow));
eqsink___test4954_u450((&(*t_p0).data.p->data[i].Field1), T5_);
}
```

It might be more efficient in some cases

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

(cherry picked from commit 92141e82ed)
2024-04-18 10:29:59 +02:00
SirOlaf
5e20e935dd Fix #22826: Don't skip generic instances in type comparison (#22828)
Close #22826

I am not sure why this code skips generic insts, so letting CI tell me.
Update: It has told me nothing. Maybe someone knows during review.

Issue itself seems to be that the generic instance is skipped thus it
ends up being just `float` which makes it use the wrong generic instance
of the proc because it matches the one in cache

---------

Co-authored-by: SirOlaf <>
(cherry picked from commit c13c48500b)
2024-04-18 10:29:53 +02:00
ringabout
106c7d2afb fixes #22844; uses arrays to store holeyenums for iterations; much more efficient than sets and reasonable for holeyenums (#22845)
fixes #22844

(cherry picked from commit e10878085e)
2024-04-18 10:29:47 +02:00
ringabout
755a15d8d4 fixes #19250; fixes #22259; ORC AssertionDefect not containsManagedMemory(n.typ) (#22823)
fixes #19250
fixes #22259

The strings, seqs, refs types all have this flag, why should closures be
treated differently?

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

(cherry picked from commit f5d70e7fa7)
2024-04-18 10:29:33 +02:00
ringabout
a5a6a4d39e fixes #22790; use cast suppress AnyEnumConv warnings for enums withou… (#22813)
…t holes

fixes #22790

(cherry picked from commit ecaccafa6c)
2024-04-18 09:04:17 +02:00
ringabout
3120c6e839 use lent for the return value of index accesses of tables (#22812)
ref https://forum.nim-lang.org/t/10525

(cherry picked from commit 9d7acd001f)
2024-04-18 09:03:06 +02:00
ringabout
0d255b4ea1 suppress incorrect var T destructor warnings for newFinalizer in stdlib (#22810)
in `std/nre`
```nim
proc initRegex(pattern: string, flags: int, study = true): Regex =
  new(result, destroyRegex)
```
gives incorrect warnings like

```
C:\Users\blue\Documents\Nim\lib\impure\nre.nim(252, 6) Error: A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter [Deprecated
```

(cherry picked from commit 14d25eedfd)
2024-04-18 09:02:55 +02:00
ringabout
e9067c03c6 allows cast int to bool/enum in VM (#22809)
Since they are integer types, by mean of allowing cast integer to enums
in VM, we can suppress some enum warnings in the stdlib in the unified
form, namely using a cast expression.

(cherry picked from commit 2cf214d6d4)
2024-04-18 09:02:44 +02:00
ringabout
999d29d2a0 fixes #8893; guard against array access in renderer (#22807)
fixes #8893

(cherry picked from commit 81b2ae747e)
2024-04-18 09:02:38 +02:00
ringabout
08d37c2d7e fixes #22787; marks var section in the loop as reassign preventing cursor (#22800)
fixes #22787

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit efa64aa49b)
2024-04-18 09:02:29 +02:00
Ryan McConnell
4f78a4dd3e Make typeRel behave to spec (#22261)
The goal of this PR is to make `typeRel` accurate to it's definition for
generics:
```
# 3) When used with two type classes, it will check whether the types
# matching the first type class (aOrig) are a strict subset of the types matching
# the other (f). This allows us to compare the signatures of generic procs in
# order to give preferrence to the most specific one:
```

I don't want this PR to break any code, and I want to preserve all of
Nims current behaviors. I think that making this more accurate will help
serve as ground work for the future. It may not be possible to not break
anything but this is my attempt.

So that it is understood, this code was part of another PR (#22143) but
that problem statement only needed this change by extension. It's more
organized to split two problems into two PRs and this issue, being
non-breaking, should be a more immediate improvement.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit b2ca6bedae)
2024-04-18 09:01:14 +02:00
ringabout
bb82d46ef1 fixes #22696; func strutils.join for non-strings uses proc $ which can have side effects (#22770)
fixes #22696
partially revert https://github.com/nim-lang/Nim/pull/16281

`join` calls `$` interally, which might introduce a sideeffect call.

(cherry picked from commit 5eeafbf550)
2024-04-18 09:01:09 +02:00
Juan M Gómez
567900be76 case macro now can be used inside generic. Fixes #20435 (#22752)
fixes #20435

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
Co-authored-by: Jake Leahy <jake@leahy.dev>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 0c179db657)
2024-04-18 09:01:03 +02:00
ringabout
cb899ccf25 ref #19727; implement setLenUninit for seqsv2 (#22767)
ref #19727

(cherry picked from commit 285cbcb6aa)
2024-04-18 09:00:58 +02:00
ringabout
710fd14393 fixes #10542; suppresses varargs conversion warnings (#22757)
fixes #10542
revives and close #20169

(cherry picked from commit a7a0cfe8eb)
2024-04-18 09:00:51 +02:00
ringabout
499ad5b63c fixes #22706; turn "unknown hint" into a hint (#22755)
fixes #22706

(cherry picked from commit 3979e83fcb)
2024-04-18 09:00:46 +02:00
ringabout
8ab6cf1ca8 fixes #22246; generate __builtin_unreachable hints for case defaults (#22737)
fixes #22246
resurrects #22350

(cherry picked from commit a1b6fa9420)
2024-04-18 09:00:35 +02:00
ringabout
577ffbc57c items, pairs and friends now use unCheckedInc (#22729)
`{.push overflowChecks: off.}` works in backends. Though it could be
implemented as a magic function.

By inspecting the generated C code, the overflow check is eliminated in
the debug or release mode.

![image](https://github.com/nim-lang/Nim/assets/43030857/49c3dbf4-675e-414a-b972-b91cf218c9f8)

Likewise, the index checking is probably not needed.

(cherry picked from commit d82bc0a29f)
2024-04-18 09:00:28 +02:00
Juan M Gómez
e918a762f7 fixes compiler crash by preventing exportc on generics (#22731)
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit fefde3a735)
2024-04-18 09:00:05 +02:00
metagn
d56000eaff make parseEnum skip type aliases for enum type sym (#22727)
fixes #22726

(cherry picked from commit 51cb493b22)
2024-04-18 08:59:51 +02:00
litlighilit
cc7f76a247 Update osfiles.nim, make moveFile consider permission on *nix (#22719)
see https://github.com/nim-lang/Nim/issues/22674

(cherry picked from commit 741285b335)
2024-04-18 08:59:35 +02:00
ringabout
adf3e3ceb5 fixes incorrect cint overflow in system (#22718)
fixes #22700

(cherry picked from commit 63c2ea5566)
2024-04-18 08:59:28 +02:00
SirOlaf
344feb0dd1 Fix #22713: Make size unknown for tyForward (#22714)
Close #22713

---------

Co-authored-by: SirOlaf <>
(cherry picked from commit fcf4c1ae17)
2024-04-17 16:02:18 +02:00
metagn
dc6058fb9b implement semgnrc for tuple and object type nodes (#22709)
fixes #22699

(cherry picked from commit 8836207a4e)
2024-04-17 16:01:48 +02:00
Amjad Ben Hedhili
bbf32118a2 followup of #22568 (#22690)
(cherry picked from commit 38b58239e8)
2024-04-17 16:01:36 +02:00
metagn
b2cb5f55ac refactor semtempl ident declarations, some special word use (#22693)
`semtempl` is refactored to combine the uses of `getIdentNode`,
`onlyReplaceParams`, `isTemplParam` and most of `replaceIdentBySym` into
a single `getIdentReplaceParams` proc. This might fix possible problems
with injections of `nkAccQuoted`.

Some special word comparison in `ast` and `semtempl` are also made more
efficient.

(cherry picked from commit ac1804aba6)
2024-04-17 16:01:29 +02:00
ringabout
7083d397f3 fixes the discVal register is used after free in vmgen (#22688)
follow up https://github.com/nim-lang/Nim/pull/11955

(cherry picked from commit b1a8d6976f)
2024-04-17 16:01:23 +02:00
Amjad Ben Hedhili
3ddacd90d1 Remove some unnecessary initialization in seq operations (#22677)
* `PrepareSeqAdd`
* `add`
* `setLen`
* `grow`

Merge after #21842.

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
(cherry picked from commit fbb5ac512c)
2024-04-17 16:01:17 +02:00
ringabout
cb3e5d3f28 [minor] merge similar branches in vmgen (#22683)
(cherry picked from commit 2ce9197d3a)
2024-04-17 16:01:11 +02:00
Amjad Ben Hedhili
12f2c36aca Make newSeqOfCap not initialize memory. (#21842)
It's used in `newSeqUninitialized`.

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
(cherry picked from commit 8853fb0775)
2024-04-17 15:53:16 +02:00
ringabout
a12cb273b3 fixes #22664; guard against potential seqs self assignments (#22671)
fixes #22664

(cherry picked from commit 5f13e15e0a)
2024-04-17 15:51:26 +02:00
Juan M Gómez
1bbbb2f46b fixes #22662 Procs with constructor pragma doesn't initialize object's fields (#22665)
fixes #22662 Procs with constructor pragma doesn't initialize object's
fields

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit d45270bdf7)
2024-04-17 15:51:15 +02:00
SirOlaf
28cef86a09 Fix #21742: Check generic alias depth before skip (#22443)
Close #21742

Checking if there's any side-effects and if just changing typeRel is
adequate for this issue before trying to look into related ones.

`skipBoth` is also not that great, it can lead to code that works
sometimes but fails when the proc is instantiated with branching
aliases. This is mostly an issue with error clarity though.

---------

Co-authored-by: SirOlaf <unknown>
Co-authored-by: SirOlaf <>
(cherry picked from commit 2a8c759df0)
2024-04-17 14:07:06 +02:00
metagn
9ac5666271 make getType nodes of generic insts have full inst type (#22655)
fixes #22639 for the third time

Nodes generated by `getType` for `tyGenericInst` types, instead of
having the original `tyGenericInst` type, will have the type of the last
child (due to the `mapTypeToAst` calls which set the type to the given
argument). This will cause subsequent `getType` calls to lose
information and think it's OK to use the sym of the instantiated type
rather than fully expand the generic instantiation.

To prevent this, update the type of the node from the `mapTypeToAst`
calls to the full generic instantiation type.

(cherry picked from commit ed9e3cba07)
2024-04-17 14:06:46 +02:00
metagn
70ab7a53f3 resolve unambiguous enum symchoices from local scope, error on rest (#22606)
fixes #22598, properly fixes #21887 and fixes test case issue number

When an enum field sym choice has to choose a type, check if its name is
ambiguous in the local scope, then check if the first symbol found in
the local scope is the first symbol in the sym choice. If so, choose
that symbol. Otherwise, give an ambiguous identifier error.

The dependence on the local scope implies this will always give
ambiguity errors for unpicked enum symchoices from generics and
templates and macros from other scopes. We can change `not
isAmbiguous(...) and foundSym == first` to `not (isAmbiguous(...) and
foundSym == first)` to make it so they never give ambiguity errors, and
always pick the first symbol in the symchoice. I can do this if this is
preferred, but no code from CI seems affected.

(cherry picked from commit 480e98c479)
2024-04-17 14:06:38 +02:00
metagn
70d771d1a9 fix isNil folding for compile time closures (#22574)
fixes #20543

(cherry picked from commit bd6adbcc9d)
2024-04-17 14:06:21 +02:00
Pylgos
45aa0a4725 Fix the problem where instances of generic objects with sendable pragmas are not being cached (#22622)
remove `tfSendable` from `eqTypeFlags`

(cherry picked from commit 9f1fe8a2da)
2024-04-17 14:06:09 +02:00
Juan M Gómez
d4c1cc7a34 fixes internal error: no generic body fixes #1500 (#22580)
* fixes internal error: no generic body fixes #1500

* adds guard

* adds guard

* removes unnecessary test

* refactor: extracts containsGenericInvocationWithForward

(cherry picked from commit 0c6e13806d)
2024-04-17 14:06:00 +02:00
metagn
131a0328b5 resolve local symbols in generic type call RHS (#22610)
resolve local symbols in generic type call

fixes #14509

(cherry picked from commit f1789cc465)
2024-04-17 14:05:52 +02:00
metagn
7335495db7 don't update const symbol on const section re-sems (#22609)
fixes #19849

(cherry picked from commit 53d9fb259f)
2024-04-17 14:05:42 +02:00
ringabout
24f3272db3 fixes #22613; Default value does not work with object's discriminator (#22614)
* fixes #22613; Default value does not work with object's discriminator

fixes #22613

* merge branches

* add a test case

* fixes status

* remove outdated comments

* move collectBranchFields into the global scope

(cherry picked from commit affd3f7858)
2024-04-17 14:04:48 +02:00
metagn
3911c90d7b type annotations for variable tuple unpacking, better error messages (#22611)
* type annotations for variable tuple unpacking, better error messages

closes #17989, closes https://github.com/nim-lang/RFCs/issues/339

* update grammar

* fix test

(cherry picked from commit ba158d73dc)
2024-04-17 14:04:43 +02:00
metagn
7c44af4e22 properly fold constants for dynlib pragma (#22575)
fixes #12929

(cherry picked from commit 6b955ac4af)
2024-04-17 14:04:29 +02:00
Bung
d49d7ba90c fix #22548;environment misses for type reference in iterator access n… (#22559)
* fix #22548;environment misses for type reference in iterator access nested in closure

* fix #21737

* Update lambdalifting.nim

* remove containsCallKinds

* simplify

(cherry picked from commit 0b78b7f595)
2024-04-17 14:04:19 +02:00
ringabout
26f3dfff75 fixes a strictdef ten years long vintage bug, which counts the same thing twice (#22549)
fixes a strictdef ten years long vintage bug

(cherry picked from commit 1013378854)
2024-04-17 14:03:45 +02:00
Jacek Sieka
271632a72d Fix getAppFilename exception handling (#22544)
* Fix `getAppFilename` exception handling

avoid platform-dependendent error handling strategies

* more fixes

* space

(cherry picked from commit bc9785c08d)
2024-04-17 14:01:27 +02:00
metagn
0d02bee23f round out tuple unpacking assignment, support underscores (#22537)
* round out tuple unpacking assignment, support underscores

fixes #18710

* fix test messages

* use discard instead of continue

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 53d43e9671)
2024-04-17 14:00:45 +02:00
metagn
c39a0139fc allow non-pragma special words as user pragmas (#22526)
allow non-pragma special words as macro pragmas

fixes #22525

(cherry picked from commit 602f537eb2)
2024-04-17 12:40:00 +02:00
SirOlaf
06464fe2ff Fix #21722 (#22512)
* Keep return in mind for sink
* Keep track of return using bool instead of mode
* Update compiler/injectdestructors.nim
* Add back IsReturn

---------

Co-authored-by: SirOlaf <>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit c0ecdb01a9)
2024-04-17 12:40:00 +02:00
metagn
8eaf98d64f cascade tyFromExpr in type conversions in generic bodies (#22499)
fixes #22490, fixes #22491, adapts #22029 to type conversions

(cherry picked from commit 98c39e8e57)
2024-04-17 12:40:00 +02:00
ringabout
2a4755ddf2 fixes #22469; generates nimTestErrorFlag for top level statements (#22472)
fixes #22469; generates `nimTestErrorFlag` for top level statements

(cherry picked from commit 09d0fda7fd)
2024-04-17 12:39:59 +02:00
Pylgos
814e929eeb fix #22448 Remove structuredErrorHook temporary in tryConstExpr (#22450)
* fix #22448

* add test

(cherry picked from commit 48da472dd2)
2024-04-17 12:39:59 +02:00
ringabout
6f00b46c4b fixes move sideeffects issues [backport] (#22439)
* fixes move sideeffects issues [backport]

* fix openarray

* fixes openarray

(cherry picked from commit faf1c91e6a)
2024-04-17 12:39:44 +02:00
Bung
665480372e fix #19304 Borrowing std/times.format causes Error: illformed AST (#20659)
* fix #19304 Borrowing std/times.format causes Error: illformed AST

* follow suggestions

* mitigate for #4121

* improve error message

(cherry picked from commit 2aab03bdfb)
2024-04-17 10:57:32 +02:00
SirOlaf
2fa314a89d Fix #21760 (#22422)
* Remove call-specific replaceTypeVarsN

* Run for all call kinds and ignore typedesc

* Testcase

---------

Co-authored-by: SirOlaf <>
(cherry picked from commit baf350493b)
2024-04-17 10:55:19 +02:00
Bung
bd63124a00 fix #12938 index type of array in type section without static (#20529)
* fix #12938 nim compiler assertion fail when literal integer is passed as template argument for array size

* use new flag tfImplicitStatic

* fix

* fix #14193

* correct tfUnresolved add condition

* clean test

(cherry picked from commit d53a89e453)
2024-04-17 10:55:14 +02:00
Bung
b67d0a64b4 fix #20891 Illegal capture error of env its self (#22414)
* fix #20891 Illegal capture error of env its self

* fix innerClosure too earlier, make condition shorter

(cherry picked from commit 989da75b84)
2024-04-17 10:55:05 +02:00
ringabout
09109c55c9 clean up gc:arc or gc:orc in docs and in error messages (#22408)
* clean up gc:arc/orc in docs

* in error messages

(cherry picked from commit 10a6e4c236)
2024-04-17 10:54:56 +02:00
Bung
c1745b5924 fix #18823 Passing Natural to bitops.BitsRange[T] parameter in generi… (#20683)
* fix #18823 Passing Natural to bitops.BitsRange[T] parameter in generic proc is compile error

(cherry picked from commit 37d8f32ae9)
2024-04-17 09:37:18 +02:00
ringabout
a7394440b9 Delete parse directory, which was pushed wrongly before [backport] (#22401)
Delete parse directory

(cherry picked from commit 614a18cd05)
2024-04-17 09:37:02 +02:00
Bung
42e4190e1a add test for #3907 (#21069)
* add test for #3907

(cherry picked from commit 137d608d7d)
2024-04-17 09:36:48 +02:00
norrath-hero-cn
36b339bbcf Prevent early destruction of gFuns, fixes AddressSanitizer: heap-use-after-free (#22386)
Prevent destruction of gFuns before callClosures

(cherry picked from commit e0396900ed)
2024-04-17 09:36:41 +02:00
Andreas Rumpf
fb2cc06557 destructors: better docs [backport:2.0] (#22391)
(cherry picked from commit 9872453365)
2024-04-17 09:36:34 +02:00
Tomohiro
ac66f6ce7a Fix searchExtPos so that it returns -1 when the path is not a file ext (#22245)
* Fix searchExtPos so that it returns -1 when the path is not a file ext

* fix comparision expression

* Remove splitDrive from searchExtPos

(cherry picked from commit db435a4a79)
2024-04-17 09:36:23 +02:00
Bung
f4d99c8d56 fix #20883 Unspecified generic on default value segfaults the compiler (#21172)
* fix #20883 Unspecified generic on default value segfaults the compiler

* fallback to isGeneric

* change to closer error

* Update t20883.nim

(cherry picked from commit 26f183043f)
2024-04-17 09:36:10 +02:00
ringabout
d097028307 fixes a typo in the manual (#22383)
ref 0d3bde95f5 (commitcomment-122093273)

(cherry picked from commit 7c2a2c8dc8)
2024-04-17 09:35:58 +02:00
ringabout
7fbf42960b follow up #22322; fixes changelog (#22381)
(cherry picked from commit fb7acd6600)
2024-04-17 09:35:13 +02:00
konsumlamm
526dbf0cf2 Make repr(HSlice) always available (#22332)
Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
(cherry picked from commit d37b620757)
2024-04-17 09:34:31 +02:00
ringabout
1c19d5c72e fixes #22362; Compiler crashes with staticBoundsCheck on (#22363)
(cherry picked from commit 825a0e7df4)
2024-04-17 09:34:23 +02:00
ringabout
413395866f fixes #22360; compare with the half of randMax (#22361)
* fixes #22360; compare with the half of randMax

* add a test

(cherry picked from commit f3a7622514)
2024-04-17 09:34:15 +02:00
narimiran
3ad16028aa bump NimVersion to 2.0.5 2024-04-16 15:49:33 +02:00
Nikolay Nikolov
b47747d318 [backport v2.0] Converted the 'invalid kind for firstOrd/lastOrd(XXX)' messages from internal errors to fatal errors. (#23443) (#23446)
This fixes a nimsuggest crash when opening:
    beacon_chain/consensus_object_pools/blockchain_dag.nim
from the nimbus-eth2 project and many other .nim files (44 files, to be
precise) in the same project.

Replaces: https://github.com/nim-lang/Nim/pull/23402 (cherry picked from
commit c934d5986d)
2024-03-27 15:58:56 +01:00
Александр Старочкин
5667fbfbf3 fix position for "imported but not used" warning for modules inside brackets (#23424) 2024-03-21 08:03:31 +01:00
narimiran
d4b58b0b06 bump NimVersion to 2.0.4 2024-03-19 16:39:02 +01:00
Nikolay Nikolov
ef356cfd4b * [backport] fixed nimsuggest crash with 'Something = concept' put (erroneously) outside of a 'type' block (#23331) (#23346)
(cherry picked from commit 37ed8c8480)
2024-03-19 05:46:52 +01:00
Juan M Gómez
e374759f29 [Backport 2.0] When NimScript errors prevents NimSuggest from Init (#23349) 2024-03-09 11:42:34 +01:00
Juan M Gómez
0de70dc7f3 [backport 2.0] nimsuggest fix (#23336) 2024-03-04 09:58:54 +01:00
Andreas Rumpf
2f15b67fd9 fixes an issue with string to 'var openArray' at compile-time; [backport] (#23363)
(cherry picked from commit 24fbacc63f)
2024-03-03 15:42:20 +01:00
ringabout
e03667e920 improve error messages [backport] (#23345)
ref https://forum.nim-lang.org/t/11052

![image](https://github.com/nim-lang/Nim/assets/43030857/1df87691-32d9-46b5-b61b-6b9f7cc94862)

(cherry picked from commit 1e7ca2dc78)
2024-02-27 08:17:37 +01:00
Nikolay Nikolov
11c048b06f fixes #22753; Nimsuggest segfault with invalid assignment to table (#22781) (#23313)
fixes #22753

## Future work
We should turn all the error nodes into nodes of a nkError kind, which
could be a industrious task. But perhaps we can add a special treatment
for error nodes to make the transition smooth.

(cherry picked from commit 642ac0c1c3)

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
2024-02-24 07:54:42 +01:00
Nikolay Nikolov
22de3ae53e * fixed nimsuggest crash when opening a .nim file, that contain a {.fatal: "msg".} pragma. (#23325) (#23328)
(cherry picked from commit 773c066634)
2024-02-24 07:40:18 +01:00
Juan M Gómez
3be2514683 [Backport 2.0] Itanium mangling scheme (#23301) 2024-02-13 08:09:23 +01:00
ringabout
57658b685c fixes regression #23280; Operations on inline toOpenArray len return a wrong result (#23285)
fixes #23280

(cherry picked from commit 4b67cccf50)
2024-02-07 20:25:45 +01:00
ringabout
5f6ff1f051 compute checksum of nim files early in the pipelines (#23268)
related https://github.com/nim-lang/Nim/issues/21717 configs will be
resolved later

(cherry picked from commit 519d976f62)
2024-02-01 12:30:21 +01:00
rockcavera
7e6f4723c3 Fix system.currentSourcePath() documentation [backport 2.0] (#23243)
The documentation links for `parentDir()` and `getCurrentDir()` are
broken as they are no longer part of `std/os`. Link changed to
`std/private/ospaths2`.

(cherry picked from commit 9c155eaccc)
2024-01-30 08:11:10 +01:00
metagn
4a1151d6b4 give typedesc param nodes type T not typedesc[T] [backport:2.0] (#23115)
fixes https://github.com/nim-lang/Nim/issues/23112, fixes a mistake in
https://github.com/nim-lang/Nim/pull/22581

This makes `getType(t)` where `t` is a typedesc param with value `T`
equal to `getType(T)`.

(cherry picked from commit 3224337550)
2024-01-19 09:19:18 +01:00
Giuliano Mega
9994b019c5 Fix reset code gen for range types (#22462, #23214) (#23215)
This PR modifies `specializeResetT` so that it generates the proper
reset code for range types. I've tested it in the examples for issues
issues I had been experiencing.

(cherry picked from commit 473f259268)
2024-01-19 09:18:23 +01:00
metagn
bb930aef2d don't render underscore identifiers with id (#22538)
(cherry picked from commit 4f891aa50c)
2024-01-16 08:43:09 +01:00
Nikolay Nikolov
be66aedb9e + show the inferred exception list (as part of the type) for functions that don't have an explicit .raises pragma (#23193) (#23210)
(cherry picked from commit 18b5fb256d)
2024-01-16 15:36:03 +08:00
Juan M Gómez
4bc305db1e backport nimsuggest capabilities to 2.0 (#23191) 2024-01-09 22:48:18 +08:00
Juan M Gómez
fdb1f4451d makes nimsuggest con work under v3 [backport 2.0.2] (#23159)
Co-authored-by: Jake Leahy <jake@leahy.dev>
2024-01-08 20:30:59 +01:00
metagn
02497c9533 2.0 branch fixes: no LineTooLong, update macos CI (#23184)
Adapts #23108 to the version-2-0 branch and makes the 2.0 compiler
define `nimHasNoLineTooLong` as the devel compiler depends on it to not
turn off `hint[LineTooLong]`, which means the 2.0 compiler errors on the
devel compiler config (as mentioned in
https://github.com/nim-lang/Nim/issues/23178#issuecomment-1880511322).

Would have these as separate PRs but the macos CI PR would have to be
merged first, then the second PR would have to be rebased.
2024-01-08 13:49:20 +01:00
ringabout
0acdce502c fixes #23148; restricts infix path concatenation to what starts with / (#23150)
fixes #23148

(cherry picked from commit c7d742e484)
2024-01-03 10:57:42 +01:00
narimiran
fe1d4a6232 bump NimVersion to 2.0.3 2024-01-03 10:57:30 +01:00
narimiran
c4c44d10df bump NimVersion to 2.0.2 2023-12-14 17:51:05 +01:00
ringabout
55bb60a56b fixes #22947; static integers in quote do [backport] (#22948)
fixes #22947

(cherry picked from commit 09ea1b168f)
2023-12-14 17:48:55 +01:00
Amjad Ben Hedhili
ef8b8317ff Fix capacity for const and shallow [backport] (#22705)
(cherry picked from commit b542be1e7d)
2023-12-14 17:47:00 +01:00
Nikolay Nikolov
fff127bec7 Skip trailing asterisk when placing inlay type hints. Fixes #23067 (backport of #23068) (#23070)
(cherry picked from commit a3739751a8)
2023-12-14 09:26:01 +08:00
Kernel-Zhang
7b5289a1a4 Correction of “Tnfds” type error (#23045)
The types in Nim don't match the actual types in the header files, this
can lead to bugs in some cases.
2023-12-08 11:09:49 +01:00
Nikolay Nikolov
3a0d09c1c5 backport to version 2.0: display raises pragma in proc types (#23026) (#23028)
This affects also nimsuggest hints (e.g. on mouse hover), as well as
compiler messages.

(cherry picked from commit 618ccb6b6a)
2023-12-04 09:46:57 +01:00
metagn
3ab6012c81 fix generic param substitution in templates (#22535)
* fix generic param substitution in templates

fixes #13527, fixes #17240, fixes #6340, fixes #20033, fixes #19576, fixes #19076

* fix bare except in test, test updated packages in CI

(cherry picked from commit 1cc4d3f622)
2023-12-01 08:27:06 +01:00
Jake Leahy
add124e07a Register declaration of enum field has a use (#22990)
Currently when using `use` with nimsuggest on an enum field, it doesn't
return the definition of the field.

Breaks renaming in IDEs since it will replace all the usages, but not
the declaration

(cherry picked from commit c31bbb07fb)
2023-11-28 08:25:56 +01:00
tersec
8834f3e74d remove unnecessary side-effects from base64.encode(mime) (#22986)
Fixes https://github.com/nim-lang/Nim/issues/22985

(cherry picked from commit 26f2ea149c)
2023-11-26 07:15:40 +01:00
Nikolay Nikolov
489dd89a45 backport to v 2.0: nimsuggest: Added optional command line option '--clientProcessId:XXX' (#22969) (#22981)
When it is specified, the nimsuggest instance monitors whether this
process is still alive. In case it's found to be dead, nimsuggest shuts
itself down. Currently only implemented on POSIX and Windows platforms.
The switch is silently ignored on other platforms. Note that the Nim
language server should still try to shut down its child nimsuggest
processes. This switch just adds extra protection against crashing Nim
language server and gets rid of the remaining nimsuggest processes,
which consume memory and system resources.

(cherry picked from commit 502a4486ae)
2023-11-25 11:50:27 +01:00
Nikolay Nikolov
ba9effc8a1 Backport to 2.0: Introduced version 4 of the NimSuggest protocol. The InlayHints feature made V4 or later only. (#22953) (#22963)
Since nimsuggest now has a protocol version support detection via
`--info:protocolVer`, the InlayHints feature can be moved to protocol
V4. This way, the Nim language server can detect the nimsuggest version
and avoid sending unsupported `InlayHints` commands to older nimsuggest
versions. Related nim language server PR:
https://github.com/nim-lang/langserver/pull/60

(cherry picked from commit 4fc0027b57)
2023-11-20 09:13:33 +08:00
Nikolay Nikolov
0d93fdf607 backport to version 2.0: nimsuggest: Instead of checking for protocol version 3 exactly, check for version 3 or later. (#22945) (#22957)
Refactored the way nimsuggest checks for protocol version 3. Instead of
checking for version 3 exactly, it now checks for version 3 or later.
This way, once a version 4 is introduced, it will use version 3 as a
base line, and then extra changes to the protocol can be added on top.
No functional changes are introduced in this commit.

(cherry picked from commit 3680200df4)
2023-11-18 09:52:06 +08:00
Nikolay Nikolov
b8cec0c714 Added new command line option --info:X to nimsuggest for obtaining … (#22943)
…information. (#22940)

`--info:protocolVer` returns the highest nimsuggest protocol version
that is supported (currently, it's version 3).
`--info:nimVer` returns the Nim compiler version that nimsuggest uses
internally.

Note that you can obtain the Nim compiler version via `nimsuggest -v`,
but that requires parsing the output, which looks like this:

```
Nim Compiler Version 2.1.1 [Linux: amd64]
Compiled at 2023-11-14
Copyright (c) 2006-2023 by Andreas Rumpf

git hash: 47ddfeca5247dce992becd734d1ae44e621207b8
active boot switches: -d:release -d:danger --gc:markAndSweep
```

`--info:nimVer` will return just:

```
2.1.1
```

(cherry picked from commit d0cc02dfc4)
2023-11-16 20:30:22 +08:00
Nikolay Nikolov
4793fc0fc1 Inlay hints backport to Nim v2.0.x (#22919)
Backport of the nimsuggest inlay hints to the Nim v2.0.x series.
2023-11-09 07:33:20 +01:00
ringabout
e1b44666b0 iNim switch to the official URL (#22762)
ref https://github.com/inim-repl/INim/pull/139

(cherry picked from commit 02ba28eda5)
2023-11-08 13:55:49 +01:00
ringabout
194d21a6e4 fixes stint CI (#22756)
(cherry picked from commit 46544f234d)
2023-11-08 13:55:41 +01:00
metagn
1b80f7126a switch back to main neo in CI (#22660)
refs https://github.com/andreaferretti/neo/pull/53

(cherry picked from commit b9f039e0c3)
2023-11-08 13:55:31 +01:00
ringabout
1dc85802e2 add union to packages (#22658)
(cherry picked from commit 009ce1e17e)
2023-11-08 13:55:23 +01:00
ringabout
9e081579bb fixes yaml tests (#22595)
(cherry picked from commit dfb3a88cc3)
2023-11-08 13:55:15 +01:00
ringabout
30dad5dfa1 deprecate std/threadpool; use malebolgia, weave, nim-taskpool instead (#22576)
* deprecate `std/threadpool`; use `malebolgia` instead

* Apply suggestions from code review

* Apply suggestions from code review

* change the URL of inim

(cherry picked from commit a7a0105d8c)
2023-11-08 13:55:01 +01:00
ringabout
6d32066055 update test command of important packages (#22485)
(cherry picked from commit 9296b45de4)
2023-11-08 13:54:54 +01:00
ringabout
86e5545c25 fixes #22866; fixes #19998; ensure destruction for Object construction with custom destructors (#22901)
fixes #22866;
fixes #19998

(cherry picked from commit b68e0aab4c)
2023-11-02 14:40:40 +01:00
ringabout
f4e80d51ff bump node to 20.x; since 16.x is End-of-Life (#22892)
(cherry picked from commit f61311f7a0)
2023-10-31 07:08:42 +01:00
ringabout
1db305af1c fixes #22868; fixes std/nre leaks under ARC/ORC (#22872)
fixes #22868

(cherry picked from commit d66f3febd1)
2023-10-29 18:14:45 +01:00
握猫猫
805b4e2dc2 fix use after free (#22854)
1. `freeAddrInfo` is called prematurely, the variable `myAddr` is still
in use
2. Use defer syntax to ensure that `freeAddrInfo` is also called on
exceptions

(cherry picked from commit 562a5fb8f9)
2023-10-24 08:57:38 +02:00
rockcavera
e8e9948223 fix #22834 (#22843)
fix #22834

Edit: also fixes `result.addrList` when IPv6, which previously only
performed a `result.addrList = cstringArrayToSeq(s.h_addr_list)` which
does not provide the textual representation of an IPv6

(cherry picked from commit 27deacecaa)
2023-10-24 08:57:25 +02:00
Amjad Ben Hedhili
75441dcdc3 Initialize newString in js [backport:1.6] (#22745)
```nim
echo newString(8)
```

results in:
```
D:\User\test.js:25
                  var code_33556944 = c_33556931.toString(16);
                                                 ^

TypeError: Cannot read properties of undefined (reading 'toString')
    at toJSStr (D:\User\test.js:25:50)
    at rawEcho (D:\User\test.js:70:16)
    at Object.<anonymous> (D:\User\test.js:101:1)
    at Module._compile (node:internal/modules/cjs/loader:1095:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1147:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47

Node.js v17.0.1
Error: execution of an external program failed: '"C:\Program Files\nodejs\node.exe" --unhandled-rejections=strict D:\User\test.js'
```

(cherry picked from commit eadd0d72cf)
2023-10-24 08:49:16 +02:00
SirOlaf
d30565f810 Fix #17509: Continue instead of return with unfinished generics (#22563)
Close #17509

Current knowledge:
- delaying cache fixes the issue
- changing return of `if inst.len < key.len:` in `searchInstTypes` to
`continue` fixes the issue. With return the broken types are also cached
over and over

Related issues are completely unaffected as of now, so there must be
something deeper.

I am also still trying to find the true cause, so feel free to ignore
for now

---------

Co-authored-by: SirOlaf <>
(cherry picked from commit ee4a219012)
2023-09-18 12:13:00 +02:00
Amjad Ben Hedhili
5c916b645b Remove some unnecessary initialization in string operations (#22579)
* `prepareAdd`
* `toNimStr`
* `setLengthStrV2`
* `NimAsgnStrV2`
* `prepareMutation`
* Some cleanups

(cherry picked from commit a4df44d9fb)
2023-09-18 12:13:00 +02:00
SirOlaf
9ad184841e Exclude block from endsInNoReturn, fix regression (#22632)
Co-authored-by: SirOlaf <>
(cherry picked from commit d2f36c071b)
2023-09-18 12:13:00 +02:00
metagn
7536d7f94a use dummy dest for void branches to fix noreturn in VM (#22617)
fixes #22216

(cherry picked from commit 2542dc09c8)
2023-09-18 12:13:00 +02:00
metagn
ee0d3f9b34 unify explicit generic param semchecking in calls (#22618)
fixes #9040

(cherry picked from commit 6738f44af3)
2023-09-18 12:13:00 +02:00
SirOlaf
d3cf815dbe Fix #22604: Make endsInNoReturn traverse the tree (#22612)
* Rewrite endsInNoReturn

* Handle `try` stmt again and add tests

* Fix unreachable code warning

* Remove unreachable code in semexprs again

* Check `it.len` before skip

* Move import of assertions

---------

Co-authored-by: SirOlaf <>
(cherry picked from commit 3b206ed988)
2023-09-18 12:12:59 +02:00
ringabout
b477c2adcc fixes #17197; fixes #22560; fixes the dest of newSeqOfCap in refc (#22594)
(cherry picked from commit 5bd1afc3f9)
2023-09-18 12:12:59 +02:00
metagn
78d4aa3d01 handle typedesc params in VM (#22581)
* handle typedesc params in VM

fixes #15760

* add test

* fix getType(typedesc) test

(cherry picked from commit 2e4e2f8f50)
2023-09-18 12:12:59 +02:00
metagn
855f5c8b81 clearer error for different size int/float cast in VM (#22582)
refs #16547

(cherry picked from commit b6cea7b599)
2023-09-18 12:12:59 +02:00
metagn
7ab0f3beec correct logic for qualified symbol in templates (#22577)
* correct logic for qualified symbol in templates

fixes #19865

* add test

(cherry picked from commit 3de8d75513)
2023-09-18 12:12:59 +02:00
ringabout
ba8c9a9d1e newStringOfCap now won't initialize all elements anymore (#22568)
newStringOfCap nows won't initialize all elements anymore

(cherry picked from commit 2e7c8a339f)
2023-09-18 12:12:59 +02:00
metagn
87c1200c93 test case haul for old generic/template/macro issues (#22564)
* test case haul for old generic/template/macro issues

closes #12582, closes #19552, closes #2465, closes #4596, closes #15246,
closes #12683, closes #7889, closes #4547, closes #12415, closes #2002,
closes #1771, closes #5121

The test for #5648 is also moved into its own test
from `types/tissues_types` due to not being joinable.

* fix template gensym test

(cherry picked from commit c19fd69b69)
2023-09-18 12:12:58 +02:00
ringabout
d46e71bea3 fixes #22541; peg matchLen can raise an unlisted exception: Exception (#22545)
The `mopProc` is a recursive function.

(cherry picked from commit c56a712e7d)
2023-09-18 12:12:58 +02:00
SirOlaf
4cf2658235 Fix #21532: Check if template return is untyped (#22517)
* Don't ignore return in semTemplateDef

* Add test

---------

Co-authored-by: SirOlaf <>
(cherry picked from commit 3de75ffc02)
2023-09-18 12:12:58 +02:00
metagn
5fa48e97b6 fix getNullValue for cstring in VM, make other VM code aware of nil cstring (#22527)
* fix getNullValue for cstring in VM

fixes #22524

* very ugly fixes, but fix #15730

* nil cstring len works, more test lines

* fix high

(cherry picked from commit 942f846f04)
2023-09-18 12:12:58 +02:00
ringabout
562fb0d14b fixes #22481; fixes card undefined misalignment behavior (#22484)
* fixes `card` undefined misalignment behavior

* Update lib/system/sets.nim

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit ade75a1483)
2023-09-18 12:12:58 +02:00
Emery Hemingway
320ecc69a0 Add Linux constant SO_BINDTODEVICE (#22468)
(cherry picked from commit 1927ae72d0)
2023-09-18 12:12:58 +02:00
Bung
106cf7a9f7 Fix #5780 (#22428)
* fix #5780

(cherry picked from commit 46e94c83d4)
2023-09-18 10:13:38 +02:00
Jake Leahy
7221c6f156 Fix crash when using uninstantiated generic (#22379)
* Add test case

* Add in a bounds check when accessing generic types

Removes idnex out of bounds exception when comparing a generic that isn't fully instantiated

(cherry picked from commit 3efabd3ec6)
2023-09-18 10:13:27 +02:00
ringabout
4a7cdaecb4 fixes #22262; fixes -d:useMalloc broken with --mm:none and --threads on (#22355)
* fixes #22262; -d:useMalloc broken with --mm:none and threads on

* fixes

(cherry picked from commit a23e53b490)
2023-09-18 10:13:09 +02:00
Amjad Ben Hedhili
81d58d2cc8 Make capacity work with refc [backport] (#22697)
followup of #19771.

(cherry picked from commit 325341866f)
2023-09-14 06:02:46 +02:00
Andreas Rumpf
1080f5eba2 produce better code for object constructions and 'result' [backport] (#22668)
(cherry picked from commit 8f5b90f886)
2023-09-14 06:02:30 +02:00
ringabout
18e62ad136 fixes #22619; don't lift cursor fields in the hook calls (#22638)
fixes https://github.com/nim-lang/Nim/issues/22619

It causes double free for closure iterators because cursor fields are
destroyed in the lifted destructors of `Env`.

Besides, according to the Nim manual

> In fact, cursor more generally prevents object
construction/destruction pairs and so can also be useful in other
contexts.

At least, destruction of cursor fields might cause troubles.

todo
- [x] tests
- [x] revert a certain old PR

---------

Co-authored-by: zerbina <100542850+zerbina@users.noreply.github.com>
(cherry picked from commit eb91cf991a)
2023-09-05 11:50:59 +02:00
metagn
f03aa1d0b5 define toList procs after add for lists [backport] (#22573)
fixes #22543

(cherry picked from commit 94454addb2)
2023-09-05 11:50:41 +02:00
Daniel Belmes
ef63d47ecd fix server caching issue causing Theme failures (#22378)
* fix server caching issue causing Theme failures

* Fix tester to ignore version cache param

* fix case of people using -d:nimTestsNimdocFixup

* rsttester needed the same fix

(cherry picked from commit 7bf7496557)
2023-08-22 08:28:30 +02:00
ringabout
0a58af8cbf fixes #22357; don't sink elements of var tuple cursors (#22486)
(cherry picked from commit 940b1607b8)
2023-08-22 08:23:56 +02:00
Jason Beetham
8359f9683a When in object handles procedure call again, fixes #22474 (#22480)
Ping @narimiran please backport to the 2.0 line.

(cherry picked from commit 6c4e7835bf)
2023-08-22 08:23:32 +02:00
ringabout
c3425c05e9 relax the parameter of ensureMove; allow let statements (#22466)
* relax the parameter of `ensureMove`; allow let statements

* fixes the test

(cherry picked from commit 4c89223171)
2023-08-22 08:23:06 +02:00
Alberto Torres
3fa378533a Fix #22366 by making nimlf_/nimln_ part of the same line (backport) (#22508) 2023-08-22 08:22:58 +02:00
Amjad Ben Hedhili
4850bb5a59 fix seq.capacity [backport] (#22505) 2023-08-22 08:22:42 +02:00
ringabout
037f536e7e fixes #22419; async/closure environment does not align local variables (#22425)
* fixes #22419; async/closure environment does not align local variables

* Apply suggestions from code review

* Update tests/align/talign.nim

Co-authored-by: Jacek Sieka <arnetheduck@gmail.com>

* apply code review

* update tests

---------

Co-authored-by: Jacek Sieka <arnetheduck@gmail.com>
(cherry picked from commit 5334dc921f)
2023-08-11 10:49:49 +02:00
ringabout
287fbc5fb5 fixes #22387; Undefined behavior when with hash(...) (#22404)
* fixes #22387; Undefined behavior when with hash(...)

* fixes vm

* fixes nimscript

(cherry picked from commit 47d06d3d4c)
2023-08-11 10:49:30 +02:00
metagn
f553288db3 block ambiguous type conversion dotcalls in generics (#22375)
fixes #22373

(cherry picked from commit 3aaef9e4cf)
2023-08-11 10:49:11 +02:00
norrath-hero-cn
e0e386314f fixes AddressSanitizer: global-buffer-overflow in getAppFilename on windows 10 (#22380)
fixes AddressSanitizer: global-buffer-overflow

(cherry picked from commit 73a29d72e3)
2023-08-11 10:48:21 +02:00
Michal Maršálek
1de6a7a023 Fix the position of "Grey" in colors.nim (#22358)
Update the position of "Grey"

(cherry picked from commit da368885da)
2023-08-11 10:47:54 +02:00
narimiran
d1db4c29fa bump NimVersion to 2.0.1 2023-08-11 10:47:05 +02:00
narimiran
a488067a41 bump NimVersion to 2.0.0 2023-07-31 21:22:28 +02:00
1222 changed files with 17027 additions and 46417 deletions

View File

@@ -7,16 +7,17 @@ body:
- type: markdown
attributes:
value: |
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.
- **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.
- type: textarea
id: description
attributes:
label: Description
description: |
Describe the problem. Code example can be given here.
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
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.
validations:
required: true
@@ -24,9 +25,7 @@ body:
id: nim-version
attributes:
label: Nim Version
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.
description: Copy and paste the output of `nim -v` on the command line. For development versions, make sure to include the commit hash.
validations:
required: true
@@ -35,7 +34,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 or detailed information will be fixed faster.
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
render: text
- type: textarea
@@ -43,14 +42,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 or detailed information will be fixed faster.
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
render: text
- type: textarea
id: known-workarounds
id: possible-solution
attributes:
label: Known Workarounds
description: Provide any known workarounds.
label: Possible Solution
description: Propose a possible solution.
validations:
required: false
@@ -65,9 +64,12 @@ body:
- type: markdown
attributes:
value: |
- 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).
- 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).
- 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 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).
- 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 (eg `choosenim 1.2.0`).
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)

View File

@@ -5,17 +5,10 @@ on:
types: created
jobs:
bisects:
if: |
github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '!nim ') && github.event.issue.pull_request == null && github.event.comment.author_association != 'NONE'
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, windows-latest, macos-latest]
name: ${{ matrix.platform }}-bisects
runs-on: ${{ matrix.platform }}
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Install OpenSSL (Windows)
if: |
@@ -24,15 +17,13 @@ jobs:
shell: 'powershell'
# v2 wont work here, because uses "hardcoded" nim versions, action "dynamically" finds version with bug.
- uses: jiro4989/setup-nim-action@v1
- uses: jiro4989/setup-nim-action@v1
with:
nim-version: 'devel'
- uses: juancarlospaco/nimrun-action@nim
if: |
runner.os == 'Linux' && contains(github.event.comment.body, '-d:linux' ) ||
runner.os == 'Windows' && contains(github.event.comment.body, '-d:windows') ||
runner.os == 'macOS' && contains(github.event.comment.body, '-d:osx' ) ||
runner.os == 'Linux' && !contains(github.event.comment.body, '-d:linux') && !contains(github.event.comment.body, '-d:windows') && !contains(github.event.comment.body, '-d:osx')
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Dependencies
run: sudo apt-get install --no-install-recommends -yq valgrind
- uses: juancarlospaco/nimrun-action@nim
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

115
.github/workflows/ci_bench.yml vendored Normal file
View File

@@ -0,0 +1,115 @@
name: Benchmarks CI
on:
pull_request:
push:
branches:
- 'devel'
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04]
cpu: [amd64]
name: '${{ matrix.os }}'
runs-on: ${{ matrix.os }}
timeout-minutes: 60 # refs bug #18178
steps:
- name: 'Checkout'
uses: actions/checkout@v3
with:
fetch-depth: 2
- name: 'Install node.js 20.x'
uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
run: |
sudo apt-fast update -qq
DEBIAN_FRONTEND='noninteractive' \
sudo apt-fast install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
valgrind libc6-dbg libblas-dev xorg-dev
- name: 'Add build binaries to PATH'
shell: bash
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
- name: 'Build csourcesAny'
shell: bash
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
- name: 'Build koch'
shell: bash
run: nim c koch
- name: 'Build Nim'
shell: bash
run: ./koch boot -d:release -d:nimStrictMode --lib:lib
- name: 'Build Nimble'
shell: bash
run: ./koch nimble
- name: 'Action'
shell: bash
run: nim c -r -d:release ci/action.nim
- name: 'Checkout minimize'
uses: actions/checkout@v3
with:
repository: 'nim-lang/ci_bench'
path: minimize
- name: 'Run minimize benchmarks'
shell: bash
run: ./minimize/minimize ci-bench
- name: 'Restore minimize cached database'
uses: actions/cache/restore@v3
with:
path: minimize.csv
key: minimize-db-key
- name: 'Update minimize db'
shell: bash
run: ./minimize/minimize update-db
- name: 'Save minimize cached database'
if: |
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
matrix.target == 'linux'
uses: actions/cache/save@v3
with:
path: minimize.csv
key: minimize-db-key
- name: 'Generate minimize report'
shell: bash
run: ./minimize/minimize generate-report
- name: 'Archive minimize report'
uses: actions/upload-artifact@v3
with:
name: minimize-report
path: |
minimize/minimize.html
minimize/minimize.csv
# Requires additional permissions, see:
# https://github.com/nim-lang/Nim/actions/runs/4778177321/jobs/8494423792?pr=21566
# - name: 'Publish HTML report'
# uses: rossjrw/pr-preview-action@v1
# with:
# source-dir: minimize
# umbrella-dir: minimize
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Extract md summary
run: |
cat minimize/summary.md >> $GITHUB_STEP_SUMMARY

View File

@@ -2,7 +2,8 @@ name: Nim Docs CI
on:
push:
paths:
- 'compiler/**.nim'
- 'compiler/docgen.nim'
- 'compiler/renderverbatim.nim'
- 'config/nimdoc.cfg'
- 'doc/**.rst'
- 'doc/**.md'
@@ -17,7 +18,8 @@ on:
pull_request:
# Run only on changes on these files.
paths:
- 'compiler/**.nim'
- 'compiler/docgen.nim'
- 'compiler/renderverbatim.nim'
- 'config/nimdoc.cfg'
- 'doc/**.rst'
- 'doc/**.md'
@@ -43,7 +45,7 @@ jobs:
- target: linux
os: ubuntu-22.04
- target: windows
os: windows-latest
os: windows-2019
- target: osx
os: macos-15
@@ -53,7 +55,7 @@ jobs:
steps:
- name: 'Checkout'
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 2
@@ -109,7 +111,7 @@ jobs:
if: |
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
matrix.target == 'linux'
uses: crazy-max/ghaction-github-pages@v4
uses: crazy-max/ghaction-github-pages@v5
with:
build_dir: doc/html
env:

76
.github/workflows/ci_gcc14.yml vendored Normal file
View File

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

View File

@@ -4,7 +4,6 @@ on:
push:
branches:
- 'devel'
- 'version-2-2'
- 'version-2-0'
- 'version-1-6'
- 'version-1-2'
@@ -18,12 +17,12 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
batch: ["0_3", "1_3", "2_3"] # list of `index_num`
os: [ubuntu-latest, macos-14]
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
include:
- os: ubuntu-latest
cpu: amd64
- os: macos-latest
- os: macos-14
cpu: arm64
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
runs-on: ${{ matrix.os }}
@@ -33,14 +32,14 @@ jobs:
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
steps:
- name: 'Checkout'
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 2
- name: 'Install node.js 20.x'
uses: actions/setup-node@v4
- name: 'Install node.js'
uses: actions/setup-node@v6
with:
node-version: '20.x'
node-version: 24
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'

View File

@@ -17,14 +17,14 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: 'Checkout'
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 2
- name: 'Install node.js'
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ''
node-version: 24
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'

View File

@@ -9,7 +9,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v11
- uses: actions/stale@v10
with:
days-before-pr-stale: 365
days-before-pr-close: 30

1
.gitignore vendored
View File

@@ -68,7 +68,6 @@ testament.db
/csources
/csources_v1
/csources_v2
/csources_v3
/dist/
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it

View File

@@ -73,8 +73,8 @@ jobs:
- task: NodeTool@0
inputs:
versionSpec: '20.x'
displayName: 'Install node.js 20.x'
versionSpec: '16.x'
displayName: 'Install node.js 16.x'
condition: and(succeeded(), eq(variables['skipci'], 'false'))
- bash: |
@@ -111,7 +111,6 @@ jobs:
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
cat << EOF > bin/gcc
#!/bin/bash

View File

@@ -1,154 +1,135 @@
# v2.x.x - yyyy-mm-dd
# v2.2.0 - yyyy-mm-dd
## Changes affecting backward compatibility
- `-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.
- 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.
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
- Unknown warnings and hints now gives warnings `warnUnknownNotes` instead of
errors.
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
- The bare `except:` now panics on `Defect`. Use `except Exception:` or `except Defect:` to catch `Defect`. `--legacy:noPanicOnExcept` is provided for a transition period.
- With `-d:nimPreviewCStringComparisons`, comparsions (`<`, `>`, `<=`, `>=`) between cstrings switch from reference semantics to value semantics like `==` and `!=`.
- `std/parsesql` has been moved to a nimble package, use `nimble` or `atlas` to install it.
- With `-d:nimPreviewDuplicateModuleError`, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Use `import foo as foo1` (or other aliases) to avoid collisions.
- Adds the switch `--mangle:nim|cpp`, which selects `nim` or `cpp` style name mangling when used with `debuginfo` on, defaults to `cpp`.
- The second parameter of `succ`, `pred`, `inc`, and `dec` in `system` now accepts `SomeInteger` (previously `Ordinal`).
- Bitshift operators (`shl`, `shr`, `ashr`) now apply bitmasking to the right operand in the C/C++/VM/JS backends.
- Adds a new warning `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts do not trigger warnings. `int` to `Natural` and `Positive` conversions do not trigger warnings, which can be enabled with `--warning:systemRangeConversion`.
- Procedure compatibility also checks the backend representation of the
parameter and result types, not just their source-level shape. Use
`--legacy:procParamTypeBackendAliases` to restore the older behavior.
- `-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.
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
## Standard library additions and changes
[//]: # "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.
- `strutils.multiReplace` overload for character set replacements in a single pass.
Useful for string sanitation. Follows existing multiReplace semantics.
- `std/files` adds:
- Exports `CopyFlag` enum and `FilePermission` type for fine-grained control of file operations
- New file operation procs with `Path` support:
- `getFilePermissions`, `setFilePermissions` for managing permissions
- `tryRemoveFile` for file deletion
- `copyFile` with configurable buffer size and symlink handling
- `copyFileWithPermissions` to preserve file attributes
- `copyFileToDir` for copying files into directories
- `std/dirs` adds:
- New directory operation procs with `Path` support:
- `copyDir` with special file handling options
- `copyDirWithPermissions` to recursively preserve attributes
- `system.setLenUninit` now supports refc, JS and VM backends.
- `system.setLenUninit` for the `string` type. Allows setting length without initializing new memory on growth.
- `std/parseopt` now supports multiple parser modes via a `CliMode` enum.
Modes include `Nim` (default, fully compatible) and two new experimental modes:
`Lax` and `Gnu` for different option parsing behaviors.
- `std/symlinks.expandSymlink` now supports Windows symlinks and junctions with
POSIX-like single-hop `readlink` semantics.
- `std/nre2` is added to replace deprecated NRE.
- `system.typeof` adds a new parameter `modifierMode` to specify how type modifiers are handled.
[//]: # "Changes:"
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
- `std/re` and `std/nre` are deprecated as PCRE library is obsolete.
Use https://github.com/nitely/nim-regex or `std/nre2`.
See: https://github.com/nim-lang/Nim/issues/23668.
- `std/pegs` now correctly lexes UTF-8 bytes inside bare identifier-style
terminals, so case-insensitive matching of non-ASCII terms (e.g. ``\i café``)
works without single-quoting.
- `std/uri`: The `?` operator now appends query parameters to an existing query
string instead of replacing it. Fixes [#19782](https://github.com/nim-lang/Nim/issues/19782).
- `std/jsonutils`: `fromJson` now throws an exception when converting to `array`/`seq` if the JSON isn't an array instead of silently failing
[//]: # "Additions:"
- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
- Added `setLenUninit` to system, which doesn't initalize
slots when enlarging a sequence.
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
- Added Viewport API for the JavaScript targets in the `dom` module.
- 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`.
[//]: # "Deprecations:"
[//]: # "Removals:"
## Language changes
- An experimental option `--experimental:typeBoundOps` has been added that
implements the RFC https://github.com/nim-lang/RFCs/issues/380.
This makes the behavior of interfaces like `hash`, `$`, `==` etc. more
reliable for nominal types across indirect/restricted imports.
- 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
# objs.nim
import std/hashes
const value = "captured"
template foo(x: int, body: untyped): untyped =
let value {.inject.} = "injected"
body
type
Obj* = object
x*, y*: int
z*: string # to be ignored for equality
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"
proc `==`*(a, b: Obj): bool =
a.x == b.x and a.y == b.y
template oldTempl(): string =
block:
foo(123):
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
echo oldTempl() # "captured"
proc hash*(a: Obj): Hash =
$!(hash(a.x) &! hash(a.y))
{.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
# main.nim
{.experimental: "typeBoundOps".}
from objs import Obj # objs.hash, objs.`==` not imported
import std/tables
const value = "captured"
template foo(x: int, body: untyped): untyped =
let value {.inject.} = "injected"
{.push experimental: "genericsOpenSym".}
body
{.pop.}
var t: Table[Obj, int]
t[Obj(x: 3, y: 4, z: "debug")] = 34
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
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"
```
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#typeminusbound-overloads)
for more information.
## Compiler changes
- Fixed a bug where `sizeof(T)` inside a `typedesc` template called from a generic type's
`when` clause would error with "'sizeof' requires '.importc' types to be '.completeStruct'".
The issue was that `hasValuelessStatics` in `semtypinst.nim` didn't recognize
`tyTypeDesc(tyGenericParam)` as an unresolved generic parameter.
- The JS backend now implements write-through for `var openArray` parameters that
receive a `toOpenArray` view (bug #15952): mutations reach the caller's storage
instead of silently writing to a copy. Fixed homogeneous numeric arrays
(`array[N, T]`, JS typed arrays) slice via `subarray`; `seq` and non-numeric
arrays slice via a `{base, off, len}` view. This also covers seq/non-numeric-array
write-through, pass-through, re-slicing and `@` (openArray-to-seq) of such views.
## Tool changes
- Added `--raw` flag when generating JSON docs to not render markup.
- Added `--stdinfile` flag to name of the file used when running program from stdin (defaults to `stdinfile.nim`)
- Added `--styleCheck:warning` flag to treat style check violations as warnings.
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.
## Documentation changes
- Added documentation for the `completeStruct` pragma in the manual.

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

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 usage of it in the wild. If you still need it, add this
and we didn't find any usages of it in the wild. If you still need it, add this
piece of code to your project:
```nim
type

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` handles case sensitivity
* Fix how `relativePath` handle case sensitiviy
* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
* VM: no special casing for big endian machines
@@ -43,7 +43,7 @@
* threadpool: fix link in docs (#12258)
* Fix spellings (#12277)
* fix #12278, don't expose internal PCRE documentation
* Fixed "Documentation of quitprocs is wrong" [#12279](https://github.com/nim-lang/Nim/issues/12279)
* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
* Fix typo in docs
* Fix reference to parseSpec proc in readme
* [doc/tut1] removed discard discussion in comments

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

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 expressiveness. The following code used to be
This new definition is much easier to understand, the price is some expressitivity. The following code used to be
accepted:
```nim

View File

@@ -205,7 +205,7 @@
proc prc(): int =
123
iterator iter(): int {.closure.} =
iterator iter(): int =
yield 123
proc takesProc[T: proc](x: T) = discard

View File

@@ -1,248 +0,0 @@
# 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 @@
# v2.xx.x - yyyy-mm-dd
# v1.xx.x - yyyy-mm-dd
This is an example file.
The changes should go to changelog.md!

View File

@@ -3,7 +3,7 @@ import std/[strutils, os, osproc, parseutils, strformat]
proc main() =
var msg = ""
const cmd = "./koch boot --mm:orc -d:release"
const cmd = "./koch boot --gc:orc -d:release"
let (output, exitCode) = execCmdEx(cmd)

View File

@@ -8,7 +8,7 @@ const
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
nkAddr, nkHiddenAddr,
nkObjDownConv, nkObjUpConv}
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv, nkCast}
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
proc skipConvDfa*(n: PNode): PNode =
result = n
@@ -74,7 +74,7 @@ proc aliases*(obj, field: PNode): AliasKind =
# x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
# x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
template collectImportantNodes(result, n) =
var result: seq[PNode] = @[]
var result: seq[PNode]
var n = n
while true:
case n.kind
@@ -125,3 +125,4 @@ proc aliases*(obj, field: PNode): AliasKind =
else:
result = maybe
else: assert false # unreachable

View File

@@ -10,9 +10,7 @@
## Simple alias analysis for the HLO and the code generators.
import
ast, astalgo, types, trees
import std/intsets
ast, astalgo, types, trees, intsets
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -51,16 +49,14 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
case a.kind
of tyObject:
if a.baseClass != nil:
result = isPartOfAux(a.baseClass.skipTypes(skipPtrs), b, marker)
if a[0] != nil:
result = isPartOfAux(a[0].skipTypes(skipPtrs), b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct, tyAlias, tySink:
result = isPartOfAux(skipModifier(a), b, marker)
of tySet, tyArray:
result = isPartOfAux(a.elementType, b, marker)
of tyTuple:
for aa in a.kids:
result = isPartOfAux(aa, b, marker)
result = isPartOfAux(lastSon(a), b, marker)
of tyArray, tySet, tyTuple:
for i in 0..<a.len:
result = isPartOfAux(a[i], b, marker)
if result == arYes: return
else: discard
@@ -107,7 +103,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
if a.kind == b.kind:
case a.kind
of nkSym:
const varKinds = {skVar, skTemp, skResult, skProc, skFunc}
const varKinds = {skVar, skTemp, skProc, skFunc}
# same symbol: aliasing:
if a.sym.id == b.sym.id: result = arYes
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
@@ -118,8 +114,6 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
# use expensive type check:
if isPartOf(a.sym.typ, b.sym.typ) != arNo:
result = arMaybe
else:
result = arNo
of nkBracketExpr:
result = isPartOf(a[0], b[0])
if a.len >= 2 and b.len >= 2:
@@ -155,7 +149,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
result = isPartOf(a[1], b[1])
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isPartOf(a[0], b[0])
else: result = arNo
else: discard
# Calls return a new location, so a default of ``arNo`` is fine.
else:
# go down recursively; this is quite demanding:
@@ -171,7 +165,6 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
of DerefKinds:
# a* !<| b[] iff
result = arNo
if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a, b[0])
if result == arNo: result = arMaybe
@@ -193,9 +186,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a[0], b)
if result == arNo: result = arMaybe
else:
result = arNo
else: result = arNo
else: discard
of nkObjConstr:
result = arNo
for i in 1..<b.len:
@@ -213,6 +204,4 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
of nkBracket:
if b.len > 0:
result = isPartOf(a, b[0])
else:
result = arNo
else: result = arNo
else: discard

File diff suppressed because it is too large Load Diff

View File

@@ -12,18 +12,23 @@
# the data structures here are used in various places of the compiler.
import
ast, astyaml, options, lineinfos, idents, rodutils,
ast, hashes, intsets, options, lineinfos, ropes, idents, rodutils,
msgs
import std/[hashes, intsets]
import std/strutils except addf
export astyaml.treeToYaml, astyaml.typeToYaml, astyaml.symToYaml, astyaml.lineInfoToStr
import strutils except addf
when defined(nimPreviewSlimSystem):
import std/assertions
proc hashNode*(p: RootRef): Hash
proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
# Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes.
# If maxRecDepht <> -1 then it won't print the whole graph.
proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
# these are for debugging only: They are not really deprecated, but I want
# the warning so that release versions do not contain debugging statements:
@@ -31,6 +36,15 @@ proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
proc typekinds*(t: PType) {.deprecated.} =
var t = t
var s = ""
while t != nil and t.len > 0:
s.add $t.kind
s.add " "
t = t.lastSon
echo s
template debug*(x: PSym|PType|PNode) {.deprecated.} =
when compiles(c.config):
debug(c.config, x)
@@ -65,6 +79,16 @@ template mdbg*: bool {.deprecated.} =
else:
error()
# --------------------------- ident tables ----------------------------------
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
proc idTableGet*(t: TIdTable, key: int): RootRef
proc idTablePut*(t: var TIdTable, key: PIdObj, val: RootRef)
proc idTableHasObjectAsKey*(t: TIdTable, key: PIdObj): bool
# checks if `t` contains the `key` (compared by the pointer value, not only
# `key`'s id)
proc idNodeTableGet*(t: TIdNodeTable, key: PIdObj): PNode
proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode)
# ---------------------------------------------------------------------------
proc lookupInRecord*(n: PNode, field: PIdent): PSym
@@ -85,7 +109,7 @@ type
data*: TIIPairSeq
proc initIITable*(x: var TIITable)
proc initIiTable*(x: var TIITable)
proc iiTableGet*(t: TIITable, key: int): int
proc iiTablePut*(t: var TIITable, key, val: int)
@@ -173,7 +197,6 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym =
result = nil
proc sameIgnoreBacktickGensymInfo(a, b: string): bool =
result = false
if a[0] != b[0]: return false
var alen = a.len - 1
while alen > 0 and a[alen] != '`': dec(alen)
@@ -203,11 +226,10 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym =
## Named parameters are special because a named parameter can be
## gensym'ed and then they have '\`<number>' suffix that we need to
## ignore, see compiler / evaltempl.nim, snippet:
## ```nim
## ```
## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id),
## if c.instLines: actual.info else: templ.info)
## ```
result = nil
for i in 1..<list.len:
let it = list[i].sym
if it.name.id == ident.id or
@@ -220,7 +242,169 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
import std/tables
proc rspaces(x: int): Rope =
# returns x spaces
result = rope(spaces(x))
proc toYamlChar(c: char): string =
case c
of '\0'..'\x1F', '\x7F'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4)
of '\'', '\"', '\\': result = '\\' & c
else: result = $c
proc makeYamlString*(s: string): Rope =
# We have to split long strings into many ropes. Otherwise
# this could trigger InternalError(111). See the ropes module for
# further information.
const MaxLineLength = 64
result = ""
var res = "\""
for i in 0..<s.len:
if (i + 1) mod MaxLineLength == 0:
res.add('\"')
res.add("\n")
result.add(rope(res))
res = "\"" # reset
res.add(toYamlChar(s[i]))
res.add('\"')
result.add(rope(res))
proc flagsToStr[T](flags: set[T]): Rope =
if flags == {}:
result = rope("[]")
else:
result = ""
for x in items(flags):
if result != "": result.add(", ")
result.add(makeYamlString($x))
result = "[" & result & "]"
proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope =
result = "[$1, $2, $3]" % [makeYamlString(toFilename(conf, info)),
rope(toLinenumber(info)),
rope(toColumn(info))]
proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet,
indent, maxRecDepth: int): Rope
proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet,
indent, maxRecDepth: int): Rope
proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet,
indent, maxRecDepth: int): Rope
proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
if n == nil:
result = rope("null")
elif containsOrIncl(marker, n.id):
result = "\"$1\"" % [rope(n.name.s)]
else:
var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1)
#rope("typ"), typeToYamlAux(conf, n.typ, marker,
# indent + 2, maxRecDepth - 1),
let istr = rspaces(indent + 2)
result = rope("{")
result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
result.addf("$N$1\"name\": $2", [istr, makeYamlString(n.name.s)])
result.addf("$N$1\"typ\": $2", [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth - 1)])
if conf != nil:
# if we don't pass the config, we probably don't care about the line info
result.addf("$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
if card(n.flags) > 0:
result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
result.addf("$N$1\"magic\": $2", [istr, makeYamlString($n.magic)])
result.addf("$N$1\"ast\": $2", [istr, ast])
result.addf("$N$1\"options\": $2", [istr, flagsToStr(n.options)])
result.addf("$N$1\"position\": $2", [istr, rope(n.position)])
result.addf("$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)])
result.addf("$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)])
if card(n.loc.flags) > 0:
result.addf("$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)])
result.addf("$N$1\"r\": $2", [istr, n.loc.r])
result.addf("$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)])
result.addf("$N$1}", [rspaces(indent)])
proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
var sonsRope: Rope
if n == nil:
sonsRope = rope("null")
elif containsOrIncl(marker, n.id):
sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
strutils.toHex(cast[int](n), sizeof(n) * 2))]
else:
if n.len > 0:
sonsRope = rope("[")
for i in 0..<n.len:
if i > 0: sonsRope.add(",")
sonsRope.addf("$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n[i],
marker, indent + 4, maxRecDepth - 1)])
sonsRope.addf("$N$1]", [rspaces(indent + 2)])
else:
sonsRope = rope("null")
let istr = rspaces(indent + 2)
result = rope("{")
result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
result.addf("$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)])
result.addf("$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)])
if card(n.flags) > 0:
result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
result.addf("$N$1\"callconv\": $2", [istr, makeYamlString($n.callConv)])
result.addf("$N$1\"size\": $2", [istr, rope(n.size)])
result.addf("$N$1\"align\": $2", [istr, rope(n.align)])
result.addf("$N$1\"sons\": $2", [istr, sonsRope])
proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
if n == nil:
result = rope("null")
else:
var istr = rspaces(indent + 2)
result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
if maxRecDepth != 0:
if conf != nil:
result.addf(",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
case n.kind
of nkCharLit..nkInt64Lit:
result.addf(",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
result.addf(",$N$1\"floatVal\": $2",
[istr, rope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit:
result.addf(",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
result.addf(",$N$1\"sym\": $2",
[istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)])
of nkIdent:
if n.ident != nil:
result.addf(",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
else:
result.addf(",$N$1\"ident\": null", [istr])
else:
if n.len > 0:
result.addf(",$N$1\"sons\": [", [istr])
for i in 0..<n.len:
if i > 0: result.add(",")
result.addf("$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n[i],
marker, indent + 4, maxRecDepth - 1)])
result.addf("$N$1]", [istr])
result.addf(",$N$1\"typ\": $2",
[istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
result.addf("$N$1}", [rspaces(indent)])
proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
result = treeToYamlAux(conf, n, marker, indent, maxRecDepth)
proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
result = typeToYamlAux(conf, n, marker, indent, maxRecDepth)
proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
result = symToYamlAux(conf, n, marker, indent, maxRecDepth)
import tables
const backrefStyle = "\e[90m"
const enumStyle = "\e[34m"
@@ -384,12 +568,14 @@ proc value(this: var DebugPrinter; value: PType) =
this.key "n"
this.value value.n
this.key "sons"
this.openBracket
for i, a in value.ikids:
if i > 0: this.comma
this.value a
this.closeBracket
if value.len > 0:
this.key "sons"
this.openBracket
for i in 0..<value.len:
this.value value[i]
if i != value.len - 1:
this.comma
this.closeBracket
if value.n != nil:
this.key "n"
@@ -458,33 +644,30 @@ proc value(this: var DebugPrinter; value: PNode) =
proc debug(n: PSym; conf: ConfigRef) =
var this = DebugPrinter(
visited: initTable[pointer, int](),
renderSymType: true,
useColor: not defined(windows)
)
var this: DebugPrinter
this.visited = initTable[pointer, int]()
this.renderSymType = true
this.useColor = not defined(windows)
this.value(n)
echo($this.res)
proc debug(n: PType; conf: ConfigRef) =
var this = DebugPrinter(
visited: initTable[pointer, int](),
renderSymType: true,
useColor: not defined(windows)
)
var this: DebugPrinter
this.visited = initTable[pointer, int]()
this.renderSymType = true
this.useColor = not defined(windows)
this.value(n)
echo($this.res)
proc debug(n: PNode; conf: ConfigRef) =
var this = DebugPrinter(
visited: initTable[pointer, int](),
renderSymType: false,
useColor: not defined(windows)
)
var this: DebugPrinter
this.visited = initTable[pointer, int]()
#this.renderSymType = true
this.useColor = not defined(windows)
this.value(n)
echo($this.res)
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
proc nextTry(h, maxHash: Hash): Hash =
result = ((5 * h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
@@ -713,69 +896,118 @@ iterator items*(tab: TStrTable): PSym =
yield s
s = nextIter(it, tab)
proc isNil(x: ItemId): bool {.inline.} =
x.module == 0 and x.item == 0
proc hasEmptySlot[T](data: TIdPairSeq[T]): bool =
proc hasEmptySlot(data: TIdPairSeq): bool =
for h in 0..high(data):
if isNil(data[h].key):
if data[h].key == nil:
return true
result = false
proc idTableRawGet[T](t: TIdTable[T], key: int): int =
proc idTableRawGet(t: TIdTable, key: int): int =
var h: Hash
h = key and high(t.data) # start with real hash value
while not isNil(t.data[h].key):
if toId(t.data[h].key) == key:
while t.data[h].key != nil:
if t.data[h].key.id == key:
return h
h = nextTry(h, high(t.data))
result = - 1
proc getOrDefault*[T](t: TIdTable[T], key: ItemId): T =
var index = idTableRawGet(t, toId(key))
proc idTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool =
var index = idTableRawGet(t, key.id)
if index >= 0: result = t.data[index].key == key
else: result = false
proc idTableGet(t: TIdTable, key: PIdObj): RootRef =
var index = idTableRawGet(t, key.id)
if index >= 0: result = t.data[index].val
else: result = default(T)
else: result = nil
template idTableGet*[T](t: TIdTable[T], key: PType | PSym): T =
getOrDefault(t, key.itemId)
proc idTableGet(t: TIdTable, key: int): RootRef =
var index = idTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc idTableRawInsert[T](data: var TIdPairSeq[T], key: ItemId, val: T) =
iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
for i in 0..high(t.data):
if t.data[i].key != nil:
yield (t.data[i].key.id, t.data[i].val)
proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
var h: Hash
let keyId = toId(key)
h = keyId and high(data)
while not isNil(data[h].key):
assert(toId(data[h].key) != keyId)
h = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
h = nextTry(h, high(data))
assert(isNil(data[h].key))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc `[]=`*[T](t: var TIdTable[T], key: ItemId, val: T) =
proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
var
index: int
n: TIdPairSeq[T]
index = idTableRawGet(t, toId(key))
n: TIdPairSeq
index = idTableRawGet(t, key.id)
if index >= 0:
assert(not isNil(t.data[index].key))
assert(t.data[index].key != nil)
t.data[index].val = val
else:
if mustRehash(t.data.len, t.counter):
newSeq(n, t.data.len * GrowthFactor)
for i in 0..high(t.data):
if not isNil(t.data[i].key):
if t.data[i].key != nil:
idTableRawInsert(n, t.data[i].key, t.data[i].val)
assert(hasEmptySlot(n))
swap(t.data, n)
idTableRawInsert(t.data, key, val)
inc(t.counter)
template idTablePut*[T](t: var TIdTable[T], key: PType | PSym, val: T) =
t[key.itemId] = val
iterator idTablePairs*[T](t: TIdTable[T]): tuple[key: ItemId, val: T] =
iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
for i in 0..high(t.data):
if not isNil(t.data[i].key):
yield (t.data[i].key, t.data[i].val)
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
var h: Hash
h = key.id and high(t.data) # start with real hash value
while t.data[h].key != nil:
if t.data[h].key.id == key.id:
return h
h = nextTry(h, high(t.data))
result = - 1
proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
var index: int
index = idNodeTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: Hash
h = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
h = nextTry(h, high(data))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
var index = idNodeTableRawGet(t, key)
if index >= 0:
assert(t.data[index].key != nil)
t.data[index].val = val
else:
if mustRehash(t.data.len, t.counter):
var n: TIdNodePairSeq
newSeq(n, t.data.len * GrowthFactor)
for i in 0..high(t.data):
if t.data[i].key != nil:
idNodeTableRawInsert(n, t.data[i].key, t.data[i].val)
swap(t.data, n)
idNodeTableRawInsert(t.data, key, val)
inc(t.counter)
iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
for i in 0..high(t.data):
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc initIITable(x: var TIITable) =
x.counter = 0
@@ -823,7 +1055,6 @@ proc iiTablePut(t: var TIITable, key, val: int) =
inc(t.counter)
proc listSymbolNames*(symbols: openArray[PSym]): string =
result = ""
for sym in symbols:
if result.len > 0:
result.add ", "

View File

@@ -5,7 +5,7 @@ import options, ast, msgs
proc typSym*(t: PType): PSym =
result = t.sym
if result == nil and t.kind == tyGenericInst: # this might need to be refined
result = t.genericHead.sym
result = t[0].sym
proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) =
result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)]

View File

@@ -1,160 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# AST YAML printing
import "."/[ast, lineinfos, msgs, options, rodutils]
import std/[intsets, strutils]
proc addYamlString*(res: var string; s: string) =
res.add "\""
for c in s:
case c
of '\0' .. '\x1F', '\x7F' .. '\xFF':
res.add("\\u" & strutils.toHex(ord(c), 4))
of '\"', '\\':
res.add '\\' & c
else:
res.add c
res.add('\"')
proc makeYamlString(s: string): string =
result = ""
result.addYamlString(s)
proc flagsToStr[T](flags: set[T]): string =
if flags == {}:
result = "[]"
else:
result = ""
for x in items(flags):
if result != "":
result.add(", ")
result.addYamlString($x)
result = "[" & result & "]"
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): string =
result = "["
result.addYamlString(toFilename(conf, info))
result.addf ", $1, $2]", toLinenumber(info), toColumn(info)
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; nl: bool, indent, maxRecDepth: int)
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; nl: bool, indent, maxRecDepth: int)
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; nl: bool, indent, maxRecDepth: int)
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; nl: bool, indent: int; maxRecDepth: int) =
if n == nil:
res.add("null")
elif containsOrIncl(marker, n.id):
res.addYamlString(n.name.s)
else:
let istr = spaces(indent * 4)
if nl:
res.addf("\n$1", istr)
res.addf("kind: $1", [makeYamlString($n.kind)])
res.addf("\n$1name: $2", [istr, makeYamlString(n.name.s)])
res.addf("\n$1typ: ", [istr])
res.typeToYamlAux(conf, n.typ, marker, true, indent + 1, maxRecDepth - 1)
if conf != nil:
# if we don't pass the config, we probably don't care about the line info
res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
if card(n.flags) > 0:
res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
res.addf("\n$1magic: $2", [istr, makeYamlString($n.magic)])
res.addf("\n$1ast: ", [istr])
res.treeToYamlAux(conf, n.ast, marker, true, indent + 1, maxRecDepth - 1)
res.addf("\n$1options: $2", [istr, flagsToStr(n.options)])
res.addf("\n$1position: $2", [istr, $n.position])
res.addf("\n$1k: $2", [istr, makeYamlString($n.loc.k)])
res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)])
if card(n.loc.flags) > 0:
res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
res.addf("\n$1lode: ", [istr])
res.treeToYamlAux(conf, n.loc.lode, marker, true, indent + 1, maxRecDepth - 1)
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; nl: bool, indent: int; maxRecDepth: int) =
if n == nil:
res.add("null")
elif containsOrIncl(marker, n.id):
res.addf "\"$1 @$2\"" % [$n.kind, strutils.toHex(cast[uint](n), sizeof(n) * 2)]
else:
let istr = spaces(indent * 4)
if nl:
res.addf("\n$1", istr)
res.addf("kind: $2", [istr, makeYamlString($n.kind)])
res.addf("\n$1sym: ", istr)
res.symToYamlAux(conf, n.sym, marker, true, indent + 1, maxRecDepth - 1)
res.addf("\n$1n: ", istr)
res.treeToYamlAux(conf, n.n, marker, true, indent + 1, maxRecDepth - 1)
if card(n.flags) > 0:
res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
res.addf("\n$1callconv: $2", [istr, makeYamlString($n.callConv)])
res.addf("\n$1size: $2", [istr, $(n.size)])
res.addf("\n$1align: $2", [istr, $(n.align)])
if n.hasElementType:
res.addf("\n$1sons:", istr)
for a in n.kids:
res.addf("\n$1 - ", istr)
res.typeToYamlAux(conf, a, marker, false, indent + 1, maxRecDepth - 1)
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; nl: bool, indent: int;
maxRecDepth: int) =
if n == nil:
res.add("null")
else:
var istr = spaces(indent * 4)
if nl:
res.addf("\n$1", istr)
res.addf("kind: $1" % [makeYamlString($n.kind)])
if maxRecDepth != 0:
if conf != nil:
res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
case n.kind
of nkCharLit .. nkUInt64Lit:
res.addf("\n$1intVal: $2", [istr, $(n.intVal)])
of nkFloatLit .. nkFloat128Lit:
res.addf("\n$1floatVal: $2", [istr, n.floatVal.toStrMaxPrecision])
of nkStrLit .. nkTripleStrLit:
res.addf("\n$1strVal: $2", [istr, makeYamlString(n.strVal)])
of nkSym:
res.addf("\n$1sym: ", [istr])
res.symToYamlAux(conf, n.sym, marker, true, indent + 1, maxRecDepth)
of nkIdent:
if n.ident != nil:
res.addf("\n$1ident: $2", [istr, makeYamlString(n.ident.s)])
else:
res.addf("\n$1ident: null", [istr])
else:
if n.len > 0:
res.addf("\n$1sons: ", [istr])
for i in 0 ..< n.len:
res.addf("\n$1 - ", [istr])
res.treeToYamlAux(conf, n[i], marker, false, indent + 1, maxRecDepth - 1)
if n.typ != nil:
res.addf("\n$1typ: ", [istr])
res.typeToYamlAux(conf, n.typ, marker, true, indent + 1, maxRecDepth)
proc treeToYaml*(conf: ConfigRef; n: PNode; indent: int = 0; maxRecDepth: int = -1): string =
var marker = initIntSet()
result = newStringOfCap(1024)
result.treeToYamlAux(conf, n, marker, false, indent, maxRecDepth)
proc typeToYaml*(conf: ConfigRef; n: PType; indent: int = 0; maxRecDepth: int = -1): string =
var marker = initIntSet()
result = newStringOfCap(1024)
result.typeToYamlAux(conf, n, marker, false, indent, maxRecDepth)
proc symToYaml*(conf: ConfigRef; n: PSym; indent: int = 0; maxRecDepth: int = -1): string =
var marker = initIntSet()
result = newStringOfCap(1024)
result.symToYamlAux(conf, n, marker, false, indent, maxRecDepth)

View File

@@ -0,0 +1,25 @@
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)

View File

@@ -87,11 +87,5 @@ const populationCount: array[uint8, uint8] = block:
arr
proc bitSetCard*(x: TBitSet): BiggestInt =
result = 0
for it in x:
result.inc int(populationCount[it])
proc bitSetToWord*(s: TBitSet; size: int): BiggestUInt =
result = 0
for j in 0..<size:
if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8))

View File

@@ -38,7 +38,6 @@ template less(a, b): bool = cmp(a, b) < 0
template eq(a, b): bool = cmp(a, b) == 0
proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
result = default(Val)
var x = b.root
while x.isInternal:
for j in 0..<x.entries:

View File

@@ -1,121 +0,0 @@
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

@@ -11,11 +11,7 @@
proc canRaiseDisp(p: BProc; n: PNode): bool =
# we assume things like sysFatal cannot raise themselves
if n.kind == nkSym and n.sym.kind == skMethod:
# A base method may be overridden by a branch with a wider exception set.
# Its inferred effects describe only the base body, not every vtable target.
result = true
elif n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
result = false
elif optPanics in p.config.globalOptions or
(n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
@@ -51,7 +47,6 @@ proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
# cannot analyse the location; assume the worst
return true
result = false
if le != nil:
for i in 1..<ri.len:
let r = ri[i]
@@ -80,23 +75,6 @@ proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool =
else:
result = false
proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
if returnType.kind in {tyVar, tyLent}:
# we don't need to worry about var/lent return types
result = false
elif hasDestructor(returnType) and getAttachedOp(p.module.g.graph, returnType, attachedDestructor) != nil:
let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor)
var op = initLocExpr(p, newSymNode(dtor))
var callee = rdLoc(op)
let destroy = if dtor.typ.firstParamType.kind == tyVar:
callee & "(&" & rdLoc(tmp) & ")"
else:
callee & "(" & rdLoc(tmp) & ")"
raiseExitCleanup(p, destroy)
result = true
else:
result = false
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
callee, params: Rope) =
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
@@ -104,9 +82,9 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
var pl = callee & "(" & params
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
if typ.returnType != nil:
if typ[0] != nil:
var flags: TAssignmentFlags = {}
if typ.returnType.kind in {tyOpenArray, tyVarargs}:
if typ[0].kind in {tyOpenArray, tyVarargs}:
# perhaps generate no temp if the call doesn't have side effects
flags.incl needTempForOpenArray
if isInvalidReturnType(p.config, typ):
@@ -114,7 +92,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
# Great, we can use 'd':
if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
elif d.k notin {locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
discard "resetLoc(p, d)"
@@ -122,7 +100,8 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
pl.add(");\n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
pl.add(addrLoc(p.config, tmp))
pl.add(");\n")
line(p, cpsStmts, pl)
@@ -136,38 +115,35 @@ 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.snippet = pl
d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
d = getTempCpp(p, typ.returnType, pl)
if d.k == locNone and p.splitDecls == 0:
getTempCpp(p, typ[0], d, pl)
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
var list = initLoc(locCall, d.lode, OnUnknown)
list.snippet = pl
genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
if d.k == locNone: getTemp(p, typ[0], d)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
elif isHarmlessStore(p, canRaise, d):
var useTemp = false
if d.k == locNone:
useTemp = true
d = getTemp(p, typ.returnType)
if d.k == locNone: getTemp(p, typ[0], d)
assert(d.t != nil) # generate an assignment to d:
var list = initLoc(locCall, d.lode, OnUnknown)
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)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, flags) # no need for deep copying
if canRaise: raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var list = initLoc(locCall, d.lode, OnUnknown)
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)
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, tmp, list, flags) # no need for deep copying
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {})
else:
pl.add(");\n")
@@ -192,9 +168,10 @@ proc reifiedOpenArray(n: PNode): bool {.inline.} =
result = true
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) =
var a = initLocExpr(p, q[1])
var b = initLocExpr(p, q[2])
var c = initLocExpr(p, q[3])
var a, b, c: TLoc
initLocExpr(p, q[1], a)
initLocExpr(p, q[2], b)
initLocExpr(p, q[3], c)
# bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
# are mapped into ctPtrToArray, the dereference of which is skipped
# in the `genDeref`. We need to skip these ptrs here
@@ -242,7 +219,6 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
lengthExpr)
else:
result = ("", "")
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
@@ -259,10 +235,11 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
for i in 0..<q.len-1:
genStmts(p, q[i])
q = q.lastSon
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType)
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
result.add x & ", " & y
else:
var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
var a: TLoc
initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
case skipTypes(a.t, abstractVar+{tyStatic}).kind
of tyOpenArray, tyVarargs:
if reifiedOpenArray(n):
@@ -278,7 +255,8 @@ 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(snippet: "(*$1)" % [a.rdLoc])
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
dataFieldAccessor(p, "*" & a.rdLoc)]
@@ -288,14 +266,15 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
of tyArray:
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
of tyPtr, tyRef:
case elementType(a.t).kind
case lastSon(a.t).kind
of tyString, tySequence:
var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
dataFieldAccessor(p, "*" & a.rdLoc)]
of tyArray:
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, elementType(a.t)))]
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
else:
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
@@ -306,17 +285,18 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
# Also don't regress for non ARC-builds, too risky.
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
getSize(p.config, a.lode.typ) < 1024:
result = getTemp(p, a.lode.typ, needsInit=false)
getTemp(p, a.lode.typ, result, needsInit=false)
genAssignment(p, result, a, {})
else:
result = a
proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
result = getTemp(p, a.lode.typ, needsInit=false)
proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
getTemp(p, a.lode.typ, result, needsInit=false)
genAssignment(p, result, a, {})
proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
var a = initLocExpr(p, n[0])
var a: TLoc
initLocExpr(p, n[0], a)
appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
@@ -326,11 +306,11 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
var n = if n.kind != nkHiddenAddr: n else: n[0]
openArrayLoc(p, param.typ, n, result)
elif ccgIntroducedPtr(p.config, param, call[0].typ.returnType) and
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]) and
(optByRef notin param.options or not p.module.compileToCpp):
a = initLocExpr(p, n)
initLocExpr(p, n, a)
if n.kind in {nkCharLit..nkNilLit}:
addAddrLoc(p.config, expressionsNeedsTmp(p, a), result)
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
else:
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
@@ -340,9 +320,9 @@ 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)
initLocExprSingleUse(p, n, a)
a = withTmpIfNeeded(p, a, needsTmp)
if needsIndirect: a.flags.incl lfIndirect
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
@@ -356,11 +336,11 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
else:
addRdLoc(a, result)
else:
a = initLocExprSingleUse(p, n)
if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
initLocExprSingleUse(p, n, a)
if param.typ.kind in abstractPtrs:
let typ = skipTypes(param.typ, abstractPtrs)
if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
a.snippet = "(($1) ($2))" %
if typ.sym != nil and sfImportc in typ.sym.flags:
a.r = "(($1) ($2))" %
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
#assert result != nil
@@ -370,13 +350,12 @@ proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
if n.kind == nkStringToCString:
genArgStringToCString(p, n, result, needsTmp)
else:
a = initLocExprSingleUse(p, n)
initLocExprSingleUse(p, n, a)
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
import aliasanalysis
proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
result = false
for p in potentialWrites:
if p.aliases(n) != no or n.aliases(p) != no:
return true
@@ -406,7 +385,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
of nkCallKinds:
case n.getMagic:
of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mAddr, mNew, mNewFinalize, mWasMoved, mDestroy:
mAddr, mNew, mNewFinalize, mWasMoved, mDestroy, mReset:
getPotentialWrites(n[1], true, result)
for i in 2..<n.len:
getPotentialWrites(n[i], mutate, result)
@@ -432,13 +411,13 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
# We must generate temporaries in cases like #14396
# to keep the strict Left-To-Right evaluation
var needTmp = newSeq[bool](ri.len - 1)
var potentialWrites: seq[PNode] = @[]
var potentialWrites: seq[PNode]
for i in countdown(ri.len - 1, 1):
if ri[i].skipTrivialIndirections.kind == nkSym:
needTmp[i - 1] = potentialAlias(ri[i], potentialWrites)
else:
#if not ri[i].typ.isCompileTimeOnly:
var potentialReads: seq[PNode] = @[]
var potentialReads: seq[PNode]
getPotentialReads(ri[i], potentialReads)
for n in potentialReads:
if not needTmp[i - 1]:
@@ -452,7 +431,7 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
var oldLen = result.len
for i in 1..<ri.len:
if i < typ.n.len:
if i < typ.len:
assert(typ.n[i].kind == nkSym)
let paramType = typ.n[i]
if not paramType.typ.isCompileTimeOnly:
@@ -472,11 +451,13 @@ proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
res = res & "_actual".rope
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc
# this is a hotspot in the compiler
var op = initLocExpr(p, ri[0])
initLocExpr(p, ri[0], op)
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInstOwned)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
var params = newRopeAppender()
genParams(p, ri, typ, params)
@@ -494,11 +475,13 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
var op = initLocExpr(p, ri[0])
var op: TLoc
initLocExpr(p, ri[0], op)
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInstOwned)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
var pl = newRopeAppender()
genParams(p, ri, typ, pl)
@@ -511,14 +494,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
let rawProc = getClosureType(p.module, typ, clHalf)
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
if typ.returnType != nil:
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if ri.len > 1: pl.add(", ")
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
# Great, we can use 'd':
if d.k == locNone:
d = getTemp(p, typ.returnType, needsInit=true)
getTemp(p, typ[0], d, needsInit=true)
elif d.k notin {locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
discard "resetLoc(p, d)"
@@ -526,29 +509,33 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
genCallPattern()
if canRaise: raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
pl.add(addrLoc(p.config, tmp))
genCallPattern()
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {}) # no need for deep copying
elif isHarmlessStore(p, canRaise, d):
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: getTemp(p, typ[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType)
var tmp: TLoc
getTemp(p, typ[0], tmp)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, tmp, list, {})
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {})
@@ -558,14 +545,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
argsCounter: var int) =
if i < typ.n.len:
if i < typ.len:
# 'var T' is 'T&' in C++. This means we ignore the request of
# any nkHiddenAddr when it's a 'var T'.
let paramType = typ.n[i]
assert(paramType.kind == nkSym)
if paramType.typ.isCompileTimeOnly:
discard
elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
if argsCounter > 0: result.add ", "
genArgNoParam(p, ri[i][0], result)
inc argsCounter
@@ -640,7 +627,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
# for better or worse c2nim translates the 'this' argument to a 'var T'.
# However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience.
internalAssert p.config, i < typ.n.len
internalAssert p.config, i < typ.len
assert(typ.n[i].kind == nkSym)
# if the parameter is lying (tyVar) and thus we required an additional deref,
# skip the deref:
@@ -712,7 +699,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro
inc j
inc i
of '\'':
var idx, stars: int = 0
var idx, stars: int
if scanCppGenericSlot(pat, i, idx, stars):
var t = resolveStarsInCppType(typ, idx, stars)
if t == nil: result.add("void")
@@ -726,31 +713,34 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro
result.add(substr(pat, start, i - 1))
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op = initLocExpr(p, ri[0])
var op: TLoc
initLocExpr(p, ri[0], op)
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.snippet
let pat = $ri[0].sym.loc.r
internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@', '\''}):
var pl = newRopeAppender()
genPatternCall(p, ri, pat, typ, pl)
# simpler version of 'fixupCall' that works with the pl+params combination:
var typ = skipTypes(ri[0].typ, abstractInst)
if typ.returnType != nil:
if typ[0] != nil:
if p.module.compileToCpp and lfSingleUse in d.flags:
# do not generate spurious temporaries for C++! For C we're better off
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.snippet = pl
d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: getTemp(p, typ[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
list.snippet = pl
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add(";\n")
@@ -760,27 +750,30 @@ 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.snippet)
pl.add(op.r)
var params = newRopeAppender()
for i in 2..<ri.len:
assert(typ.len == typ.n.len)
genOtherArg(p, ri, i, typ, params, argsCounter)
fixupCall(p, le, ri, d, pl, params)
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# generates a crappy ObjC call
var op = initLocExpr(p, ri[0])
var op: TLoc
initLocExpr(p, ri[0], op)
var pl = "["
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.snippet
let pat = $ri[0].sym.loc.r
internalAssert p.config, pat.len > 0
var start = 3
if ' ' in pat:
start = 1
pl.add(op.snippet)
pl.add(op.r)
if ri.len > 1:
pl.add(": ")
genArg(p, ri[1], typ.n[1].sym, ri, pl)
@@ -789,12 +782,13 @@ 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.snippet)
pl.add(op.r)
if ri.len > 2:
pl.add(": ")
genArg(p, ri[2], typ.n[2].sym, ri, pl)
for i in start..<ri.len:
if i >= typ.n.len:
assert(typ.len == typ.n.len)
if i >= typ.len:
internalError(p.config, ri.info, "varargs for objective C method?")
assert(typ.n[i].kind == nkSym)
var param = typ.n[i].sym
@@ -802,29 +796,31 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
pl.add(param.name.s)
pl.add(": ")
genArg(p, ri[i], param, ri, pl)
if typ.returnType != nil:
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if ri.len > 1: pl.add(" ")
# beware of 'result = p(result)'. We always allocate a temporary:
if d.k in {locTemp, locNone}:
# We already got a temp. Great, special case it:
if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
pl.add("Result: ")
pl.add(addrLoc(p.config, d))
pl.add("];\n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
pl.add(addrLoc(p.config, tmp))
pl.add("];\n")
line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying
else:
pl.add("]")
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: getTemp(p, typ[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, ri, OnUnknown)
list.snippet = pl
var list: TLoc
initLoc(list, locCall, ri, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add("];\n")

File diff suppressed because it is too large Load Diff

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.snippet == "": fillObjectFields(p.module, typ)
if disc.loc.r == "": 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.snippet])
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
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.snippet], disc.loc.t)
specializeResetT(p, "$1.$2" % [accessor, disc.loc.r], disc.loc.t)
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
if field.loc.snippet == "": fillObjectFields(p.module, typ)
if field.loc.r == "": fillObjectFields(p.module, typ)
if field.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
else: internalError(p.config, n.info, "specializeResetN()")
proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
@@ -54,45 +54,25 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
case typ.kind
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
tySink, tyOwned:
specializeResetT(p, accessor, skipModifier(typ))
specializeResetT(p, accessor, lastSon(typ))
of tyArray:
let arraySize = lengthOrd(p.config, typ.indexType)
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
let arraySize = lengthOrd(p.config, typ[0])
var i: TLoc
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i)
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.snippet, arraySize])
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
[i.r, arraySize])
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ[1])
lineF(p, cpsStmts, "}$n", [])
of tyObject:
var x = typ.baseClass
if x != nil: x = x.skipTypes(skipPtrs)
specializeResetT(p, accessor.parentObj(p.module), x)
if typ.n != nil:
if typ.sym != nil and sfImportc in typ.sym.flags:
# imported C struct, nimZeroMem
lineCg(p, cpsStmts, "#nimZeroMem((void**)&$1, sizeof($2));$n",
[accessor, getTypeDesc(p.module, typ)])
else:
specializeResetN(p, accessor, typ.n, typ)
if isCaseObj(typ.n):
# The active branch was released above. Clear the complete object so
# stale bytes from overlapping branches cannot be traced by the GC.
# type
# Foo = object
# case kind: bool
# of true:
# a: ref Bar # 8 bytes (pointer)
# of false:
# b: int # 4 bytes
# specializeResetT for b emits accessor.b = 0 — writes 4 bytes
# But the union is 8 bytes wide (sized by the largest branch)
# The remaining 4 bytes where a used to live are untouched
# Those stale bytes could contain a heap pointer the GC traces → crash
lineCg(p, cpsStmts, "#nimZeroMem((void**)&$1, sizeof($2));$n",
[accessor, getTypeDesc(p.module, typ)])
for i in 0..<typ.len:
var x = typ[i]
if x != nil: x = x.skipTypes(skipPtrs)
specializeResetT(p, accessor.parentObj(p.module), x)
if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
of tyTuple:
let typ = getUniqueType(typ)
for i, a in typ.ikids:
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
for i in 0..<typ.len:
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), typ[i])
of tyString, tyRef, tySequence:
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
@@ -115,12 +95,12 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
of ctInt8, ctInt16, ctInt32, ctInt64:
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
else:
raiseAssert "unexpected set type kind"
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
doAssert false, "unexpected set type kind"
of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}:
discard
proc specializeReset(p: BProc, a: TLoc) =

View File

@@ -50,7 +50,6 @@ proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
result = true
proc inExceptBlockLen(p: BProc): int =
result = 0
for x in p.nestedTryStmts:
if x.inExcept: result.inc
@@ -72,6 +71,7 @@ template startBlock(p: BProc, start: FormatStr = "{$n",
proc endBlock(p: BProc)
proc genVarTuple(p: BProc, n: PNode) =
var tup, field: TLoc
if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple")
# if we have a something that's been captured, use the lowering instead:
@@ -83,7 +83,7 @@ proc genVarTuple(p: BProc, n: PNode) =
# check only the first son
var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym)
let hcrCond = if forHcr: getTempName(p.module) else: ""
var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]] = @[]
var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]]
# determine if the tuple is constructed at top-level scope or inside of a block (if/while/block)
let isGlobalInBlock = forHcr and p.blocks.len > 2
# do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block)
@@ -95,7 +95,7 @@ proc genVarTuple(p: BProc, n: PNode) =
startBlock(p)
genLineDir(p, n)
var tup = initLocExpr(p, n[^1])
initLocExpr(p, n[^1], tup)
var t = tup.t.skipTypes(abstractInst)
for i in 0..<n.len-2:
let vn = n[i]
@@ -108,12 +108,12 @@ proc genVarTuple(p: BProc, n: PNode) =
else:
assignLocalVar(p, vn)
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
var field = initLoc(locExpr, vn, tup.storage)
initLoc(field, locExpr, vn, tup.storage)
if t.kind == tyTuple:
field.snippet = "$1.Field$2" % [rdLoc(tup), rope(i)]
field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
else:
if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
field.snippet = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
field.r = "$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.snippet, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
[hcrCond, curr.loc.r, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
@@ -169,7 +169,7 @@ proc endBlock(p: BProc, blockEnd: Rope) =
proc endBlock(p: BProc) =
let topBlock = p.blocks.len - 1
let frameLen = p.blocks[topBlock].frameLen
var blockEnd: Rope = ""
var blockEnd: Rope
if frameLen > 0:
blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope])
if p.blocks[topBlock].label.len != 0:
@@ -202,11 +202,11 @@ proc genState(p: BProc, n: PNode) =
elif n0.kind == nkStrLit:
linefmt(p, cpsStmts, "$1: ;$n", [n0.strVal])
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int, isReturnStmt = false) =
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
# Called by return and break stmts.
# Deals with issues faced when jumping out of try/except/finally stmts.
var stack = newSeq[tuple[fin: PNode, inExcept: bool, isHidden: bool, label: Natural]](0)
var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0)
inc p.withinBlockLeaveActions
for i in 1..howManyTrys:
@@ -234,7 +234,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int, isReturnStmt
# Pop exceptions that was handled by the
# except-blocks we are in
if noSafePoints notin p.flags and not (isReturnStmt and isClosureIterator(p.prc.typ)):
if noSafePoints notin p.flags:
for i in countdown(howManyExcepts-1, 0):
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
@@ -244,7 +244,8 @@ proc genGotoState(p: BProc, n: PNode) =
# switch (x.state) {
# case 0: goto STATE0;
# ...
var a: TLoc = initLocExpr(p, n[0])
var a: TLoc
initLocExpr(p, n[0], a)
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
p.flags.incl beforeRetNeeded
lineF(p, cpsStmts, "case -1:$n", [])
@@ -263,15 +264,15 @@ proc genGotoState(p: BProc, n: PNode) =
proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc
d = initLoc(locExpr, n, OnUnknown)
initLoc(d, locExpr, n, OnUnknown)
if n[0].kind == nkClosure:
a = initLocExpr(p, n[0][1])
d.snippet = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
initLocExpr(p, n[0][1], a)
d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
else:
a = initLocExpr(p, n[0])
initLocExpr(p, n[0], a)
# the environment is guaranteed to contain the 'state' field at offset 1:
d.snippet = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}:
@@ -289,32 +290,14 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
#echo "New code produced for ", v.name.s, " ", p.config $ value.info
genBracedInit(p, value, isConst = false, v.typ, result)
proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string =
result = ""
proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) =
var params = newRopeAppender()
var argsCounter = 0
let typ = skipTypes(call[0].typ, abstractInst)
let typ = skipTypes(value[0].typ, abstractInst)
assert(typ.kind == tyProc)
for i in 1..<call.len:
#if it's a type we can just generate here another initializer as we are in an initializer context
if call[i].kind == nkCall and call[i][0].kind == nkSym and call[i][0].sym.kind == skType:
if argsCounter > 0: result.add ","
result.add genCppInitializer(p.module, p, call[i][0].sym.typ, didGenTemp)
else:
#We need to test for temp in globals, see: #23657
let param =
if typ[i].kind in {tyVar} and call[i].kind == nkHiddenAddr:
call[i][0]
else:
call[i]
if not param.typ.isCompileTimeOnly and (param.kind != nkBracketExpr or param.typ.kind in
{tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray,
tyVarargs, tySequence, tyString, tyCstring, tyTuple}):
let tempLoc = initLocExprSingleUse(p, param)
didGenTemp = didGenTemp or tempLoc.k == locTemp
genOtherArg(p, call, i, typ, result, argsCounter)
proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope, didGenTemp: var bool) =
let params = genCppParamsForCtor(p, call, didGenTemp)
for i in 1..<value.len:
assert(typ.len == typ.n.len)
genOtherArg(p, value, i, typ, params, argsCounter)
if params.len == 0:
decl = runtimeFormat("$#;\n", [decl])
else:
@@ -341,14 +324,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
# v.owner.kind != skModule:
targetProc = p.module.preInitProc
if isCppCtorCall and not containsHiddenPointer(v.typ):
var didGenTemp = false
callGlobalVarCppCtor(targetProc, v, vn, value, didGenTemp)
if didGenTemp:
message(p.config, vn.info, warnGlobalVarConstructorTemporary, vn.sym.name.s)
#We fail to call the constructor in the global scope so we do the call inside the main proc
assignGlobalVar(targetProc, vn, valueAsRope)
var loc = initLocExprSingleUse(targetProc, value)
genAssignment(targetProc, v.loc, loc, {})
callGlobalVarCppCtor(targetProc, v, vn, value)
else:
assignGlobalVar(targetProc, vn, valueAsRope)
@@ -365,7 +341,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
# Only do this for complex types that may need a call to `objectInit`
if sfThread in v.flags and emulatedThreadVars(p.config) and
isComplexValueType(v.typ):
loc = initLocExprSingleUse(p.module.preInitProc, vn)
initLocExprSingleUse(p.module.preInitProc, vn, loc)
genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, constructObj)
# Alternative construction using default constructor (which may zeromem):
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
@@ -383,15 +359,11 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
var decl = localVarDecl(p, vn)
var tmp: TLoc
if isCppCtorCall:
var didGenTemp = false
genCppVarForCtor(p, value, decl, didGenTemp)
genCppVarForCtor(p, v, vn, value, decl)
line(p, cpsStmts, decl)
else:
tmp = initLocExprSingleUse(p, value)
if value.kind == nkEmpty:
lineF(p, cpsStmts, "$#;\n", [decl])
else:
lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc])
initLocExprSingleUse(p, value, tmp)
lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc])
return
assignLocalVar(p, vn)
initLocalVar(p, v, imm)
@@ -405,7 +377,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.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
[v.loc.r, 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 +386,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.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
startBlock(targetProc)
if value.kind != nkEmpty and valueAsRope.len == 0:
genLineDir(targetProc, vn)
@@ -436,7 +408,8 @@ proc genSingleVar(p: BProc, a: PNode) =
proc genClosureVar(p: BProc, a: PNode) =
var immediateAsgn = a[2].kind != nkEmpty
var v: TLoc = initLocExpr(p, a[0])
var v: TLoc
initLocExpr(p, a[0], v)
genLineDir(p, a)
if immediateAsgn:
loadInto(p, a[0], a[2], v)
@@ -471,7 +444,7 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
a: TLoc
lelse: TLabel
if not isEmptyType(n.typ) and d.k == locNone:
d = getTemp(p, n.typ)
getTemp(p, n.typ, d)
genLineDir(p, n)
let lend = getLabel(p)
for it in n.sons:
@@ -479,7 +452,7 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
if d.k == locTemp and isEmptyType(n.typ): d.k = locNone
if it.len == 2:
startBlock(p)
a = initLocExprSingleUse(p, it[0])
initLocExprSingleUse(p, it[0], a)
lelse = getLabel(p)
inc(p.labels)
lineF(p, cpsStmts, "if (!$1) goto $2;$n",
@@ -509,8 +482,7 @@ proc genReturnStmt(p: BProc, t: PNode) =
if (t[0].kind != nkEmpty): genStmts(p, t[0])
blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len,
howManyExcepts = p.inExceptBlockLen,
isReturnStmt = true)
howManyExcepts = p.inExceptBlockLen)
if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags:
# If we're in a finally block, and we came here by exception
# consume it before we return.
@@ -548,7 +520,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
# wrapped inside stmt lists by inject destructors won't be recognised
let n = n.flattenStmts()
var casePos = -1
var arraySize: int = 0
var arraySize: int
for i in 0..<n.len:
let it = n[i]
if it.kind == nkCaseStmt:
@@ -582,7 +554,8 @@ proc genComputedGoto(p: BProc; n: PNode) =
genStmts(p, n[j])
let caseStmt = n[casePos]
var a: TLoc = initLocExpr(p, caseStmt[0])
var a: TLoc
initLocExpr(p, caseStmt[0], a)
# first goto:
lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
@@ -620,7 +593,8 @@ proc genComputedGoto(p: BProc; n: PNode) =
else:
genStmts(p, it)
var a: TLoc = initLocExpr(p, caseStmt[0])
var a: TLoc
initLocExpr(p, caseStmt[0], a)
lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
endBlock(p)
@@ -648,7 +622,7 @@ proc genWhileStmt(p: BProc, t: PNode) =
else:
p.breakIdx = startBlock(p, "while (1) {$n")
p.blocks[p.breakIdx].isLoop = true
a = initLocExpr(p, t[0])
initLocExpr(p, t[0], a)
if (t[0].kind != nkIntLit) or (t[0].intVal == 0):
lineF(p, cpsStmts, "if (!$1) goto ", [rdLoc(a)])
assignLabel(p.blocks[p.breakIdx], p.s(cpsStmts))
@@ -667,7 +641,7 @@ proc genBlock(p: BProc, n: PNode, d: var TLoc) =
# bug #4505: allocate the temp in the outer scope
# so that it can escape the generated {}:
if d.k == locNone:
d = getTemp(p, n.typ)
getTemp(p, n.typ, d)
d.flags.incl(lfEnforceDeref)
preserveBreakIdx:
p.breakIdx = startBlock(p)
@@ -687,13 +661,14 @@ proc genParForStmt(p: BProc, t: PNode) =
preserveBreakIdx:
let forLoopVar = t[0].sym
var rangeA, rangeB: TLoc
assignLocalVar(p, t[0])
#initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack)
#discard mangleName(forLoopVar)
let call = t[1]
assert(call.len == 4 or call.len == 5)
var rangeA = initLocExpr(p, call[1])
var rangeB = initLocExpr(p, call[2])
initLocExpr(p, call[1], rangeA)
initLocExpr(p, call[2], rangeB)
# $n at the beginning because of #9710
if call.len == 4: # procName(a, b, annotation)
@@ -710,7 +685,8 @@ proc genParForStmt(p: BProc, t: PNode) =
rangeA.rdLoc, rangeB.rdLoc,
call[3].getStr.rope])
else: # `||`(a, b, step, annotation)
var step: TLoc = initLocExpr(p, call[3])
var step: TLoc
initLocExpr(p, call[3], step)
lineF(p, cpsStmts, "$n#pragma omp $5$n" &
"for ($1 = $2; $1 <= $3; $1 += $4)",
[forLoopVar.loc.rdLoc,
@@ -756,39 +732,13 @@ proc raiseExit(p: BProc) =
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
[p.nestedTryStmts[^1].label])
proc raiseExitCleanup(p: BProc, destroy: string) =
assert p.config.exc == excGoto
if nimErrorFlagDisabled notin p.flags:
p.flags.incl nimErrorFlagAccessed
if p.nestedTryStmts.len == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$1; goto BeforeRet_;}$n", [destroy])
else:
lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$2; goto LA$1_;}$n",
[p.nestedTryStmts[^1].label, destroy])
proc finallyActions(p: BProc) =
if p.config.exc != excGoto:
# Walk past compiler-injected `nkHiddenTryStmt` wrappers (e.g. ARC's
# destructor try/finally that wraps `except T as e:` bodies) to reach
# the user's actual try. We must NOT walk past a real user try whose
# body we are currently in, because a raise from there will be caught
# by that try's own except branches rather than escaping outward.
#
# If after skipping wrappers the next entry is a user try in its
# except branch (inExcept=true), inline its finally body before the
# raise propagates — without this, the C++ sibling-catch rule would
# cause the user's catch(...)/finally pair to be bypassed and the
# finally would be silently dropped.
for i in countdown(p.nestedTryStmts.high, 0):
if p.nestedTryStmts[i].isHidden:
continue
if p.nestedTryStmts[i].inExcept:
let finallyBlock = p.nestedTryStmts[i].fin
if finallyBlock != nil:
genSimpleBlock(p, finallyBlock[0])
return
if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
# if the current try stmt have a finally block,
# we must execute it before reraising
let finallyBlock = p.nestedTryStmts[^1].fin
if finallyBlock != nil:
genSimpleBlock(p, finallyBlock[0])
proc raiseInstr(p: BProc; result: var Rope) =
if p.config.exc == excGoto:
@@ -806,19 +756,16 @@ proc raiseInstr(p: BProc; result: var Rope) =
proc genRaiseStmt(p: BProc, t: PNode) =
if t[0].kind != nkEmpty:
var a: TLoc = initLocExprSingleUse(p, t[0])
var a: TLoc
initLocExprSingleUse(p, t[0], a)
finallyActions(p)
var e = rdLoc(a)
discard getTypeDesc(p.module, t[0].typ)
var typ = skipTypes(t[0].typ, abstractPtrs)
case p.config.exc
of excCpp:
# XXX For reasons that currently escape me, this is only required by the new
# C++ based exception handling:
if p.config.exc == excCpp:
blockLeaveActions(p, howManyTrys = 0, howManyExcepts = p.inExceptBlockLen)
of excGoto:
blockLeaveActions(p, howManyTrys = 0,
howManyExcepts = (if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: 1 else: 0))
else:
discard
genLineDir(p, t)
if isImportedException(typ, p.config):
lineF(p, cpsStmts, "throw $1;$n", [e])
@@ -840,12 +787,12 @@ template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
var x, y: TLoc
for i in 0..<b.len - 1:
if b[i].kind == nkRange:
x = initLocExpr(p, b[i][0])
y = initLocExpr(p, b[i][1])
initLocExpr(p, b[i][0], x)
initLocExpr(p, b[i][1], y)
lineCg(p, cpsStmts, rangeFormat,
[rdCharLoc(e), rdCharLoc(x), rdCharLoc(y), labl])
else:
x = initLocExpr(p, b[i])
initLocExpr(p, b[i], x)
lineCg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl])
proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
@@ -887,7 +834,8 @@ template genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
template genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
rangeFormat, eqFormat: FormatStr) =
var a: TLoc = initLocExpr(p, t[0])
var a: TLoc
initLocExpr(p, t[0], a)
var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, t.len-1, a)
fixLabel(p, lend)
@@ -897,8 +845,8 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
var x: TLoc
for i in 0..<b.len - 1:
assert(b[i].kind != nkRange)
x = initLocExpr(p, b[i])
var j: int = 0
initLocExpr(p, b[i], x)
var j: int
case b[i].kind
of nkStrLit..nkTripleStrLit:
j = int(hashString(p.config, b[i].strVal) and high(branches))
@@ -921,7 +869,8 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
var bitMask = math.nextPowerOfTwo(strings) - 1
var branches: seq[Rope]
newSeq(branches, bitMask + 1)
var a: TLoc = initLocExpr(p, t[0]) # first pass: generate ifs+goto:
var a: TLoc
initLocExpr(p, t[0], a) # fist pass: generate ifs+goto:
var labId = p.labels
for i in 1..<t.len:
inc(p.labels)
@@ -957,7 +906,6 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
proc branchHasTooBigRange(b: PNode): bool =
result = false
for it in b:
# last son is block
if (it.kind == nkRange) and
@@ -965,7 +913,6 @@ proc branchHasTooBigRange(b: PNode): bool =
return true
proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
result = 0
for i in 1..<n.len:
var branch = n[i]
var stmtBlock = lastSon(branch)
@@ -1001,7 +948,8 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
var splitPoint = ifSwitchSplitPoint(p, n)
# generate if part (might be empty):
var a: TLoc = initLocExpr(p, n[0])
var a: TLoc
initLocExpr(p, n[0], a)
var lend = if splitPoint > 0: genIfForCaseUntil(p, n, d,
rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
eqFormat = "if ($1 == $2) goto $3;$n",
@@ -1034,7 +982,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
proc genCase(p: BProc, t: PNode, d: var TLoc) =
genLineDir(p, t)
if not isEmptyType(t.typ) and d.k == locNone:
d = getTemp(p, t.typ)
getTemp(p, t.typ, d)
case skipTypes(t[0].typ, abstractVarRange).kind
of tyString:
genStringCase(p, t, tyString, d)
@@ -1074,7 +1022,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
throw;
}
} catch(...) {
// C++ exception occurred, not under Nim's control.
// C++ exception occured, not under Nim's control.
}
{
/* finally: */
@@ -1085,16 +1033,16 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
p.module.includeHeader("<exception>")
if not isEmptyType(t.typ) and d.k == locNone:
d = getTemp(p, t.typ)
getTemp(p, t.typ, d)
genLineDir(p, t)
inc(p.labels, 2)
let etmp = p.labels
#init on locals, fixes #23306
lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp])
lineCg(p, cpsStmts, "std::exception_ptr T$1_;$n", [etmp])
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
p.nestedTryStmts.add((fin, false, 0.Natural))
if t.kind == nkHiddenTryStmt:
lineCg(p, cpsStmts, "try {$n", [])
@@ -1125,7 +1073,6 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
if hasIf: lineF(p, cpsStmts, "else ", [])
startBlock(p)
# we handled the error:
linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
expr(p, t[i][0], d)
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
endBlock(p)
@@ -1251,11 +1198,11 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
expr(p, body, d)
if not isEmptyType(t.typ) and d.k == locNone:
d = getTemp(p, t.typ)
getTemp(p, t.typ, d)
genLineDir(p, t)
cgsym(p.module, "popCurrentExceptionEx")
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
p.nestedTryStmts.add((fin, false, 0.Natural))
startBlock(p, "try {$n")
expr(p, t[0], d)
endBlock(p)
@@ -1324,12 +1271,12 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
let lab = p.labels
let hasExcept = t[1].kind == nkExceptBranch
if hasExcept: inc p.withinTryWithExcept
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, Natural lab))
p.nestedTryStmts.add((fin, false, Natural lab))
p.flags.incl nimErrorFlagAccessed
if not isEmptyType(t.typ) and d.k == locNone:
d = getTemp(p, t.typ)
getTemp(p, t.typ, d)
expr(p, t[0], d)
@@ -1436,7 +1383,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
# propagateCurrentException();
#
if not isEmptyType(t.typ) and d.k == locNone:
d = getTemp(p, t.typ)
getTemp(p, t.typ, d)
let quirkyExceptions = p.config.exc == excQuirky or
(t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags)
if not quirkyExceptions:
@@ -1445,7 +1392,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
p.flags.incl noSafePoints
genLineDir(p, t)
cgsym(p.module, "Exception")
var safePoint: Rope = ""
var safePoint: Rope
if not quirkyExceptions:
safePoint = getTempName(p.module)
linefmt(p, cpsLocals, "#TSafePoint $1;$n", [safePoint])
@@ -1476,7 +1423,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint])
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
p.nestedTryStmts.add((fin, quirkyExceptions, t.kind == nkHiddenTryStmt, 0.Natural))
p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural))
expr(p, t[0], d)
if not quirkyExceptions:
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
@@ -1510,8 +1457,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
if optTinyRtti in p.config.globalOptions:
let checkFor = $getObjDepth(t[i][j].typ)
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
[memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
else:
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
@@ -1542,31 +1488,28 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
var res = ""
let offset =
if isAsmStmt: 1 # first son is pragmas
else: 0
for i in offset..<t.len:
let it = t[i]
for it in t.sons:
case it.kind
of nkStrLit..nkTripleStrLit:
res.add(it.strVal)
of nkSym:
var sym = it.sym
if sym.kind in {skProc, skFunc, skIterator, skMethod}:
var a: TLoc = initLocExpr(p, it)
var a: TLoc
initLocExpr(p, it, a)
res.add($rdLoc(a))
elif sym.kind == skType:
res.add($getTypeDesc(p.module, sym.typ))
else:
discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
fillBackendName(p.module, sym)
res.add($sym.loc.snippet)
res.add($sym.loc.r)
of nkTypeOfExpr:
res.add($getTypeDesc(p.module, it.typ))
else:
discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs))
var a: TLoc = initLocExpr(p, it)
var a: TLoc
initLocExpr(p, it, a)
res.add($a.rdLoc)
if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props:
@@ -1591,21 +1534,6 @@ proc genAsmStmt(p: BProc, t: PNode) =
assert(t.kind == nkAsmStmt)
genLineDir(p, t)
var s = newRopeAppender()
var asmSyntax = ""
if (let p = t[0]; p.kind == nkPragma):
for i in p:
if whichPragma(i) == wAsmSyntax:
asmSyntax = i[1].strVal
if asmSyntax != "" and
not (
asmSyntax == "gcc" and hasGnuAsm in CC[p.config.cCompiler].props or
asmSyntax == "vcc" and hasGnuAsm notin CC[p.config.cCompiler].props):
localError(
p.config, t.info,
"Your compiler does not support the specified inline assembler")
genAsmOrEmitStmt(p, t, isAsmStmt=true, s)
# see bug #2362, "top level asm statements" seem to be a mis-feature
# but even if we don't do this, the example in #2362 cannot possibly
@@ -1643,9 +1571,9 @@ proc genPragma(p: BProc, n: PNode) =
case whichPragma(it)
of wEmit: genEmit(p, it)
of wPush:
processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
processPushBackendOption(p.optionsStack, p.options, n, i+1)
of wPop:
processPopBackendOption(p.config, p.optionsStack, p.options)
processPopBackendOption(p.optionsStack, p.options)
else: discard
@@ -1683,10 +1611,11 @@ when false:
expr(p, call, d)
proc asgnFieldDiscriminant(p: BProc, e: PNode) =
var a, tmp: TLoc
var dotExpr = e[0]
if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0]
var a = initLocExpr(p, e[0])
var tmp: TLoc = getTemp(p, a.t)
initLocExpr(p, e[0], a)
getTemp(p, a.t, tmp)
expr(p, e[1], tmp)
if p.inUncheckedAssignSection == 0:
let field = dotExpr[1].sym
@@ -1704,8 +1633,9 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
else:
let le = e[0]
let ri = e[1]
var a: TLoc = initLoc(locNone, le, OnUnknown)
var a: TLoc
discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar)
initLoc(a, locNone, le, OnUnknown)
a.flags.incl(lfEnforceDeref)
a.flags.incl(lfPrepareForMutation)
genLineDir(p, le) # it can be a nkBracketExpr, which may raise
@@ -1717,7 +1647,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
loadInto(p, le, ri, a)
proc genStmts(p: BProc, t: PNode) =
var a: TLoc = default(TLoc)
var a: TLoc
let isPush = p.config.hasHint(hintExtendedContext)
if isPush: pushInfoContext(p.config, t.info)

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.snippet])
m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
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.snippet])
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
proc generateThreadLocalStorage(m: BModule) =
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):

View File

@@ -21,7 +21,7 @@ const
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
proc genCaseRange(p: BProc, branch: PNode)
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
typ: PType) =
@@ -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.snippet == "": fillObjectFields(c.p.module, typ)
if disc.loc.r == "": 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.snippet])
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
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.snippet == "": fillObjectFields(c.p.module, typ)
if field.loc.r == "": fillObjectFields(c.p.module, typ)
if field.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")
genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
else: internalError(c.p.config, n.info, "genTraverseProc()")
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
@@ -71,36 +71,38 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
case typ.kind
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
tySink, tyOwned:
genTraverseProc(c, accessor, skipModifier(typ))
genTraverseProc(c, accessor, lastSon(typ))
of tyArray:
let arraySize = lengthOrd(c.p.config, typ.indexType)
var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
let arraySize = lengthOrd(c.p.config, typ[0])
var i: TLoc
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
var oldCode = p.s(cpsStmts)
freeze oldCode
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.snippet, arraySize])
[i.r, arraySize])
let oldLen = p.s(cpsStmts).len
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ[1])
if p.s(cpsStmts).len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
else:
lineF(p, cpsStmts, "}$n", [])
of tyObject:
var x = typ.baseClass
if x != nil: x = x.skipTypes(skipPtrs)
genTraverseProc(c, accessor.parentObj(c.p.module), x)
for i in 0..<typ.len:
var x = typ[i]
if x != nil: x = x.skipTypes(skipPtrs)
genTraverseProc(c, accessor.parentObj(c.p.module), x)
if typ.n != nil: genTraverseProc(c, accessor, typ.n, typ)
of tyTuple:
let typ = getUniqueType(typ)
for i, a in typ.ikids:
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a)
for i in 0..<typ.len:
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), typ[i])
of tyRef:
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
of tySequence:
if optSeqDestructors notin c.p.module.config.globalOptions:
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
elif containsGarbageCollectedRef(typ.elementType):
elif containsGarbageCollectedRef(typ.lastSon):
# destructor based seqs are themselves not traced but their data is, if
# they contain a GC'ed type:
lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
@@ -117,15 +119,17 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
var p = c.p
assert typ.kind == tySequence
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
var i: TLoc
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
var oldCode = p.s(cpsStmts)
freeze oldCode
var a = TLoc(snippet: accessor)
var a: TLoc
a.r = accessor
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.snippet, lenExpr(c.p, a)])
[i.r, lenExpr(c.p, a)])
let oldLen = p.s(cpsStmts).len
genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType)
genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ[0])
if p.s(cpsStmts).len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
@@ -133,6 +137,7 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
lineF(p, cpsStmts, "}$n", [])
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
var c: TTraversalClosure
var p = newProc(nil, m)
result = "Marker_" & getTypeName(m, origTyp, sig)
let
@@ -145,19 +150,18 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
lineF(p, cpsLocals, "$1 a;$n", [t])
lineF(p, cpsInit, "a = ($1)p;$n", [t])
var c = TTraversalClosure(p: p,
visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
)
c.p = p
c.visitorFrmt = "op" # "#nimGCvisit((void*)$1, op);$n"
assert typ.kind != tyTypeDesc
if typ.kind == tySequence:
genTraverseProcSeq(c, "a".rope, typ)
else:
if skipTypes(typ.elementType, typedescInst+{tyOwned}).kind == tyArray:
if skipTypes(typ[0], typedescInst+{tyOwned}).kind == tyArray:
# C's arrays are broken beyond repair:
genTraverseProc(c, "a".rope, typ.elementType)
genTraverseProc(c, "a".rope, typ[0])
else:
genTraverseProc(c, "(*a)".rope, typ.elementType)
genTraverseProc(c, "(*a)".rope, typ[0])
let generatedProc = "$1 {$n$2$3$4}\n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
@@ -173,6 +177,7 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
discard genTypeInfoV1(m, s.loc.t, info)
var c: TTraversalClosure
var p = newProc(nil, m)
var sLoc = rdLoc(s.loc)
result = getTempName(m)
@@ -181,10 +186,8 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
accessThreadLocalVar(p, s)
sLoc = "NimTV_->" & sLoc
var c = TTraversalClosure(p: p,
visitorFrmt: "0" # "#nimGCvisit((void*)$1, 0);$n"
)
c.visitorFrmt = "0" # "#nimGCvisit((void*)$1, 0);$n"
c.p = p
let header = "static N_NIMCALL(void, $1)(void)" % [result]
genTraverseProc(c, sLoc, s.loc.t)

File diff suppressed because it is too large Load Diff

View File

@@ -10,8 +10,8 @@
# This module declares some helpers for the C code generator.
import
ast, types, msgs, wordrecg,
platform, trees, options, cgendata, mangleutils, renderer
ast, types, hashes, strutils, msgs, wordrecg,
platform, trees, options, cgendata
import std/[hashes, strutils, formatfloat]
@@ -21,16 +21,13 @@ when defined(nimPreviewSlimSystem):
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind
of nkStmtList:
result = nil
for i in 0..<n.len:
result = getPragmaStmt(n[i], w)
if result != nil: break
of nkPragma:
result = nil
for i in 0..<n.len:
if whichPragma(n[i]) == w: return n[i]
else:
result = nil
else: discard
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
result = getPragmaStmt(n, w) != nil
@@ -68,6 +65,53 @@ proc makeSingleLineCString*(s: string): string =
c.toCChar(result)
result.add('\"')
proc mangle*(name: string): string =
result = newStringOfCap(name.len)
var start = 0
if name[0] in Digits:
result.add("X" & name[0])
start = 1
var requiresUnderscore = false
template special(x) =
result.add x
requiresUnderscore = true
for i in start..<name.len:
let c = name[i]
case c
of 'a'..'z', '0'..'9', 'A'..'Z':
result.add(c)
of '_':
# we generate names like 'foo_9' for scope disambiguations and so
# disallow this here:
if i > 0 and i < name.len-1 and name[i+1] in Digits:
discard
else:
result.add(c)
of '$': special "dollar"
of '%': special "percent"
of '&': special "amp"
of '^': special "roof"
of '!': special "emark"
of '?': special "qmark"
of '*': special "star"
of '+': special "plus"
of '-': special "minus"
of '/': special "slash"
of '\\': special "backslash"
of '=': special "eq"
of '<': special "lt"
of '>': special "gt"
of '~': special "tilde"
of ':': special "colon"
of '.': special "dot"
of '@': special "at"
of '|': special "bar"
else:
result.add("X" & toHex(ord(c), 2))
requiresUnderscore = true
if requiresUnderscore:
result.add "_"
proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
case int(getSize(conf, typ))
of 1: result = ctInt8
@@ -120,14 +164,16 @@ proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
result.add "_u"
result.add $s.itemId.item
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string =
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
#Module::Type
var name = s.name.s & extra
var name = s.name.s
if makeUnique:
name = makeUnique(m, s, name)
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
proc elementType*(n: PType): PType {.inline.} = n.sons[^1]
proc encodeType*(m: BModule; t: PType): string =
result = ""
var kindName = ($t.kind)[2..^1]
kindName[0] = toLower($kindName[0])[0]
@@ -138,10 +184,10 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
result = encodeName(t[0].sym.name.s)
result.add "I"
for i in 1..<t.len - 1:
result.add encodeType(m, t[i], staticLists)
result.add encodeType(m, t[i])
result.add "E"
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
tyPtr, tyRef, tyVar, tyLent, tySink, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
result =
case t.kind:
of tySequence: encodeName("seq")
@@ -150,13 +196,8 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
for i in 0..<t.len:
let s = t[i]
if s.isNil: continue
result.add encodeType(m, s, staticLists)
result.add encodeType(m, s)
result.add "E"
of tyStatic:
if t.n != nil:
staticLists.add "_s" & renderTree(t.n)
else:
raiseAssert "unreachable"
of tyRange:
var val = "range_"
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
@@ -169,7 +210,7 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
result = encodeName(kindName)
of tyAlias, tyInferred, tyOwned:
result = encodeType(m, t.elementType, staticLists)
result = encodeType(m, t.elementType)
else:
assert false, "encodeType " & $t.kind

View File

@@ -10,15 +10,12 @@
## This module implements the C code generator.
import
ast, astalgo, trees, platform, magicsys, extccomp, options,
ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
nversion, nimsets, msgs, bitsets, idents, types,
ccgutils, ropes, wordrecg, treetab, cgmeth,
ccgutils, os, ropes, math, wordrecg, treetab, cgmeth,
rodutils, renderer, cgendata, aliases,
lowerings, lineinfos, pathutils, transf,
injectdestructors, astmsgs, modulepaths, pushpoppragmas,
mangleutils
from expanddefaults import caseObjDefaultBranch
lowerings, tables, sets, ndi, lineinfos, pathutils, transf,
injectdestructors, astmsgs, modulepaths, backendpragmas
import pipelineutils
@@ -28,10 +25,10 @@ when defined(nimPreviewSlimSystem):
when not defined(leanCompiler):
import spawn, semparallel
import std/strutils except `%`, addf # collides with ropes.`%`
import strutils except `%`, addf # collides with ropes.`%`
from ic / ic import ModuleBackendFlag
import std/[dynlib, math, tables, sets, os, intsets, hashes]
import dynlib
const
# we use some ASCII control characters to insert directives that will be converted to real code in a postprocessing pass
@@ -70,9 +67,12 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
var ms = getModule(s)
result = m.g.modules[ms.position]
proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc =
result = TLoc(k: k, storage: s, lode: lode,
snippet: "", flags: flags)
proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}) =
result.k = k
result.storage = s
result.lode = lode
result.r = ""
result.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
@@ -80,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.snippet == "": a.snippet = r
if a.r == "": a.r = r
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} =
# fills the loc if it is not already initialized
@@ -97,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)
@@ -127,7 +127,7 @@ proc getModuleDllPath(m: BModule, module: int): Rope =
proc getModuleDllPath(m: BModule, s: PSym): Rope =
result = getModuleDllPath(m.g.modules[s.itemId.module])
import std/macros
import macros
proc cgFormatValue(result: var string; value: string) =
result.add value
@@ -301,8 +301,6 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool =
p.lastLineInfo.line = info.line
p.lastLineInfo.fileIndex = info.fileIndex
result = true
else:
result = false
proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) =
if optLineDir in conf.options:
@@ -311,14 +309,10 @@ proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) =
genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex)
proc genLineDir(p: BProc, t: PNode) =
if p == p.module.preInitProc: return
let line = t.info.safeLineNm
if optEmbedOrigSrc in p.config.globalOptions:
var code = sourceLine(p.config, t.info)
if code.endsWith('\\'):
code.add "#"
p.s(cpsStmts).add("// " & code & "\L")
p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
let lastFileIndex = p.lastLineInfo.fileIndex
let freshLine = freshLineInfo(p, t.info)
if freshLine:
@@ -343,15 +337,15 @@ proc getTempName(m: BModule): Rope =
proc rdLoc(a: TLoc): Rope =
# 'read' location (deref if indirect)
if lfIndirect in a.flags:
result = "(*" & a.snippet & ")"
result = "(*" & a.r & ")"
else:
result = a.snippet
result = a.r
proc addRdLoc(a: TLoc; result: var Rope) =
if lfIndirect in a.flags:
result.add "(*" & a.snippet & ")"
result.add "(*" & a.r & ")"
else:
result.add a.snippet
result.add a.r
proc lenField(p: BProc): Rope {.inline.} =
result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
@@ -374,9 +368,6 @@ proc dataField(p: BProc): Rope =
else:
result = rope"->data"
proc genProcPrototype(m: BModule, sym: PSym)
include cbuilder
include ccgliterals
include ccgtypes
@@ -389,22 +380,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.snippet & ")"
result.add "(&" & a.r & ")"
else:
result.add a.snippet
result.add a.r
proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
result = "(&" & a.snippet & ")"
result = "(&" & a.r & ")"
else:
result = a.snippet
result = a.r
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.snippet & ")"
result = "(&" & a.r & ")"
else:
result = a.snippet
result = a.r
proc rdCharLoc(a: TLoc): Rope =
# read a location that may need a char-cast:
@@ -417,7 +408,6 @@ type
needToCopy
needToCopySinkParam
needTempForOpenArray
needAssignCall
TAssignmentFlags = set[TAssignmentFlag]
proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
@@ -449,7 +439,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc,
linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)])
of frEmbedded:
if optTinyRtti in p.config.globalOptions:
var tmp: TLoc = default(TLoc)
var tmp: TLoc
if mode == constructRefObj:
let objType = t.skipTypes(abstractInst+{tyRef})
rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp)
@@ -486,12 +476,9 @@ 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):
var didGenTemp = false
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(loc), genCppInitializer(p.module, p, typ, didGenTemp)])
return
if isImportedCppType(typ): return
if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
assert loc.snippet != ""
assert loc.r != ""
let atyp = skipTypes(loc.t, abstractInst)
if atyp.kind in {tyVar, tyLent}:
@@ -500,8 +487,9 @@ proc resetLoc(p: BProc, loc: var TLoc) =
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
elif not isComplexValueType(typ):
if containsGcRef:
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
nilLoc.snippet = rope("NIM_NIL")
var nilLoc: TLoc
initLoc(nilLoc, locTemp, loc.lode, OnStack)
nilLoc.r = rope("NIM_NIL")
genRefAssign(p, loc, nilLoc)
else:
linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)])
@@ -517,17 +505,9 @@ proc resetLoc(p: BProc, loc: var TLoc) =
else:
# array passed as argument decayed into pointer, bug #7332
# so we use getTypeDesc here rather than rdLoc(loc)
let tyDesc = getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))
if p.module.compileToCpp and isOrHasImportedCppType(typ):
if lfIndirect in loc.flags:
#C++ cant be just zeroed. We need to call the ctors
var tmp = getTemp(p, loc.t)
linefmt(p, cpsStmts,"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
[addrLoc(p.config, loc), addrLoc(p.config, tmp), tyDesc])
else:
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
[addrLoc(p.config, loc), tyDesc])
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
[addrLoc(p.config, loc),
getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))])
# XXX: We can be extra clever here and call memset only
# on the bytes following the m_type field?
genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
@@ -538,14 +518,15 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
elif not isComplexValueType(typ):
if containsGarbageCollectedRef(loc.t):
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
nilLoc.snippet = rope("NIM_NIL")
var nilLoc: TLoc
initLoc(nilLoc, locTemp, loc.lode, OnStack)
nilLoc.r = rope("NIM_NIL")
genRefAssign(p, loc, nilLoc)
else:
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))])
else:
if (not isTemp or containsGarbageCollectedRef(loc.t)) and not hasNoInit(loc.t):
if not isTemp or containsGarbageCollectedRef(loc.t):
# don't use nimZeroMem for temporary values for performance if we can
# avoid it:
if not isOrHasImportedCppType(typ):
@@ -565,16 +546,17 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
if not immediateAsgn:
constructLoc(p, v.loc)
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
inc(p.labels)
result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
storage: OnStack, flags: {})
result.r = "T" & rope(p.labels) & "_"
if p.module.compileToCpp and isOrHasImportedCppType(t):
var didGenTemp = false
linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.snippet,
genCppInitializer(p.module, p, t, didGenTemp)])
linefmt(p, cpsLocals, "$1 $2{};$n", [getTypeDesc(p.module, t, dkVar), result.r])
else:
linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.snippet])
linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r])
result.k = locTemp
result.lode = lodeTyp t
result.storage = OnStack
result.flags = {}
constructLoc(p, result, not needsInit)
when false:
# XXX Introduce a compiler switch in order to detect these easily.
@@ -585,21 +567,25 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo
writeStackTrace()
proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc =
proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) =
inc(p.labels)
result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
storage: OnStack, flags: {})
linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.snippet, value])
result.r = "T" & rope(p.labels) & "_"
linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, dkVar), result.r, value])
result.k = locTemp
result.lode = lodeTyp t
result.storage = OnStack
result.flags = {}
proc getIntTemp(p: BProc): TLoc =
proc getIntTemp(p: BProc, result: var TLoc) =
inc(p.labels)
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.snippet])
result.r = "T" & rope(p.labels) & "_"
linefmt(p, cpsLocals, "NI $1;$n", [result.r])
result.k = locTemp
result.storage = OnStack
result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt)
result.flags = {}
proc localVarDecl(p: BProc; n: PNode): Rope =
result = ""
let s = n.sym
if s.loc.k == locNone:
fillLocalName(p, s)
@@ -618,20 +604,16 @@ 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.snippet)
result.add(s.loc.r)
else:
result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet])
result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
proc assignLocalVar(p: BProc, n: PNode) =
#assert(s.loc.k == locNone) # not yet assigned
# this need not be fulfilled for inline procs; they are regenerated
# for each module that uses them!
let nl = if optLineDir in p.config.options: "" else: "\n"
var decl = localVarDecl(p, n)
if p.module.compileToCpp and isOrHasImportedCppType(n.typ):
var didGenTemp = false
decl.add genCppInitializer(p.module, p, n.typ, didGenTemp)
decl.add ";" & nl
let decl = localVarDecl(p, n) & (if p.module.compileToCpp and isOrHasImportedCppType(n.typ): "{};" else: ";") & nl
line(p, cpsLocals, decl)
include ccgthreadvars
@@ -661,11 +643,22 @@ 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.snippet, value])
decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
else:
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.snippet])
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool)
proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope)
proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) =
let s = vn.sym
fillBackendName(p.module, s)
fillLoc(s.loc, locGlobalVar, vn, OnHeap)
var decl: Rope
let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
genGlobalVarDecl(p, vn, td, "", decl)
decl.add " " & $s.loc.r
genCppVarForCtor(p, v, vn, value, decl)
p.module.s[cfsVars].add decl
proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
let s = n.sym
@@ -679,7 +672,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.snippet = mangleDynLibProc(s)
s.loc.r = mangleDynLibProc(s)
if value != "":
internalError(p.config, n.info, ".dynlib variables cannot have a value")
return
@@ -710,31 +703,19 @@ 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.snippet])
decl.addf(" $1;$n", [s.loc.r])
else:
decl.addf(" $1 = $2;$n", [s.loc.snippet, value])
decl.addf(" $1 = $2;$n", [s.loc.r, value])
else:
decl.addf(" $1;$n", [s.loc.snippet])
decl.addf(" $1;$n", [s.loc.r])
p.module.s[cfsVars].add(decl)
if p.withinLoop > 0 and value == "":
# fixes tests/run/tzeroarray:
resetLoc(p, s.loc)
proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var bool) =
let s = vn.sym
fillBackendName(p.module, s)
fillLoc(s.loc, locGlobalVar, vn, OnHeap)
var decl: Rope = ""
let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
genGlobalVarDecl(p, vn, td, "", decl)
decl.add " " & $s.loc.snippet
genCppVarForCtor(p, value, decl, didGenTemp)
if didGenTemp: return # generated in the caller
p.module.s[cfsVars].add decl
proc assignParam(p: BProc, s: PSym, retType: PType) =
assert(s.loc.snippet != "")
assert(s.loc.r != "")
scopeMangledParam(p, s)
proc fillProcLoc(m: BModule; n: PNode) =
@@ -754,20 +735,19 @@ proc genVarPrototype(m: BModule, n: PNode)
proc requestConstImpl(p: BProc, sym: PSym)
proc genStmts(p: BProc, t: PNode)
proc expr(p: BProc, n: PNode, d: var TLoc)
proc genProcPrototype(m: BModule, sym: PSym)
proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
proc intLiteral(i: BiggestInt; result: var Rope)
proc genLiteral(p: BProc, n: PNode; result: var Rope)
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int)
proc raiseExit(p: BProc)
proc raiseExitCleanup(p: BProc, destroy: string)
proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc =
result = initLoc(locNone, e, OnUnknown, flags)
proc initLocExpr(p: BProc, e: PNode, result: var TLoc, flags: TLocFlags = {}) =
initLoc(result, locNone, e, OnUnknown, flags)
expr(p, e, result)
proc initLocExprSingleUse(p: BProc, e: PNode): TLoc =
result = initLoc(locNone, e, OnUnknown)
proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
initLoc(result, locNone, e, OnUnknown)
if e.kind in nkCallKinds and (e[0].kind != nkSym or e[0].sym.magic == mNone):
# We cannot check for tfNoSideEffect here because of mutable parameters.
discard "bug #8202; enforce evaluation order for nested calls for C++ too"
@@ -787,11 +767,15 @@ $1define nimfr_(proc, file) \
TFrame FR_; \
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_);
$1define nimln_(n) \
FR_.line = n;
$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 nimlf_(n, file) \
FR_.line = n; FR_.filename = file;
$1define nimln_(n) \
FR_.line = n;
$1define nimlf_(n, file) \
FR_.line = n; FR_.filename = file;
"""
if p.module.s[cfsFrameDefines].len == 0:
@@ -854,8 +838,9 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
var p = newProc(nil, m)
p.options.excl optStackTrace
p.flags.incl nimErrorFlagDisabled
var dest: TLoc = initLoc(locTemp, lib.path, OnStack)
dest.snippet = getTempName(m)
var dest: TLoc
initLoc(dest, locTemp, lib.path, OnStack)
dest.r = getTempName(m)
appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
[getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)])
expr(p, lib.path, dest)
@@ -873,7 +858,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.snippet is the external name!
# NOTE: sym.loc.r is the external name!
result = rope(sym.name.s)
else:
result = rope(strutils.`%`("Dl_$1_", $sym.id))
@@ -881,18 +866,19 @@ proc mangleDynLibProc(sym: PSym): Rope =
proc symInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex
let isCall = isGetProcAddr(lib)
var extname = sym.loc.snippet
var extname = sym.loc.r
if not isCall: loadDynamicLib(m, lib)
var tmp = mangleDynLibProc(sym)
sym.loc.snippet = tmp # from now on we only need the internal name
sym.loc.r = tmp # from now on we only need the internal name
sym.typ.sym = nil # generate a new name
inc(m.labels, 2)
if isCall:
let n = lib.path
var a: TLoc = initLocExpr(m.initProc, n[0])
var a: TLoc
initLocExpr(m.initProc, n[0], a)
var params = rdLoc(a) & "("
for i in 1..<n.len-1:
a = initLocExpr(m.initProc, n[i])
initLocExpr(m.initProc, n[i], a)
params.add(rdLoc(a))
params.add(", ")
let load = "\t$1 = ($2) ($3$4));$n" %
@@ -911,24 +897,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.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
proc varInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex
var extname = sym.loc.snippet
var extname = sym.loc.r
loadDynamicLib(m, lib)
incl(sym.loc.flags, lfIndirect)
var tmp = mangleDynLibProc(sym)
sym.loc.snippet = tmp # from now on we only need the internal name
sym.loc.r = 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.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
[sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
proc symInDynamicLibPartial(m: BModule, sym: PSym) =
sym.loc.snippet = mangleDynLibProc(sym)
sym.loc.r = mangleDynLibProc(sym)
sym.typ.sym = nil # generate a new name
proc cgsymImpl(m: BModule; sym: PSym) {.inline.} =
@@ -951,7 +937,7 @@ proc cgsymValue(m: BModule, name: string): Rope =
cgsymImpl m, sym
else:
rawMessage(m.config, errGenerated, "system module needs: " & name)
result = sym.loc.snippet
result = sym.loc.r
if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}:
result.addActualSuffixForHCR(m.module, sym)
@@ -1028,7 +1014,6 @@ proc containsResult(n: PNode): bool =
if containsResult(n[i]): return true
proc easyResultAsgn(n: PNode): PNode =
result = nil
case n.kind
of nkStmtList, nkStmtListExpr:
var i = 0
@@ -1153,14 +1138,7 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
else:
allPathsInBranch(n[i].lastSon)
of nkCallKinds:
if canRaiseDisp(p, n[0]) or
(n[0].kind == nkSym and sfNoReturn in n[0].sym.flags):
# requires initializations when encountering unreachable code
result = InitRequired
elif n[0].kind == nkSym and
n[0].sym.magic in {mUnaryMinusI..mAbsI, mAddI..mPred} and
optOverflowCheck in p.config.options:
# arithmetic operations may raise exceptions
if canRaiseDisp(p, n[0]):
result = InitRequired
else:
for i in 0..<n.safeLen:
@@ -1178,7 +1156,7 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
proc getProcTypeCast(m: BModule, prc: PSym): Rope =
result = getTypeDesc(m, prc.loc.t)
if prc.typ.callConv == ccClosure:
var rettype, params: Rope = ""
var rettype, params: Rope
var check = initIntSet()
genProcParams(m, prc.typ, rettype, params, check)
result = "$1(*)$2" % [rettype, params]
@@ -1196,22 +1174,21 @@ proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
proc genProcAux*(m: BModule, prc: PSym) =
var p = newProc(prc, m)
var header = newRopeAppender()
let isCppMember = m.config.backend == backendCpp and sfCppMember * prc.flags != {}
if isCppMember:
if m.config.backend == backendCpp and {sfVirtual, sfConstructor} * prc.flags != {}:
genMemberProcHeader(m, prc, header)
else:
genProcHeader(m, prc, header)
var returnStmt: Rope = ""
assert(prc.ast != nil)
var procBody = transformBody(m.g.graph, m.idgen, prc, {})
var procBody = transformBody(m.g.graph, m.idgen, prc, dontUseCache)
if sfInjectDestructors in prc.flags:
procBody = injectDestructorCalls(m.g.graph, m.idgen, prc, procBody)
let tmpInfo = prc.info
discard freshLineInfo(p, prc.info)
if sfPure notin prc.flags and prc.typ.returnType != nil:
if sfPure notin prc.flags and prc.typ[0] != nil:
if resultPos >= prc.ast.len:
internalError(m.config, prc.info, "proc has no result symbol")
let resNode = prc.ast[resultPos]
@@ -1220,12 +1197,13 @@ proc genProcAux*(m: BModule, prc: PSym) =
if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
var decl = localVarDecl(p, resNode)
var a: TLoc = initLocExprSingleUse(p, val)
var a: TLoc
initLocExprSingleUse(p, val, a)
linefmt(p, cpsStmts, "$1 = $2;$n", [decl, rdLoc(a)])
else:
# declare the result symbol:
assignLocalVar(p, resNode)
assert(res.loc.snippet != "")
assert(res.loc.r != "")
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
@@ -1235,12 +1213,10 @@ proc genProcAux*(m: BModule, prc: PSym) =
initLocalVar(p, res, immediateAsgn=false)
returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
elif sfConstructor in prc.flags:
resNode.sym.loc.flags.incl lfIndirect
fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
prc.loc.snippet = getTypeDesc(m, resNode.sym.loc.t, dkVar)
else:
fillResult(p.config, resNode, prc.typ)
assignParam(p, res, prc.typ.returnType)
assignParam(p, res, prc.typ[0])
# We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)'
# to 'unsureAsgn(result, x)'
# Sketch why this is correct: If 'result' points to a stack location
@@ -1258,19 +1234,19 @@ proc genProcAux*(m: BModule, prc: PSym) =
for i in 1..<prc.typ.n.len:
let param = prc.typ.n[i].sym
if param.typ.isCompileTimeOnly: continue
assignParam(p, param, prc.typ.returnType)
assignParam(p, param, prc.typ[0])
closureSetup(p, prc)
genProcBody(p, procBody)
prc.info = tmpInfo
var generatedProc: Rope = ""
var generatedProc: Rope
generatedProc.genCLineDir prc.info, m.config
if isNoReturn(p.module, prc):
if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
header = "__declspec(noreturn) " & header
if sfPure in prc.flags:
if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
header = "__declspec(naked) " & header
generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N",
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
@@ -1302,7 +1278,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.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
[prc.loc.r, prc.loc.r & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
result = (sfCompileToCpp in m.module.flags and
@@ -1315,7 +1291,7 @@ proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
proc genProcPrototype(m: BModule, sym: PSym) =
useHeader(m, sym)
if lfNoDecl in sym.loc.flags or sfCppMember * sym.flags != {}: return
if lfNoDecl in sym.loc.flags or {sfVirtual, sfConstructor} * sym.flags != {}: return
if lfDynamicLib in sym.loc.flags:
if sym.itemId.module != m.module.position and
not containsOrIncl(m.declaredThings, sym.id):
@@ -1362,7 +1338,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.snippet, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
[prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
else:
symInDynamicLibPartial(m, prc)
elif prc.typ.callConv == ccInline:
@@ -1379,8 +1355,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.snippet = nil
# prc.loc.snippet = mangleName(m, prc)
# prc.loc.r = nil
# prc.loc.r = mangleName(m, prc)
genProcPrototype(m, prc)
genProcAux(m, prc)
elif sfImportc notin prc.flags:
@@ -1392,7 +1368,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.snippet, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
[prc.loc.r, 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
@@ -1445,7 +1421,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.snippet != "")
assert(sym.loc.r != "")
incl(m.declaredThings, sym.id)
if sfThread in sym.flags:
declareThreadVar(m, sym, true)
@@ -1459,9 +1435,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.snippet])
m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)])
proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
@@ -1494,19 +1470,17 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope =
## Returns a mangled module name.
result = mangleModuleName(conf, filename).mangle
result.add 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 = mangleModuleName(m.g.config, m.filename).mangle
result.add mangleModuleName(m.g.config, m.filename).mangle
proc getSomeInitName(m: BModule, suffix: string): Rope =
if not m.hcrOn:
result = getSomeNameForModule(m)
else:
result = ""
result.add suffix
proc getInitName(m: BModule): Rope =
@@ -1525,10 +1499,10 @@ proc genMainProc(m: BModule) =
## this function is called in cgenWriteModules after all modules are closed,
## it means raising dependency on the symbols is too late as it will not propagate
## into other modules, only simple rope manipulations are allowed
var preMainCode: Rope = ""
var preMainCode: Rope
if m.hcrOn:
proc loadLib(handle: string, name: string): Rope =
result = ""
let prc = magicsys.getCompilerProc(m.g.graph, name)
assert prc != nil
let n = newStrNode(nkStrLit, prc.annex.path.strVal)
@@ -1552,7 +1526,7 @@ proc genMainProc(m: BModule) =
else:
preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix])
var posixCmdLine: Rope = ""
var posixCmdLine: Rope
if optNoMain notin m.config.globalOptions:
posixCmdLine.add "N_LIB_PRIVATE int cmdCount;\L"
posixCmdLine.add "N_LIB_PRIVATE char** cmdLine;\L"
@@ -1858,11 +1832,11 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R
var extname = prefix & sym
var tmp = mangleDynLibProc(prc)
prc.loc.snippet = tmp
prc.loc.r = tmp
prc.typ.sym = nil
if not containsOrIncl(m.declaredThings, prc.id):
m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.snippet, getTypeDesc(m, prc.loc.t, dkVar)])
m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, 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)]
@@ -1947,6 +1921,9 @@ 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", [])
@@ -2086,7 +2063,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
result.preInitProc = newProc(nil, result)
result.preInitProc.flags.incl nimErrorFlagDisabled
result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated
result.dataCache = initNodeTable()
initNodeTable(result.dataCache)
result.typeStack = @[]
result.typeNodesName = getTempName(result)
result.nimTypesName = getTempName(result)
@@ -2095,6 +2072,9 @@ 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))
@@ -2178,23 +2158,6 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
genStmts(p, n)
proc handleProcGlobals(m: BModule) =
var procGlobals: seq[PNode] = move m.g.graph.procGlobals
for i in 0..<procGlobals.len:
var stmts = ""
# fixes recursive calls #24997
swap stmts, m.preInitProc.s(cpsStmts)
var transformedN = procGlobals[i]
if sfInjectDestructors in m.module.flags:
transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN)
genStmts(m.preInitProc, transformedN)
swap stmts, m.preInitProc.s(cpsStmts)
handleProcGlobals(m)
m.preInitProc.s(cpsStmts).add stmts
proc genTopLevelStmt*(m: BModule; n: PNode) =
## Also called from `ic/cbackend.nim`.
if pipelineutils.skipCodegen(m.config, n): return
@@ -2210,8 +2173,6 @@ proc genTopLevelStmt*(m: BModule; n: PNode) =
else:
genProcBody(m.initProc, transformedN)
handleProcGlobals(m)
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
if optForceFullMake notin m.config.globalOptions:
if not moduleHasChanged(m.g.graph, m.module):
@@ -2243,6 +2204,7 @@ 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)
@@ -2260,10 +2222,12 @@ 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)
@@ -2274,24 +2238,7 @@ proc updateCachedModule(m: BModule) =
cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf)
proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym =
let prefixedName = m.config.nimMainPrefix & "NimDestroyGlobals"
let procname = getIdent(graph.cache, prefixedName)
result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info)
result.typ = newProcType(m.module.info, m.idgen, m.module.owner)
result.typ.callConv = ccCDecl
incl result.flags, sfExportc
result.loc.snippet = prefixedName
if isDynlib:
incl(result.loc.flags, lfExportLib)
let theProc = newNodeI(nkProcDef, m.module.info, bodyPos+1)
for i in 0..<theProc.len: theProc[i] = newNodeI(nkEmpty, m.module.info)
theProc[namePos] = newSymNode(result)
theProc[bodyPos] = body
result.ast = theProc
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode): PNode =
## Also called from IC.
if sfMainModule in m.module.flags:
# phase ordering problem here: We need to announce this
@@ -2302,13 +2249,6 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}:
for i in countdown(high(graph.globalDestructors), 0):
n.add graph.globalDestructors[i]
else:
var body = newNodeI(nkStmtList, m.module.info)
for i in countdown(high(graph.globalDestructors), 0):
body.add graph.globalDestructors[i]
body.flags.incl nfTransf # should not be further transformed
let dtor = generateLibraryDestroyGlobals(graph, m, body, optGenDynLib in m.config.globalOptions)
genProcAux(m, dtor)
if pipelineutils.skipCodegen(m.config, n): return
if moduleHasChanged(graph, m.module):
# if the module is cached, we don't regenerate the main proc
@@ -2347,11 +2287,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
if m.g.forwardedProcs.len == 0:
incl m.flags, objHasKidsValid
if optMultiMethods in m.g.config.globalOptions or
m.g.config.selectedGC notin {gcArc, gcOrc, gcAtomicArc} or
vtables notin m.g.config.features:
generateIfMethodDispatchers(graph, m.idgen)
result = generateMethodDispatchers(graph, m.idgen)
let mm = m
m.g.modulesClosed.add mm

View File

@@ -10,10 +10,8 @@
## This module contains the data structures for the C code generation phase.
import
ast, ropes, options,
lineinfos, pathutils, modulegraphs
import std/[intsets, tables, sets]
ast, ropes, options, intsets,
tables, ndi, lineinfos, pathutils, modulegraphs, sets
type
TLabel* = Rope # for the C generator a label is just a rope
@@ -75,13 +73,10 @@ type
flags*: set[TCProcFlag]
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
currLineInfo*: TLineInfo # AST codegen will make this superfluous
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, isHidden: bool, label: Natural]]
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
# in how many nested try statements we are
# (the vars must be volatile then)
# `inExcept` is true when we are in the except part of a try block.
# `isHidden` is true for compiler-injected `nkHiddenTryStmt` wrappers
# (e.g. ARC's destructor try/finally around `except T as e:` bodies);
# finallyActions walks past such wrappers to reach the user's try.
# bool is true when are in the except part of a try block
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
# using return in finally statements
labels*: Natural # for generating unique labels in the C proc
@@ -91,7 +86,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, TNoteKinds)]
optionsStack*: seq[TOptions]
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
@@ -175,6 +170,7 @@ 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

@@ -10,15 +10,12 @@
## This module implements code generation for methods.
import
options, ast, msgs, idents, renderer, types, magicsys,
sempass2, modulegraphs, lineinfos, astalgo
import std/intsets
intsets, options, ast, msgs, idents, renderer, types, magicsys,
sempass2, modulegraphs, lineinfos
when defined(nimPreviewSlimSystem):
import std/assertions
import std/[tables]
proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
var dest = skipTypes(d, abstractPtrs)
@@ -47,15 +44,13 @@ proc getDispatcher*(s: PSym): PSym =
if dispatcherPos < s.ast.len:
result = s.ast[dispatcherPos].sym
doAssert sfDispatcher in result.flags
else:
result = nil
proc methodCall*(n: PNode; conf: ConfigRef): PNode =
result = n
# 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:
@@ -67,25 +62,22 @@ type
MethodResult = enum No, Invalid, Yes
proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
result = No
if a.name.id != b.name.id: return
if a.typ.signatureLen != b.typ.signatureLen:
if a.typ.len != b.typ.len:
return
var i = 0
for x, y in paramTypePairs(a.typ, b.typ):
inc i
var aa = x
var bb = y
for i in 1..<a.typ.len:
var aa = a.typ[i]
var bb = b.typ[i]
while true:
aa = skipTypes(aa, {tyGenericInst, tyAlias})
bb = skipTypes(bb, {tyGenericInst, tyAlias})
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
aa = aa.elementType
bb = bb.elementType
aa = aa.lastSon
bb = bb.lastSon
else:
break
if sameType(x, y):
if sameType(a.typ[i], b.typ[i]):
if aa.kind == tyObject and result != Invalid:
result = Yes
elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
@@ -104,10 +96,10 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
if result == Yes:
# check for return type:
# ignore flags of return types; # bug #22673
if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}):
if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped:
if not sameTypeOrNil(a.typ[0], b.typ[0], {IgnoreFlags}):
if b.typ[0] != nil and b.typ[0].kind == tyUntyped:
# infer 'auto' from the base to make it consistent:
b.typ.setReturnType a.typ.returnType
b.typ[0] = a.typ[0]
else:
return No
@@ -126,15 +118,15 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
incl(disp.flags, sfDispatcher)
excl(disp.flags, sfExported)
let old = disp.typ
disp.typ = copyType(disp.typ, idgen, disp.typ.owner)
disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner)
copyTypeProps(g, idgen.module, disp.typ, old)
# we can't inline the dispatcher itself (for now):
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
disp.ast = copyTree(s.ast)
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
disp.loc.snippet = ""
if s.typ.returnType != nil:
disp.loc.r = ""
if s.typ[0] != nil:
if disp.ast.len > resultPos:
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
else:
@@ -158,15 +150,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
disp.ast[resultPos] = copyTree(meth.ast[resultPos])
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
var witness: PSym = nil
if s.typ.firstParamType.owner.getModule != s.getModule and vtables in g.config.features and not
g.config.isDefined("nimInternalNonVtablesTesting"):
localError(g.config, s.info, errGenerated, "method `" & s.name.s &
"` can be defined only in the same module with its type (" & s.typ.firstParamType.typeToString() & ")")
if sfImportc in s.flags:
localError(g.config, s.info, errGenerated, "method `" & s.name.s &
"` is not allowed to have 'importc' pragmas")
var witness: PSym
for i in 0..<g.methods.len:
let disp = g.methods[i].dispatcher
case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
@@ -185,11 +169,6 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
of Invalid:
if witness.isNil: witness = g.methods[i].methods[0]
# create a new dispatcher:
# stores the id and the position
if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable:
g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1
else:
g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId)
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
#echo "adding ", s.info
if witness != nil:
@@ -198,9 +177,8 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
elif sfBase notin s.flags:
message(g.config, s.info, warnUseBase)
proc relevantCol*(methods: seq[PSym], col: int): bool =
proc relevantCol(methods: seq[PSym], col: int): bool =
# returns true iff the position is relevant
result = false
var t = methods[0].typ[col].skipTypes(skipPtrs)
if t.kind == tyObject:
for i in 1..high(methods):
@@ -209,8 +187,7 @@ proc relevantCol*(methods: seq[PSym], col: int): bool =
return true
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
result = 0
for col in FirstParamAt..<a.typ.signatureLen:
for col in 1..<a.typ.len:
if contains(relevantCols, col):
var aa = skipTypes(a.typ[col], skipPtrs)
var bb = skipTypes(b.typ[col], skipPtrs)
@@ -218,7 +195,7 @@ proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
if (d != high(int)) and d != 0:
return d
proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
# we use shellsort here; fast and simple
var n = a.len
var h = 1
@@ -237,16 +214,16 @@ proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
a[j] = v
if h == 1: break
proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
var base = methods[0].ast[dispatcherPos].sym
result = base
var paramLen = base.typ.signatureLen
var paramLen = base.typ.len
var nilchecks = newNodeI(nkStmtList, base.info)
var disp = newNodeI(nkIfStmt, base.info)
var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
let boolType = getSysType(g, unknownLineInfo, tyBool)
for col in FirstParamAt..<paramLen:
for col in 1..<paramLen:
if contains(relevantCols, col):
let param = base.typ.n[col].sym
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
@@ -255,7 +232,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
for meth in 0..high(methods):
var curr = methods[meth] # generate condition:
var cond: PNode = nil
for col in FirstParamAt..<paramLen:
for col in 1..<paramLen:
if contains(relevantCols, col):
var isn = newNodeIT(nkCall, base.info, boolType)
isn.add newSymNode(iss)
@@ -270,7 +247,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
cond = a
else:
cond = isn
let retTyp = base.typ.returnType
let retTyp = base.typ[0]
let call = newNodeIT(nkCall, base.info, retTyp)
call.add newSymNode(curr)
for col in 1..<paramLen:
@@ -296,13 +273,14 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
nilchecks.flags.incl nfTransf # should not be further transformed
result.ast[bodyPos] = nilchecks
proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
proc generateMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator): PNode =
result = newNode(nkStmtList)
for bucket in 0..<g.methods.len:
var relevantCols = initIntSet()
for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen:
for col in 1..<g.methods[bucket].methods[0].typ.len:
if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
if optMultiMethods notin g.config.globalOptions:
# if multi-methods are not enabled, we are interested only in the first field
break
sortBucket(g.methods[bucket].methods, relevantCols)
g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, idgen)
result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols, idgen))

File diff suppressed because it is too large Load Diff

View File

@@ -11,9 +11,7 @@
import
options, idents, nimconf, extccomp, commands, msgs,
lineinfos, modulegraphs, condsyms, pathutils
import std/[os, parseopt]
lineinfos, modulegraphs, condsyms, os, pathutils, parseopt
proc prependCurDir*(f: AbsoluteFile): AbsoluteFile =
when defined(unix):

View File

@@ -24,12 +24,10 @@ 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, enumutils]
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
import
msgs, options, nversion, condsyms, extccomp, platform,
wordrecg, nimblecmd, lineinfos, pathutils
import std/pathnorm
wordrecg, nimblecmd, lineinfos, pathutils, pathnorm
from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect
@@ -118,7 +116,7 @@ const
errInvalidCmdLineOption = "invalid command line option: '$1'"
errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
errOffHintsError = "'off', 'hint', 'warning', 'error' or 'usages' expected, but '$1' found"
errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found"
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
@@ -186,7 +184,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
info: TLineInfo; orig: string; conf: ConfigRef) =
var id = "" # arg = key or [key] or key:val or [key]:val; with val=on|off
var i = 0
var notes: set[TMsgKind] = {}
var notes: set[TMsgKind]
var isBracket = false
if i < arg.len and arg[i] == '[':
isBracket = true
@@ -203,14 +201,13 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
else: invalidCmdLineOption(conf, pass, orig, info)
let isSomeHint = state in {wHint, wHintAsError}
let isSomeWarning = state in {wWarning, wWarningAsError}
template findNote(noteMin, noteMax, name) =
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
let x = findStr(noteMin, noteMax, id, errUnknown)
if x != errUnknown: notes = {TNoteKind(x)}
else:
if isSomeHint or isSomeWarning:
message(conf, info, warnUnknownNotes, "unknown $#: $#" % [name, id])
if isSomeHint:
message(conf, info, hintUnknownHint, id)
else:
localError(conf, info, "unknown $#: $#" % [name, id])
case id.normalize
@@ -249,7 +246,6 @@ 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)
@@ -271,17 +267,13 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
of "none": result = conf.selectedGC == gcNone
of "stack", "regions": result = conf.selectedGC == gcRegions
of "atomicarc": result = conf.selectedGC == gcAtomicArc
else:
result = false
localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
of "opt":
case arg.normalize
of "speed": result = contains(conf.options, optOptimizeSpeed)
of "size": result = contains(conf.options, optOptimizeSize)
of "none": result = conf.options * {optOptimizeSpeed, optOptimizeSize} == {}
else:
result = false
localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
of "verbosity": result = $conf.verbosity == arg
of "app":
case arg.normalize
@@ -291,9 +283,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
not contains(conf.globalOptions, optGenGuiApp)
of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and
not contains(conf.globalOptions, optGenGuiApp)
else:
result = false
localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
of "dynliboverride":
result = isDynlibOverride(conf, arg)
of "exceptions":
@@ -302,18 +292,8 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
of "setjmp": result = conf.exc == excSetjmp
of "quirky": result = conf.exc == excQuirky
of "goto": result = conf.exc == excGoto
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)
else: localError(conf, info, errInvalidExceptionSystem % arg)
else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
case switch.normalize
@@ -359,15 +339,10 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
result = contains(conf.options, optTrMacros)
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
of "nilseqs", "nilchecks", "taintmode":
warningOptionNoop(switch)
result = false
of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
of "panics": result = contains(conf.globalOptions, optPanics)
of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64)
of "mangle": result = contains(conf.globalOptions, optItaniumMangle)
else:
result = false
invalidCmdLineOption(conf, passCmd1, switch, info)
else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
notRelativeToProj = false): AbsoluteDir =
@@ -409,8 +384,7 @@ proc makeAbsolute(s: string): AbsoluteFile =
proc setTrackingInfo(conf: ConfigRef; dirty, file, line, column: string,
info: TLineInfo) =
## set tracking info, common code for track, trackDirty, & ideTrack
var ln: int = 0
var col: int = 0
var ln, col: int
if parseUtils.parseInt(line, ln) <= 0:
localError(conf, info, errInvalidNumber % line)
if parseUtils.parseInt(column, col) <= 0:
@@ -460,7 +434,7 @@ template handleStdinOrCmdInput =
conf.outDir = getNimcacheDir(conf)
proc handleStdinInput*(conf: ConfigRef) =
conf.projectName = conf.stdinFile.string
conf.projectName = "stdinfile"
conf.projectIsStdin = true
handleStdinOrCmdInput()
@@ -475,7 +449,6 @@ proc parseCommand*(command: string): Command =
of "objc", "compiletooc": cmdCompileToOC
of "js", "compiletojs": cmdCompileToJS
of "r": cmdCrun
of "m": cmdM
of "run": cmdTcc
of "check": cmdCheck
of "e": cmdNimscript
@@ -620,17 +593,10 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
defineSymbol(conf.symbols, "gcregions")
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
proc pathRelativeToConfig(arg: string, pass: TCmdLinePass, conf: ConfigRef): string =
if pass == passPP and not isAbsolute(arg):
assert isAbsolute(conf.currentConfigDir), "something is wrong with currentConfigDir"
result = conf.currentConfigDir / arg
else:
result = arg
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf: ConfigRef) =
var key = ""
var val = ""
var
key, val: string
case switch.normalize
of "eval":
expectArg(conf, switch, arg, pass, info)
@@ -645,7 +611,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
for path in nimbleSubs(conf, arg):
addPath(conf, if pass == passPP: processCfgPath(conf, path, info)
else: processPath(conf, path, info), info)
of "nimblepath":
of "nimblepath", "babelpath":
if switch.normalize == "babelpath": deprecatedAlias(switch, "nimblepath")
if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
expectArg(conf, switch, arg, pass, info)
var path = processPath(conf, arg, info, notRelativeToProj=true)
@@ -655,7 +622,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
nimblePath(conf, path, info)
path = nimbleDir / RelativeDir"pkgs"
nimblePath(conf, path, info)
of "nonimblepath":
of "nonimblepath", "nobabelpath":
if switch.normalize == "nobabelpath": deprecatedAlias(switch, "nonimblepath")
expectNoArg(conf, switch, arg, pass, info)
disableNimblePath(conf)
of "clearnimblepath":
@@ -672,7 +640,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
# refs bug #18674, otherwise `--os:windows` messes up with `--nimcache` set
# in config nims files, e.g. via: `import os; switch("nimcache", "/tmp/somedir")`
if conf.target.targetOS == osWindows and DirSep == '/': arg = arg.replace('\\', '/')
conf.nimcacheDir = processPath(conf, pathRelativeToConfig(arg, pass, conf), info, notRelativeToProj=true)
conf.nimcacheDir = processPath(conf, arg, info, notRelativeToProj=true)
of "out", "o":
expectArg(conf, switch, arg, pass, info)
let f = splitFile(processPath(conf, arg, info, notRelativeToProj=true).string)
@@ -761,14 +729,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf.globalOptions.excl optCDebug
else:
localError(conf, info, "expected native|gdb|on|off but found " & arg)
of "mangle":
case arg.normalize
of "nim":
conf.globalOptions.excl optItaniumMangle
of "cpp":
conf.globalOptions.incl optItaniumMangle
else:
localError(conf, info, "expected nim|cpp but found " & arg)
of "g": # alias for --debugger:native
conf.globalOptions.incl optCDebug
conf.options.incl optLineDir
@@ -784,7 +744,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "hotcodereloading":
processOnOffSwitchG(conf, {optHotCodeReloading}, arg, pass, info)
if conf.hcrOn:
warningDeprecated(conf, info, "hotCodeReloading is deprecated, see https://github.com/nim-lang/RFCs/issues/573 for further information")
defineSymbol(conf.symbols, "hotcodereloading")
defineSymbol(conf.symbols, "useNimRtl")
# hardcoded linking with dynamic runtime for MSVC for smaller binaries
@@ -815,10 +774,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if conf.backend == backendJs or conf.cmd == cmdNimscript: discard
else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
#if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
of "tlsemulation":
processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
if optTlsEmulation in conf.globalOptions:
conf.legacyFeatures.incl emitGenerics
of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
of "implicitstatic":
processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
of "patterns", "trmacros":
@@ -892,19 +848,11 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "import":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
let m = findModule(conf, arg, toFullPath(conf, info)).string
if m.len == 0:
localError(conf, info, "Cannot resolve filename: " & arg)
else:
conf.implicitImports.add(if arg.startsWith(stdPrefix): arg else: m)
conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
of "include":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
let m = findModule(conf, arg, toFullPath(conf, info)).string
if m.len == 0:
localError(conf, info, "Cannot resolve filename: " & arg)
else:
conf.implicitIncludes.add m
conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
of "listcmd":
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
of "asm":
@@ -934,29 +882,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
defineSymbol(conf.symbols, "nodejs")
of "maxloopiterationsvm":
expectArg(conf, switch, arg, pass, info)
var value: int = 10_000_000
discard parseSaturatedNatural(arg, value)
if 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
conf.maxLoopIterationsVM = parseInt(arg)
of "errormax":
expectArg(conf, switch, arg, pass, info)
# Note: `nim check` (etc) can overwrite this.
# `0` is meaningless, give it a useful meaning as in clang's -ferror-limit
# If user doesn't set this flag and the code doesn't either, it'd
# have the same effect as errorMax = 1
var value: int = 0
discard parseSaturatedNatural(arg, value)
conf.errorMax = if value == 0: high(int) else: value
of "stdinfile":
expectArg(conf, switch, arg, pass, info)
conf.stdinFile = if os.isAbsolute(arg): AbsoluteFile(arg)
else: AbsoluteFile(getCurrentDir() / arg)
let ret = parseInt(arg)
conf.errorMax = if ret == 0: high(int) else: ret
of "verbosity":
expectArg(conf, switch, arg, pass, info)
let verbosity = parseInt(arg)
@@ -970,9 +904,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf.mainPackageNotes = conf.notes
of "parallelbuild":
expectArg(conf, switch, arg, pass, info)
var value: int = 0
discard parseSaturatedNatural(arg, value)
conf.numberOfProcessors = value
conf.numberOfProcessors = parseInt(arg)
of "version", "v":
expectNoArg(conf, switch, arg, pass, info)
writeVersionInfo(conf, pass)
@@ -1098,9 +1030,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "shownonexports":
expectNoArg(conf, switch, arg, pass, info)
showNonExportedFields(conf)
of "raw":
expectNoArg(conf, switch, arg, pass, info)
docRawOutput(conf)
of "exceptions":
case arg.normalize
of "cpp": conf.exc = excCpp
@@ -1132,10 +1061,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
defineSymbol(conf.symbols, "nimSeqsV2")
of "stylecheck":
case arg.normalize
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError, optStyleWarning}
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError, optStyleWarning}
of "warning": conf.globalOptions = conf.globalOptions + {optStyleWarning} - {optStyleHint, optStyleError}
of "error": conf.globalOptions = conf.globalOptions + {optStyleError} - {optStyleHint, optStyleWarning}
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError}
of "error": conf.globalOptions = conf.globalOptions + {optStyleError}
of "usages": conf.globalOptions.incl optStyleUsages
else: localError(conf, info, errOffHintsError % arg)
of "showallmismatches":
@@ -1185,8 +1113,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
else: invalidCmdLineOption(conf, pass, switch, info)
proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
var cmd = ""
var arg = ""
var cmd, arg: string
splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
processSwitch(cmd, arg, pass, gCmdLineInfo, config)
@@ -1213,10 +1140,7 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
config.projectName = unixToNativePath(p.key)
config.arguments = cmdLineRest(p)
result = true
elif pass != passCmd2:
setCommandEarly(config, p.key)
result = false
else: result = false
elif pass != passCmd2: setCommandEarly(config, p.key)
else:
if pass == passCmd1: config.commandArgs.add p.key
if argsCount == 1:
@@ -1227,6 +1151,4 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
config.projectName = unixToNativePath(p.key)
config.arguments = cmdLineRest(p)
result = true
else:
result = false
inc argsCount

View File

@@ -11,16 +11,15 @@
## for details. Note this is a first implementation and only the "Concept matching"
## section has been implemented.
import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types,
layeredtable, semtypinst
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets
import std/sets
from magicsys import addSonSkipIntLit
when defined(nimPreviewSlimSystem):
import std/assertions
const
logBindings = when defined(debugConcepts): true else: false
logBindings = false
## Code dealing with Concept declarations
## --------------------------------------
@@ -29,11 +28,23 @@ proc declareSelf(c: PContext; info: TLineInfo) =
## Adds the magical 'Self' symbols to the current scope.
let ow = getCurrOwner(c)
let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info)
s.typ = newType(tyTypeDesc, c.idgen, ow)
s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow)
s.typ.flags.incl {tfUnresolved, tfPacked}
s.typ.add newType(tyEmpty, c.idgen, ow)
s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow)
addDecl(c, s, info)
proc isSelf*(t: PType): bool {.inline.} =
## Is this the magical 'Self' type?
t.kind == tyTypeDesc and tfPacked in t.flags
proc makeTypeDesc*(c: PContext, typ: PType): PType =
if typ.kind == tyTypeDesc and not isSelf(typ):
result = typ
else:
result = newTypeS(tyTypeDesc, c)
incl result.flags, tfCheckedForDestructor
result.addSonSkipIntLit(typ, c.idgen)
proc semConceptDecl(c: PContext; n: PNode): PNode =
## Recursive helper for semantic checking for the concept declaration.
## Currently we only support (possibly empty) lists of statements
@@ -71,419 +82,150 @@ proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
## ----------------
type
MatchFlags* = enum
mfDontBind # Do not export bindings from the concept match
mfBindGenericParam # Export inferred invocation parameters despite mfDontBind
mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand
ConceptTypePair = tuple[conceptId, typeId: ItemId]
## Pair of (concept type id, implementation type id) used for cycle detection
MatchCon = object ## Context we pass around during concept matching.
bindings: LayeredIdTable
marker: HashSet[ConceptTypePair] ## Tracks (concept, type) pairs being checked to detect cycles.
inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
## that turned out to be wrong.
marker: IntSet ## Some protection against wild runaway recursions.
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
## cannot be fixed that easily.
## Thus we special case it here.
concpt: PType ## current concept being evaluated
flags: set[MatchFlags]
MatchKind = enum
mkNoMatch, mkSubset, mkSame
const
asymmetricConceptParamMods = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred} # param modifiers that to not have to match implementation -> concept
bindableTypes = {tyGenericParam, tyOr, tyTypeDesc}
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool
proc existingBinding(m: MatchCon; key: PType): PType =
## checks if we bound the type variable 'key' already to some
## concrete type.
result = m.bindings.lookup(key)
if result == nil:
result = key
for i in 0..<m.inferred.len:
if m.inferred[i][0] == key: return m.inferred[i][1]
return nil
const
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred}
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
proc unrollGenericParam(param: PType): PType =
result = param.skipTypes(ignorableForArgType)
while result.kind in {tyGenericParam, tyTypeDesc} and result.hasElementType and result.elementType.kind != tyNone:
result = result.elementType
proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable .} =
if v.kind == tyTypeDesc:
return false
var value = unrollGenericParam(v)
if value.kind == tyGenericParam:
value = existingBinding(m, value)
if value.kind == tyGenericParam:
if value.hasElementType:
value = value.elementType
else:
return true
if value.kind == tyStatic:
return false
if m.magic in {mArrPut, mArrGet} and value.kind in arrPutGetMagicApplies:
value = value.last
let old = existingBinding(m, key)
if old != key:
# check previously bound value
if not matchType(c, old, value, m):
return false
elif key.hasElementType and not key.elementType.isNil and key.elementType.kind != tyNone:
# check constaint
if matchType(c, unrollGenericParam(key), value, m) == false:
return false
when logBindings: echo "bind table adding '", key, "', ", value
assert value != nil
assert value.kind != tyVoid
m.bindings.put(key, value)
return true
proc defSignatureType(n: PNode): PType = n[0].sym.typ
proc conceptBody*(n: PType): PNode = n.n.lastSon
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 procDefSignature(s: PSym): PNode {. deprecated .} =
var nc = s.ast.copyNode()
for i in 0 .. 5:
nc.add s.ast[i]
nc
proc matchKids(c: PContext; f, a: PType; m: var MatchCon, start=0): bool=
result = true
for i in start ..< f.kidsLen - ord(f.kind in {tyGenericInst, tyGenericInvocation}):
if not matchType(c, f[i], a[i], m): return false
iterator traverseTyOr(t: PType): PType {. closure .}=
for i in t.kids:
case i.kind:
of tyGenericParam:
if i.hasElementType:
for s in traverseTyOr(i.elementType):
yield s
else:
yield i
else:
yield i
proc matchConceptToImpl(c: PContext, f, potentialImpl: PType; m: var MatchCon): bool =
assert not(potentialImpl.reduceToBase.kind == tyConcept)
let concpt = f.reduceToBase
# Handle self-referential concepts: when a concept references itself in its body
# (e.g., `A = concept; proc test(x: Self, y: A)`), the inner type A has n=nil.
# We detect this by checking if the concept has the same symbol name as the
# one we're currently matching and has no body (n=nil).
if concpt.n.isNil:
if concpt.sym != nil and m.concpt.sym != nil and
concpt.sym == m.concpt.sym:
# Self-reference: check if potentialImpl matches what we're already checking
return potentialImpl.id == m.potentialImplementation.id
# Concept without body that's not a self-reference - cannot match
return false
# Cycle detection: track (concept, type) pairs to prevent infinite recursion.
# Returns true on cycle (coinductive semantics) to support co-dependent concepts.
let pair: ConceptTypePair = (concpt.itemId, potentialImpl.itemId)
if pair in m.marker:
return true
m.marker.incl pair
var efPot = potentialImpl
if potentialImpl.isSelf:
if m.concpt.n == concpt.n:
m.marker.excl pair
return true
efPot = m.potentialImplementation
var oldBindings = m.bindings
m.bindings = newTypeMapLayer(m.bindings)
let oldPotentialImplementation = m.potentialImplementation
m.potentialImplementation = efPot
let oldConcept = m.concpt
m.concpt = concpt
var invocation: PType = nil
if f.kind in {tyGenericInvocation, tyGenericInst}:
invocation = f
result = processConcept(c, concpt, invocation, oldBindings, m)
m.potentialImplementation = oldPotentialImplementation
m.concpt = oldConcept
m.bindings = oldBindings
m.marker.excl pair
proc cmpConceptDefs(c: PContext, fn, an: PNode, m: var MatchCon): bool=
if fn.kind != an.kind:
return false
if fn[namePos].sym.name != an[namePos].sym.name:
return false
let
ft = fn.defSignatureType
at = an.defSignatureType
if ft.len != at.len:
return false
for i in 1 ..< ft.n.len:
m.bindings = m.bindings.newTypeMapLayer()
let aType = at.n[i].typ
let fType = ft.n[i].typ
if aType.isSelf and fType.isSelf:
continue
if not matchType(c, fType, aType, m):
m.bindings.setToPreviousLayer()
return false
result = true
if not matchReturnType(c, ft.returnType, at.returnType, m):
m.bindings.setToPreviousLayer()
result = false
proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind =
# XXX: In the future this may need extra parameters to carry info for container types
if fc.n == ac.n:
# This will have to take generic parameters into account at some point
return mkSame
let
fn = fc.conceptBody
an = ac.conceptBody
sameLen = fc.len == ac.len
var match = false
for fdef in fn:
var cmpResult = false
for ia, ndef in an:
match = cmpConceptDefs(c, fdef, ndef, m)
if match:
break
if not match:
return mkNoMatch
return mkSubset
proc isObjectSubtype(f, a: PType): bool =
var t = a
result = false
while t != nil:
t = t.baseClass
if t == nil:
break
t = t.skipTypes({tyPtr,tyRef})
if t == nil:
break
if t.kind != tyObject:
break
if sameObjectTypes(f, t):
result = true
break
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
## The heart of the concept matching process. 'f' is the formal parameter of some
## routine inside the concept that we're looking for. 'a' is the formal parameter
## of a routine that might match.
var
a = ao
f = fo
if a.isSelf:
if m.magic in {mArrPut, mArrGet}:
return false
a = m.potentialImplementation
if a.kind in bindableTypes:
a = existingBinding(m, ao)
if a == ao and a.kind == tyGenericParam and a.hasElementType and a.elementType.kind != tyNone:
a = a.elementType
if f.isConcept:
if a.acceptsAllTypes:
return false
if a.skipTypes(ignorableForArgType).isConcept:
# if f is a subset of a then any match to a will also match f. Not the other way around
return conceptsMatch(c, a.reduceToBase, f.reduceToBase, m) >= mkSubset
else:
return matchConceptToImpl(c, f, a, m)
result = false
const
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
case f.kind
of tyAlias:
result = matchType(c, f.skipModifier, a, m)
result = matchType(c, f.lastSon, a, m)
of tyTypeDesc:
if isSelf(f):
let ua = a.skipTypes(asymmetricConceptParamMods)
if m.magic in {mArrPut, mArrGet}:
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
bindParam(c, m, a, last m.potentialImplementation)
result = true
#elif ua.isConcept:
# result = matchType(c, m.concpt, ua, m)
else:
result = matchType(c, a.skipTypes(ignorableForArgType), m.potentialImplementation, m)
#let oldLen = m.inferred.len
result = matchType(c, a, m.potentialImplementation, m)
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
#m.inferred.setLen oldLen
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
else:
if a.kind == tyTypeDesc:
if not(a.hasElementType) or a.elementType.kind == tyNone:
if a.kind == tyTypeDesc and f.len == a.len:
for i in 0..<a.len:
if not matchType(c, f[i], a[i], m): return false
return true
of tyGenericInvocation:
if a.kind == tyGenericInst and a[0].kind == tyGenericBody:
if sameType(f[0], a[0]) and f.len == a.len-1:
for i in 1 ..< f.len:
if not matchType(c, f[i], a[i], m): return false
return true
of tyGenericParam:
let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
result = false
else:
let old = existingBinding(m, f)
if old == nil:
if f.len > 0 and f[0].kind != tyNone:
# also check the generic's constraints:
let oldLen = m.inferred.len
result = matchType(c, f[0], a, m)
m.inferred.setLen oldLen
if result:
when logBindings: echo "A adding ", f, " ", ak
m.inferred.add((f, ak))
elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}:
when logBindings: echo "B adding ", f, " ", lastSon ak
m.inferred.add((f, lastSon ak))
result = true
elif f.hasElementType:
result = matchType(c, f.elementType, a.elementType, m)
else:
when logBindings: echo "C adding ", f, " ", ak
m.inferred.add((f, ak))
#echo "binding ", typeToString(ak), " to ", typeToString(f)
result = true
elif not m.marker.containsOrIncl(old.id):
result = matchType(c, old, ak, m)
if m.magic == mArrPut and ak.kind == tyGenericParam:
result = true
#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.
if a.kind == f.kind:
result = matchType(c, f.elementType, a.elementType, m)
result = matchType(c, f.sons[0], a.sons[0], m)
elif m.magic == mArrPut:
result = matchType(c, f.elementType, a, m)
of tyEnum, tyObject, tyDistinct:
if a.kind in ignorableForArgType:
result = matchType(c, f, a.skipTypes(ignorableForArgType), m)
result = matchType(c, f.sons[0], a, m)
else:
if a.kind == tyGenericInst:
# tyOr does this to generic typeclasses
result = a.base.sym == f.sym
else:
result = sameType(f, a)
if not result and f.kind == tyObject and a.kind == tyObject:
result = isObjectSubtype(f, a)
result = false
of tyEnum, tyObject, tyDistinct:
result = sameType(f, a)
of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
result = a.skipTypes(ignorableForArgType).kind == f.kind
of tyBool, tyChar, tyInt..tyUInt64:
let ak = a.skipTypes(ignorableForArgType)
result = ak.kind == f.kind or ak.kind == tyOrdinal or
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr:
if f.kind == tyArray and f.kidsLen == 3 and a.kind == tyArray:
# XXX: this is a work-around!
# system.nim creates these for the magic array typeclass
result = true
else:
let ak = a.skipTypes(ignorableForArgType - {f.kind})
if ak.kind == f.kind:
if f.base.kind == tyNone:
result = true
elif f.kidsLen == ak.kidsLen:
result = matchKids(c, f, ak, m)
of tyGenericInvocation, tyGenericInst:
result = false
let ea = a.skipTypes(ignorableForArgType)
if ea.kind in {tyGenericInst, tyGenericInvocation}:
var
k1 = f.kidsLen - ord(f.kind == tyGenericInst)
k2 = ea.kidsLen - ord(ea.kind == tyGenericInst)
if sameType(f.genericHead, ea.genericHead) and k1 == k2:
result = true
for i in 1 ..< k2:
if not matchType(c, f[i], ea[i], m):
result = false
break
elif f.kind == tyGenericInvocation:
# bind potential generic constraints into body
let body = f.base
for i in 1 ..< len(f):
bindParam(c,m,body[i-1], f[i])
result = matchType(c, body, a, m)
else: # tyGenericInst
result = matchType(c, f.last, a, m)
of tyOrdinal:
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
of tyStatic:
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)
of tyGenericParam:
if a.acceptsAllTypes:
discard bindParam(c, m, f, a)
result = f.acceptsAllTypes
else:
result = bindParam(c, m, f, a)
of tyAnything:
result = true
of tyNot:
if a.kind == tyNot:
result = matchType(c, f.elementType, a.elementType, m)
else:
m.bindings = m.bindings.newTypeMapLayer()
result = not matchType(c, f.elementType, a, m)
m.bindings.setToPreviousLayer()
of tyAnd:
m.bindings = m.bindings.newTypeMapLayer()
result = true
for ff in traverseTyOr(f):
let r = matchType(c, ff, a, m)
if not r:
m.bindings.setToPreviousLayer()
result = false
break
of tyGenericBody:
var ak = a
if a.kind == tyGenericBody:
ak = last(a)
result = matchType(c, last(f), ak, m)
of tyCompositeTypeClass:
if a.kind == tyCompositeTypeClass:
result = matchKids(c, f, a, m)
else:
result = matchType(c, last(f), a, m)
of tyBuiltInTypeClass:
let target = f.genericHead.kind
result = a.skipTypes(ignorableForArgType).reduceToBase.kind == target
(ak.kind == tyGenericParam and ak.len > 0 and ak[0].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
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
tyGenericInst:
let ak = a.skipTypes(ignorableForArgType - {f.kind})
if ak.kind == f.kind and f.len == ak.len:
for i in 0..<ak.len:
if not matchType(c, f[i], ak[i], m): return false
return true
of tyOr:
let oldLen = m.inferred.len
if a.kind == tyOr:
# say the concept requires 'int|float|string' if the potentialImplementation
# says 'int|string' that is good enough.
var covered = 0
for ff in traverseTyOr(f):
for aa in traverseTyOr(a):
m.bindings = m.bindings.newTypeMapLayer()
let r = matchType(c, ff, aa, m)
for i in 0..<f.len:
for j in 0..<a.len:
let oldLenB = m.inferred.len
let r = matchType(c, f[i], a[j], m)
if r:
inc covered
break
m.bindings.setToPreviousLayer()
m.inferred.setLen oldLenB
result = covered >= a.kidsLen
result = covered >= a.len
if not result:
m.inferred.setLen oldLen
else:
for ff in f.kids:
m.bindings = m.bindings.newTypeMapLayer()
result = matchType(c, ff, a, m)
for i in 0..<f.len:
result = matchType(c, f[i], a, m)
if result: break # and remember the binding!
m.bindings.setToPreviousLayer()
of tySet:
result = false
if a.kind == tySet:
result = matchType(c, f.elementType, a.elementType, m)
m.inferred.setLen oldLen
of tyNot:
if a.kind == tyNot:
result = matchType(c, f[0], a[0], m)
else:
let oldLen = m.inferred.len
result = not matchType(c, f[0], a, m)
m.inferred.setLen oldLen
of tyAnything:
result = true
of tyOrdinal:
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
else:
result = false
if result and ao.kind == tyGenericParam:
let bf = if f.isSelf: m.potentialImplementation else: f
if bindParam(c, m, ao, bf):
when logBindings: echo " ^ reverse binding"
proc checkConstraint(c: PContext; f, a: PType; m: var MatchCon): bool =
result = matchType(c, f, a, m) or matchType(c, a, f, m)
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
## Like 'matchType' but with extra logic dealing with proc return types
@@ -493,38 +235,30 @@ proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
elif a == nil:
result = false
else:
result = checkConstraint(c, f, a, m)
result = matchType(c, f, a, m)
proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
## or similar.
# watch out: only add bindings after a completely successful match.
m.bindings = m.bindings.newTypeMapLayer()
let oldLen = m.inferred.len
let can = candidate.typ.n
let con = defSignatureType(n).n
let con = n[0].sym.typ.n
if can.len < con.len:
# too few arguments, cannot be a match:
return false
if can.len > con.len:
# too many arguments (not optional)
for i in con.len ..< can.len:
if can[i].sym.ast == nil:
return false
when defined(debugConcepts):
echo "considering: ", renderTree(candidate.procDefSignature), " ", candidate.magic
let common = min(can.len, con.len)
for i in 1 ..< common:
if not checkConstraint(c, con[i].typ, can[i].typ, m):
m.bindings.setToPreviousLayer()
if not matchType(c, con[i].typ, can[i].typ, m):
m.inferred.setLen oldLen
return false
if not matchReturnType(c, n.defSignatureType.returnType, candidate.typ.returnType, m):
m.bindings.setToPreviousLayer()
if not matchReturnType(c, n[0].sym.typ.sons[0], candidate.typ.sons[0], m):
m.inferred.setLen oldLen
return false
# all other parameters have to be optional parameters:
@@ -532,7 +266,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
assert can[i].kind == nkSym
if can[i].sym.ast == nil:
# has too many arguments one of which is not optional:
m.bindings.setToPreviousLayer()
m.inferred.setLen oldLen
return false
return true
@@ -540,14 +274,12 @@ 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.
result = false
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
for candidate in candidates:
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
m.magic = candidate.magic
if matchSym(c, candidate, n, m):
result = true
break
if matchSym(c, candidate, n, m): return true
result = false
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
## Traverse the concept's AST ('n') and see if every declaration inside 'n'
@@ -580,76 +312,7 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
# error was reported earlier.
result = false
proc resolvedBinding(c: PContext; t: PType; m: MatchCon): PType =
## An inferred concept parameter can refer to an implementation-local
## generic parameter, for example `Elem[Impl.T]`. Resolve it while the
## matcher's private bindings (`Impl.T -> int`) are still available.
if t.containsUnresolvedType:
prepareMetatypeForSigmatch(c, m.bindings, m.concpt.sym.info, t)
else:
t
proc fixBindings(c: PContext; bindings: var LayeredIdTable; concpt: PType;
invocation: PType; m: var MatchCon) =
# invocation != nil means we have a non-atomic concept:
if invocation != nil and invocation.kind == tyGenericInvocation:
assert concpt.sym.typ.kind == tyGenericBody
for i in 0 .. concpt.sym.typ.len - 1:
let thisSym = concpt.sym.typ[i]
if lookup(bindings, thisSym) != nil:
# dont trust the bindings over existing ones
continue
let found = m.bindings.lookup(thisSym)
if found != nil:
let resolved = resolvedBinding(c, found, m)
when logBindings: echo "Invocation bind: ", thisSym, " ", resolved
bindings.put(thisSym, resolved)
# bind even more generic parameters
let genBody = invocation.base
assert genBody.kind == tyGenericBody
for i in FirstGenericParamAt ..< invocation.kidsLen:
let bpram = genBody[i - 1]
if lookup(bindings, invocation[i]) != nil:
# dont trust the bindings over existing ones
continue
let boundV = lookup(bindings, bpram)
when logBindings: echo "generic body bind: '", invocation[i], "' '", boundV, "'"
if boundV != nil:
bindings.put(invocation[i], boundV)
bindings.put(concpt, m.potentialImplementation)
proc fixConstraintBindings(c: PContext; bindings: var LayeredIdTable;
invocation: PType; m: MatchCon) =
## Propagates only the dependent parameters of a concept constraint. The
## concept itself and its private matcher bindings must remain unbound so
## that independent constraints using the same concept don't get coupled.
if invocation != nil and invocation.kind == tyGenericInvocation:
let genBody = invocation.base
assert genBody.kind == tyGenericBody
for i in FirstGenericParamAt ..< invocation.kidsLen:
if lookup(bindings, invocation[i]) == nil:
let boundValue = m.bindings.lookup(genBody[i - 1])
if boundValue != nil:
bindings.put(invocation[i], resolvedBinding(c, boundValue, m))
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool =
m.bindings = m.bindings.newTypeMapLayer()
if invocation != nil and invocation.kind == tyGenericInst:
let genericBody = invocation.base
for i in 1..<invocation.kidsLen-1:
# instGenericContainer can bind `tyVoid`
if invocation[i].kind != tyVoid:
bindParam(c, m, genericBody[i-1], invocation[i])
result = conceptMatchNode(c, concpt.conceptBody, m)
if result:
if mfDontBind notin m.flags:
fixBindings(c, bindings, concpt, invocation, m)
elif mfBindGenericParam in m.flags:
fixConstraintBindings(c, bindings, invocation, m)
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType, flags: set[MatchFlags] = {}): bool =
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; 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
@@ -658,16 +321,26 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
## we do not add any bindings at all on an unsuccessful match!
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags, marker: initHashSet[ConceptTypePair]())
if arg.isConcept:
result = conceptsMatch(c, concpt.reduceToBase, arg.reduceToBase, m) >= mkSubset
elif arg.acceptsAllTypes:
# XXX: I think this is wrong, or at least partially wrong. Can still test ambiguous types
result = false
elif mfCheckGeneric in m.flags:
# prioritize concepts the least. Specifically if the arg is not a catch all as per above
result = true
else:
result = processConcept(c, concpt, invocation, bindings, m)
var m = MatchCon(inferred: @[], potentialImplementation: arg)
result = conceptMatchNode(c, concpt.n.lastSon, m)
if result:
for (a, b) in m.inferred:
if b.kind == tyGenericParam:
var dest = b
while true:
dest = existingBinding(m, dest)
if dest == nil or dest.kind != tyGenericParam: break
if dest != nil:
bindings.idTablePut(a, dest)
when logBindings: echo "A bind ", a, " ", dest
else:
bindings.idTablePut(a, b)
when logBindings: echo "B bind ", a, " ", b
# we have a match, so bind 'arg' itself to 'concpt':
bindings.idTablePut(concpt, arg)
# invocation != nil means we have a non-atomic concept:
if invocation != nil and arg.kind == tyGenericInst and invocation.len == arg.len-1:
# bind even more generic parameters
assert invocation.kind == tyGenericInvocation
for i in 1 ..< invocation.len:
bindings.idTablePut(invocation[i], arg[i])

View File

@@ -10,7 +10,7 @@
# This module handles the conditional symbols.
import
std/strtabs
strtabs
from options import Feature
from lineinfos import hintMin, hintMax, warnMin, warnMax
@@ -159,20 +159,7 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasEnsureMove")
defineSymbol("nimHasNoReturnError")
defineSymbol("nimUseStrictDefs")
defineSymbol("nimHasNolineTooLong")
defineSymbol("nimHasCastExtendedVm")
defineSymbol("nimHasWarnStdPrefix")
defineSymbol("nimHasVtables")
defineSymbol("nimHasGenericsOpenSym2")
defineSymbol("nimHasNolineTooLong")
defineSymbol("nimHasGenericsOpenSym3")
defineSymbol("nimHasJsNoLambdaLifting")
defineSymbol("nimHasDefaultFloatRoundtrip")
defineSymbol("nimHasXorSet")
defineSymbol("nimHasPreviewDuplicateModuleError")
defineSymbol("nimHasSetLengthSeqUninitMagic")
defineSymbol("nimHasImplicitRangeConversion")

View File

@@ -14,7 +14,7 @@ import options, ast, ropes, pathutils, msgs, lineinfos
import modulegraphs
import std/[os, parseutils]
import std/strutils except addf
import strutils except addf
import std/private/globs
when defined(nimPreviewSlimSystem):
@@ -42,7 +42,7 @@ proc toNimblePath(s: string, isStdlib: bool): string =
let sub = "lib/"
var start = s.find(sub)
if start < 0:
raiseAssert "unreachable"
doAssert false
else:
start += sub.len
let base = s[start..^1]
@@ -105,6 +105,11 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
changeFileExt(project, "dot"))
proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
result = PGen(module: module, config: graph.config, graph: graph)
var g: PGen
new(g)
g.module = module
g.config = graph.config
g.graph = graph
if graph.backend == nil:
graph.backend = Backend(dotGraph: "")
result = g

View File

@@ -1,340 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2025 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Generate a .build.nif file for nifmake from a Nim project.
## This enables incremental and parallel compilation using the `m` switch.
import std / [os, tables, sets, times, osproc, strutils]
import options, msgs, lineinfos
import "../dist/nimony/src/lib" / [nifstreams, nifcursors, bitabs, nifreader, nifbuilder]
import "../dist/nimony/src/gear2" / modnames
type
FilePair = object
nimFile: string
modname: string
Node = ref object
files: seq[FilePair] # main file + includes
deps: seq[int] # indices into DepContext.nodes
id: int
DepContext = object
config: ConfigRef
nifler: string
nodes: seq[Node]
processedModules: Table[string, int] # modname -> node index
includeStack: seq[string]
proc toPair(c: DepContext; f: string): FilePair =
FilePair(nimFile: f, modname: moduleSuffix(f, cast[seq[string]](c.config.searchPaths)))
proc depsFile(c: DepContext; f: FilePair): string =
getNimcacheDir(c.config).string / f.modname & ".deps.nif"
proc parsedFile(c: DepContext; f: FilePair): string =
getNimcacheDir(c.config).string / f.modname & ".p.nif"
proc semmedFile(c: DepContext; f: FilePair): string =
getNimcacheDir(c.config).string / f.modname & ".nif"
proc findNifler(): string =
# Look for nifler in common locations
result = findExe("nifler")
if result.len == 0:
# Try relative to nim executable
let nimDir = getAppDir()
result = nimDir / "nifler"
if not fileExists(result):
result = nimDir / ".." / "nimony" / "bin" / "nifler"
if not fileExists(result):
result = ""
proc runNifler(c: DepContext; nimFile: string): bool =
## Run nifler deps on a file if needed. Returns true on success.
let pair = c.toPair(nimFile)
let depsPath = c.depsFile(pair)
# Check if deps file is up-to-date
if fileExists(depsPath) and fileExists(nimFile):
if getLastModificationTime(depsPath) > getLastModificationTime(nimFile):
return true # Already up-to-date
# Create output directory if needed
createDir(parentDir(depsPath))
# Run nifler deps
let cmd = quoteShell(c.nifler) & " deps " & quoteShell(nimFile) & " " & quoteShell(depsPath)
let exitCode = execShellCmd(cmd)
result = exitCode == 0
proc resolveFile(c: DepContext; origin, toResolve: string): string =
## Resolve an import path relative to origin file
# Handle std/ prefix
var path = toResolve
if path.startsWith("std/"):
path = path.substr(4)
# Try relative to origin first
let originDir = parentDir(origin)
result = originDir / path.addFileExt("nim")
if fileExists(result):
return result
# Try search paths
for searchPath in c.config.searchPaths:
result = searchPath.string / path.addFileExt("nim")
if fileExists(result):
return result
result = ""
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node)
proc processInclude(c: var DepContext; includePath: string; current: Node) =
let resolved = resolveFile(c, current.files[current.files.len - 1].nimFile, includePath)
if resolved.len == 0 or not fileExists(resolved):
return
# Check for recursive includes
for s in c.includeStack:
if s == resolved:
return # Skip recursive include
c.includeStack.add resolved
current.files.add c.toPair(resolved)
traverseDeps(c, c.toPair(resolved), current)
discard c.includeStack.pop()
proc processImport(c: var DepContext; importPath: string; current: Node) =
let resolved = resolveFile(c, current.files[0].nimFile, importPath)
if resolved.len == 0 or not fileExists(resolved):
return
let pair = c.toPair(resolved)
let existingIdx = c.processedModules.getOrDefault(pair.modname, -1)
if existingIdx == -1:
# New module - create node and process it
let newNode = Node(files: @[pair], id: c.nodes.len)
current.deps.add newNode.id
c.processedModules[pair.modname] = newNode.id
c.nodes.add newNode
traverseDeps(c, pair, newNode)
else:
# Already processed - just add dependency
if existingIdx notin current.deps:
current.deps.add existingIdx
proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
## Read a .deps.nif file and process imports/includes
let depsPath = c.depsFile(pair)
if not fileExists(depsPath):
return
var s = nifstreams.open(depsPath)
defer: nifstreams.close(s)
discard processDirectives(s.r)
var t = next(s)
if t.kind != ParLe:
return
# Skip to content (past stmts tag)
t = next(s)
while t.kind != EofToken:
if t.kind == ParLe:
let tag = pool.tags[t.tagId]
case tag
of "import", "fromimport":
# Read import path
t = next(s)
# Check for "when" marker (conditional import)
if t.kind == Ident and pool.strings[t.litId] == "when":
t = next(s) # skip it, still process the import
# Handle path expression (could be ident, string, or infix like std/foo)
var importPath = ""
if t.kind == Ident:
importPath = pool.strings[t.litId]
elif t.kind == StringLit:
importPath = pool.strings[t.litId]
elif t.kind == ParLe and pool.tags[t.tagId] == "infix":
# Handle std / foo style imports
t = next(s) # skip infix tag
if t.kind == Ident: # operator (/)
t = next(s)
if t.kind == Ident: # first part (std)
importPath = pool.strings[t.litId]
t = next(s)
if t.kind == Ident: # second part (foo)
importPath = importPath & "/" & pool.strings[t.litId]
if importPath.len > 0:
processImport(c, importPath, current)
# Skip to end of import node
var depth = 1
while depth > 0:
t = next(s)
if t.kind == ParLe: inc depth
elif t.kind == ParRi: dec depth
of "include":
# Read include path
t = next(s)
if t.kind == Ident and pool.strings[t.litId] == "when":
t = next(s) # skip conditional marker
var includePath = ""
if t.kind == Ident:
includePath = pool.strings[t.litId]
elif t.kind == StringLit:
includePath = pool.strings[t.litId]
if includePath.len > 0:
processInclude(c, includePath, current)
# Skip to end
var depth = 1
while depth > 0:
t = next(s)
if t.kind == ParLe: inc depth
elif t.kind == ParRi: dec depth
else:
# Skip unknown node
var depth = 1
while depth > 0:
t = next(s)
if t.kind == ParLe: inc depth
elif t.kind == ParRi: dec depth
t = next(s)
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node) =
## Process a module: run nifler and read deps
if not runNifler(c, pair.nimFile):
rawMessage(c.config, errGenerated, "nifler failed for: " & pair.nimFile)
return
readDepsFile(c, pair, current)
proc generateBuildFile(c: DepContext): string =
## Generate the .build.nif file for nifmake
result = getNimcacheDir(c.config).string / c.nodes[0].files[0].modname & ".build.nif"
var b = nifbuilder.open(result)
defer: b.close()
b.addHeader("nim deps", "nifmake")
b.addTree "stmts"
# Define nifler command
b.addTree "cmd"
b.addSymbolDef "nifler"
b.addStrLit c.nifler
b.addStrLit "parse"
b.addStrLit "--deps"
b.addTree "input"
b.endTree()
b.addTree "output"
b.endTree()
b.endTree()
# Define nim m command
b.addTree "cmd"
b.addSymbolDef "nim_m"
b.addStrLit getAppFilename()
b.addStrLit "m"
# Add search paths
for p in c.config.searchPaths:
b.addStrLit "--path:" & p.string
b.addTree "input"
b.addIntLit 0
b.endTree()
b.endTree()
# Build rules for parsing (nifler)
var seenFiles = initHashSet[string]()
for node in c.nodes:
for pair in node.files:
let parsed = c.parsedFile(pair)
if not seenFiles.containsOrIncl(parsed):
b.addTree "do"
b.addIdent "nifler"
b.addTree "input"
b.addStrLit pair.nimFile
b.endTree()
b.addTree "output"
b.addStrLit parsed
b.endTree()
b.addTree "output"
b.addStrLit c.depsFile(pair)
b.endTree()
b.endTree()
# Build rules for semantic checking (nim m)
for i in countdown(c.nodes.len - 1, 0):
let node = c.nodes[i]
let pair = node.files[0]
b.addTree "do"
b.addIdent "nim_m"
# Input: all parsed files for this module
for f in node.files:
b.addTree "input"
b.addStrLit c.parsedFile(f)
b.endTree()
# Also depend on semmed files of dependencies
for depIdx in node.deps:
b.addTree "input"
b.addStrLit c.semmedFile(c.nodes[depIdx].files[0])
b.endTree()
# Output: semmed file
b.addTree "output"
b.addStrLit c.semmedFile(pair)
b.endTree()
b.addTree "args"
b.addStrLit pair.nimFile
b.endTree()
b.endTree()
b.endTree() # stmts
proc commandDeps*(conf: ConfigRef) =
## Main entry point for `nim deps`
when not defined(nimKochBootstrap):
let nifler = findNifler()
if nifler.len == 0:
rawMessage(conf, errGenerated, "nifler tool not found. Install nimony or add nifler to PATH.")
return
let projectFile = conf.projectFull.string
if not fileExists(projectFile):
rawMessage(conf, errGenerated, "project file not found: " & projectFile)
return
# Create nimcache directory
createDir(getNimcacheDir(conf).string)
var c = DepContext(
config: conf,
nifler: nifler,
nodes: @[],
processedModules: initTable[string, int](),
includeStack: @[]
)
# Create root node for main project file
let rootPair = c.toPair(projectFile)
let rootNode = Node(files: @[rootPair], id: 0)
c.nodes.add rootNode
c.processedModules[rootPair.modname] = 0
# Process dependencies
traverseDeps(c, rootPair, rootNode)
# Generate build file
let buildFile = generateBuildFile(c)
rawMessage(conf, hintSuccess, "generated: " & buildFile)
rawMessage(conf, hintSuccess, "run: nifmake run " & buildFile)
else:
rawMessage(conf, errGenerated, "nim deps not available in bootstrap build")

View File

@@ -22,9 +22,8 @@
## "A GraphFree Approach to DataFlow Analysis" by Markus Mohnen.
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
import ast, lineinfos, renderer, aliasanalysis
import ast, intsets, lineinfos, renderer, aliasanalysis
import std/private/asciitables
import std/intsets
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -46,10 +45,10 @@ type
case isTryBlock: bool
of false:
label: PSym
breakFixups: seq[(TPosition, seq[PNode])] # Contains the gotos for the breaks along with their pending finales
breakFixups: seq[(TPosition, seq[PNode])] #Contains the gotos for the breaks along with their pending finales
of true:
finale: PNode
raiseFixups: seq[TPosition] # Contains the gotos for the raises
raiseFixups: seq[TPosition] #Contains the gotos for the raises
Con = object
code: ControlFlowGraph
@@ -61,7 +60,6 @@ type
proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
# for debugging purposes
# first iteration: compute all necessary labels:
result = ""
var jumpTargets = initIntSet()
let last = if last < 0: c.len-1 else: min(last, c.len-1)
for i in start..last:
@@ -113,7 +111,7 @@ proc patch(c: var Con, p: TPosition) =
proc gen(c: var Con; n: PNode)
proc popBlock(c: var Con; oldLen: int) =
var exits: seq[TPosition] = @[]
var exits: seq[TPosition]
exits.add c.gotoI()
for f in c.blocks[oldLen].breakFixups:
c.patch(f[0])
@@ -130,6 +128,10 @@ template withBlock(labl: PSym; body: untyped) =
body
popBlock(c, oldLen)
proc isTrue(n: PNode): bool =
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
n.kind == nkIntLit and n.intVal != 0
template forkT(body) =
let lab1 = c.forkI()
body
@@ -181,6 +183,14 @@ proc genIf(c: var Con, n: PNode) =
goto Lend3
L3:
D
goto Lend3 # not eliminated to simplify the join generation
Lend3:
join F3
Lend2:
join F2
Lend:
join F1
]#
var endings: seq[TPosition] = @[]
let oldInteresting = c.interestingInstructions
@@ -205,6 +215,7 @@ proc genAndOr(c: var Con; n: PNode) =
# fork lab1
# asgn dest, b
# lab1:
# join F1
c.gen(n[1])
forkT:
c.gen(n[2])
@@ -252,7 +263,7 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
if c.blocks[i].isTryBlock:
c.blocks[i].raiseFixups.add lab1
else:
var trailingFinales: seq[PNode] = @[]
var trailingFinales: seq[PNode]
if c.inTryStmt > 0:
# Ok, we are in a try, lets see which (if any) try's we break out from:
for b in countdown(c.blocks.high, i):
@@ -315,7 +326,7 @@ proc genRaise(c: var Con; n: PNode) =
if c.blocks[i].isTryBlock:
genBreakOrRaiseAux(c, i, n)
return
assert false # Unreachable
assert false #Unreachable
else:
genNoReturn(c)
@@ -371,7 +382,7 @@ proc genCall(c: var Con; n: PNode) =
if t != nil: t = t.skipTypes(abstractInst)
for i in 1..<n.len:
gen(c, n[i])
if t != nil and i < t.signatureLen and isOutParam(t[i]):
if t != nil and i < t.len and isOutParam(t[i]):
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
genDef(c, n[i])
# every call can potentially raise:
@@ -381,6 +392,7 @@ proc genCall(c: var Con; n: PNode) =
# fork lab1
# goto exceptionHandler (except or finally)
# lab1:
# join F1
forkT:
for i in countdown(c.blocks.high, 0):
if c.blocks[i].isTryBlock:
@@ -439,8 +451,8 @@ proc gen(c: var Con; n: PNode) =
genUse(c, n)
of nkIfStmt, nkIfExpr: genIf(c, n)
of nkWhenStmt:
# This is "when nimvm" node. Chose the second branch.
gen(c, n[1][0])
# This is "when nimvm" node. Chose the first branch.
gen(c, n[0][1])
of nkCaseStmt: genCase(c, n)
of nkWhileStmt: genWhile(c, n)
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
@@ -454,10 +466,10 @@ proc gen(c: var Con; n: PNode) =
of nkPragmaBlock: gen(c, n.lastSon)
of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
gen(c, n[0])
of nkConv, nkExprColonExpr, nkExprEqExpr, PathKinds1:
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
gen(c, n[1])
of nkVarSection, nkLetSection: genVarSection(c, n)
of nkDefer: raiseAssert "dfa construction pass requires the elimination of 'defer'"
of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
else: discard
when false:

View File

@@ -15,16 +15,15 @@
## For corresponding users' documentation see [Nim DocGen Tools Guide].
import
ast, options, msgs, idents,
ast, strutils, strtabs, algorithm, sequtils, options, msgs, os, idents,
wordrecg, syntaxes, renderer, lexer,
packages/docutils/[rst, rstidx, rstgen, dochelpers],
trees, types,
typesrenderer, astalgo, lineinfos,
pathutils, nimpaths, renderverbatim, packages
json, xmltree, trees, types,
typesrenderer, astalgo, lineinfos, intsets,
pathutils, tables, nimpaths, renderverbatim, osproc, packages
import packages/docutils/rstast except FileIndex, TLineInfo
import std/[os, strutils, strtabs, algorithm, json, osproc, tables, intsets, xmltree, sequtils]
from std/uri import encodeUrl
from uri import encodeUrl
from nodejs import findNodeJs
when defined(nimPreviewSlimSystem):
@@ -115,7 +114,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, invalid)
result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid)
if result == invalid:
let fname = toFullPath(d.conf, fileIndex)
result = addFilename(d.sharedState, fname)
@@ -148,7 +147,7 @@ proc cmpDecimalsIgnoreCase(a, b: string): int =
limitB = iB
while limitA < aLen and isDigit(a[limitA]): inc limitA
while limitB < bLen and isDigit(b[limitB]): inc limitB
var pos = max(limitA-iA, limitB-iB)
var pos = max(limitA-iA, limitB-iA)
while pos > 0:
if limitA-pos < iA: # digit in `a` is 0 effectively
result = ord('0') - ord(b[limitB-pos])
@@ -171,7 +170,6 @@ proc cmpDecimalsIgnoreCase(a, b: string): int =
proc prettyString(a: object): string =
# xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906
result = ""
for k, v in fieldPairs(a):
result.add k & ": " & $v & "\n"
@@ -217,16 +215,12 @@ proc whichType(d: PDoc; n: PNode): PSym =
if n.kind == nkSym:
if d.types.strTableContains(n.sym):
result = n.sym
else:
result = nil
else:
result = nil
for i in 0..<n.safeLen:
let x = whichType(d, n[i])
if x != nil: return x
proc attachToType(d: PDoc; p: PSym): PSym =
result = nil
let params = p.ast[paramsPos]
template check(i) =
result = whichType(d, params[i])
@@ -289,7 +283,7 @@ template declareClosures(currentFilename: AbsoluteFile, destFile: string) =
let outDirPath: RelativeFile =
presentationPath(conf, AbsoluteFile(basedir / targetRelPath))
# use presentationPath because `..` path can be be mangled to `_._`
result = (string(conf.outDir / outDirPath), "")
result.targetPath = string(conf.outDir / outDirPath)
if not fileExists(result.targetPath):
# this can happen if targetRelPath goes to parent directory `OUTDIR/..`.
# Trying it, this may cause ambiguities, but allows us to insert
@@ -349,7 +343,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
if conf.configVars.hasKey("doc.googleAnalytics") and
conf.configVars.hasKey("doc.plausibleAnalytics"):
raiseAssert "Either use googleAnalytics or plausibleAnalytics"
doAssert false, "Either use googleAnalytics or plausibleAnalytics"
if conf.configVars.hasKey("doc.googleAnalytics"):
result.analytics = """
@@ -374,7 +368,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
result.seenSymbols = newStringTable(modeCaseInsensitive)
result.id = 100
result.jEntriesFinal = newJArray()
result.types = initStrTable()
initStrTable result.types
result.onTestSnippet =
proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) {.gcsafe.} =
if conf.docCmd == docCmdSkip: return
@@ -433,9 +427,6 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
proc genComment(d: PDoc, n: PNode): PRstNode =
if n.comment.len > 0:
if optDocRaw in d.conf.globalOptions:
return newRstLeaf(n.comment)
d.sharedState.currFileIdx = addRstFileIndex(d, n.info)
try:
result = parseRst(n.comment,
@@ -444,8 +435,6 @@ proc genComment(d: PDoc, n: PNode): PRstNode =
d.conf, d.sharedState)
except ERecoverableError:
result = newRstNode(rnLiteralBlock, @[newRstLeaf(n.comment)])
else:
result = nil
proc genRecCommentAux(d: PDoc, n: PNode): PRstNode =
if n == nil: return nil
@@ -480,7 +469,6 @@ proc getPlainDocstring(n: PNode): string =
elif startsWith(n.comment, "##"):
result = n.comment
else:
result = ""
for i in 0..<n.safeLen:
result = getPlainDocstring(n[i])
if result.len > 0: return
@@ -496,8 +484,9 @@ proc externalDep(d: PDoc; module: PSym): string =
proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
renderFlags: TRenderFlags = {};
procLink: string) =
var r: TSrcGen = initTokRender(n, renderFlags)
var r: TSrcGen
var literal = ""
initTokRender(r, n, renderFlags)
var kind = tkEof
var tokenPos = 0
var procTokenPos = 0
@@ -540,11 +529,10 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
elif s != nil and s.kind in {skType, skVar, skLet, skConst} and
sfExported in s.flags and s.owner != nil and
belongsToProjectPackage(d.conf, s.owner) and d.target == outHtml:
let href = (if d.module == s.owner: ""
else: externalDep(d, s.owner).changeFileExt("html")
) & "#" & literal
result.addf "<a href=\"$1\"><span class=\"Identifier\">$2</span></a>",
[href, escLit]
let external = externalDep(d, s.owner)
result.addf "<a href=\"$1#$2\"><span class=\"Identifier\">$3</span></a>",
[changeFileExt(external, "html"), literal,
escLit]
else:
dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [escLit])
@@ -612,9 +600,7 @@ proc runAllExamples(d: PDoc) =
rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
# removeFile(outp.changeFileExt(ExeExt)) # it's in nimcache, no need to remove
proc quoted(a: string): string =
result = ""
result.addQuoted(a)
proc quoted(a: string): string = result.addQuoted(a)
proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): (string, int, int) =
# xxx expose in compiler/lineinfos.nim
@@ -740,7 +726,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var ItemPre,
let (rdoccmd, code) = prepareExample(d, n, topLevel)
var msg = "Example:"
if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd
var s: string = ""
var s: string
dispA(d.conf, s, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
"\n\n\\textbf{$1}\n", [msg])
dest.add s
@@ -834,7 +820,7 @@ proc getName(n: PNode): string =
of nkAccQuoted:
result = "`"
for i in 0..<n.len: result.add(getName(n[i]))
result.add('`')
result = "`"
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = getName(n[0])
else:
@@ -956,17 +942,14 @@ proc genDeprecationMsg(d: PDoc, n: PNode): string =
if n[1].kind in {nkStrLit..nkTripleStrLit}:
result = getConfigVar(d.conf, "doc.deprecationmsg") % [
"label", "Deprecated:", "message", xmltree.escape(n[1].strVal)]
else:
result = ""
else:
raiseAssert "unreachable"
doAssert false
type DocFlags = enum
kDefault
kForceExport
proc genSeeSrc(d: PDoc, path: string, line: int): string =
result = ""
let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
if docItemSeeSrc.len > 0:
let path = relativeTo(AbsoluteFile path, AbsoluteDir getCurrentDir(), '/')
@@ -1004,12 +987,11 @@ proc getTypeKind(n: PNode): string =
proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
## Converts symbol info (names/types/parameters) in `n` into format
## `LangSymbol` convenient for ``rst.nim``/``dochelpers.nim``.
result = LangSymbol(name: baseName.nimIdentNormalize,
symKind: k.toHumanStr
)
result.name = baseName.nimIdentNormalize
result.symKind = k.toHumanStr
if k in routineKinds:
var
paramTypes: seq[string] = @[]
paramTypes: seq[string]
renderParamTypes(paramTypes, n[paramsPos], toNormalize=true)
let paramNames = renderParamNames(n[paramsPos], toNormalize=true)
# In some rare cases (system.typeof) parameter type is not set for default:
@@ -1034,8 +1016,9 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
genNode = n[miscPos][1] # FIXME: what is index 1?
if genNode != nil:
var literal = ""
var r: TSrcGen = initTokRender(genNode, {renderNoBody, renderNoComments,
renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
var r: TSrcGen
initTokRender(r, genNode, {renderNoBody, renderNoComments,
renderNoPragmas, renderNoProcDefs, renderExpandUsing})
var kind = tkEof
while true:
getNextTok(r, kind, literal)
@@ -1055,15 +1038,16 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
var result = ""
var literal, plainName = ""
var kind = tkEof
var comm: ItemPre = default(ItemPre)
var comm: ItemPre
if n.kind in routineDefs:
getAllRunnableExamples(d, n, comm)
else:
comm.add genRecComment(d, n)
var r: TSrcGen
# Obtain the plain rendered string for hyperlink titles.
var r: TSrcGen = initTokRender(n, {renderNoBody, renderNoComments, renderDocComments,
renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments,
renderNoPragmas, renderNoProcDefs, renderExpandUsing})
while true:
getNextTok(r, kind, literal)
if kind == tkEof:
@@ -1090,9 +1074,6 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
symbolOrIdEnc = encodeUrl(symbolOrId, usePlus = false)
deprecationMsg = genDeprecationMsg(d, pragmaNode)
rstLangSymbol = toLangSymbol(k, n, cleanPlainSymbol)
symNameNode =
if nameNode.kind == nkPostfix: nameNode[1]
else: nameNode
# we generate anchors automatically for subsequent use in doc comments
let lineinfo = rstast.TLineInfo(
@@ -1103,10 +1084,10 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
priority = symbolPriority(k), info = lineinfo,
module = addRstFileIndex(d, FileIndex d.module.position))
var renderFlags = {renderNoBody, renderNoComments, renderDocComments,
renderSyms, renderExpandUsing, renderNoPostfix}
if nonExports:
renderFlags.incl renderNonExportedFields
let renderFlags =
if nonExports: {renderNoBody, renderNoComments, renderDocComments, renderSyms,
renderExpandUsing, renderNonExportedFields}
else: {renderNoBody, renderNoComments, renderDocComments, renderSyms, renderExpandUsing}
nodeToHighlightedHtml(d, n, result, renderFlags, symbolOrIdEnc)
let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int)
@@ -1129,19 +1110,18 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').changeFileExt(HtmlExt).string
var attype = ""
if k in routineKinds and symNameNode.kind == nkSym:
if k in routineKinds and nameNode.kind == nkSym:
let att = attachToType(d, nameNode.sym)
if att != nil:
attype = esc(d.target, att.name.s)
elif k == skType and symNameNode.kind == nkSym and
symNameNode.sym.typ.kind in {tyEnum, tyBool}:
let etyp = symNameNode.sym.typ
elif k == skType and nameNode.kind == nkSym and nameNode.sym.typ.kind in {tyEnum, tyBool}:
let etyp = nameNode.sym.typ
for e in etyp.n:
if e.sym.kind != skEnumField: continue
let plain = renderPlainSymbolName(e)
let symbolOrId = d.newUniquePlainSymbol(plain)
setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId,
term = plain, linkTitle = symNameNode.sym.name.s & '.' & plain,
term = plain, linkTitle = nameNode.sym.name.s & '.' & plain,
linkDesc = xmltree.escape(getPlainDocstring(e).docstringSummary),
line = n.info.line.int)
@@ -1162,8 +1142,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
linkTitle = detailedName,
linkDesc = xmltree.escape(plainDocstring.docstringSummary),
line = n.info.line.int)
if k == skType and symNameNode.kind == nkSym:
d.types.strTableAdd symNameNode.sym
if k == skType and nameNode.kind == nkSym:
d.types.strTableAdd nameNode.sym
proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): JsonItem =
if not isVisible(d, nameNode): return
@@ -1171,21 +1151,15 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
name = getNameEsc(d, nameNode)
comm = genRecComment(d, n)
r: TSrcGen
renderFlags = {renderNoBody, renderNoComments, renderDocComments,
renderExpandUsing, renderNoPostfix}
renderFlags = {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing}
if nonExports:
renderFlags.incl renderNonExportedFields
r = initTokRender(n, renderFlags)
result = JsonItem(json: %{ "name": %name, "type": %($k), "line": %n.info.line.int,
initTokRender(r, n, renderFlags)
result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"col": %n.info.col}
)
if comm != nil:
if optDocRaw in d.conf.globalOptions:
result.json["description"] = %comm.text
else:
result.rst = comm
result.rstField = "description"
result.rst = comm
result.rstField = "description"
if r.buf.len > 0:
result.json["code"] = %r.buf
if k in routineKinds:
@@ -1212,9 +1186,10 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
result.json["signature"]["genericParams"] = newJArray()
for genericParam in n[genericParamsPos]:
var param = %{"name": %($genericParam)}
if genericParam.sym.typ.len > 0:
if genericParam.sym.typ.sons.len > 0:
param["types"] = newJArray()
param["types"] = %($genericParam.sym.typ.elementType)
for kind in genericParam.sym.typ.sons:
param["types"].add %($kind)
result.json["signature"]["genericParams"].add param
if optGenIndex in d.conf.globalOptions:
genItem(d, n, nameNode, k, kForceExport)
@@ -1308,8 +1283,6 @@ proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
let s = n[namePos].sym
if tfReturnsNew in s.typ.flags:
result = newIdentNode(getIdent(cache, "new"), n.info)
else:
result = nil
proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
let spec = effectSpec(x, effectType)
@@ -1328,12 +1301,10 @@ 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)
else:
result = nil
proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
let s = n[namePos].sym
@@ -1347,8 +1318,6 @@ proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName
if effects.len > 0:
result = newTreeI(nkExprColonExpr, n.info,
newIdentNode(getIdent(cache, pragmaName), n.info), effects)
else:
result = nil
proc documentRaises*(cache: IdentCache; n: PNode) =
if n[namePos].kind != nkSym: return
@@ -1415,18 +1384,14 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
of nkExportStmt:
for it in n:
if it.kind == nkSym:
if d.module != nil and d.module == it.sym.owner: # in current module
# bug #23051; don't generate documentation for exported symbols again
if sfExported notin it.sym.flags:
generateDoc(d, it.sym.ast, orig, config, kForceExport)
# else it's to be handled in `of XxxSection` branch
if d.module != nil and d.module == it.sym.owner:
generateDoc(d, it.sym.ast, orig, config, kForceExport)
elif it.sym.ast != nil:
# only export symbols in imported modules, not in current module
exportSym(d, it.sym)
of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
of nkCallKinds:
var comm = default(ItemPre)
var comm: ItemPre
getAllRunnableExamples(d, n, comm)
if comm.len != 0: d.modDescPre.add(comm)
else: discard
@@ -1535,7 +1500,7 @@ proc finishGenerateDoc*(d: var PDoc) =
overloadChoices.sort(cmp)
var nameContent = ""
for item in overloadChoices:
var itemDesc: string = ""
var itemDesc: string
renderItemPre(d, item.descRst, itemDesc)
nameContent.add(
getConfigVar(d.conf, "doc.item") % (
@@ -1561,7 +1526,7 @@ proc finishGenerateDoc*(d: var PDoc) =
for i, entry in d.jEntriesPre:
if entry.rst != nil:
let resolved = resolveSubs(d.sharedState, entry.rst)
var str: string = ""
var str: string
renderRstToOut(d[], resolved, str)
entry.json[entry.rstField] = %str
d.jEntriesPre[i].rst = nil
@@ -1676,7 +1641,7 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) =
for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase):
var overloadChoices = d.tocTable[kind][plainName]
overloadChoices.sort(cmp)
var content: string = ""
var content: string
for item in overloadChoices:
content.add item.content
d.toc2[kind].add getConfigVar(d.conf, "doc.section.toc2") % [
@@ -1707,7 +1672,7 @@ proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile):
proc genOutFile(d: PDoc, groupedToc = false): string =
var
code, content: string = ""
code, content: string
title = ""
var j = 0
var toc = ""
@@ -1816,7 +1781,7 @@ proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) =
proc writeOutputJson*(d: PDoc, useWarning = false) =
runAllExamples(d)
var modDesc: string = ""
var modDesc: string
for desc in d.modDescFinal:
modDesc &= desc
let content = %*{"orig": d.filename,
@@ -1824,11 +1789,11 @@ proc writeOutputJson*(d: PDoc, useWarning = false) =
"moduleDescription": modDesc,
"entries": d.jEntriesFinal}
if optStdout in d.conf.globalOptions:
writeLine(stdout, $content)
write(stdout, $content)
else:
let dir = d.destFile.splitFile.dir
createDir(dir)
var f: File = default(File)
var f: File
if open(f, d.destFile, fmWrite):
write(f, $content)
close(f)
@@ -1900,9 +1865,6 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
else:
#echo getOutFile(gProjectFull, JsonExt)
let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
conf.outFile = filename.relativeTo(conf.outDir)
let dir = filename.splitFile.dir
createDir(dir)
try:
writeFile(filename, content)
except IOError:
@@ -1923,10 +1885,8 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
if optStdout in d.conf.globalOptions:
write(stdout, content)
else:
#echo getOutFile(gProjectFull, TagsExt)
let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
conf.outFile = filename.relativeTo(conf.outDir)
let dir = filename.splitFile.dir
createDir(dir)
try:
writeFile(filename, content)
except IOError:

View File

@@ -39,12 +39,10 @@ template closeImpl(body: untyped) {.dirty.} =
discard
proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
result = nil
closeImpl:
writeOutput(g.doc, useWarning, groupedToc)
proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
result = nil
closeImpl:
writeOutputJson(g.doc, useWarning)

View File

@@ -14,7 +14,7 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
res.typ = getSysType(g, info, tyString)
result.typ = newType(tyProc, idgen, t.owner)
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
result.typ.n = newNodeI(nkFormalParams, info)
rawAddSon(result.typ, res.typ)
result.typ.n.add newNodeI(nkEffectList, info)
@@ -33,10 +33,6 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
#newIntTypeNode(nkIntLit, field.position, t)
# safety branch for invalid data:
caseStmt.add newTree(nkElse,
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res),
newStrNode("", info))))
body.add(caseStmt)
@@ -60,15 +56,14 @@ proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
if obj.kind == nkRecCase and obj[0].kind == nkSym and obj[0].sym == field:
result = obj
else:
result = nil
for x in obj:
result = searchObjCaseImpl(x, field)
if result != nil: break
proc searchObjCase(t: PType; field: PSym): PNode =
result = searchObjCaseImpl(t.n, field)
if result == nil and t.baseClass != nil:
result = searchObjCase(t.baseClass.skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
if result == nil and t.len > 0:
result = searchObjCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
doAssert result != nil
proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
@@ -80,7 +75,7 @@ proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGra
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
res.typ = getSysType(g, info, tyUInt8)
result.typ = newType(tyProc, idgen, t.owner)
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
result.typ.n = newNodeI(nkFormalParams, info)
rawAddSon(result.typ, res.typ)
result.typ.n.add newNodeI(nkEffectList, info)

View File

@@ -10,8 +10,7 @@
## This module contains support code for new-styled error
## handling via an `nkError` node kind.
import ast, renderer, options, types
import std/strutils
import ast, renderer, options, strutils, types
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -41,8 +40,7 @@ proc newError*(wrongNode: PNode; k: ErrorKind; args: varargs[PNode]): PNode =
let innerError = errorSubNode(wrongNode)
if innerError != nil:
return innerError
var idgen = idGeneratorForPackage(-1'i32)
result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, nil))
result = newNodeIT(nkError, wrongNode.info, newType(tyError, ItemId(module: -1, item: -1), nil))
result.add wrongNode
result.add newIntNode(nkIntLit, ord(k))
for a in args: result.add a
@@ -52,8 +50,7 @@ proc newError*(wrongNode: PNode; msg: string): PNode =
let innerError = errorSubNode(wrongNode)
if innerError != nil:
return innerError
var idgen = idGeneratorForPackage(-1'i32)
result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, nil))
result = newNodeIT(nkError, wrongNode.info, newType(tyError, ItemId(module: -1, item: -1), nil))
result.add wrongNode
result.add newIntNode(nkIntLit, ord(CustomError))
result.add newStrNode(msg, wrongNode.info)

View File

@@ -9,11 +9,9 @@
## This file implements the FFI part of the evaluator for Nim code.
import ast, types, options, msgs, lineinfos
from std/os import getAppFilename
import libffi/libffi
import std/[tables, dynlib]
import ast, types, options, tables, dynlib, msgs, lineinfos
from os import getAppFilename
import pkg/libffi
when defined(windows):
const libcDll = "msvcrt.dll"
@@ -39,10 +37,9 @@ else:
var gExeHandle = loadLib()
proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer =
result = nil
if dll in cache:
return cache[dll]
var libs: seq[string] = @[]
var libs: seq[string]
libCandidates(dll, libs)
for c in libs:
result = loadLib(c)
@@ -64,7 +61,7 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
let lib = sym.annex
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
globalError(conf, sym.info, "dynlib needs to be a string lit")
var theAddr: pointer = nil
var theAddr: pointer
if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
# first try this exe itself:
@@ -99,7 +96,7 @@ proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
result = addr libffi.type_pointer
of tyDistinct, tyAlias, tySink:
result = mapType(conf, t.skipModifier)
result = mapType(conf, t[0])
else:
result = nil
# too risky:
@@ -111,7 +108,6 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI
of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI
of ccCDecl: result = DEFAULT_ABI
else:
result = default(TABI)
globalError(conf, info, "cannot map calling convention to FFI")
template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[]
@@ -126,18 +122,16 @@ proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
else:
result = sizeof(pointer) + packSize(conf, v[0], typ.elementType)
result = sizeof(pointer) + packSize(conf, v[0], typ.lastSon)
of tyDistinct, tyGenericInst, tyAlias, tySink:
result = packSize(conf, v, typ.skipModifier)
result = packSize(conf, v, typ[0])
of tyArray:
# consider: ptr array[0..1000_000, int] which is common for interfacing;
# we use the real length here instead
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
elif v.len != 0:
result = v.len * packSize(conf, v[0], typ.elementType)
else:
result = 0
result = v.len * packSize(conf, v[0], typ[1])
else:
result = getSize(conf, typ).int
@@ -146,7 +140,6 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
case n.kind
of nkRecList:
result = nil
for i in 0..<n.len:
result = getField(conf, n[i], position)
if result != nil: return
@@ -161,8 +154,7 @@ proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
else: internalError(conf, n.info, "getField(record case branch)")
of nkSym:
if n.sym.position == position: result = n.sym
else: result = nil
else: result = nil
else: discard
proc packObject(conf: ConfigRef, x: PNode, typ: PType, res: pointer) =
internalAssert conf, x.kind in {nkObjConstr, nkPar, nkTupleConstr}
@@ -234,19 +226,19 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
packRecCheck = 0
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
inc packRecCheck
pack(conf, v[0], typ.elementType, res +! sizeof(pointer))
pack(conf, v[0], typ.lastSon, res +! sizeof(pointer))
dec packRecCheck
awr(pointer, res +! sizeof(pointer))
of tyArray:
let baseSize = getSize(conf, typ.elementType)
let baseSize = getSize(conf, typ[1])
for i in 0..<v.len:
pack(conf, v[i], typ.elementType, res +! i * baseSize)
pack(conf, v[i], typ[1], res +! i * baseSize)
of tyObject, tyTuple:
packObject(conf, v, typ, res)
of tyNil:
discard
of tyDistinct, tyGenericInst, tyAlias, tySink:
pack(conf, v, typ.skipModifier, res)
pack(conf, v, typ[0], res)
else:
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
@@ -275,7 +267,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,15 +290,15 @@ 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
if result.kind != nkBracket:
globalError(conf, n.info, "cannot map value from FFI")
let baseSize = getSize(conf, typ.elementType)
let baseSize = getSize(conf, typ[1])
for i in 0..<result.len:
result[i] = unpack(conf, x +! i * baseSize, typ.elementType, result[i])
result[i] = unpack(conf, x +! i * baseSize, typ[1], result[i])
proc canonNodeKind(k: TNodeKind): TNodeKind =
case k
@@ -319,7 +311,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 +325,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)
@@ -364,7 +356,6 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
else:
result = nil
globalError(conf, n.info, "cannot map value from FFI (tyEnum, tySet)")
of tyFloat: awf(nkFloatLit, rd(float, x))
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
@@ -387,10 +378,9 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
awi(nkPtrLit, cast[int](p))
elif n != nil and n.len == 1:
internalAssert(conf, n.kind == nkRefTy)
n[0] = unpack(conf, p, typ.elementType, n[0])
n[0] = unpack(conf, p, typ.lastSon, n[0])
result = n
else:
result = nil
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
of tyObject, tyTuple:
result = unpackObject(conf, x, typ, n)
@@ -405,10 +395,9 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
of tyNil:
setNil()
of tyDistinct, tyGenericInst, tyAlias, tySink:
result = unpack(conf, x, typ.skipModifier, n)
result = unpack(conf, x, typ.lastSon, n)
else:
# XXX what to do with 'array' here?
result = nil
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
@@ -427,7 +416,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
@@ -444,24 +433,24 @@ proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
let typ = call[0].typ
if prep_cif(cif, mapCallConv(conf, typ.callConv, call.info), cuint(call.len-1),
mapType(conf, typ.returnType), sig) != OK:
mapType(conf, typ[0]), sig) != OK:
globalError(conf, call.info, "error in FFI call")
var args: ArgList = default(ArgList)
var args: ArgList
let fn = cast[pointer](call[0].intVal)
for i in 1..<call.len:
var t = call[i].typ
args[i-1] = alloc0(packSize(conf, call[i], t))
pack(conf, call[i], t, args[i-1])
let retVal = if isEmptyType(typ.returnType): pointer(nil)
else: alloc(getSize(conf, typ.returnType).int)
let retVal = if isEmptyType(typ[0]): pointer(nil)
else: alloc(getSize(conf, typ[0]).int)
libffi.call(cif, fn, retVal, args)
if retVal.isNil:
result = newNode(nkEmpty)
else:
result = unpack(conf, retVal, typ.returnType, nil)
result = unpack(conf, retVal, typ[0], nil)
result.info = call.info
if retVal != nil: dealloc retVal
@@ -481,7 +470,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")
@@ -489,7 +478,7 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
mapType(conf, fntyp[0]), sig) != OK:
globalError(conf, info, "error in FFI call")
var cargs: ArgList = default(ArgList)
var cargs: ArgList
let fn = cast[pointer](fn.intVal)
for i in 0..len-1:
let t = args[i+start].typ

View File

@@ -9,16 +9,16 @@
## Template evaluation engine. Now hygienic.
import options, ast, astalgo, msgs, renderer, lineinfos, idents, trees
import std/strutils
import
strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents, trees
type
TemplCtx = object
owner, genSymOwner: PSym
instLines: bool # use the instantiation lines numbers
isDeclarative: bool
mapping: SymMapping # every gensym'ed symbol needs to be mapped to some
# new symbol
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
# new symbol
config: ConfigRef
ic: IdentCache
instID: int
@@ -33,19 +33,6 @@ 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)
@@ -57,14 +44,14 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
handleParam actual[s.position]
elif (s.owner != nil) and (s.kind == skGenericParam or
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam):
handleParam actual[s.owner.typ.signatureLen + s.position - 1]
handleParam actual[s.owner.typ.len + s.position - 1]
else:
internalAssert c.config, sfGenSym in s.flags or s.kind == skType
var x = idTableGet(c.mapping, s)
var x = PSym(idTableGet(c.mapping, s))
if x == nil:
x = copySym(s, c.idgen)
# sem'check needs to set the owner properly later, see bug #9476
setOwner(x, nil) # c.genSymOwner
x.owner = nil # c.genSymOwner
#if x.kind == skParam and x.owner.kind == skModule:
# internalAssert c.config, false
idTablePut(c.mapping, s, x)
@@ -130,7 +117,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
# now that we have working untyped parameters.
genericParams = if fromHlo: 0
else: s.ast[genericParamsPos].len
expectedRegularParams = s.typ.paramsLen
expectedRegularParams = s.typ.len-1
givenRegularParams = totalParams - genericParams
if givenRegularParams < 0: givenRegularParams = 0
@@ -182,7 +169,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;
@@ -196,14 +183,14 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
# replace each param by the corresponding node:
var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
var ctx = TemplCtx(owner: tmpl,
genSymOwner: genSymOwner,
config: conf,
ic: ic,
mapping: initSymMapping(),
instID: instID[],
idgen: idgen
)
var ctx: TemplCtx
ctx.owner = tmpl
ctx.genSymOwner = genSymOwner
ctx.config = conf
ctx.ic = ic
initIdTable(ctx.mapping)
ctx.instID = instID[]
ctx.idgen = idgen
let body = tmpl.ast[bodyPos]
#echo "instantion of ", renderTree(body, {renderIds})

View File

@@ -1,131 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import lineinfos, ast, types
proc caseObjDefaultBranch*(obj: PNode; branch: Int128): int =
result = 0
for i in 1 ..< obj.len:
for j in 0 .. obj[i].len - 2:
if obj[i][j].kind == nkRange:
let x = getOrdValue(obj[i][j][0])
let y = getOrdValue(obj[i][j][1])
if branch >= x and branch <= y:
return i
elif getOrdValue(obj[i][j]) == branch:
return i
if obj[i].len == 1:
# else branch
return i
return 1
template newZero(t: PType; info: TLineInfo; k = nkIntLit): PNode = newNodeIT(k, info, t)
proc expandDefault*(t: PType; info: TLineInfo): PNode
proc expandField(s: PSym; info: TLineInfo): PNode =
result = newNodeIT(nkExprColonExpr, info, s.typ)
result.add newSymNode(s)
result.add expandDefault(s.typ, info)
proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) =
case n.kind
of nkRecList:
for i in 0..<n.len:
expandDefaultN(n[i], info, res)
of nkRecCase:
res.add expandField(n[0].sym, info)
var branch = Zero
let constOrNil = n[0].sym.astdef
if constOrNil != nil:
branch = getOrdValue(constOrNil)
let selectedBranch = caseObjDefaultBranch(n, branch)
let b = lastSon(n[selectedBranch])
expandDefaultN b, info, res
of nkSym:
res.add expandField(n.sym, info)
else:
discard
proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) =
if t.baseClass != nil:
expandDefaultObj(t.baseClass, info, res)
expandDefaultN(t.n, info, res)
proc expandDefault(t: PType; info: TLineInfo): PNode =
case t.kind
of tyInt: result = newZero(t, info, nkIntLit)
of tyInt8: result = newZero(t, info, nkInt8Lit)
of tyInt16: result = newZero(t, info, nkInt16Lit)
of tyInt32: result = newZero(t, info, nkInt32Lit)
of tyInt64: result = newZero(t, info, nkInt64Lit)
of tyUInt: result = newZero(t, info, nkUIntLit)
of tyUInt8: result = newZero(t, info, nkUInt8Lit)
of tyUInt16: result = newZero(t, info, nkUInt16Lit)
of tyUInt32: result = newZero(t, info, nkUInt32Lit)
of tyUInt64: result = newZero(t, info, nkUInt64Lit)
of tyFloat: result = newZero(t, info, nkFloatLit)
of tyFloat32: result = newZero(t, info, nkFloat32Lit)
of tyFloat64: result = newZero(t, info, nkFloat64Lit)
of tyFloat128: result = newZero(t, info, nkFloat64Lit)
of tyChar: result = newZero(t, info, nkCharLit)
of tyBool: result = newZero(t, info, nkIntLit)
of tyEnum:
# Could use low(T) here to finally fix old language quirks
result = newZero(t, info, nkIntLit)
of tyRange:
# Could use low(T) here to finally fix old language quirks
result = expandDefault(skipModifier t, info)
of tyVoid: result = newZero(t, info, nkEmpty)
of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned:
result = expandDefault(t.skipModifier, info)
of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
if t.hasElementType:
result = expandDefault(t.skipModifier, info)
else:
result = newZero(t, info, nkEmpty)
of tyFromExpr:
if t.n != nil and t.n.typ != nil:
result = expandDefault(t.n.typ, info)
else:
result = newZero(t, info, nkEmpty)
of tyArray:
result = newZero(t, info, nkBracket)
let n = toInt64(lengthOrd(nil, t))
for i in 0..<n:
result.add expandDefault(t.elementType, info)
of tyPtr, tyRef, tyProc, tyPointer, tyCstring:
result = newZero(t, info, nkNilLit)
of tyVar, tyLent:
let e = t.elementType
if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
# skip the modifier, `var openArray` is a (ptr, len) pair too:
result = expandDefault(e, info)
else:
result = newZero(e, info, nkNilLit)
of tySet:
result = newZero(t, info, nkCurly)
of tyObject:
result = newNodeIT(nkObjConstr, info, t)
result.add newNodeIT(nkType, info, t)
expandDefaultObj(t, info, result)
of tyTuple:
result = newZero(t, info, nkTupleConstr)
for it in t.kids:
result.add expandDefault(it, info)
of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray:
result = newZero(t, info, nkBracket)
of tyString:
result = newZero(t, info, nkStrLit)
of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
tyNil, tyGenericInvocation, 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:
# https://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
template compiler(name, settings: untyped): untyped =
proc name: TInfoCC {.compileTime.} = settings
@@ -162,11 +162,7 @@ compiler vcc:
linkerExe: "cl",
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
includeCmd: " /I",
# 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:",
linkDirCmd: " /LIBPATH:",
linkLibCmd: " $1.lib",
debug: " /RTC1 /Z7 ",
pic: "",
@@ -176,22 +172,6 @@ compiler vcc:
cppXsupport: "",
props: {hasCpp, hasAssume, hasDeclspec})
# Nvidia CUDA NVCC Compiler
compiler nvcc:
result = gcc()
result.name = "nvcc"
result.compilerExe = "nvcc"
result.cppCompiler = "nvcc"
result.compileTmpl = "-c -x cu -Xcompiler=\"$options\" $include -o $objfile $file"
result.linkTmpl = "$buildgui $builddll -o $exefile $objfiles -Xcompiler=\"$options\""
# AMD HIPCC Compiler (rocm/cuda)
compiler hipcc:
result = clang()
result.name = "hipcc"
result.compilerExe = "hipcc"
result.cppCompiler = "hipcc"
compiler clangcl:
result = vcc()
result.name = "clang_cl"
@@ -257,8 +237,8 @@ compiler tcc:
linkerExe: "tcc",
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
includeCmd: " -I",
linkDirCmd: " -L",
linkLibCmd: " -l$1",
linkDirCmd: "", # XXX: not supported yet
linkLibCmd: "", # XXX: not supported yet
debug: " -g ",
pic: "",
asmStmtFrmt: "asm($1);$n",
@@ -305,9 +285,7 @@ const
envcc(),
icl(),
icc(),
clangcl(),
hipcc(),
nvcc()]
clangcl()]
hExt* = ".h"
@@ -514,10 +492,6 @@ proc vccplatform(conf: ConfigRef): string =
of cpuArm: " --platform:arm"
of cpuAmd64: " --platform:amd64"
else: ""
else:
result = ""
else:
result = ""
proc getLinkOptions(conf: ConfigRef): string =
result = conf.linkOptions & " " & conf.linkOptionsCmd & " "
@@ -566,7 +540,7 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool =
# NOTE: should we need the full version, use -dumpfullversion
let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except IOError, OSError, ValueError: ("", 1)
if exitCode == 0:
var major: int = 0
var major: int
discard parseInt(s, major)
result = major >= 5
else:
@@ -676,7 +650,7 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1")
let currentHash = footprint(conf, cfile)
var f: File = default(File)
var f: File
if open(f, hashFile.string, fmRead):
let oldHash = parseSecureHash(f.readLine())
close(f)
@@ -786,7 +760,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
# https://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
# http://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"
@@ -810,61 +784,7 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body
(ose.msg & " " & $ose.errorCode))
raise
proc createMacAppBundle(conf: ConfigRef; exefile: AbsoluteFile) =
let (dir, name, _) = splitFile(exefile.string)
let appBundleName = name & ".app"
let appBundlePath = dir / appBundleName
let contentsPath = appBundlePath / "Contents"
let macosPath = contentsPath / "MacOS"
createDir(macosPath)
let bundleExePath = macosPath / name
copyFileWithPermissions(exefile.string, bundleExePath)
let infoPlistPath = contentsPath / "Info.plist"
proc xmlEscape(s: string): string =
result = newStringOfCap(s.len)
for c in items(s):
case c:
of '<': result.add("&lt;")
of '>': result.add("&gt;")
of '&': result.add("&amp;")
of '"': result.add("&quot;")
of '\'': result.add("&apos;")
else:
if ord(c) < 32:
result.add("&#" & $ord(c) & ';')
else:
result.add(c)
let escapedName = xmlEscape(name)
let infoPlistContent = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>$1</string>
<key>CFBundleIdentifier</key>
<string>com.nim.$1</string>
<key>CFBundleName</key>
<string>$1</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSUIElement</key>
<string>1</string>
</dict>
</plist>""" % [escapedName]
writeFile(infoPlistPath, infoPlistContent)
removeFile(exefile.string)
rawMessage(conf, hintUserRaw, "Created Mac app bundle: " & appBundlePath)
proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] =
result = @[]
when defined(macosx):
if optCDebug in conf.globalOptions and optGenStaticLib notin conf.globalOptions:
# if needed, add an option to skip or override location
@@ -947,7 +867,6 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu
result = conf.getNimcacheDir / RelativeFile(targetName)
proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string =
result = ""
if conf.hasHint(hintCC):
if optListCmd in conf.globalOptions or conf.verbosity > 1:
result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd)
@@ -970,15 +889,15 @@ proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) =
proc callCCompiler*(conf: ConfigRef) =
var
linkCmd: string = ""
linkCmd: string
extraCmds: seq[string]
if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
return # speed up that call if only compiling and no script shall be
# generated
#var c = cCompiler
var script: Rope = ""
var cmds: TStringSeq = default(TStringSeq)
var prettyCmds: TStringSeq = default(TStringSeq)
var cmds: TStringSeq
var prettyCmds: TStringSeq
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
for idx, it in conf.toCompile:
@@ -1047,10 +966,6 @@ proc callCCompiler*(conf: ConfigRef) =
preventLinkCmdMaxCmdLen(conf, linkCmd)
for cmd in extraCmds:
execExternalProgram(conf, cmd, hintExecuting)
# create Mac app bundle for GUI apps on macOS
when defined(macosx):
if conf.globalOptions * {optGenGuiApp, optGenDynLib, optGenStaticLib} == {optGenGuiApp}:
createMacAppBundle(conf, mainOutput)
else:
linkCmd = ""
if optGenScript in conf.globalOptions:
@@ -1066,11 +981,10 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
# works out of the box with `hashMainCompilationParams`.
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
type BuildCache = object
cacheVersion: string
outputFile: string
outputLastModificationTime: string
compile: seq[(string, string)]
link: seq[string]
linkcmd: string
@@ -1114,15 +1028,12 @@ 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)
proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool =
result = false
if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true
var bcache: BuildCache = default(BuildCache)
var bcache: BuildCache
try: bcache.fromJson(jsonFile.string.parseFile)
except IOError, OSError, ValueError:
stderr.write "Warning: JSON processing failed for: $#\n" % jsonFile.string
@@ -1136,11 +1047,9 @@ 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)
var bcache: BuildCache
try: bcache.fromJson(jsonFile.string.parseFile)
except ValueError, KeyError, JsonKindError:
let e = getCurrentException()
@@ -1153,8 +1062,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
globalError(conf, gCmdLineInfo,
"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 cmds, prettyCmds: TStringSeq
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
for (name, cmd) in bcache.compile:
cmds.add cmd
@@ -1164,7 +1072,6 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting)
proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
result = ""
for it in list:
result.addf("--file:r\"$1\"$N", [rope(it.cname.string)])

View File

@@ -10,11 +10,9 @@
# This module implements Nim's standard template filter.
import
llstream, ast, msgs, options,
llstream, strutils, ast, msgs, options,
filters, lineinfos, pathutils
import std/strutils
type
TParseState = enum
psDirective, psTempl
@@ -203,15 +201,17 @@ proc parseLine(p: var TTmplParser) =
proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
call: PNode): PLLStream =
var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0),
outp: llStreamOpen(""), inp: stdin,
subsChar: charArg(conf, call, "subschar", 1, '$'),
nimDirective: charArg(conf, call, "metachar", 2, '#'),
emit: strArg(conf, call, "emit", 3, "result.add"),
conc: strArg(conf, call, "conc", 4, " & "),
toStr: strArg(conf, call, "tostring", 5, "$"),
x: newStringOfCap(120)
)
var p: TTmplParser
p.config = conf
p.info = newLineInfo(conf, filename, 0, 0)
p.outp = llStreamOpen("")
p.inp = stdin
p.subsChar = charArg(conf, call, "subschar", 1, '$')
p.nimDirective = charArg(conf, call, "metachar", 2, '#')
p.emit = strArg(conf, call, "emit", 3, "result.add")
p.conc = strArg(conf, call, "conc", 4, " & ")
p.toStr = strArg(conf, call, "tostring", 5, "$")
p.x = newStringOfCap(120)
# do not process the first line which contains the directive:
if llStreamReadLine(p.inp, p.x):
inc p.info.line

View File

@@ -10,11 +10,9 @@
# This module implements Nim's simple filters and helpers for filters.
import
llstream, idents, ast, msgs, options,
llstream, idents, strutils, ast, msgs, options,
renderer, pathutils
import std/strutils
proc invalidPragma(conf: ConfigRef; n: PNode) =
localError(conf, n.info,
"'$1' not allowed here" % renderTree(n, {renderNoComments}))
@@ -31,30 +29,23 @@ proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode =
return n[i]
proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char =
var x = getArg(conf, n, name, pos)
if x == nil: result = default
elif x.kind == nkCharLit: result = chr(int(x.intVal))
else:
result = default(char)
invalidPragma(conf, n)
else: invalidPragma(conf, n)
proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string =
var x = getArg(conf, n, name, pos)
if x == nil: result = default
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
else:
result = ""
invalidPragma(conf, n)
else: invalidPragma(conf, n)
proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool =
var x = getArg(conf, n, name, pos)
if x == nil: result = default
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
else:
result = false
invalidPragma(conf, n)
else: invalidPragma(conf, n)
proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
var pattern = strArg(conf, call, "startswith", 1, "")

View File

@@ -9,9 +9,8 @@
## Module that implements ``gorge`` for the compiler.
import msgs, options, lineinfos, pathutils
import std/[os, osproc, streams]
import msgs, os, osproc, streams, options,
lineinfos, pathutils
when defined(nimPreviewSlimSystem):
import std/syncio
@@ -30,11 +29,10 @@ proc readOutput(p: Process): (string, int) =
proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
let workingDir = parentDir(toFullPath(conf, info))
result = ("", 0)
if cache.len > 0:
let h = secureHash(cmd & "\t" & input & "\t" & cache)
let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string
var f: File = default(File)
var f: File
if optForceFullMake notin conf.globalOptions and open(f, filename):
result = (f.readAll, 0)
f.close

View File

@@ -46,15 +46,11 @@ proc isLocation(n: PNode): bool = not n.isValue
proc isLet(n: PNode): bool =
if n.kind == nkSym:
if n.sym.kind in {skLet, skConst, skTemp, skForVar}: # guard immutable variables
if n.sym.kind in {skLet, skTemp, skForVar}:
result = true
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
abstractInst).kind notin {tyVar}:
result = true
else:
result = false
else:
result = false
proc isVar(n: PNode): bool =
n.kind == nkSym and n.sym.kind in {skResult, skVar} and
@@ -140,8 +136,6 @@ proc neg(n: PNode; o: Operators): PNode =
result = a
elif b != nil:
result = b
else:
result = nil
else:
# leave not (a == 4) as it is
result = newNodeI(nkCall, n.info, 2)
@@ -336,8 +330,6 @@ proc usefulFact(n: PNode; o: Operators): PNode =
result = n
elif n[1].getMagic in someLen or n[2].getMagic in someLen:
result = n
else:
result = nil
of someLe+someLt:
if isLetLocation(n[1], true) or isLetLocation(n[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
@@ -345,18 +337,12 @@ proc usefulFact(n: PNode; o: Operators): PNode =
elif n[1].getMagic in someLen or n[2].getMagic in someLen:
# XXX Rethink this whole idea of 'usefulFact' for semparallel
result = n
else:
result = nil
of mIsNil:
if isLetLocation(n[1], false) or isVar(n[1]):
result = n
else:
result = nil
of someIn:
if isLetLocation(n[1], true):
result = n
else:
result = nil
of mAnd:
let
a = usefulFact(n[1], o)
@@ -370,14 +356,10 @@ proc usefulFact(n: PNode; o: Operators): PNode =
result = a
elif b != nil:
result = b
else:
result = nil
of mNot:
let a = usefulFact(n[1], o)
if a != nil:
result = a.neg(o)
else:
result = nil
of mOr:
# 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
# with that knowledge...
@@ -394,8 +376,6 @@ proc usefulFact(n: PNode; o: Operators): PNode =
result[1] = a
result[2] = b
result = result.neg(o)
else:
result = nil
elif n.kind == nkSym and n.sym.kind == skLet:
# consider:
# let a = 2 < x
@@ -404,12 +384,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
# We make can easily replace 'a' by '2 < x' here:
if n.sym.astdef != nil:
result = usefulFact(n.sym.astdef, o)
else:
result = nil
elif n.kind == nkStmtListExpr:
result = usefulFact(n.lastSon, o)
else:
result = nil
type
TModel* = object
@@ -475,9 +451,8 @@ proc hasSubTree(n, x: PNode): bool =
of nkEmpty..nkNilLit:
result = n.sameTree(x)
of nkFormalParams:
result = false
discard
else:
result = false
for i in 0..<n.len:
if hasSubTree(n[i], x): return true
@@ -508,8 +483,6 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
proc valuesUnequal(a, b: PNode): bool =
if a.isValue and b.isValue:
result = not sameValue(a, b)
else:
result = false
proc impliesEq(fact, eq: PNode): TImplication =
let (loc, val) = if isLocation(eq[1]): (1, 2) else: (2, 1)
@@ -520,26 +493,16 @@ proc impliesEq(fact, eq: PNode): TImplication =
# this is not correct; consider: a == b; a == 1 --> unknown!
if sameTree(fact[2], eq[val]): result = impYes
elif valuesUnequal(fact[2], eq[val]): result = impNo
else:
result = impUnknown
elif sameTree(fact[2], eq[loc]):
if sameTree(fact[1], eq[val]): result = impYes
elif valuesUnequal(fact[1], eq[val]): result = impNo
else:
result = impUnknown
else:
result = impUnknown
of mInSet:
# remember: mInSet is 'contains' so the set comes first!
if sameTree(fact[2], eq[loc]) and isValue(eq[val]):
if inSet(fact[1], eq[val]): result = impYes
else: result = impNo
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesEq")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesEq")
else: discard
proc leImpliesIn(x, c, aSet: PNode): TImplication =
if c.kind in {nkCharLit..nkUInt64Lit}:
@@ -549,19 +512,13 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication =
var value = newIntNode(c.kind, firstOrd(nil, x.typ))
# don't iterate too often:
if c.intVal - value.intVal < 1000:
var i, pos, neg: int = 0
var i, pos, neg: int
while value.intVal <= c.intVal:
if inSet(aSet, value): inc pos
else: inc neg
inc i; inc value.intVal
if pos == i: result = impYes
elif neg == i: result = impNo
else:
result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
proc geImpliesIn(x, c, aSet: PNode): TImplication =
if c.kind in {nkCharLit..nkUInt64Lit}:
@@ -572,23 +529,17 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
let max = lastOrd(nil, x.typ)
# don't iterate too often:
if max - getInt(value) < toInt128(1000):
var i, pos, neg: int = 0
var i, pos, neg: int
while value.intVal <= max:
if inSet(aSet, value): inc pos
else: inc neg
inc i; inc value.intVal
if pos == i: result = impYes
elif neg == i: result = impNo
else: result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
proc compareSets(a, b: PNode): TImplication =
if equalSets(nil, a, b): result = impYes
elif intersectSets(nil, a, b).len == 0: result = impNo
else: result = impUnknown
proc impliesIn(fact, loc, aSet: PNode): TImplication =
case fact[0].sym.magic
@@ -599,32 +550,22 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication =
elif sameTree(fact[2], loc):
if inSet(aSet, fact[1]): result = impYes
else: result = impNo
else:
result = impUnknown
of mInSet:
if sameTree(fact[2], loc):
result = compareSets(fact[1], aSet)
else:
result = impUnknown
of someLe:
if sameTree(fact[1], loc):
result = leImpliesIn(fact[1], fact[2], aSet)
elif sameTree(fact[2], loc):
result = geImpliesIn(fact[2], fact[1], aSet)
else:
result = impUnknown
of someLt:
if sameTree(fact[1], loc):
result = leImpliesIn(fact[1], fact[2].pred, aSet)
elif sameTree(fact[2], loc):
# 4 < x --> 3 <= x
result = geImpliesIn(fact[2], fact[1].pred, aSet)
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesIn")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesIn")
else: discard
proc valueIsNil(n: PNode): TImplication =
if n.kind == nkNilLit: impYes
@@ -636,19 +577,13 @@ proc impliesIsNil(fact, eq: PNode): TImplication =
of mIsNil:
if sameTree(fact[1], eq[1]):
result = impYes
else:
result = impUnknown
of someEq:
if sameTree(fact[1], eq[1]):
result = valueIsNil(fact[2].skipConv)
elif sameTree(fact[2], eq[1]):
result = valueIsNil(fact[1].skipConv)
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesIsNil")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesIsNil")
else: discard
proc impliesGe(fact, x, c: PNode): TImplication =
assert isLocation(x)
@@ -659,57 +594,32 @@ proc impliesGe(fact, x, c: PNode): TImplication =
# fact: x = 4; question x >= 56? --> true iff 4 >= 56
if leValue(c, fact[2]): result = impYes
else: result = impNo
else:
result = impUnknown
elif sameTree(fact[2], x):
if isValue(fact[1]) and isValue(c):
if leValue(c, fact[1]): result = impYes
else: result = impNo
else:
result = impUnknown
else:
result = impUnknown
of someLt:
if sameTree(fact[1], x):
if isValue(fact[2]) and isValue(c):
# fact: x < 4; question N <= x? --> false iff N <= 4
if leValue(fact[2], c): result = impNo
else: result = impUnknown
# fact: x < 4; question 2 <= x? --> we don't know
else:
result = impUnknown
elif sameTree(fact[2], x):
# fact: 3 < x; question: N-1 < x ? --> true iff N-1 <= 3
if isValue(fact[1]) and isValue(c):
if leValue(c.pred, fact[1]): result = impYes
else: result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
of someLe:
if sameTree(fact[1], x):
if isValue(fact[2]) and isValue(c):
# fact: x <= 4; question x >= 56? --> false iff 4 <= 56
if leValue(fact[2], c): result = impNo
# fact: x <= 4; question x >= 2? --> we don't know
else:
result = impUnknown
else:
result = impUnknown
elif sameTree(fact[2], x):
# fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3
if isValue(fact[1]) and isValue(c):
if leValue(c, fact[1]): result = impYes
else: result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesGe")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesGe")
else: discard
proc impliesLe(fact, x, c: PNode): TImplication =
if not isLocation(x):
@@ -724,59 +634,35 @@ proc impliesLe(fact, x, c: PNode): TImplication =
# fact: x = 4; question x <= 56? --> true iff 4 <= 56
if leValue(fact[2], c): result = impYes
else: result = impNo
else:
result = impUnknown
elif sameTree(fact[2], x):
if isValue(fact[1]) and isValue(c):
if leValue(fact[1], c): result = impYes
else: result = impNo
else:
result = impUnknown
else:
result = impUnknown
of someLt:
if sameTree(fact[1], x):
if isValue(fact[2]) and isValue(c):
# fact: x < 4; question x <= N? --> true iff N-1 <= 4
if leValue(fact[2], c.pred): result = impYes
else:
result = impUnknown
# fact: x < 4; question x <= 2? --> we don't know
else:
result = impUnknown
elif sameTree(fact[2], x):
# fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3
if isValue(fact[1]) and isValue(c):
if leValue(c, fact[1]): result = impNo
else: result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
of someLe:
if sameTree(fact[1], x):
if isValue(fact[2]) and isValue(c):
# fact: x <= 4; question x <= 56? --> true iff 4 <= 56
if leValue(fact[2], c): result = impYes
else: result = impUnknown
# fact: x <= 4; question x <= 2? --> we don't know
else:
result = impUnknown
elif sameTree(fact[2], x):
# fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3
if isValue(fact[1]) and isValue(c):
if leValue(c, fact[1].pred): result = impNo
else:result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesLe")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesLe")
else: discard
proc impliesLt(fact, x, c: PNode): TImplication =
# x < 3 same as x <= 2:
@@ -788,8 +674,6 @@ proc impliesLt(fact, x, c: PNode): TImplication =
let q = x.pred
if q != x:
result = impliesLe(fact, q, c)
else:
result = impUnknown
proc `~`(x: TImplication): TImplication =
case x
@@ -841,7 +725,6 @@ proc factImplies(fact, prop: PNode): TImplication =
proc doesImply*(facts: TModel, prop: PNode): TImplication =
assert prop.kind in nkCallKinds
result = impUnknown
for f in facts.s:
# facts can be invalidated, in which case they are 'nil':
if not f.isNil:
@@ -870,7 +753,7 @@ template isSub(x): untyped = x.getMagic in someSub
template isVal(x): untyped = x.kind in {nkCharLit..nkUInt64Lit}
template isIntVal(x, y): untyped = x.intVal == y
import std/macros
import macros
macro `=~`(x: PNode, pat: untyped): bool =
proc m(x, pat, conds: NimNode) =
@@ -1017,7 +900,6 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode =
proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
# now check for inferrable facts: a <= b and b <= c implies a <= c
result = impUnknown
for i in 0..m.s.high:
let fact = m.s[i]
if fact != nil and fact.getMagic in someLe:
@@ -1063,7 +945,7 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
let b = fact[2]
if a.kind == nkSym: replacements.add((a,b))
else: replacements.add((b,a))
var m = TModel()
var m: TModel
var a = aa
var b = bb
if replacements.len > 0:
@@ -1098,13 +980,13 @@ proc addFactLt*(m: var TModel; a, b: PNode) =
addFactLe(m, a, bb)
proc settype(n: PNode): PType =
var idgen = idGeneratorForPackage(-1'i32)
result = newType(tySet, idgen, n.typ.owner)
result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner)
var idgen: IdGenerator
addSonSkipIntLit(result, n.typ, idgen)
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)
@@ -1170,7 +1052,7 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
# set field name to discriminator field name
a[1] = check[2]
# set discriminator field type: important for `neg`
a.typ() = check[2].typ
a.typ = check[2].typ
result[2] = a
# 'access.kind != nkDotExpr' can happen for object constructors
# which we don't check yet

View File

@@ -10,18 +10,16 @@
# This include implements the high level optimization pass.
# included from sem.nim
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode
proc hlo(c: PContext, n: PNode): PNode
proc evalPattern(c: PContext, n, orig: PNode): PNode =
internalAssert c.config, n.kind == nkCall and n[0].kind == nkSym
# we need to ensure that the resulting AST is semchecked. However, it's
# awful to semcheck before macro invocation, so we don't and treat
# templates and macros as immediate in this context.
var rule: string =
if c.config.hasHint(hintPattern):
renderTree(n, {renderNoComments})
else:
""
var rule: string
if c.config.hasHint(hintPattern):
rule = renderTree(n, {renderNoComments})
let s = n[0].sym
case s.kind
of skMacro:
@@ -61,11 +59,10 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
# activate this pattern again:
c.patterns[i] = pattern
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
proc hlo(c: PContext, n: PNode): PNode =
inc(c.hloLoopDetector)
# simply stop and do not perform any further transformations:
if loopDetector > 300:
message(c.config, n.info, warnUser, "term rewrite macro instantiation too nested")
return n
if c.hloLoopDetector > 300: return n
case n.kind
of nkMacroDef, nkTemplateDef, procDefs:
# already processed (special cases in semstmts.nim)
@@ -73,7 +70,7 @@ proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
else:
if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and
n[0].kind == nkSym and
{sfGlobal, sfPure} <= n[0].sym.flags:
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
# do not optimize 'var g {.global} = re(...)' again!
return n
result = applyPatterns(c, n)
@@ -81,7 +78,7 @@ proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
# no optimization applied, try subtrees:
for i in 0..<result.safeLen:
let a = result[i]
let h = hlo(c, a, loopDetector)
let h = hlo(c, a)
if h != a: result[i] = h
else:
# perform type checking, so that the replacement still fits:
@@ -91,15 +88,17 @@ proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
result = fitNode(c, n.typ, result, n.info)
# optimization has been applied so check again:
result = commonOptimizations(c.graph, c.idgen, c.module, result)
result = hlo(c, result, loopDetector + 1)
result = hlo(c, result)
result = commonOptimizations(c.graph, c.idgen, c.module, result)
proc hloBody(c: PContext, n: PNode): PNode =
# fast exit:
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
result = hlo(c, n, 0)
c.hloLoopDetector = 0
result = hlo(c, n)
proc hloStmt(c: PContext, n: PNode): PNode =
# fast exit:
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
result = hlo(c, n, 0)
c.hloLoopDetector = 0
result = hlo(c, n)

View File

@@ -1,8 +1,7 @@
## A BiTable is a table that can be seen as an optimized pair
## of `(Table[LitId, Val], Table[Val, LitId])`.
import std/hashes
import rodfiles
import hashes, rodfiles
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -14,8 +13,6 @@ type
vals: seq[T] # indexed by LitId
keys: seq[LitId] # indexed by hash(val)
proc initBiTable*[T](): BiTable[T] = BiTable[T](vals: @[], keys: @[])
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = (h + 1) and maxHash
@@ -36,7 +33,9 @@ proc mustRehash(length, counter: int): bool {.inline.} =
result = (length * 2 < counter * 3) or (length - counter < 4)
const
idStart = 1
idStart = 256 ##
## Ids do not start with 0 but with this value. The IR needs it.
## TODO: explain why
template idToIdx(x: LitId): int = x.int - idStart
@@ -119,12 +118,6 @@ proc load*[T](f: var RodFile; t: var BiTable[T]) =
loadSeq(f, t.vals)
loadSeq(f, t.keys)
proc sizeOnDisc*(t: BiTable[string]): int =
result = 4
for x in t.vals:
result += x.len + 4
result += t.keys.len * sizeof(LitId)
when isMainModule:
var t: BiTable[string]

View File

@@ -18,7 +18,7 @@
## also doing cross-module dependency tracking and DCE that we don't need
## anymore. DCE is now done as prepass over the entire packed module graph.
import std/[packedsets, algorithm, tables]
import std/packedsets, algorithm, tables
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -50,9 +50,10 @@ proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var Alive
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
cgen.genTopLevelStmt(bmod, n)
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
for disp in getDispatchers(g):
genProcAux(bmod, disp)
let disps = finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
if disps != nil:
for disp in disps:
genProcAux(bmod, disp.sym)
m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
@@ -100,7 +101,7 @@ proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool
var f2 = rodfiles.open(asymFile.string)
f2.loadHeader()
f2.loadSection aliveSymsSection
var oldData: seq[int32] = @[]
var oldData: seq[int32]
f2.loadSeq(oldData)
f2.close
if f2.err == ok and oldData == s:
@@ -146,12 +147,12 @@ proc generateCode*(g: ModuleGraph) =
var alive = computeAliveSyms(g.packed, g.config)
when false:
for i in 0..<len(g.packed):
for i in 0..high(g.packed):
echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i))
# First pass: Setup all the backend modules for all the modules that have
# changed:
for i in 0..<len(g.packed):
for i in 0..high(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
@@ -173,7 +174,7 @@ proc generateCode*(g: ModuleGraph) =
let mainModuleIdx = g.config.projectMainIdx2.int
# We need to generate the main module last, because only then
# all init procs have been registered:
for i in 0..<len(g.packed):
for i in 0..high(g.packed):
if i != mainModuleIdx:
genPackedModule(g, i, alive)
if mainModuleIdx >= 0:

View File

@@ -40,14 +40,10 @@ proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): boo
if ({sfExportc, sfCompilerProc} * flags != {}) or
(symPtr.kind == skMethod):
result = true
else:
result = false
# XXX: This used to be a condition to:
# (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
if sfCompilerProc in flags:
c.compilerProcs[g[c.thisModule].fromDisk.strings[symPtr.name]] = (c.thisModule, symId)
else:
result = false
template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
@@ -109,13 +105,13 @@ proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: N
discard "ignore non-sym atoms"
of nkSym:
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, tree[n].soperand)
followLater(c, g, c.thisModule, n.operand)
of nkModuleRef:
let (n1, n2) = sons2(tree, n)
assert n1.kind == nkNone
assert n2.kind == nkNone
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
let m = n1.litId
let item = tree[n2].soperand
let item = n2.operand
let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
followLater(c, g, otherModule, item)
of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr,
@@ -131,7 +127,7 @@ proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: N
rangeCheckAnalysis(c, g, tree, n)
of nkProcDef, nkConverterDef, nkMethodDef, nkFuncDef, nkIteratorDef:
if n.firstSon.kind == nkSym and isNotGeneric(n):
let item = tree[n.firstSon].soperand
let item = n.firstSon.operand
if isExportedToC(c, g, item):
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, item)
@@ -153,7 +149,7 @@ proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms =
var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf),
thisModule: -1, alive: newSeq[IntSet](g.len),
options: conf.options)
for i in countdown(len(g)-1, 0):
for i in countdown(high(g), 0):
if g[i].status != undefined:
c.thisModule = i
for p in allNodes(g[i].fromDisk.topLevel):

View File

@@ -7,17 +7,15 @@
# distribution, for details about the copyright.
#
import std/[hashes, tables, intsets, monotimes]
import hashes, tables, intsets
import packed_ast, bitabs, rodfiles
import ".." / [ast, idents, lineinfos, msgs, ropes, options,
pathutils, condsyms, packages, modulepaths]
#import ".." / [renderer, astalgo]
from std/os import removeFile, isAbsolute
from os import removeFile, isAbsolute
import ../../dist/checksums/src/checksums/sha1
import iclineinfos
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions, formatfloat]
@@ -43,28 +41,25 @@ type
bodies*: PackedTree # other trees. Referenced from typ.n and sym.ast by their position.
#producedGenerics*: Table[GenericKey, SymId]
exports*: seq[(LitId, int32)]
hidden: seq[(LitId, int32)]
reexports: seq[(LitId, PackedItemId)]
hidden*: seq[(LitId, int32)]
reexports*: seq[(LitId, PackedItemId)]
compilerProcs*: seq[(LitId, int32)]
converters*, methods*, trmacros*, pureEnums*: seq[int32]
typeInstCache*: seq[(PackedItemId, PackedItemId)]
procInstCache*: seq[PackedInstantiation]
attachedOps*: seq[(PackedItemId, TTypeAttachedOp, PackedItemId)]
methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
methodsPerType*: seq[(PackedItemId, PackedItemId)]
dispatchers*: seq[PackedItemId]
emittedTypeInfo*: seq[string]
backendFlags*: set[ModuleBackendFlag]
syms*: OrderedTable[int32, PackedSym]
types*: OrderedTable[int32, PackedType]
syms*: seq[PackedSym]
types*: seq[PackedType]
strings*: BiTable[string] # we could share these between modules.
numbers*: BiTable[BiggestInt] # we also store floats in here so
# that we can assure that every bit is kept
man*: LineInfoManager
cfg: PackedConfig
@@ -80,36 +75,37 @@ type
symMarker*: IntSet #Table[ItemId, SymId] # ItemId.item -> SymId
config*: ConfigRef
proc toString*(tree: PackedTree; pos: NodePos; m: PackedModule; nesting: int;
proc toString*(tree: PackedTree; n: NodePos; m: PackedModule; nesting: int;
result: var string) =
let pos = n.int
if result.len > 0 and result[^1] notin {' ', '\n'}:
result.add ' '
result.add $tree[pos].kind
case tree[pos].kind
of nkEmpty, nkNilLit, nkType: discard
case tree.nodes[pos].kind
of nkNone, nkEmpty, nkNilLit, nkType: discard
of nkIdent, nkStrLit..nkTripleStrLit:
result.add " "
result.add m.strings[LitId tree[pos].uoperand]
result.add m.strings[LitId tree.nodes[pos].operand]
of nkSym:
result.add " "
result.add m.strings[m.syms[tree[pos].soperand].name]
result.add m.strings[m.syms[tree.nodes[pos].operand].name]
of directIntLit:
result.add " "
result.addInt tree[pos].soperand
result.addInt tree.nodes[pos].operand
of externSIntLit:
result.add " "
result.addInt m.numbers[LitId tree[pos].uoperand]
result.addInt m.numbers[LitId tree.nodes[pos].operand]
of externUIntLit:
result.add " "
result.addInt cast[uint64](m.numbers[LitId tree[pos].uoperand])
result.addInt cast[uint64](m.numbers[LitId tree.nodes[pos].operand])
of nkFloatLit..nkFloat128Lit:
result.add " "
result.addFloat cast[BiggestFloat](m.numbers[LitId tree[pos].uoperand])
result.addFloat cast[BiggestFloat](m.numbers[LitId tree.nodes[pos].operand])
else:
result.add "(\n"
for i in 1..(nesting+1)*2: result.add ' '
for child in sonsReadonly(tree, pos):
for child in sonsReadonly(tree, n):
toString(tree, child, m, nesting + 1, result)
result.add "\n"
for i in 1..nesting*2: result.add ' '
@@ -235,14 +231,10 @@ proc addImportFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex)
m.imports.add toLitId(f, c, m)
proc addHidden*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
assert s.kind != skUnknown
let nameId = getOrIncl(m.strings, s.name.s)
m.hidden.add((nameId, s.itemId.item))
assert s.itemId.module == c.thisModule
proc addExported*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
assert s.kind != skUnknown
assert s.itemId.module == c.thisModule
let nameId = getOrIncl(m.strings, s.name.s)
m.exports.add((nameId, s.itemId.item))
@@ -261,8 +253,6 @@ proc addMethod*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
m.methods.add s.itemId.item
proc addReexport*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
assert s.kind != skUnknown
if s.kind == skModule: return
let nameId = getOrIncl(m.strings, s.name.s)
m.reexports.add((nameId, PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m),
item: s.itemId.item)))
@@ -294,8 +284,7 @@ proc toLitId(x: BiggestInt; m: var PackedModule): LitId =
result = getOrIncl(m.numbers, x)
proc toPackedInfo(x: TLineInfo; c: var PackedEncoder; m: var PackedModule): PackedLineInfo =
pack(m.man, toLitId(x.fileIndex, c, m), x.line.int32, x.col.int32)
#PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m))
PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m))
proc safeItemId(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId {.inline.} =
## given a symbol, produce an ItemId with the correct properties
@@ -347,7 +336,7 @@ proc storeTypeLater(t: PType; c: var PackedEncoder; m: var PackedModule): Packed
proc storeSymLater(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId =
if s.isNil: return nilItemId
assert s.itemId.module >= 0
assert s.itemId.item >= 0
assert s.itemId.module >= 0
result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item)
if s.itemId.module == c.thisModule:
# the sym belongs to this module, so serialize it here, eventually.
@@ -362,15 +351,15 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item)
if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item):
#if t.uniqueId.item >= m.types.len:
# setLen m.types, t.uniqueId.item+1
if t.uniqueId.item >= m.types.len:
setLen m.types, t.uniqueId.item+1
var p = PackedType(id: t.uniqueId.item, kind: t.kind, flags: t.flags, callConv: t.callConv,
var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv,
size: t.size, align: t.align, nonUniqueId: t.itemId.item,
paddingAtEnd: t.paddingAtEnd)
storeNode(p, t, n)
p.typeInst = t.typeInst.storeType(c, m)
for kid in kids t:
for kid in items t.sons:
p.types.add kid.storeType(c, m)
c.addMissing t.sym
p.sym = t.sym.safeItemId(c, m)
@@ -383,9 +372,10 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib =
## the plib hangs off the psym via the .annex field
if l.isNil: return
result = PackedLib(kind: l.kind, generated: l.generated,
isOverridden: l.isOverridden, name: toLitId($l.name, m)
)
result.kind = l.kind
result.generated = l.generated
result.isOverridden = l.isOverridden
result.name = toLitId($l.name, m)
storeNode(result, l, path)
proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId =
@@ -396,12 +386,12 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item)
if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item):
#if s.itemId.item >= m.syms.len:
# setLen m.syms, s.itemId.item+1
if s.itemId.item >= m.syms.len:
setLen m.syms, s.itemId.item+1
assert sfForward notin s.flags
var p = PackedSym(id: s.itemId.item, kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
position: s.position, offset: s.offset, disamb: s.disamb, options: s.options,
name: s.name.s.toLitId(m))
@@ -414,7 +404,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.snippet, m)
p.externalName = toLitId(s.loc.r, m)
p.locFlags = s.loc.flags
c.addMissing s.typ
p.typ = s.typ.storeType(c, m)
@@ -423,7 +413,6 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
p.annex = toPackedLib(s.annex, c, m)
when hasFFI:
p.cname = toLitId(s.cname, m)
p.instantiatedFrom = s.instantiatedFrom.safeItemId(c, m)
# fill the reserved slot, nothing else:
m.syms[s.itemId.item] = p
@@ -431,59 +420,53 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
## add a remote symbol reference to the tree
let info = n.info.toPackedInfo(c, m)
if n.typ != n.sym.typ:
ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total
info = info, flags = n.flags,
typeId = storeTypeLater(n.typ, c, m))
else:
ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total
info = info, flags = n.flags)
ir.addNode(kind = nkNone, info = info,
operand = toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
ir.addNode(kind = nkNone, info = info,
operand = n.sym.itemId.item)
ir.nodes.add PackedNode(kind: nkModuleRef, operand: 3.int32, # spans 3 nodes in total
typeId: storeTypeLater(n.typ, c, m), info: info, flags: n.flags)
ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
operand: toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
operand: n.sym.itemId.item)
proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
## serialize a node into the tree
if n == nil:
ir.addNode(kind = nkNilRodNode, operand = 1, info = NoLineInfo)
ir.nodes.add PackedNode(kind: nkNilRodNode, flags: {}, operand: 1)
return
let info = toPackedInfo(n.info, c, m)
case n.kind
of nkNone, nkEmpty, nkNilLit, nkType:
ir.addNode(kind = n.kind, flags = n.flags, operand = 0,
typeId = storeTypeLater(n.typ, c, m), info = info)
ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, operand: 0,
typeId: storeTypeLater(n.typ, c, m), info: info)
of nkIdent:
ir.addNode(kind = n.kind, flags = n.flags,
operand = int32 getOrIncl(m.strings, n.ident.s),
typeId = storeTypeLater(n.typ, c, m), info = info)
ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
operand: int32 getOrIncl(m.strings, n.ident.s),
typeId: storeTypeLater(n.typ, c, m), info: info)
of nkSym:
if n.sym.itemId.module == c.thisModule:
# it is a symbol that belongs to the module we're currently
# packing:
let id = n.sym.storeSymLater(c, m).item
if n.typ != n.sym.typ:
ir.addNode(kind = nkSym, flags = n.flags, operand = id,
info = info,
typeId = storeTypeLater(n.typ, c, m))
else:
ir.addNode(kind = nkSym, flags = n.flags, operand = id,
info = info)
ir.nodes.add PackedNode(kind: nkSym, flags: n.flags, operand: id,
typeId: storeTypeLater(n.typ, c, m), info: info)
else:
# store it as an external module reference:
addModuleRef(n, ir, c, m)
of directIntLit:
ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
operand: int32(n.intVal),
typeId: storeTypeLater(n.typ, c, m), info: info)
of externIntLit:
ir.addNode(kind = n.kind, flags = n.flags,
operand = int32 getOrIncl(m.numbers, n.intVal),
typeId = storeTypeLater(n.typ, c, m), info = info)
ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
operand: int32 getOrIncl(m.numbers, n.intVal),
typeId: storeTypeLater(n.typ, c, m), info: info)
of nkStrLit..nkTripleStrLit:
ir.addNode(kind = n.kind, flags = n.flags,
operand = int32 getOrIncl(m.strings, n.strVal),
typeId = storeTypeLater(n.typ, c, m), info = info)
ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
operand: int32 getOrIncl(m.strings, n.strVal),
typeId: storeTypeLater(n.typ, c, m), info: info)
of nkFloatLit..nkFloat128Lit:
ir.addNode(kind = n.kind, flags = n.flags,
operand = int32 getOrIncl(m.numbers, cast[BiggestInt](n.floatVal)),
typeId = storeTypeLater(n.typ, c, m), info = info)
ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
operand: int32 getOrIncl(m.numbers, cast[BiggestInt](n.floatVal)),
typeId: storeTypeLater(n.typ, c, m), info: info)
else:
let patchPos = ir.prepare(n.kind, n.flags,
storeTypeLater(n.typ, c, m), info)
@@ -508,8 +491,8 @@ proc toPackedProcDef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var
# do not serialize the body of the proc, it's unnecessary since
# n[0].sym.ast has the sem'checked variant of it which is what
# everybody should use instead.
ir.addNode(kind = nkEmpty, flags = {}, operand = 0,
typeId = nilItemId, info = info)
ir.nodes.add PackedNode(kind: nkEmpty, flags: {}, operand: 0,
typeId: nilItemId, info: info)
ir.patch patchPos
proc toPackedNodeIgnoreProcDefs(n: PNode, encoder: var PackedEncoder; m: var PackedModule) =
@@ -582,20 +565,6 @@ proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile =
result = changeFileExt(completeGeneratedFilePath(conf,
mangleModuleName(conf, f).AbsoluteFile), ext)
const
BenchIC* = false
when BenchIC:
var gloadBodies: MonoTime
template bench(x, body) =
let start = getMonoTime()
body
x = x + (getMonoTime() - start)
else:
template bench(x, body) = body
proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef;
ignoreConfig = false): RodFileError =
var f = rodfiles.open(filename.string)
@@ -613,10 +582,6 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
f.loadSection section
f.loadSeq data
template loadTableSection(section, data) {.dirty.} =
f.loadSection section
f.loadOrderedTable data
template loadTabSection(section, data) {.dirty.} =
f.loadSection section
f.load data
@@ -624,48 +589,40 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
loadTabSection stringsSection, m.strings
loadSeqSection checkSumsSection, m.includes
if config.cmd != cmdM and not includesIdentical(m, config):
if not includesIdentical(m, config):
f.err = includeFileChanged
loadSeqSection depsSection, m.imports
bench gloadBodies:
loadTabSection numbersSection, m.numbers
loadTabSection numbersSection, m.numbers
loadSeqSection exportsSection, m.exports
loadSeqSection hiddenSection, m.hidden
loadSeqSection reexportsSection, m.reexports
loadSeqSection exportsSection, m.exports
loadSeqSection hiddenSection, m.hidden
loadSeqSection reexportsSection, m.reexports
loadSeqSection compilerProcsSection, m.compilerProcs
loadSeqSection compilerProcsSection, m.compilerProcs
loadSeqSection trmacrosSection, m.trmacros
loadSeqSection trmacrosSection, m.trmacros
loadSeqSection convertersSection, m.converters
loadSeqSection methodsSection, m.methods
loadSeqSection pureEnumsSection, m.pureEnums
loadSeqSection convertersSection, m.converters
loadSeqSection methodsSection, m.methods
loadSeqSection pureEnumsSection, m.pureEnums
loadSeqSection toReplaySection, m.toReplay.nodes
loadSeqSection topLevelSection, m.topLevel.nodes
loadSeqSection bodiesSection, m.bodies.nodes
loadSeqSection symsSection, m.syms
loadSeqSection typesSection, m.types
loadTabSection toReplaySection, m.toReplay
loadTabSection topLevelSection, m.topLevel
loadSeqSection typeInstCacheSection, m.typeInstCache
loadSeqSection procInstCacheSection, m.procInstCache
loadSeqSection attachedOpsSection, m.attachedOps
loadSeqSection methodsPerTypeSection, m.methodsPerType
loadSeqSection enumToStringProcsSection, m.enumToStringProcs
loadSeqSection typeInfoSection, m.emittedTypeInfo
loadTabSection bodiesSection, m.bodies
loadTableSection symsSection, m.syms
loadTableSection typesSection, m.types
loadSeqSection typeInstCacheSection, m.typeInstCache
loadSeqSection procInstCacheSection, m.procInstCache
loadSeqSection attachedOpsSection, m.attachedOps
loadSeqSection methodsPerGenericTypeSection, m.methodsPerGenericType
loadSeqSection enumToStringProcsSection, m.enumToStringProcs
loadSeqSection methodsPerTypeSection, m.methodsPerType
loadSeqSection dispatchersSection, m.dispatchers
loadSeqSection typeInfoSection, m.emittedTypeInfo
f.loadSection backendFlagsSection
f.loadPrim m.backendFlags
f.loadSection sideChannelSection
f.load m.man
f.loadSection backendFlagsSection
f.loadPrim m.backendFlags
close(f)
result = f.err
@@ -695,10 +652,6 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
f.storeSection section
f.store data
template storeTableSection(section, data) {.dirty.} =
f.storeSection section
f.storeOrderedTable data
storeTabSection stringsSection, m.strings
storeSeqSection checkSumsSection, m.includes
@@ -718,29 +671,24 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
storeSeqSection methodsSection, m.methods
storeSeqSection pureEnumsSection, m.pureEnums
storeTabSection toReplaySection, m.toReplay
storeTabSection topLevelSection, m.topLevel
storeSeqSection toReplaySection, m.toReplay.nodes
storeSeqSection topLevelSection, m.topLevel.nodes
storeTabSection bodiesSection, m.bodies
storeTableSection symsSection, m.syms
storeSeqSection bodiesSection, m.bodies.nodes
storeSeqSection symsSection, m.syms
storeTableSection typesSection, m.types
storeSeqSection typesSection, m.types
storeSeqSection typeInstCacheSection, m.typeInstCache
storeSeqSection procInstCacheSection, m.procInstCache
storeSeqSection attachedOpsSection, m.attachedOps
storeSeqSection methodsPerGenericTypeSection, m.methodsPerGenericType
storeSeqSection enumToStringProcsSection, m.enumToStringProcs
storeSeqSection methodsPerTypeSection, m.methodsPerType
storeSeqSection dispatchersSection, m.dispatchers
storeSeqSection enumToStringProcsSection, m.enumToStringProcs
storeSeqSection typeInfoSection, m.emittedTypeInfo
f.storeSection backendFlagsSection
f.storePrim m.backendFlags
f.storeSection sideChannelSection
f.store m.man
close(f)
encoder.disable()
if f.err != ok:
@@ -775,36 +723,20 @@ type
status*: ModuleStatus
symsInit, typesInit, loadedButAliveSetChanged*: bool
fromDisk*: PackedModule
syms: OrderedTable[int32, PSym] # indexed by itemId
types: OrderedTable[int32, PType]
syms: seq[PSym] # indexed by itemId
types: seq[PType]
module*: PSym # the one true module symbol.
iface, ifaceHidden: Table[PIdent, seq[PackedItemId]]
# PackedItemId so that it works with reexported symbols too
# ifaceHidden includes private symbols
type
PackedModuleGraph* = object
pm*: seq[LoadedModule] # indexed by FileIndex
when BenchIC:
depAnalysis: MonoTime
loadBody: MonoTime
loadSym, loadType, loadBodies: MonoTime
when BenchIC:
proc echoTimes*(m: PackedModuleGraph) =
echo "analysis: ", m.depAnalysis, " loadBody: ", m.loadBody, " loadSym: ",
m.loadSym, " loadType: ", m.loadType, " all bodies: ", gloadBodies
template `[]`*(m: PackedModuleGraph; i: int): LoadedModule = m.pm[i]
template len*(m: PackedModuleGraph): int = m.pm.len
PackedModuleGraph* = seq[LoadedModule] # indexed by FileIndex
proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType
proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym
proc toFileIndexCached*(c: var PackedDecoder; g: PackedModuleGraph; thisModule: int; f: LitId): FileIndex =
if f == LitId(0):
result = InvalidFileIdx
elif c.lastLit == f and c.lastModule == thisModule:
if c.lastLit == f and c.lastModule == thisModule:
result = c.lastFile
else:
result = toFileIndex(f, g[thisModule].fromDisk, c.config)
@@ -815,9 +747,8 @@ proc toFileIndexCached*(c: var PackedDecoder; g: PackedModuleGraph; thisModule:
proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
x: PackedLineInfo): TLineInfo =
assert g[thisModule].status in {loaded, storing, stored}
let (fileId, line, col) = unpack(g[thisModule].fromDisk.man, x)
result = TLineInfo(line: line.uint16, col: col.int16,
fileIndex: toFileIndexCached(c, g, thisModule, fileId))
result = TLineInfo(line: x.line, col: x.col,
fileIndex: toFileIndexCached(c, g, thisModule, x.file))
proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode =
@@ -831,14 +762,16 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
result.flags = n.flags
case k
of nkNone, nkEmpty, nkNilLit, nkType:
of nkEmpty, nkNilLit, nkType:
discard
of nkIdent:
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))
result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand))
if result.typ == nil:
result.typ() = result.sym.typ
result.typ = result.sym.typ
of directIntLit:
result.intVal = tree.nodes[n.int].operand
of externIntLit:
result.intVal = g[thisModule].fromDisk.numbers[n.litId]
of nkStrLit..nkTripleStrLit:
@@ -847,12 +780,12 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
result.floatVal = cast[BiggestFloat](g[thisModule].fromDisk.numbers[n.litId])
of nkModuleRef:
let (n1, n2) = sons2(tree, n)
assert n1.kind == nkNone
assert n2.kind == nkNone
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
transitionNoneToSym(result)
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand))
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
if result.typ == nil:
result.typ() = result.sym.typ
result.typ = result.sym.typ
else:
for n0 in sonsReadonly(tree, n):
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
@@ -884,7 +817,6 @@ proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; thisModule:
proc loadProcBody(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode =
result = nil
var i = 0
for n0 in sonsReadonly(tree, n):
if i == bodyPos:
@@ -942,38 +874,23 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
result.guard = loadSym(c, g, si, s.guard)
result.bitsize = s.bitsize
result.alignment = s.alignment
setOwner(result, loadSym(c, g, si, s.owner))
result.owner = loadSym(c, g, si, s.owner)
let externalName = g[si].fromDisk.strings[s.externalName]
if externalName != "":
result.loc.snippet = externalName
result.loc.r = rope externalName
result.loc.flags = s.locFlags
result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom)
proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex; cachedModules: var seq[FileIndex]): bool
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex; m: var LoadedModule)
proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym =
if s == nilItemId:
result = nil
else:
let si = moduleIndex(c, g, thisModule, s)
if si >= g.len:
g.pm.setLen(si+1)
if g[si].status == undefined and c.config.cmd == cmdM:
var cachedModules: seq[FileIndex] = @[]
discard needsRecompile(g, c.config, c.cache, FileIndex(si), cachedModules)
for m in cachedModules:
loadToReplayNodes(g, c.config, c.cache, m, g[int m])
assert g[si].status in {loaded, storing, stored}
#if not g[si].symsInit:
# g[si].symsInit = true
# setLen g[si].syms, g[si].fromDisk.syms.len
if not g[si].symsInit:
g[si].symsInit = true
setLen g[si].syms, g[si].fromDisk.syms.len
if g[si].syms.getOrDefault(s.item) == nil:
if g[si].syms[s.item] == nil:
if g[si].fromDisk.syms[s.item].kind != skModule:
result = symHeaderFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item)
# store it here early on, so that recursions work properly:
@@ -982,7 +899,6 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s:
else:
result = g[si].module
assert result != nil
g[si].syms[s.item] = result
else:
result = g[si].syms[s.item]
@@ -998,15 +914,13 @@ 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)
setOwner(result, loadSym(c, g, si, t.owner))
result.owner = loadSym(c, g, si, t.owner)
when false:
for op, item in pairs t.attachedOps:
result.attachedOps[op] = loadSym(c, g, si, item)
result.typeInst = loadType(c, g, si, t.typeInst)
var sons = newSeq[PType]()
for son in items t.types:
sons.add loadType(c, g, si, son)
result.setSons(sons)
result.sons.add loadType(c, g, si, son)
loadAstBody(t, n)
when false:
for gen, id in items t.methods:
@@ -1020,20 +934,18 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t
assert g[si].status in {loaded, storing, stored}
assert t.item > 0
#if not g[si].typesInit:
# g[si].typesInit = true
# setLen g[si].types, g[si].fromDisk.types.len
if not g[si].typesInit:
g[si].typesInit = true
setLen g[si].types, g[si].fromDisk.types.len
if g[si].types.getOrDefault(t.item) == nil:
if g[si].types[t.item] == nil:
result = typeHeaderFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item)
# store it here early on, so that recursions work properly:
g[si].types[t.item] = result
typeBodyFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item, result)
#assert result.itemId.item == t.item, $(result.itemId.item, t.item)
assert result.itemId.item > 0, $(result.itemId.item, t.item)
else:
result = g[si].types[t.item]
assert result.itemId.item > 0, "2"
assert result.itemId.item > 0
proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex; m: var LoadedModule) =
@@ -1062,7 +974,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
name: getIdent(cache, splitFile(filename).name),
info: newLineInfo(fileIdx, 1, 1),
position: int(fileIdx))
setOwner(m.module, getPackage(conf, cache, fileIdx))
m.module.owner = getPackage(conf, cache, fileIdx)
m.module.flags = m.fromDisk.moduleFlags
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
@@ -1083,37 +995,30 @@ proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache
# Does the file belong to the fileIdx need to be recompiled?
let m = int(fileIdx)
if m >= g.len:
g.pm.setLen(m+1)
g.setLen(m+1)
case g[m].status
of undefined:
g[m].status = loading
let fullpath = msgs.toFullPath(conf, fileIdx)
let rod = toRodFile(conf, AbsoluteFile fullpath)
let err = loadRodFile(rod, g[m].fromDisk, conf, ignoreConfig = conf.cmd == cmdM)
let err = loadRodFile(rod, g[m].fromDisk, conf)
if err == ok:
if conf.cmd == cmdM:
result = optForceFullMake in conf.globalOptions
# check its dependencies:
for dep in g[m].fromDisk.imports:
let fid = toFileIndex(dep, g[m].fromDisk, conf)
# Warning: we need to traverse the full graph, so
# do **not use break here**!
if needsRecompile(g, conf, cache, fid, cachedModules):
result = true
if not result:
setupLookupTables(g, conf, cache, fileIdx, g[m])
cachedModules.add fileIdx
g[m].status = loaded
result = false
else:
result = optForceFullMake in conf.globalOptions
# check its dependencies:
let imp = g[m].fromDisk.imports
for dep in imp:
let fid = toFileIndex(dep, g[m].fromDisk, conf)
# Warning: we need to traverse the full graph, so
# do **not use break here**!
if needsRecompile(g, conf, cache, fid, cachedModules):
result = true
if not result:
setupLookupTables(g, conf, cache, fileIdx, g[m])
cachedModules.add fileIdx
g[m].status = loaded
else:
g.pm[m] = LoadedModule(status: outdated, module: g[m].module)
g[m] = LoadedModule(status: outdated, module: g[m].module)
else:
loadError(err, rod, conf)
g[m].status = outdated
@@ -1128,13 +1033,12 @@ proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache
proc moduleFromRodFile*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex; cachedModules: var seq[FileIndex]): PSym =
## Returns 'nil' if the module needs to be recompiled.
bench g.depAnalysis:
if needsRecompile(g, conf, cache, fileIdx, cachedModules):
result = nil
else:
result = g[int fileIdx].module
assert result != nil
assert result.position == int(fileIdx)
if needsRecompile(g, conf, cache, fileIdx, cachedModules):
result = nil
else:
result = g[int fileIdx].module
assert result != nil
assert result.position == int(fileIdx)
for m in cachedModules:
loadToReplayNodes(g, conf, cache, m, g[int m])
@@ -1148,43 +1052,46 @@ template setupDecoder() {.dirty.} =
proc loadProcBody*(config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; s: PSym): PNode =
bench g.loadBody:
let mId = s.itemId.module
let mId = s.itemId.module
var decoder = PackedDecoder(
lastModule: int32(-1),
lastLit: LitId(0),
lastFile: FileIndex(-1),
config: config,
cache: cache)
let pos = g[mId].fromDisk.syms[s.itemId.item].ast
assert pos != emptyNodeId
result = loadProcBody(decoder, g, mId, g[mId].fromDisk.bodies, NodePos pos)
proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; module: int; id: PackedItemId): PType =
if id.item < g[module].types.len:
result = g[module].types[id.item]
else:
result = nil
if result == nil:
var decoder = PackedDecoder(
lastModule: int32(-1),
lastLit: LitId(0),
lastFile: FileIndex(-1),
config: config,
cache: cache)
let pos = g[mId].fromDisk.syms[s.itemId.item].ast
assert pos != emptyNodeId
result = loadProcBody(decoder, g, mId, g[mId].fromDisk.bodies, NodePos pos)
proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; module: int; id: PackedItemId): PType =
bench g.loadType:
result = g[module].types.getOrDefault(id.item)
if result == nil:
var decoder = PackedDecoder(
lastModule: int32(-1),
lastLit: LitId(0),
lastFile: FileIndex(-1),
config: config,
cache: cache)
result = loadType(decoder, g, module, id)
result = loadType(decoder, g, module, id)
proc loadSymFromId*(config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; module: int; id: PackedItemId): PSym =
bench g.loadSym:
result = g[module].syms.getOrDefault(id.item)
if result == nil:
var decoder = PackedDecoder(
lastModule: int32(-1),
lastLit: LitId(0),
lastFile: FileIndex(-1),
config: config,
cache: cache)
result = loadSym(decoder, g, module, id)
if id.item < g[module].syms.len:
result = g[module].syms[id.item]
else:
result = nil
if result == nil:
var decoder = PackedDecoder(
lastModule: int32(-1),
lastLit: LitId(0),
lastFile: FileIndex(-1),
config: config,
cache: cache)
result = loadSym(decoder, g, module, id)
proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; config: ConfigRef): ItemId =
if id.module == LitId(0):
@@ -1192,6 +1099,19 @@ proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; confi
else:
ItemId(module: toFileIndex(id.module, g[thisModule].fromDisk, config).int32, item: id.item)
proc checkForHoles(m: PackedModule; config: ConfigRef; moduleId: int) =
var bugs = 0
for i in 1 .. high(m.syms):
if m.syms[i].kind == skUnknown:
echo "EMPTY ID ", i, " module ", moduleId, " ", toFullPath(config, FileIndex(moduleId))
inc bugs
assert bugs == 0
when false:
var nones = 0
for i in 1 .. high(m.types):
inc nones, m.types[i].kind == tyNone
assert nones < 1
proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
moduleSym: PSym; m: PackedModule) =
# For now only used for heavy debugging. In the future we could use this to reduce the
@@ -1231,11 +1151,9 @@ proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache;
if it.i < it.values.len:
result = loadSym(it.decoder, g, int(module), it.values[it.i])
inc it.i
else:
result = nil
proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; module: FileIndex; importHidden: bool): PSym =
g: var PackedModuleGraph; module: FileIndex, importHidden: bool): PSym =
it.decoder = PackedDecoder(
lastModule: int32(-1),
lastLit: LitId(0),
@@ -1250,15 +1168,11 @@ proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache;
if it.i < it.values.len:
result = loadSym(it.decoder, g, int(module), it.values[it.i])
inc it.i
else:
result = nil
proc nextRodIter*(it: var RodIter; g: var PackedModuleGraph): PSym =
if it.i < it.values.len:
result = loadSym(it.decoder, g, it.module, it.values[it.i])
inc it.i
else:
result = nil
iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache;
g: var PackedModuleGraph; module: FileIndex;
@@ -1291,12 +1205,12 @@ proc searchForCompilerproc*(m: LoadedModule; name: string): int32 =
# ------------------------- .rod file viewer ---------------------------------
proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =
var m: PackedModule = PackedModule()
var m: PackedModule
let err = loadRodFile(rodfile, m, config, ignoreConfig=true)
if err != ok:
config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err
when false:
when true:
echo "exports:"
for ex in m.exports:
echo " ", m.strings[ex[0]], " local ID: ", ex[1]
@@ -1312,32 +1226,13 @@ proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =
for ex in m.hidden:
echo " ", m.strings[ex[0]], " local ID: ", ex[1]
when false:
echo "all symbols"
for i in 0..high(m.syms):
if m.syms[i].name != LitId(0):
echo " ", m.strings[m.syms[i].name], " local ID: ", i, " kind ", m.syms[i].kind
else:
echo " <anon symbol?> local ID: ", i, " kind ", m.syms[i].kind
echo "all symbols"
for i in 0..high(m.syms):
if m.syms[i].name != LitId(0):
echo " ", m.strings[m.syms[i].name], " local ID: ", i, " kind ", m.syms[i].kind
else:
echo " <anon symbol?> local ID: ", i, " kind ", m.syms[i].kind
echo "symbols: ", m.syms.len, " types: ", m.types.len,
" top level nodes: ", m.topLevel.len, " other nodes: ", m.bodies.len,
" top level nodes: ", m.topLevel.nodes.len, " other nodes: ", m.bodies.nodes.len,
" strings: ", m.strings.len, " numbers: ", m.numbers.len
echo "SIZES:"
echo "symbols: ", m.syms.len * sizeof(PackedSym), " types: ", m.types.len * sizeof(PackedType),
" top level nodes: ", m.topLevel.len * sizeof(PackedNode),
" other nodes: ", m.bodies.len * sizeof(PackedNode),
" strings: ", sizeOnDisc(m.strings)
when false:
var tt = 0
var fc = 0
for x in m.topLevel:
if x.kind == nkSym or x.typeId == nilItemId: inc tt
if x.flags == {}: inc fc
for x in m.bodies:
if x.kind == nkSym or x.typeId == nilItemId: inc tt
if x.flags == {}: inc fc
let total = float(m.topLevel.len + m.bodies.len)
echo "nodes with nil type: ", tt, " in % ", tt.float / total
echo "nodes with empty flags: ", fc.float / total

View File

@@ -1,84 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2024 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# For the line information we use 32 bits. They are used as follows:
# Bit 0 (AsideBit): If we have inline line information or not. If not, the
# remaining 31 bits are used as an index into a seq[(LitId, int, int)].
#
# We use 10 bits for the "file ID", this means a program can consist of as much
# as 1024 different files. (If it uses more files than that, the overflow bit
# would be set.)
# This means we have 21 bits left to encode the (line, col) pair. We use 7 bits for the column
# so 128 is the limit and 14 bits for the line number.
# The packed representation supports files with up to 16384 lines.
# Keep in mind that whenever any limit is reached the AsideBit is set and the real line
# information is kept in a side channel.
import std / assertions
const
AsideBit = 1
FileBits = 10
LineBits = 14
ColBits = 7
FileMax = (1 shl FileBits) - 1
LineMax = (1 shl LineBits) - 1
ColMax = (1 shl ColBits) - 1
static:
assert AsideBit + FileBits + LineBits + ColBits == 32
import .. / ic / [bitabs, rodfiles] # for LitId
type
PackedLineInfo* = distinct uint32
LineInfoManager* = object
aside: seq[(LitId, int32, int32)]
const
NoLineInfo* = PackedLineInfo(0'u32)
proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo =
if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax:
let col = if col < 0'i32: 0'u32 else: col.uint32
let line = if line < 0'i32: 0'u32 else: line.uint32
# use inline representation:
result = PackedLineInfo((file.uint32 shl 1'u32) or (line shl uint32(AsideBit + FileBits)) or
(col shl uint32(AsideBit + FileBits + LineBits)))
else:
result = PackedLineInfo((m.aside.len shl 1) or AsideBit)
m.aside.add (file, line, col)
proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) =
let i = i.uint32
if (i and 1'u32) == 0'u32:
# inline representation:
result = (LitId((i shr 1'u32) and FileMax.uint32),
int32((i shr uint32(AsideBit + FileBits)) and LineMax.uint32),
int32((i shr uint32(AsideBit + FileBits + LineBits)) and ColMax.uint32))
else:
result = m.aside[int(i shr 1'u32)]
proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId =
result = unpack(m, i)[0]
proc store*(r: var RodFile; m: LineInfoManager) = storeSeq(r, m.aside)
proc load*(r: var RodFile; m: var LineInfoManager) = loadSeq(r, m.aside)
when isMainModule:
var m = LineInfoManager(aside: @[])
for i in 0'i32..<16388'i32:
for col in 0'i32..<100'i32:
let packed = pack(m, LitId(1023), i, col)
let u = unpack(m, packed)
assert u[0] == LitId(1023)
assert u[1] == i
assert u[2] == col
echo m.aside.len

View File

@@ -10,7 +10,7 @@
## Integrity checking for a set of .rod files.
## The set must cover a complete Nim project.
import std/[sets, tables]
import sets
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -72,16 +72,15 @@ proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) =
c.thisModule = oldThisModule
proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
let t = findType(tree, n)
if t != nilItemId:
checkType(c, t)
if tree[n.int].typeId != nilItemId:
checkType(c, tree[n.int].typeId)
case n.kind
of nkEmpty, nkNilLit, nkType, nkNilRodNode:
discard
of nkIdent:
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
of nkSym:
checkLocalSym(c, tree[n].soperand)
checkLocalSym(c, tree.nodes[n.int].operand)
of directIntLit:
discard
of externIntLit, nkFloatLit..nkFloat128Lit:
@@ -90,9 +89,9 @@ proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
of nkModuleRef:
let (n1, n2) = sons2(tree, n)
assert n1.kind == nkNone
assert n2.kind == nkNone
checkForeignSym(c, PackedItemId(module: n1.litId, item: tree[n2].soperand))
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
checkForeignSym(c, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
else:
for n0 in sonsReadonly(tree, n):
checkNode(c, tree, n0)
@@ -108,18 +107,18 @@ proc checkModule(c: var CheckedContext; m: PackedModule) =
# We check that:
# - Every symbol references existing types and symbols.
# - Every tree node references existing types and symbols.
for _, v in pairs(m.syms):
checkLocalSym c, v.id
for i in 0..high(m.syms):
checkLocalSym c, int32(i)
checkTree c, m.toReplay
checkTree c, m.topLevel
for e in m.exports:
#assert e[1] >= 0 and e[1] < m.syms.len
assert e[1] >= 0 and e[1] < m.syms.len
assert e[0] == m.syms[e[1]].name
for e in m.compilerProcs:
#assert e[1] >= 0 and e[1] < m.syms.len
assert e[1] >= 0 and e[1] < m.syms.len
assert e[0] == m.syms[e[1]].name
checkLocalSymIds c, m, m.converters
@@ -135,15 +134,13 @@ proc checkModule(c: var CheckedContext; m: PackedModule) =
typeInstCache*: seq[(PackedItemId, PackedItemId)]
procInstCache*: seq[PackedInstantiation]
attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
methodsPerType*: seq[(PackedItemId, PackedItemId)]
dispatchers*: seq[PackedItemId]
]#
proc checkIntegrity*(g: ModuleGraph) =
var c = CheckedContext(g: g)
for i in 0..<len(g.packed):
for i in 0..high(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
@@ -153,3 +150,4 @@ proc checkIntegrity*(g: ModuleGraph) =
of stored, storing, outdated, loaded:
c.thisModule = int32 i
checkModule(c, g.packed[i].fromDisk)

View File

@@ -11,70 +11,59 @@
## IDE-like features. It uses the set of .rod files to accomplish
## its task. The set must cover a complete Nim project.
import std/[sets, tables]
import sets
from std/os import nil
from os import nil
from std/private/miscdollars import toLocation
when defined(nimPreviewSlimSystem):
import std/assertions
import ".." / [ast, modulegraphs, msgs, options]
import iclineinfos
import packed_ast, bitabs, ic
type
UnpackedLineInfo = object
file: LitId
line, col: int
NavContext = object
g: ModuleGraph
thisModule: int32
trackPos: UnpackedLineInfo
trackPos: PackedLineInfo
alreadyEmitted: HashSet[string]
outputSep: char # for easier testing, use short filenames and spaces instead of tabs.
proc isTracked(man: LineInfoManager; current: PackedLineInfo, trackPos: UnpackedLineInfo, tokenLen: int): bool =
let (currentFile, currentLine, currentCol) = man.unpack(current)
if currentFile == trackPos.file and currentLine == trackPos.line:
proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool =
if current.file == trackPos.file and current.line == trackPos.line:
let col = trackPos.col
if col >= currentCol and col < currentCol+tokenLen:
result = true
else:
result = false
else:
result = false
if col >= current.col and col < current.col+tokenLen:
return true
proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool =
result = s.name != LitId(0) and
isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
isTracked(info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool =
let name = c.g.packed[s.module].fromDisk.syms[s.item].name
result = name != LitId(0) and
isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
isTracked(info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
const
EmptyItemId = ItemId(module: -1'i32, item: -1'i32)
proc search(c: var NavContext; tree: PackedTree): ItemId =
# We use the linear representation here directly:
for i in 0..<len(tree):
let i = NodePos(i)
case tree[i].kind
for i in 0..high(tree.nodes):
case tree.nodes[i].kind
of nkSym:
let item = tree[i].soperand
if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree[i].info):
let item = tree.nodes[i].operand
if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree.nodes[i].info):
return ItemId(module: c.thisModule, item: item)
of nkModuleRef:
let (currentFile, currentLine, currentCol) = c.g.packed[c.thisModule].fromDisk.man.unpack(tree[i].info)
if currentLine == c.trackPos.line and currentFile == c.trackPos.file:
let (n1, n2) = sons2(tree, i)
if tree.nodes[i].info.line == c.trackPos.line and tree.nodes[i].info.file == c.trackPos.file:
let (n1, n2) = sons2(tree, NodePos i)
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
let pId = PackedItemId(module: n1.litId, item: tree[n2].soperand)
let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
if searchForeignSym(c, itemId, tree[i].info):
if searchForeignSym(c, itemId, tree.nodes[i].info):
return itemId
else: discard
return EmptyItemId
@@ -84,38 +73,36 @@ proc isDecl(tree: PackedTree; n: NodePos): bool =
const declarativeNodes = procDefs + {nkMacroDef, nkTemplateDef,
nkLetSection, nkVarSection, nkUsingStmt, nkConstSection, nkTypeSection,
nkIdentDefs, nkEnumTy, nkVarTuple}
result = n.int >= 0 and tree[n].kind in declarativeNodes
result = n.int >= 0 and tree[n.int].kind in declarativeNodes
proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) =
let (fileId, line, col) = unpack(c.g.packed[c.thisModule].fromDisk.man, info)
var m = ""
var file = c.g.packed[c.thisModule].fromDisk.strings[fileId]
var file = c.g.packed[c.thisModule].fromDisk.strings[info.file]
if c.outputSep == ' ':
file = os.extractFilename file
toLocation(m, file, line, col + ColOffset)
toLocation(m, file, info.line.int, info.col.int + ColOffset)
if not c.alreadyEmitted.containsOrIncl(m):
msgWriteln c.g.config, (if isDecl: "def" else: "usage") & c.outputSep & m
proc list(c: var NavContext; tree: PackedTree; sym: ItemId) =
for i in 0..<len(tree):
let i = NodePos(i)
case tree[i].kind
for i in 0..high(tree.nodes):
case tree.nodes[i].kind
of nkSym:
let item = tree[i].soperand
let item = tree.nodes[i].operand
if sym.item == item and sym.module == c.thisModule:
usage(c, tree[i].info, isDecl(tree, parent(i)))
usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
of nkModuleRef:
let (n1, n2) = sons2(tree, i)
assert n1.kind == nkNone
assert n2.kind == nkNone
let pId = PackedItemId(module: n1.litId, item: tree[n2].soperand)
let (n1, n2) = sons2(tree, NodePos i)
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
if itemId.item == sym.item and sym.module == itemId.module:
usage(c, tree[i].info, isDecl(tree, parent(i)))
usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
else: discard
proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int =
for i in 0..<len(g.packed):
for i in 0..high(g.packed):
for k in 1..high(g.packed[i].fromDisk.includes):
# we start from 1 because the first "include" file is
# the module's filename.
@@ -147,7 +134,7 @@ proc nav(g: ModuleGraph) =
var c = NavContext(
g: g,
thisModule: int32 mid,
trackPos: UnpackedLineInfo(line: unpacked.line.int, col: unpacked.col.int, file: fileId),
trackPos: PackedLineInfo(line: unpacked.line, col: unpacked.col, file: fileId),
outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t'
)
var symId = search(c, g.packed[mid].fromDisk.topLevel)
@@ -158,7 +145,7 @@ proc nav(g: ModuleGraph) =
localError(g.config, unpacked, "no symbol at this position")
return
for i in 0..<len(g.packed):
for i in 0..high(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
@@ -175,7 +162,7 @@ proc navUsages*(g: ModuleGraph) = nav(g)
proc navDefusages*(g: ModuleGraph) = nav(g)
proc writeRodFiles*(g: ModuleGraph) =
for i in 0..<len(g.packed):
for i in 0..high(g.packed):
case g.packed[i].status
of undefined, loading, stored, loaded:
discard "nothing to do"

View File

@@ -12,12 +12,10 @@
## use this representation directly in all the transformations,
## it is superior.
import std/[hashes, tables, strtabs]
import bitabs, rodfiles
import hashes, tables, strtabs
import bitabs
import ".." / [ast, options]
import iclineinfos
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -33,12 +31,17 @@ type
item*: int32 # same as the in-memory representation
const
nilItemId* = PackedItemId(module: LitId(0), item: 0.int32)
nilItemId* = PackedItemId(module: LitId(0), item: -1.int32)
const
emptyNodeId* = NodeId(-1)
type
PackedLineInfo* = object
line*: uint16
col*: int16
file*: LitId
PackedLib* = object
kind*: TLibKind
generated*: bool
@@ -47,7 +50,6 @@ type
path*: NodeId
PackedSym* = object
id*: int32
kind*: TSymKind
name*: LitId
typ*: PackedItemId
@@ -69,10 +71,8 @@ type
when hasFFI:
cname*: LitId
constraint*: NodeId
instantiatedFrom*: PackedItemId
PackedType* = object
id*: int32
kind*: TTypeKind
callConv*: TCallingConvention
#nodekind*: TNodeKind
@@ -89,35 +89,23 @@ type
typeInst*: PackedItemId
nonUniqueId*: int32
PackedNode* = object # 8 bytes
x: uint32
PackedNode* = object # 28 bytes
kind*: TNodeKind
flags*: TNodeFlags
operand*: int32 # for kind in {nkSym, nkSymDef}: SymId
# for kind in {nkStrLit, nkIdent, nkNumberLit}: LitId
# for kind in nkInt32Lit: direct value
# for non-atom kinds: the number of nodes (for easy skipping)
typeId*: PackedItemId
info*: PackedLineInfo
PackedTree* = object ## usually represents a full Nim module
nodes: seq[PackedNode]
withFlags: seq[(int32, TNodeFlags)]
withTypes: seq[(int32, PackedItemId)]
nodes*: seq[PackedNode]
PackedInstantiation* = object
key*, sym*: PackedItemId
concreteTypes*: seq[PackedItemId]
const
NodeKindBits = 8'u32
NodeKindMask = (1'u32 shl NodeKindBits) - 1'u32
template kind*(n: PackedNode): TNodeKind = TNodeKind(n.x and NodeKindMask)
template uoperand*(n: PackedNode): uint32 = (n.x shr NodeKindBits)
template soperand*(n: PackedNode): int32 = int32(uoperand(n))
template toX(k: TNodeKind; operand: uint32): uint32 =
uint32(k) or (operand shl NodeKindBits)
template toX(k: TNodeKind; operand: LitId): uint32 =
uint32(k) or (operand.uint32 shl NodeKindBits)
template typeId*(n: PackedNode): PackedItemId = n.typ
proc `==`*(a, b: SymId): bool {.borrow.}
proc hash*(a: SymId): Hash {.borrow.}
@@ -126,35 +114,71 @@ proc `==`*(a, b: NodePos): bool {.borrow.}
proc `==`*(a, b: NodeId): bool {.borrow.}
proc newTreeFrom*(old: PackedTree): PackedTree =
result = PackedTree(nodes: @[])
result.nodes = @[]
when false: result.sh = old.sh
when false:
proc declareSym*(tree: var PackedTree; kind: TSymKind;
name: LitId; info: PackedLineInfo): SymId =
result = SymId(tree.sh.syms.len)
tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info)
proc litIdFromName*(tree: PackedTree; name: string): LitId =
result = tree.sh.strings.getOrIncl(name)
proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) =
tree.nodes.add PackedNode(kind: kind, info: info,
operand: int32 getOrIncl(tree.sh.strings, token))
proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) =
tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
proc throwAwayLastNode*(tree: var PackedTree) =
tree.nodes.setLen(tree.nodes.len-1)
proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
tree.nodes.add PackedNode(x: toX(nkIdent, uint32(s)), info: info)
tree.nodes.add PackedNode(kind: nkIdent, operand: int32(s), info: info)
proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) =
tree.nodes.add PackedNode(x: toX(nkSym, cast[uint32](s)), info: info)
tree.nodes.add PackedNode(kind: nkSym, operand: s, info: info)
proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) =
tree.nodes.add PackedNode(kind: nkInt32Lit, operand: int32(s), info: info)
proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
tree.nodes.add PackedNode(x: toX(nkSym, cast[uint32](s)), info: info)
tree.nodes.add PackedNode(kind: nkSym, operand: int32(s), info: info)
proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) =
# and this is why the IR is superior. We can copy subtrees
# via a linear scan.
let pos = n.int
let L = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
let d = dest.nodes.len
dest.nodes.setLen(d + L)
for i in 0..<L:
dest.nodes[d+i] = tree.nodes[pos+i]
when false:
proc copySym*(dest: var PackedTree; tree: PackedTree; s: SymId): SymId =
result = SymId(dest.sh.syms.len)
assert int(s) < tree.sh.syms.len
let oldSym = tree.sh.syms[s.int]
dest.sh.syms.add oldSym
type
PatchPos = distinct int
proc addNode*(t: var PackedTree; kind: TNodeKind; operand: int32;
typeId: PackedItemId = nilItemId; info: PackedLineInfo;
flags: TNodeFlags = {}) =
t.nodes.add PackedNode(x: toX(kind, cast[uint32](operand)), info: info)
if flags != {}:
t.withFlags.add (t.nodes.len.int32 - 1, flags)
if typeId != nilItemId:
t.withTypes.add (t.nodes.len.int32 - 1, typeId)
when false:
proc prepare*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo): PatchPos =
result = PatchPos tree.nodes.len
tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: PackedItemId; info: PackedLineInfo): PatchPos =
result = PatchPos tree.nodes.len
tree.addNode(kind = kind, flags = flags, operand = 0, info = info, typeId = typeId)
tree.nodes.add PackedNode(kind: kind, flags: flags, operand: 0, info: info,
typeId: typeId)
proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): PatchPos =
result = PatchPos dest.nodes.len
@@ -162,30 +186,26 @@ proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): Pat
proc patch*(tree: var PackedTree; pos: PatchPos) =
let pos = pos.int
let k = tree.nodes[pos].kind
assert k > nkNilLit
assert tree.nodes[pos].kind > nkNilLit
let distance = int32(tree.nodes.len - pos)
assert distance > 0
tree.nodes[pos].x = toX(k, cast[uint32](distance))
tree.nodes[pos].operand = distance
proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
proc `[]`*(tree: PackedTree; i: NodePos): lent PackedNode {.inline.} =
tree.nodes[i.int]
template rawSpan(n: PackedNode): int = int(uoperand(n))
proc `[]`*(tree: PackedTree; i: int): lent PackedNode {.inline.} =
tree.nodes[i]
proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
if tree.nodes[pos].kind > nkNilLit:
assert tree.nodes[pos].uoperand > 0
inc pos, tree.nodes[pos].rawSpan
assert tree.nodes[pos].operand > 0
inc pos, tree.nodes[pos].operand
else:
inc pos
iterator sonsReadonly*(tree: PackedTree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].rawSpan
let last = pos + tree.nodes[pos].operand
inc pos
while pos < last:
yield NodePos pos
@@ -206,7 +226,7 @@ iterator isons*(dest: var PackedTree; tree: PackedTree;
iterator sonsFrom1*(tree: PackedTree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].rawSpan
let last = pos + tree.nodes[pos].operand
inc pos
if pos < last:
nextChild tree, pos
@@ -220,7 +240,7 @@ iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
inc count
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].rawSpan
let last = pos + tree.nodes[pos].operand
inc pos
while pos < last and count > 2:
yield NodePos pos
@@ -230,7 +250,7 @@ iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
proc parentImpl(tree: PackedTree; n: NodePos): NodePos =
# finding the parent of a node is rather easy:
var pos = n.int - 1
while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].rawSpan - 1 < n.int)):
while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int)):
dec pos
#assert pos >= 0, "node has no parent"
result = NodePos(pos)
@@ -256,32 +276,20 @@ proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} =
proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} =
tree.nodes[n.int].kind
proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} =
LitId tree.nodes[n.int].uoperand
LitId tree.nodes[n.int].operand
proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} =
tree.nodes[n.int].info
proc findType*(tree: PackedTree; n: NodePos): PackedItemId =
for x in tree.withTypes:
if x[0] == int32(n): return x[1]
if x[0] > int32(n): return nilItemId
return nilItemId
proc findFlags*(tree: PackedTree; n: NodePos): TNodeFlags =
for x in tree.withFlags:
if x[0] == int32(n): return x[1]
if x[0] > int32(n): return {}
return {}
template typ*(n: NodePos): PackedItemId =
tree.findType(n)
tree.nodes[n.int].typeId
template flags*(n: NodePos): TNodeFlags =
tree.findFlags(n)
tree.nodes[n.int].flags
template uoperand*(n: NodePos): uint32 =
tree.nodes[n.int].uoperand
template operand*(n: NodePos): int32 =
tree.nodes[n.int].operand
proc span*(tree: PackedTree; pos: int): int {.inline.} =
if isAtom(tree, pos): 1 else: tree.nodes[pos].rawSpan
if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) =
assert(not isAtom(tree, n.int))
@@ -297,7 +305,6 @@ proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) =
result = (NodePos a, NodePos b, NodePos c)
proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos =
result = default(NodePos)
if tree.nodes[n.int].kind > nkNilLit:
var count = 0
for child in sonsReadonly(tree, n):
@@ -311,28 +318,64 @@ when false:
template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind
template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info
template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].uoperand
template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].operand
template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].soperand
template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand
proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
when false:
# xxx `nkStrLit` or `nkStrLit..nkTripleStrLit:` below?
proc strLit*(tree: PackedTree; n: NodePos): lent string =
assert n.kind == nkStrLit
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
proc strVal*(tree: PackedTree; n: NodePos): string =
assert n.kind == nkStrLit
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
#result = cookedStrLit(raw)
proc filenameVal*(tree: PackedTree; n: NodePos): string =
case n.kind
of nkStrLit:
result = strVal(tree, n)
of nkIdent:
result = tree.sh.strings[n.litId]
of nkSym:
result = tree.sh.strings[tree.sh.syms[int n.symId].name]
else:
result = ""
proc identAsStr*(tree: PackedTree; n: NodePos): lent string =
assert n.kind == nkIdent
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
const
externIntLit* = {nkCharLit,
nkIntLit,
nkInt8Lit,
nkInt16Lit,
nkInt32Lit,
nkInt64Lit,
nkUIntLit,
nkUInt8Lit,
nkUInt16Lit,
nkUInt32Lit,
nkUInt64Lit}
nkUInt64Lit} # nkInt32Lit is missing by design!
externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit}
externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt64Lit}
externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
directIntLit* = nkNone
directIntLit* = nkInt32Lit
when false:
proc identIdImpl(tree: PackedTree; n: NodePos): LitId =
if n.kind == nkIdent:
result = n.litId
elif n.kind == nkSym:
result = tree.sh.syms[int n.symId].name
else:
result = LitId(0)
template identId*(n: NodePos): LitId = identIdImpl(tree, n)
template copyInto*(dest, n, body) =
let patchPos = prepare(dest, tree, n)
@@ -344,8 +387,28 @@ template copyIntoKind*(dest, kind, info, body) =
body
patch dest, patchPos
when false:
proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool =
let litId = tree.sh.strings.getKeyId(pragma)
if litId == LitId(0):
return false
assert n.kind == nkPragma
for ch0 in sonsReadonly(tree, n):
if ch0.kind == nkExprColonExpr:
if ch0.firstSon.identId == litId:
return true
elif ch0.identId == litId:
return true
proc getNodeId*(tree: PackedTree): NodeId {.inline.} = NodeId tree.nodes.len
when false:
proc produceError*(dest: var PackedTree; tree: PackedTree; n: NodePos; msg: string) =
let patchPos = prepare(dest, nkError, n.info)
dest.add nkStrLit, msg, n.info
copyTree(dest, tree, n)
patch dest, patchPos
iterator allNodes*(tree: PackedTree): NodePos =
var p = 0
while p < tree.len:
@@ -355,13 +418,3 @@ iterator allNodes*(tree: PackedTree): NodePos =
proc toPackedItemId*(item: int32): PackedItemId {.inline.} =
PackedItemId(module: LitId(0), item: item)
proc load*(f: var RodFile; t: var PackedTree) =
loadSeq f, t.nodes
loadSeq f, t.withFlags
loadSeq f, t.withTypes
proc store*(f: var RodFile; t: PackedTree) =
storeSeq f, t.nodes
storeSeq f, t.withFlags
storeSeq f, t.withTypes

View File

@@ -14,7 +14,7 @@
import ".." / [ast, modulegraphs, trees, extccomp, btrees,
msgs, lineinfos, pathutils, options, cgmeth]
import std/tables
import tables
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -103,17 +103,6 @@ proc replayBackendProcs*(g: ModuleGraph; module: int) =
let symId = FullId(module: tmp.module, packed: it[1])
g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.methodsPerType):
let key = translateId(it[0], g.packed, module, g.config)
let tmp = translateId(it[1], g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it[1])
g.methodsPerType.mgetOrPut(key, @[]).add LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.dispatchers):
let tmp = translateId(it, g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it)
g.dispatchers.add LazySym(id: symId, sym: nil)
proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
## We remember the generic instantiations a module performed
## in order to to avoid the code bloat that generic code tends
@@ -138,12 +127,12 @@ proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
module: module, sym: FullId(module: sym.module, packed: it.sym),
concreteTypes: concreteTypes, inst: nil)
for it in mitems(g.packed[module].fromDisk.methodsPerGenericType):
for it in mitems(g.packed[module].fromDisk.methodsPerType):
let key = translateId(it[0], g.packed, module, g.config)
let col = it[1]
let tmp = translateId(it[2], g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it[2])
g.methodsPerGenericType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
g.methodsPerType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
replayBackendProcs(g, module)

View File

@@ -14,13 +14,11 @@
## compiler works and less a storage format, you're probably looking for
## the `ic` or `packed_ast` modules to understand the logical format.
from std/typetraits import supportsCopyMem
from typetraits import supportsCopyMem
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
import std / tables
## Overview
## ========
## `RodFile` represents a Rod File (versioned binary format), and the
@@ -94,16 +92,11 @@ type
typeInstCacheSection
procInstCacheSection
attachedOpsSection
methodsPerGenericTypeSection
enumToStringProcsSection
methodsPerTypeSection
dispatchersSection
enumToStringProcsSection
typeInfoSection # required by the backend
backendFlagsSection
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
sideChannelSection
namespaceSection
symnamesSection
RodFileError* = enum
ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
@@ -116,8 +109,8 @@ type
# better than exceptions.
const
RodVersion = 2
defaultCookie = [byte(0), byte('R'), byte('O'), byte('D'),
RodVersion = 1
cookie = [byte(0), byte('R'), byte('O'), byte('D'),
byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
proc setError(f: var RodFile; err: RodFileError) {.inline.} =
@@ -172,18 +165,6 @@ proc storeSeq*[T](f: var RodFile; s: seq[T]) =
for i in 0..<s.len:
storePrim(f, s[i])
proc storeOrderedTable*[K, T](f: var RodFile; s: OrderedTable[K, T]) =
if f.err != ok: return
if s.len >= high(int32):
setError f, tooBig
return
var lenPrefix = int32(s.len)
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
for _, v in s:
storePrim(f, v)
proc loadPrim*(f: var RodFile; s: var string) =
## Read a string, the length was stored as a prefix
if f.err != ok: return
@@ -225,29 +206,16 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
for i in 0..<lenPrefix:
loadPrim(f, s[i])
proc loadOrderedTable*[K, T](f: var RodFile; s: var OrderedTable[K, T]) =
## `T` must be compatible with `copyMem`, see `loadPrim`
if f.err != ok: return
var lenPrefix = int32(0)
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
s = initOrderedTable[K, T](lenPrefix)
for i in 0..<lenPrefix:
var x = default T
loadPrim(f, x)
s[x.id] = x
proc storeHeader*(f: var RodFile; cookie = defaultCookie) =
proc storeHeader*(f: var RodFile) =
## stores the header which is described by `cookie`.
if f.err != ok: return
if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
setError f, ioFailure
proc loadHeader*(f: var RodFile; cookie = defaultCookie) =
proc loadHeader*(f: var RodFile) =
## Loads the header which is described by `cookie`.
if f.err != ok: return
var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte])
var thisCookie: array[cookie.len, byte]
if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len:
setError f, ioFailure
elif thisCookie != cookie:
@@ -263,14 +231,13 @@ proc storeSection*(f: var RodFile; s: RodSection) =
proc loadSection*(f: var RodFile; expected: RodSection) =
## read the bytes value of s, sets and error if the section is incorrect.
if f.err != ok: return
var s: RodSection = default(RodSection)
var s: RodSection
loadPrim(f, s)
if expected != s and f.err == ok:
setError f, wrongSection
proc create*(filename: string): RodFile =
## create the file and open it for writing
result = default(RodFile)
if not open(result.f, filename, fmWrite):
setError result, cannotOpen
@@ -278,6 +245,5 @@ proc close*(f: var RodFile) = close(f.f)
proc open*(filename: string): RodFile =
## open the file for reading
result = default(RodFile)
if not open(result.f, filename, fmRead):
setError result, cannotOpen

View File

@@ -11,8 +11,8 @@
# An identifier is a shared immutable string that can be compared by its
# id. This module is essential for the compiler's performance.
import wordrecg
import std/hashes
import
hashes, wordrecg
when defined(nimPreviewSlimSystem):
import std/assertions

View File

@@ -10,12 +10,10 @@
## This module implements the symbol importing mechanism.
import
ast, astalgo, msgs, options, idents, lookups,
semdata, modulepaths, sigmatch, lineinfos,
modulegraphs, wordrecg
from std/strutils import `%`, startsWith, replace
from std/sequtils import addUnique
import std/[sets, tables, intsets]
intsets, ast, astalgo, msgs, options, idents, lookups,
semdata, modulepaths, sigmatch, lineinfos, sets,
modulegraphs, wordrecg, tables
from strutils import `%`
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -115,7 +113,6 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) =
template bail = globalError(c.config, n.info, "invalid pragma")
result = (nil, @[])
if n.kind == nkPragmaExpr:
if n.len == 2 and n[1].kind == nkPragma:
result[0] = n[0]
@@ -231,7 +228,12 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
for i in 0..n.safeLen-1:
importForwarded(c, n[i], exceptSet, fromMod, importSet)
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, trackUnusedImport: bool): PSym =
proc addUnique[T](x: var seq[T], y: sink T) {.noSideEffect.} =
for i in 0..high(x):
if x[i] == y: return
x.add y
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
result = realModule
template createModuleAliasImpl(ident): untyped =
createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
@@ -246,16 +248,12 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, track
result = createModuleAliasImpl(realModule.name)
if importHidden:
result.options.incl optImportHidden
let moduleIdent = if n.kind in {nkInfix, nkImportAs}: n[^1] else: n
result.info = moduleIdent.info
if trackUnusedImport:
c.unusedImports.add((result, result.info))
c.unusedImports.add((result, n.info))
c.importModuleMap[result.id] = realModule.id
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
result = (nil, false)
var ret = default(typeof(result))
var ret: typeof(result)
proc processPragma(n2: PNode): PNode =
let (result2, kws) = splitPragmas(c, n2)
result = result2
@@ -290,11 +288,10 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
toFullPath(c.config, c.graph.importStack[i+1])
c.recursiveDep = err
let trackUnusedImport = warnUnusedImportX in c.config.notes
var realModule: PSym
discard pushOptionEntry(c)
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
result = importModuleAs(c, n, realModule, transf.importHidden, trackUnusedImport)
result = importModuleAs(c, n, realModule, transf.importHidden)
popOptionEntry(c)
#echo "set back to ", L
@@ -307,10 +304,6 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
var prefix = ""
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
let moduleNameNorm = getModuleName(c.config, n).replace("\\", "/")
if belongsToStdlib(c.graph, result) and not startsWith(moduleNameNorm, stdPrefix) and
not startsWith(moduleNameNorm, "system/") and not startsWith(moduleNameNorm, "packages/"):
message(c.config, n.info, warnStdPrefix, realModule.name.s)
proc suggestMod(n: PNode; s: PSym) =
if n.kind == nkImportAs:
@@ -322,11 +315,8 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
suggestMod(n, result)
importStmtResult.add newSymNode(result, n.info)
#newStrNode(toFullPath(c.config, f), n.info)
else:
result = nil
proc afterImport(c: PContext, m: PSym) =
if isCachedModule(c.graph, m): return
# fixes bug #17510, for re-exported symbols
let realModuleId = c.importModuleMap[m.id]
for s in allSyms(c.graph, m):
@@ -338,7 +328,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
let m = myImportModule(c, it, importStmtResult)
if m != nil:
# ``addDecl`` needs to be done before ``importAllSymbols``!
addDecl(c, m) # add symbol to symbol table of module
addDecl(c, m, it.info) # add symbol to symbol table of module
importAllSymbols(c, m)
#importForwarded(c, m.ast, emptySet, m)
afterImport(c, m)
@@ -362,9 +352,11 @@ proc evalImport*(c: PContext, n: PNode): PNode =
imp[lastPos] = x[1]
impAs[1] = imp
impAs[2] = x[2]
impAs.info = x[2].info
impMod(c, impAs, result)
else:
imp[lastPos] = x
imp.info = x.info
impMod(c, imp, result)
else:
impMod(c, it, result)
@@ -375,7 +367,7 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
var m = myImportModule(c, n[0], result)
if m != nil:
n[0] = newSymNode(m)
addDecl(c, m) # add symbol to symbol table of module
addDecl(c, m, n.info) # add symbol to symbol table of module
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
for i in 1..<n.len:
@@ -390,7 +382,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
var m = myImportModule(c, n[0], result)
if m != nil:
n[0] = newSymNode(m)
addDecl(c, m) # add symbol to symbol table of module
addDecl(c, m, n.info) # add symbol to symbol table of module
importAllSymbolsExcept(c, m, readExceptSet(c, n))
#importForwarded(c, m.ast, exceptSet, m)
afterImport(c, m)

View File

@@ -14,17 +14,15 @@
## See doc/destructors.rst for a spec of the implemented rewrite rules
import
ast, astalgo, msgs, renderer, magicsys, types, idents,
options, lowerings, modulegraphs,
intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents,
strutils, options, lowerings, tables, modulegraphs,
lineinfos, parampatterns, sighashes, liftdestructors, optimizer,
varpartitions, aliasanalysis, dfa, wordrecg
import std/[strtabs, tables, strutils, intsets]
when defined(nimPreviewSlimSystem):
import std/assertions
from trees import exprStructuralEquivalent, getRoot, isCursor, whichPragma
from trees import exprStructuralEquivalent, getRoot, whichPragma
type
Con = object
@@ -72,11 +70,9 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} =
if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
assert(not containsGarbageCollectedRef(t))
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo; needsInit: bool): PNode =
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.idgen, c.owner, info)
sym.typ = typ
if not needsInit:
sym.flags.incl sfNoInit
s.vars.add(sym)
result = newSymNode(sym)
@@ -102,7 +98,6 @@ when false:
proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
let root = parampatterns.exprRoot(n, allowCalls=false)
if root == nil: return false
elif sfSingleUsedTemp in root.flags: return true
var s = addr(scope)
while s != nil:
@@ -165,26 +160,33 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
else:
result = false
template hasDestructorOrAsgn(c: var Con, typ: PType): bool =
# bug #23354; an object type could have a non-trivial assignements when it is passed to a sink parameter
hasDestructor(c, typ) or (c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
typ.kind == tyObject and not isTrivial(getAttachedOp(c.graph, typ, attachedAsgn)))
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
if not hasDestructorOrAsgn(c, n.typ): return true
# bug #23354; an object type could have a non-trival assignements when it is passed to a sink parameter
if not hasDestructor(c, n.typ) and (n.typ.kind != tyObject or isTrival(getAttachedOp(c.graph, n.typ, attachedAsgn))): return true
let m = skipConvDfa(n)
result = isLastReadImpl(n, c, s)
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
isLastReadImpl(n, c, s)
proc isFirstWrite(n: PNode; c: var Con): bool =
let m = skipConvDfa(n)
result = nfFirstWrite in m.flags
template isFullyUnpackedTuple(n: PNode): bool =
proc isCursor(n: PNode): bool =
case n.kind
of nkSym:
sfCursor in n.sym.flags
of nkDotExpr:
isCursor(n[1])
of nkCheckedFieldExpr:
isCursor(n[0])
else:
false
template isUnpackedTuple(n: PNode): bool =
## we move out all elements of unpacked tuples,
## hence unpacked tuples themselves don't need to be destroyed
## except it's already a cursor
## restricted to `skTemp`, tuple temps where not every field is unpacked should not use `skTemp`
(n.kind == nkSym and n.sym.kind == skTemp and
n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
@@ -193,8 +195,7 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFr
if inferredFromCopy:
m.add ", which is inferred from unavailable '=copy'"
if (opname == "=" or opname == "=copy" or opname == "=dup") and
ri != nil:
if (opname == "=" or opname == "=copy" or opname == "=dup") and ri != nil:
m.add "; requires a copy because it's not the last read of '"
m.add renderTree(ri)
m.add '\''
@@ -209,17 +210,15 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFr
m.add " a 'sink' parameter"
m.add "; routine: "
m.add c.owner.name.s
#m.add "\n\n"
#m.add renderTree(c.body, {renderIds})
localError(c.graph.config, ri.info, errGenerated, m)
proc makePtrType(c: var Con, baseType: PType): PType =
result = newType(tyPtr, c.idgen, c.owner)
result = newType(tyPtr, nextTypeId c.idgen, c.owner)
addSonSkipIntLit(result, baseType, c.idgen)
proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
var addrExp: PNode
if op.typ != nil and op.typ.signatureLen > 1 and op.typ.firstParamType.kind != tyVar:
if op.typ != nil and op.typ.len > 1 and op.typ[1].kind != tyVar:
addrExp = dest
else:
addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
@@ -244,12 +243,7 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode
dbg:
if kind == attachedDestructor:
echo "destructor is ", op.id, " ", op.ast
if sfError in op.flags:
if ri != nil:
checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
else:
# uses the lineinfos of `dest` is `ri` is not available
checkForErrorPragma(c, t, dest, AttachedOpToStr[kind])
if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
c.genOp(op, dest)
proc genDestroy(c: var Con; dest: PNode): PNode =
@@ -277,7 +271,7 @@ proc deepAliases(dest, ri: PNode): bool =
return aliases(dest, ri) != no
proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
if (c.inLoopCond == 0 and (isFullyUnpackedTuple(dest) or IsDecl in flags or
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
isNoInit(dest) or IsReturn in flags:
# optimize sink call into a bitwise memcopy
@@ -293,7 +287,7 @@ proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFla
if deepAliases(dest, ri):
# consider: x = x + y, it is wrong to destroy the destination first!
# tmp to support self assignments
let tmp = c.getTemp(s, dest.typ, dest.info, needsInit = false)
let tmp = c.getTemp(s, dest.typ, dest.info)
result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri),
c.genDestroy(tmp))
else:
@@ -334,7 +328,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 =
@@ -362,7 +356,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
# but fields within active case branch might need destruction
# tmp to support self assignments
let tmp = c.getTemp(s, n[1].typ, n.info, needsInit = false)
let tmp = c.getTemp(s, n[1].typ, n.info)
result = newTree(nkStmtList)
result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed))
@@ -402,7 +396,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode =
result = genOp(c, op, n)
else:
result = newNodeI(nkCall, n.info)
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
result.add(newSymNode(createMagic(c.graph, c.idgen, "`=wasMoved`", mWasMoved)))
result.add copyTree(n) #mWasMoved does not take the address
#if n.kind != nkSym:
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
@@ -410,21 +404,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
proc stabilizeBracketIndex(n: PNode; c: var Con; body: var PNode): PNode =
## Evaluate a side-effecting index once and return the stable access.
doAssert n.kind == nkBracketExpr and not isAtom(n[1])
let temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen,
c.owner, n[1].info)
temp.typ = n[1].typ
let tempAsNode = newSymNode(temp)
body.add newTree(nkLetSection, n[1].info,
newTree(nkIdentDefs, tempAsNode,
newNodeI(nkEmpty, tempAsNode.info), n[1]))
result = copyNode(n)
result.add n[0]
result.add tempAsNode
result.typ = t
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
# generate: (let tmp = v; reset(v); tmp)
@@ -437,10 +417,6 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
else:
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var n = n
if n.kind == nkBracketExpr and not isAtom(n[1]):
n = stabilizeBracketIndex(n, c, result)
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.idgen, c.owner, n.info)
temp.typ = n.typ
var v = newNodeI(nkLetSection, n.info)
@@ -463,60 +439,58 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
proc isCapturedVar(n: PNode): bool =
let root = getRoot(n)
if root != nil: result = root.name.s[0] == ':'
else: result = false
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
if not hasDestructorOrAsgn(c, nTyp):
# Non-managed (plain-old-data) type: no ownership transfer is needed.
# Return the expression directly — no temp required.
let tmp = c.getTemp(s, nTyp, n.info)
if hasDestructor(c, nTyp):
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil and tfHasOwned notin typ.flags:
if sfError in op.flags:
c.checkForErrorPragma(nTyp, n, "=dup")
else:
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
if copyOp != nil and sfError in copyOp.flags and
sfOverridden notin op.flags:
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
let src = p(n, c, s, normal)
var newCall = newTreeIT(nkCall, src.info, src.typ,
newSymNode(op),
src)
c.finishCopy(newCall, n, {}, isFromSink = true)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
newCall
)
else:
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
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,
("passing '$1' to a sink parameter introduces an implicit copy; " &
"if possible, rearrange your program's control flow to prevent it") % $n)
if c.inEnsureMove > 0:
localError(c.graph.config, n.info, errFailedMove,
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
else:
if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
assert(not containsManagedMemory(nTyp))
if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
return p(n, c, s, normal)
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let tmp = c.getTemp(s, nTyp, n.info, needsInit = false)
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil and tfHasOwned notin typ.flags:
if sfError in op.flags:
c.checkForErrorPragma(nTyp, n, "=dup")
else:
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
if copyOp != nil and sfError in copyOp.flags and
sfOverridden notin op.flags:
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
let src = p(n, c, s, normal)
var newCall = newTreeIT(nkCall, src.info, src.typ,
newSymNode(op),
src)
c.finishCopy(newCall, n, {}, isFromSink = true)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
newCall
)
else:
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
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,
("passing '$1' to a sink parameter introduces an implicit copy; " &
"if possible, rearrange your program's control flow to prevent it") % $n)
if c.inEnsureMove > 0:
localError(c.graph.config, n.info, errFailedMove,
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
# Since we know somebody will take over the produced copy, there is
# no need to destroy it.
result.add tmp
proc isDangerousSeq(t: PType): bool {.inline.} =
let t = t.skipTypes(abstractInst)
result = t.kind == tySequence and tfHasOwned notin t.elementType.flags
result = t.kind == tySequence and tfHasOwned notin t[0].flags
proc containsConstSeq(n: PNode): bool =
if n.kind == nkBracket and n.len > 0 and n.typ != nil and isDangerousSeq(n.typ):
@@ -540,7 +514,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
# produce temp creation for (fn, env). But we need to move 'env'?
# This was already done in the sink parameter handling logic.
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
let tmp = c.getTemp(s, arg.typ, arg.info, true)
let tmp = c.getTemp(s, arg.typ, arg.info)
result.add c.genSink(s, tmp, arg, {IsDecl})
result.add tmp
s.final.add c.genDestroy(tmp)
@@ -580,7 +554,7 @@ proc cycleCheck(n: PNode; c: var Con) =
proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
# move the variable declaration to the top of the frame:
s.vars.add v.sym
if isFullyUnpackedTuple(v):
if isUnpackedTuple(v):
if c.inLoop > 0:
# unpacked tuple needs reset at every loop iteration
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
@@ -619,7 +593,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
# There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
# later and use it to eliminate the temporary when theres no need for it, but its
# tricky because you would have to intercept moveOrCopy at a certain point
let tmp = c.getTemp(s.parent[], ret.typ, ret.info, needsInit = true)
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
tmp.sym.flags = tmpFlags
let cpy = if hasDestructor(c, ret.typ) and
ret.typ.kind notin {tyOpenArray, tyVarargs}:
@@ -675,7 +649,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
for j in 0 ..< it.len-1:
branch[j] = copyTree(it[j])
var ofScope = nestedScope(s, it.lastSon)
branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
processScope(c, ofScope, maybeVoid(it[^1], ofScope))
else:
processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags)
@@ -723,7 +697,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
#Condition needs to be destroyed outside of the condition/branch scope
branch[0] = p(it[0], c, s, normal)
branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
processScope(c, branchScope, maybeVoid(it[^1], branchScope))
else:
processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
@@ -769,9 +743,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
result[^1] = maybeVoid(n[^1], s)
dec c.inUncheckedAssignSection, inUncheckedAssignSection
else:
result = nil
assert(false)
else: assert(false)
proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
@@ -780,7 +752,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
result = copyNode(n)
result.add call
else:
let tmp = c.getTemp(s, n[0].typ, n.info, needsInit = true)
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)
@@ -806,27 +778,10 @@ template isCustomDestructor(c: Con, t: PType): bool =
proc hasCustomDestructor(c: Con, t: PType): bool =
result = isCustomDestructor(c, t)
var obj = t
while obj.baseClass != nil:
obj = skipTypes(obj.baseClass, abstractPtrs)
while obj.len > 0 and obj[0] != nil:
obj = skipTypes(obj[0], abstractPtrs)
result = result or isCustomDestructor(c, obj)
const
exprBranchKinds = {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt,
nkTryStmt, nkPragmaBlock}
proc distributeAsgn(asgnKind: TNodeKind; dest, ri: PNode; c: var Con; s: var Scope): PNode =
## Distributes an assignment ``dest = ri`` into the leaf expressions of
## ``ri`` when ``ri`` is an expression-based control flow construct. This
## avoids creating pointless intermediate temporaries (bug #25850). The
## descent is recursive so that nestings like ``block: ...; if c: a else: b``
## assign directly to ``dest`` instead of going through a temp per branch.
if ri.kind in exprBranchKinds:
template process(child, s): untyped =
distributeAsgn(asgnKind, dest, child, c, s)
handleNestedTempl(ri, process, willProduceStmt = true)
else:
result = newTree(asgnKind, dest, p(ri, c, s, consumed))
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode =
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}:
@@ -852,9 +807,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, sinkArg)
result[1].typ() = nTyp
result[1].typ = nTyp
else:
result[1] = p(n[1], c, s, sinkArg)
elif n.kind in {nkObjDownConv, nkObjUpConv}:
@@ -906,8 +861,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
for i in 1..<n.len:
if n[i].kind == nkExprColonExpr:
let field = lookupFieldAgain(t, n[i][0].sym)
if field != nil and (sfCursor in field.flags or field.typ.kind in {tyOpenArray, tyVarargs}):
# don't sink fields with openarray types
if field != nil and sfCursor in field.flags:
result[i][1] = p(n[i][1], c, s, normal)
else:
result[i][1] = p(n[i][1], c, s, m)
@@ -922,9 +876,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
elif c.inSpawn > 0:
c.inSpawn.dec
# 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
let parameters = n[0].typ
let L = if parameters != nil: parameters.len else: 0
when false:
var isDangerous = false
@@ -973,9 +926,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
of nkVarSection, nkLetSection:
# transform; var x = y to var x; x op y where op is a move or copy
result = newNodeI(nkStmtList, n.info)
let isInProc = c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}
for it in n:
var ri = it[^1]
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
@@ -991,15 +941,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
s.locals.add v.sym
pVarTopLevel(v, c, s, result)
if ri.kind != nkEmpty:
let isGlobalPragma = v.kind == nkSym and
{sfPure, sfGlobal} <= v.sym.flags and
isInProc
if isGlobalPragma:
c.graph.procGlobals.add newTree(nkFastAsgn, v, ri)
else:
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
result.add value
result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
elif ri.kind == nkEmpty and c.inLoop > 0:
let skipInit = v.kind == nkDotExpr and # Closure var
sfNoInit in v[1].sym.flags
@@ -1028,11 +970,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
elif isDiscriminantField(n[0]):
result = c.genDiscriminantAsgn(s, n)
elif n[1].kind in exprBranchKinds:
# Distribute the assignment into each branch to avoid
# creating pointless temporaries for expression-based control flow.
let dest = p(n[0], c, s, mode)
result = distributeAsgn(n.kind, dest, n[1], c, s)
else:
result = copyNode(n)
result.add p(n[0], c, s, mode)
@@ -1068,9 +1005,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)
@@ -1127,7 +1064,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
of nkGotoState, nkState, nkAsmStmt:
result = n
else:
result = nil
internalError(c.graph.config, n.info, "cannot inject destructors to node kind: " & $n.kind)
proc sameLocation*(a, b: PNode): bool =
@@ -1157,30 +1093,24 @@ proc sameLocation*(a, b: PNode): bool =
else: false
proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
result = newNodeI(nkStmtList, ri.info)
let newAccess = stabilizeBracketIndex(ri, c, result)
let snk = c.genSink(s, dest, newAccess, flags)
result.add snk
result.add c.genWasMoved(newAccess)
# with side effects
var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen, c.owner, ri[1].info)
temp.typ = ri[1].typ
var v = newNodeI(nkLetSection, ri[1].info)
let tempAsNode = newSymNode(temp)
proc ownsData(c: var Con; s: var Scope; orig: PNode; flags: set[MoveOrCopyFlag]): PNode =
var n = orig
while true:
case n.kind
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
n = n[0]
else:
break
if n.kind in nkCallKinds and n.typ != nil and hasDestructor(c, n.typ):
result = newNodeIT(nkStmtListExpr, orig.info, orig.typ)
let tmp = c.getTemp(s, n.typ, n.info, needsInit = true)
tmp.sym.flags.incl sfSingleUsedTemp
result.add newTree(nkFastAsgn, tmp, copyTree(n))
s.final.add c.genDestroy(tmp)
n[] = tmp[]
result.add copyTree(orig)
else:
result = nil
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
vpart[0] = tempAsNode
vpart[1] = newNodeI(nkEmpty, tempAsNode.info)
vpart[2] = ri[1]
v.add(vpart)
var newAccess = copyNode(ri)
newAccess.add ri[0]
newAccess.add tempAsNode
var snk = c.genSink(s, dest, newAccess, flags)
result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
var ri = ri
@@ -1208,7 +1138,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
of nkCallKinds:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
of nkBracketExpr:
if isFullyUnpackedTuple(ri[0]):
if isUnpackedTuple(ri[0]):
# unpacking of tuple: take over the elements
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
@@ -1244,9 +1174,8 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(s, dest, ri, flags)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
elif ri.sym.kind != skParam and
isAnalysableFieldAccess(ri, c.owner) and
isLastRead(ri, c, s) and canBeMoved(c, dest.typ):
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(s, dest, ri, flags)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
@@ -1257,22 +1186,15 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, flags, isFromSink = false)
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
if IsExplicitSink in flags:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
else:
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock:
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
# We know the result will be a stmt so we use that fact to optimize
handleNestedTempl(ri, process, willProduceStmt = true)
of nkRaiseStmt:
result = pRaiseStmt(ri, c, s)
else:
let isOwnsData = ownsData(c, s, ri2, flags)
if isOwnsData != nil:
result = moveOrCopy(dest, isOwnsData, c, s, flags)
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
canBeMoved(c, dest.typ):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(s, dest, ri, flags)

View File

@@ -6,7 +6,7 @@ Name: "Nim"
Version: "$version"
Platforms: """
windows: i386;amd64
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;s390x;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64
macosx: i386;amd64;powerpc64;arm64
solaris: i386;amd64;sparc;sparc64
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
@@ -71,7 +71,6 @@ Files: "testament"
Files: "nimsuggest"
Files: "nimsuggest/tests/*.nim"
Files: "changelogs/*.md"
Files: "ci/funs.sh"
[Lib]
Files: "lib"
@@ -147,4 +146,4 @@ licenses: "bin/nim,MIT;lib/*,MIT;"
[nimble]
pkgName: "nim"
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css;doc/nimdoc.cls"
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"

View File

@@ -3,7 +3,7 @@
## hold all from `low(BiggestInt)` to `high(BiggestUInt)`, This
## type is for that purpose.
from std/math import trunc
from math import trunc
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -34,7 +34,6 @@ proc `$`*(a: Int128): string
proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
{.noSideEffect.}:
result = Zero
when T is bool: result.sdata(0) = int32(arg)
elif T is SomeUnsignedInt:
when sizeof(arg) <= 4:
@@ -172,7 +171,6 @@ proc addToHex*(result: var string; arg: Int128) =
i -= 1
proc toHex*(arg: Int128): string =
result = ""
result.addToHex(arg)
proc inc*(a: var Int128, y: uint32 = 1) =
@@ -209,35 +207,30 @@ proc `==`*(a, b: Int128): bool =
return true
proc bitnot*(a: Int128): Int128 =
result = Zero
result.udata[0] = not a.udata[0]
result.udata[1] = not a.udata[1]
result.udata[2] = not a.udata[2]
result.udata[3] = not a.udata[3]
proc bitand*(a, b: Int128): Int128 =
result = Zero
result.udata[0] = a.udata[0] and b.udata[0]
result.udata[1] = a.udata[1] and b.udata[1]
result.udata[2] = a.udata[2] and b.udata[2]
result.udata[3] = a.udata[3] and b.udata[3]
proc bitor*(a, b: Int128): Int128 =
result = Zero
result.udata[0] = a.udata[0] or b.udata[0]
result.udata[1] = a.udata[1] or b.udata[1]
result.udata[2] = a.udata[2] or b.udata[2]
result.udata[3] = a.udata[3] or b.udata[3]
proc bitxor*(a, b: Int128): Int128 =
result = Zero
result.udata[0] = a.udata[0] xor b.udata[0]
result.udata[1] = a.udata[1] xor b.udata[1]
result.udata[2] = a.udata[2] xor b.udata[2]
result.udata[3] = a.udata[3] xor b.udata[3]
proc `shr`*(a: Int128, b: int): Int128 =
result = Zero
let b = b and 127
if b < 32:
result.sdata(3) = a.sdata(3) shr b
@@ -264,7 +257,6 @@ proc `shr`*(a: Int128, b: int): Int128 =
result.sdata(0) = a.sdata(3) shr (b and 31)
proc `shl`*(a: Int128, b: int): Int128 =
result = Zero
let b = b and 127
if b < 32:
result.udata[0] = a.udata[0] shl b
@@ -288,7 +280,6 @@ proc `shl`*(a: Int128, b: int): Int128 =
result.udata[3] = a.udata[0] shl (b and 31)
proc `+`*(a, b: Int128): Int128 =
result = Zero
let tmp0 = uint64(a.udata[0]) + uint64(b.udata[0])
result.udata[0] = cast[uint32](tmp0)
let tmp1 = uint64(a.udata[1]) + uint64(b.udata[1]) + (tmp0 shr 32)
@@ -321,7 +312,6 @@ proc abs(a: int32): int =
if a < 0: -a else: a
proc `*`(a: Int128, b: uint32): Int128 =
result = Zero
let tmp0 = uint64(a.udata[0]) * uint64(b)
let tmp1 = uint64(a.udata[1]) * uint64(b)
let tmp2 = uint64(a.udata[2]) * uint64(b)
@@ -340,11 +330,10 @@ proc `*`*(a: Int128, b: int32): Int128 =
if b < 0:
result = -result
proc `*=`(a: var Int128, b: int32) =
a = a * b
proc `*=`*(a: var Int128, b: int32): Int128 =
result = result * b
proc makeInt128(high, low: uint64): Int128 =
result = Zero
result.udata[0] = cast[uint32](low)
result.udata[1] = cast[uint32](low shr 32)
result.udata[2] = cast[uint32](high)
@@ -368,10 +357,9 @@ proc `*`*(lhs, rhs: Int128): Int128 =
proc `*=`*(a: var Int128, b: Int128) =
a = a * b
import std/bitops
import bitops
proc fastLog2*(a: Int128): int =
result = 0
if a.udata[3] != 0:
return 96 + fastLog2(a.udata[3])
if a.udata[2] != 0:
@@ -383,8 +371,6 @@ proc fastLog2*(a: Int128): int =
proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
assert(divisor != Zero)
result = (Zero, Zero)
let isNegativeA = isNegative(dividend)
let isNegativeB = isNegative(divisor)
@@ -551,28 +537,24 @@ proc toInt128*(arg: float64): Int128 =
return res
proc maskUInt64*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0]
result.udata[1] = arg.udata[1]
result.udata[2] = 0
result.udata[3] = 0
proc maskUInt32*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0]
result.udata[1] = 0
result.udata[2] = 0
result.udata[3] = 0
proc maskUInt16*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0] and 0xffff
result.udata[1] = 0
result.udata[2] = 0
result.udata[3] = 0
proc maskUInt8*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0] and 0xff
result.udata[1] = 0
result.udata[2] = 0
@@ -589,4 +571,4 @@ proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} =
of 8:
return maskUInt64(arg)
else:
raiseAssert "masking only implemented for 1, 2, 4 and 8 bytes"
assert(false, "masking only implemented for 1, 2, 4 and 8 bytes")

View File

@@ -11,9 +11,7 @@
## https://github.com/nim-lang/RFCs/issues/244 for more details.
import
ast, types, renderer
import std/intsets
ast, types, renderer, intsets
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -23,7 +21,6 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool
proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
case n.kind
of nkRecList:
result = false
for i in 0..<n.len:
result = canAliasN(arg, n[i], marker)
if result: return
@@ -39,7 +36,7 @@ proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
else: discard
of nkSym:
result = canAlias(arg, n.sym.typ, marker)
else: result = false
else: discard
proc canAlias(arg, ret: PType; marker: var IntSet): bool =
if containsOrIncl(marker, ret.id):
@@ -54,18 +51,17 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool =
of tyObject:
if isFinal(ret):
result = canAliasN(arg, ret.n, marker)
if not result and ret.baseClass != nil:
result = canAlias(arg, ret.baseClass, marker)
if not result and ret.len > 0 and ret[0] != nil:
result = canAlias(arg, ret[0], marker)
else:
result = true
of tyTuple:
result = false
for r in ret.kids:
result = canAlias(arg, r, marker)
for i in 0..<ret.len:
result = canAlias(arg, ret[i], marker)
if result: break
of tyArray, tySequence, tyDistinct, tyGenericInst,
tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
result = canAlias(arg, ret.skipModifier, marker)
result = canAlias(arg, ret.lastSon, marker)
of tyProc:
result = ret.callConv == ccClosure
else:
@@ -119,16 +115,14 @@ proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult =
if result != NotFound: return result
case t.kind
of tyObject:
if t.baseClass != nil:
result = containsDangerousRefAux(t.baseClass.skipTypes(skipPtrs), marker)
if t[0] != nil:
result = containsDangerousRefAux(t[0].skipTypes(skipPtrs), marker)
if result == NotFound: result = containsDangerousRefAux(t.n, marker)
of tyGenericInst, tyDistinct, tyAlias, tySink:
result = containsDangerousRefAux(skipModifier(t), marker)
of tyArray, tySet, tySequence:
result = containsDangerousRefAux(t.elementType, marker)
of tyTuple:
for a in t.kids:
result = containsDangerousRefAux(a, marker)
result = containsDangerousRefAux(lastSon(t), marker)
of tyArray, tySet, tyTuple, tySequence:
for i in 0..<t.len:
result = containsDangerousRefAux(t[i], marker)
if result == Found: return result
else:
discard
@@ -166,7 +160,7 @@ proc containsVariable(n: PNode): bool =
proc checkIsolate*(n: PNode): bool =
if types.containsTyRef(n.typ):
# XXX Maybe require that 'n.typ' is acyclic. This is not much
# worse than the already existing inheritance and closure restrictions.
# worse than the already exisiting inheritance and closure restrictions.
case n.kind
of nkCharLit..nkNilLit:
result = true
@@ -190,12 +184,10 @@ proc checkIsolate*(n: PNode): bool =
return false
result = true
of nkIfStmt, nkIfExpr:
result = false
for it in n:
result = checkIsolate(it.lastSon)
if not result: break
of nkCaseStmt:
result = false
for i in 1..<n.len:
result = checkIsolate(n[i].lastSon)
if not result: break
@@ -205,7 +197,6 @@ proc checkIsolate*(n: PNode): bool =
result = checkIsolate(n[i].lastSon)
if not result: break
of nkBracket, nkTupleConstr, nkPar:
result = false
for it in n:
result = checkIsolate(it)
if not result: break

File diff suppressed because it is too large Load Diff

View File

@@ -69,7 +69,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
else: internalError(p.config, n.info, "genObjectFields")
proc objHasTypeField(t: PType): bool {.inline.} =
tfInheritable in t.flags or t.baseClass != nil
tfInheritable in t.flags or t[0] != nil
proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
let kind = if objHasTypeField(typ): tyObject else: tyTuple
@@ -79,9 +79,9 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
p.g.typeInfo.addf("var NNI$1 = $2;$n",
[rope(typ.id), genObjectFields(p, typ, typ.n)])
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
if (typ.kind == tyObject) and (typ.baseClass != nil):
if (typ.kind == tyObject) and (typ[0] != nil):
p.g.typeInfo.addf("$1.base = $2;$n",
[name, genTypeInfo(p, typ.baseClass.skipTypes(skipPtrs))])
[name, genTypeInfo(p, typ[0].skipTypes(skipPtrs))])
proc genTupleFields(p: PProc, typ: PType): Rope =
var s: Rope = ""
@@ -117,17 +117,17 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
prepend(p.g.typeInfo, s)
p.g.typeInfo.add(n)
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
if typ.baseClass != nil:
if typ[0] != nil:
p.g.typeInfo.addf("$1.base = $2;$n",
[name, genTypeInfo(p, typ.baseClass)])
[name, genTypeInfo(p, typ[0])])
proc genTypeInfo(p: PProc, typ: PType): Rope =
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned} + tyUserTypeClasses)
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind
of tyDistinct:
result = genTypeInfo(p, t.skipModifier)
result = genTypeInfo(p, t[0])
of tyPointer, tyProc, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64:
var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
@@ -139,18 +139,18 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
p.g.typeInfo.addf("$1.base = $2;$n",
[result, genTypeInfo(p, t.elementType)])
[result, genTypeInfo(p, t.lastSon)])
of tyArray:
var s =
"var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
p.g.typeInfo.addf("$1.base = $2;$n",
[result, genTypeInfo(p, t.elementType)])
[result, genTypeInfo(p, t[1])])
of tyEnum: genEnumInfo(p, t, result)
of tyObject: genObjectInfo(p, t, result)
of tyTuple: genTupleInfo(p, t, result)
of tyStatic:
if t.n != nil: result = genTypeInfo(p, skipModifier t)
if t.n != nil: result = genTypeInfo(p, lastSon t)
else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')

View File

@@ -10,12 +10,10 @@
# This file implements lambda lifting for the transformator.
import
options, ast, astalgo, msgs,
idents, renderer, types, magicsys, lowerings, modulegraphs, lineinfos,
intsets, strutils, options, ast, astalgo, msgs,
idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
transf, liftdestructors, typeallowed
import std/[strutils, tables, intsets]
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -135,24 +133,21 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator)
var n = newNodeI(nkRange, iter.info)
n.add newIntNode(nkIntLit, -1)
n.add newIntNode(nkIntLit, 0)
result = newType(tyRange, idgen, iter)
result = newType(tyRange, nextTypeId(idgen), iter)
result.n = n
var intType = nilOrSysInt(g)
if intType.isNil: intType = newType(tyInt, idgen, iter)
if intType.isNil: intType = newType(tyInt, nextTypeId(idgen), iter)
rawAddSon(result, intType)
proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info)
result.typ = createClosureIterStateType(g, iter, idgen)
template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
# YYY meh, just add the state field for every closure for now, it's too
# hard to figure out if it comes from a closure iterator:
result = createObj(g, idgen, owner, info, final=false)
result.flags.incl tfFinal
if owner.isIterator:
rawAddField(result, createStateField(g, owner, idgen))
rawAddField(result, createStateField(g, owner, idgen))
proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
if resultPos < iter.ast.len:
@@ -160,7 +155,7 @@ proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym
else:
# XXX a bit hacky:
result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {})
result.typ = iter.typ.returnType
result.typ = iter.typ[0]
incl(result.flags, sfUsed)
iter.ast.add newSymNode(result)
@@ -175,23 +170,24 @@ proc addHiddenParam(routine: PSym, param: PSym) =
assert sfFromGeneric in param.flags
#echo "produced environment: ", param.id, " for ", routine.id
proc getEnvParam*(routine: PSym): PSym =
if routine.ast.isNil: return nil
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
let params = routine.ast[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
result = hidden.sym
assert sfFromGeneric in result.flags
else:
result = nil
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
result = getEnvParam(routine)
if result.isNil:
# writeStackTrace()
localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
result = routine
proc getEnvParam*(routine: PSym): PSym =
let params = routine.ast[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.name.s == paramName:
result = hidden.sym
assert sfFromGeneric in result.flags
proc interestingVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
sfGlobal notin s.flags and
@@ -200,11 +196,9 @@ proc interestingVar(s: PSym): bool {.inline.} =
proc illegalCapture(s: PSym): bool {.inline.} =
result = classifyViewType(s.typ) != noView or s.kind == skResult
proc isInnerProc*(s: PSym): bool =
proc isInnerProc(s: PSym): bool =
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
result = s.skipGenericOwner.kind in routineKinds
else:
result = false
proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
# Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would
@@ -230,14 +224,23 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf
if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
prc.flags.incl sfInjectDestructors
template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
proc interestingIterVar(s: PSym): bool {.inline.} =
# XXX optimization: Only lift the variable if it lives across
# yield/return boundaries! This can potentially speed up
# closure iterators quite a bit.
result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure
proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
## lambda lifting can be harmful for JS-like code generators.
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
jsNoLambdaLifting in conf.legacyFeatures and conf.backend == backendJs and not isCompileTime
result = conf.backend == backendJs and not isCompileTime
proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
if owner.kind != skMacro:
createTypeBoundOps(g, nil, refType.elementType, info, idgen)
createTypeBoundOps(g, nil, refType.lastSon, info, idgen)
createTypeBoundOps(g, nil, refType, info, idgen)
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
owner.flags.incl sfInjectDestructors
@@ -276,6 +279,15 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
result.add makeClosure(g, idgen, iter, env, n.info)
proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
let envParam = getHiddenParam(g, owner)
let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
let field = addField(obj, s, g.cache, idgen)
var access = newSymNode(envParam)
assert obj.kind == tyObject
result = rawIndirectAccess(access, field, s.info)
# ------------------ new stuff -------------------------------------------
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
@@ -285,8 +297,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 T> which can be captured.") %
[s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
" Consider using a <ref $2> which can be captured.") %
[s.name.s, typeToString(s.typ), 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])
@@ -304,10 +316,12 @@ type
idgen: IdGenerator
proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass =
result = DetectionPass(processed: toIntSet([fn.id]),
capturedVars: initIntSet(), ownerToType: initTable[int, PType](),
graph: g, idgen: idgen
)
result.processed = initIntSet()
result.capturedVars = initIntSet()
result.ownerToType = initTable[int, PType]()
result.processed.incl(fn.id)
result.graph = g
result.idgen = idgen
discard """
proc outer =
@@ -323,19 +337,15 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
info: TLineInfo): PType =
result = c.ownerToType.getOrDefault(owner.id)
if result.isNil:
let env = getEnvParam(owner)
if env.isNil or not owner.isIterator:
result = newType(tyRef, c.idgen, owner)
let obj = createEnvObj(c.graph, c.idgen, owner, info)
rawAddSon(result, obj)
else:
result = env.typ
result = newType(tyRef, nextTypeId(c.idgen), owner)
let obj = createEnvObj(c.graph, c.idgen, owner, info)
rawAddSon(result, obj)
c.ownerToType[owner.id] = result
proc asOwnedRef(c: var DetectionPass; t: PType): PType =
if optOwnedRefs in c.graph.config.globalOptions:
assert t.kind == tyRef
result = newType(tyOwned, c.idgen, t.owner)
result = newType(tyOwned, nextTypeId(c.idgen), t.owner)
result.flags.incl tfHasOwned
result.rawAddSon t
else:
@@ -344,7 +354,7 @@ proc asOwnedRef(c: var DetectionPass; t: PType): PType =
proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
info: TLineInfo): PType =
var r = c.getEnvTypeForOwner(owner, info)
result = newType(tyPtr, c.idgen, owner)
result = newType(tyPtr, nextTypeId(c.idgen), owner)
rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr}))
proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
@@ -408,12 +418,6 @@ Consider:
proc isTypeOf(n: PNode): bool =
n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
proc isEnvTypeForRoutine(envTyp: PType; routine: PSym): bool =
## True if `envTyp` is (maybe wrapped) env object type owned by `routine`, as
## created by `getEnvTypeForOwner` / `createEnvObj`.
let obj = envTyp.skipTypes({tyOwned, tyRef, tyPtr})
result = obj.kind == tyObject and obj.owner.id == routine.id
proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
var cp = getEnvParam(fn)
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
@@ -424,21 +428,9 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
cp.typ = t
addHiddenParam(fn, cp)
elif cp.typ != t and fn.kind != skIterator:
# Nested `liftLambdas` uses a fresh `DetectionPass`, so `getEnvTypeForOwner`
# can allocate another PType for the same logical env; the hidden param from
# the inner pass is authoritative (bug #21242).
if isEnvTypeForRoutine(cp.typ, owner) and isEnvTypeForRoutine(t, owner):
c.ownerToType[owner.id] = cp.typ
else:
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
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:
@@ -454,15 +446,25 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
if innerProc:
if s.isIterator: c.somethingToDo = true
if not c.processed.containsOrIncl(s.id):
let body = transformBody(c.graph, c.idgen, s, {useCache})
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 or iterEnvHasUpField(c.graph, s))
let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
let interested = interestingVar(s)
if ow == owner:
if owner.isIterator:
c.somethingToDo = true
addClosureParam(c, owner, n.info)
if interestingIterVar(s):
if not c.capturedVars.contains(s.id):
if not c.inTypeOf: c.capturedVars.incl(s.id)
let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
#let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
if s.name.id == getIdent(c.graph.cache, ":state").id:
obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
else:
discard addField(obj, s, c.graph.cache, c.idgen)
# direct or indirect dependency:
elif innerClosure or interested:
discard """
@@ -535,8 +537,9 @@ type
unownedEnvVars: Table[int, PNode] # only required for --newruntime
proc initLiftingPass(fn: PSym): LiftingPass =
result = LiftingPass(processed: toIntSet([fn.id]),
envVars: initTable[int, PNode]())
result.processed = initIntSet()
result.processed.incl(fn.id)
result.envVars = initTable[int, PNode]()
proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let s = n.sym
@@ -544,8 +547,8 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let envParam = getHiddenParam(g, owner)
if not envParam.isNil:
var access = newSymNode(envParam)
var obj = access.typ.elementType
while true:
let obj = access.typ[0]
assert obj.kind == tyObject
let field = getFieldFromObj(obj, s)
if field != nil:
@@ -553,7 +556,6 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let upField = lookupInRecord(obj.n, getIdent(g.cache, upName))
if upField == nil: break
access = rawIndirectAccess(access, upField, n.info)
obj = access.typ.baseClass
localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
result = n
@@ -565,7 +567,7 @@ proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idge
when false:
if owner.kind == skIterator and owner.typ.callConv == ccClosure:
let it = getHiddenParam(owner)
addUniqueField(it.typ.elementType, v)
addUniqueField(it.typ[0], v)
result = indirectAccess(newSymNode(it), v, v.info)
else:
result = newSymNode(v)
@@ -622,7 +624,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)
@@ -662,27 +664,16 @@ 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 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;
proc closureCreationForIter(iter: PNode;
d: var DetectionPass; c: var LiftingPass): PNode =
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
let iterOwner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, iterOwner, iter.info)
let owner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
incl(v.flags, sfShadowed)
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
var vnode: PNode
if iterOwner.isIterator:
let it = getHiddenParam(d.graph, iterOwner)
if owner.isIterator:
let it = getHiddenParam(d.graph, owner)
addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
vnode = indirectAccess(newSymNode(it), v, v.info)
else:
@@ -691,14 +682,12 @@ proc closureCreationForIter(owner: PSym, iter: PNode;
addVar(vs, vnode)
result.add(vs)
result.add genCreateEnv(vnode)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, iterOwner)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
if upField != nil:
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:
let u = setupEnvVar(owner, d, c, iter.info)
if u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == upField.typ.skipTypes({tyOwned, tyRef, tyPtr}):
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
u, iter.info))
else:
@@ -732,12 +721,11 @@ 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(owner, n, d, c)
result = closureCreationForIter(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)
else:
result = nil
let available = getHiddenParam(d.graph, owner)
let wanted = getHiddenParam(d.graph, s).typ
# ugh: call through some other inner proc;
@@ -764,7 +752,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
# echo renderTree(s.getBody, {renderIds})
let oldInContainer = c.inContainer
c.inContainer = 0
var body = transformBody(d.graph, d.idgen, s, {})
var body = transformBody(d.graph, d.idgen, s, dontUseCache)
body = liftCapturedVars(body, s, d, c)
if c.envVars.getOrDefault(s.id).isNil:
s.transformedBody = body
@@ -779,6 +767,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
elif s.id in d.capturedVars:
if s.owner != owner:
result = accessViaEnvParam(d.graph, n, owner)
elif owner.isIterator and interestingIterVar(s):
result = accessViaEnvParam(d.graph, n, owner)
else:
result = accessViaEnvVar(n, owner, d, c)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
@@ -799,7 +789,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:
@@ -886,19 +876,20 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType;
fn.typ.callConv = oldCC
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
idgen: IdGenerator; flags: TransformFlags): PNode =
idgen: IdGenerator, force: bool): PNode =
# XXX backend == backendJs does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ...
# However we can do lifting for the stuff which is *only* compiletime.
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and
if body.kind == nkEmpty or (
g.config.backend == backendJs and not isCompileTime) or
(fn.skipGenericOwner.kind != skModule and force notin flags):
(fn.skipGenericOwner.kind != skModule and not force):
# ignore forward declaration:
result = body
tooEarly = true
if fn.isIterator:
var d = initDetectionPass(g, fn, idgen)
addClosureParam(d, fn, body.info)
else:
var d = initDetectionPass(g, fn, idgen)
detectCapturedVars(body, fn, d)
@@ -962,7 +953,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
result = newNodeI(nkStmtList, body.info)
# static binding?
var env: PSym = nil
var env: PSym
let op = call[0]
if op.kind == nkSym and op.sym.isIterator:
# createClosure()
@@ -987,20 +978,6 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
for i in 0..<op.len-1:
result.add op[i]
elif op.kind != nkSym: # might have side effects
# bug #25046
# create a temp for the closure
# var :closureTemp
# :closureTemp = ...
let tempSym = newSym(skLet, getIdent(g.cache, ":closureTemp"), idgen, owner, body.info)
tempSym.typ = call[0].typ
let temp = newSymNode(tempSym)
var v = newNodeI(nkVarSection, body.info)
addVar(v, temp)
result.add(v)
result.add newAsgnStmt(temp, call[0], body.info)
call[0] = temp
var loopBody = newNodeI(nkStmtList, body.info, 3)
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
whileLoop[0] = newIntTypeNode(1, getSysType(g, body.info, tyBool))

View File

@@ -1,91 +0,0 @@
import ast, astalgo
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)
#copyIdTable(result.topLayer, pt.topLayer)
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.counter
proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable =
result = LayeredIdTable(topLayer: initTypeMapping(), 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
iterator pairs*(pt: LayeredIdTable): (ItemId, PType) =
var tm = pt
while true:
for (k, v) in idTablePairs(tm.topLayer):
yield (k, v)
if tm.nextLayer == nil:
break
tm.setToPreviousLayer
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

@@ -9,7 +9,7 @@
## Layouter for nimpretty.
import idents, lexer, ast, lineinfos, llstream, options, msgs, strutils, pathutils
import idents, lexer, lineinfos, llstream, options, msgs, strutils, pathutils
const
MinLineLen = 15
@@ -243,28 +243,23 @@ proc renderTokens*(em: var Emitter): string =
return content
type
FinalCheck = proc (content: string; origAst: PNode): bool {.nimcall.}
proc writeOut*(em: Emitter; content: string; origAst: PNode; check: FinalCheck) =
proc writeOut*(em: Emitter, content: string) =
## Write to disk
let outFile = em.config.absOutFile
if fileExists(outFile) and readFile(outFile.string) == content:
discard "do nothing, see #9499"
return
var f = llStreamOpen(outFile, fmWrite)
if f == nil:
rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
return
f.llStreamWrite content
llStreamClose(f)
if check(content, origAst):
var f = llStreamOpen(outFile, fmWrite)
if f == nil:
rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
return
f.llStreamWrite content
llStreamClose(f)
proc closeEmitter*(em: var Emitter; origAst: PNode; check: FinalCheck) =
proc closeEmitter*(em: var Emitter) =
## Renders emitter tokens and write to a file
let content = renderTokens(em)
em.writeOut(content, origAst, check)
em.writeOut(content)
proc wr(em: var Emitter; x: string; lt: LayoutToken) =
em.tokens.add x
@@ -451,13 +446,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
elif tok.indent >= 0:
var newlineKind = ltCrucialNewline
if em.keepIndents > 0:
# Apply the requested --indent width to "don't touch" regions (if/block
# expressions) too: keep the relative offset from the enclosing block
# baseline, but rebase it onto indWidth. Otherwise a non-default
# --indent would leave these lines at the original column and inject
# invalid indentation (see #20078).
em.indentLevel = em.indentStack.high * em.indWidth +
(tok.indent - em.indentStack[^1])
em.indentLevel = tok.indent
elif (em.lastTok in (splitters + oprSet) and
tok.tokType notin (closedPars - {tkBracketDotRi})):
if tok.tokType in openPars and tok.indent > em.indentStack[^1]:

View File

@@ -16,10 +16,8 @@
# DOS or Macintosh text files, even when it is not the native format.
import
options, msgs, platform, idents, nimlexbase, llstream,
wordrecg, lineinfos, pathutils
import std/[hashes, parseutils, strutils]
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg, lineinfos, pathutils, parseutils
when defined(nimPreviewSlimSystem):
import std/[assertions, formatfloat]
@@ -122,6 +120,7 @@ type
# this is needed because scanning comments
# needs so much look-ahead
currLineIndent*: int
strongSpaces*, allowTabs*: bool
errorHandler*: ErrorHandler
cache*: IdentCache
when defined(nimsuggest):
@@ -149,11 +148,9 @@ proc isNimIdentifier*(s: string): bool =
var i = 1
while i < sLen:
if s[i] == '_': inc(i)
if i < sLen and s[i] notin SymChars: return false
if i < sLen and s[i] notin SymChars: return
inc(i)
result = true
else:
result = false
proc `$`*(tok: Token): string =
case tok.tokType
@@ -175,6 +172,32 @@ proc printTok*(conf: ConfigRef; tok: Token) =
# xxx factor with toLocation
msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & $tok.tokType & " " & $tok)
proc initToken*(L: var Token) =
L.tokType = tkInvalid
L.iNumber = 0
L.indent = 0
L.spacing = {}
L.literal = ""
L.fNumber = 0.0
L.base = base10
L.ident = nil
when defined(nimpretty):
L.commentOffsetA = 0
L.commentOffsetB = 0
proc fillToken(L: var Token) =
L.tokType = tkInvalid
L.iNumber = 0
L.indent = 0
L.spacing = {}
setLen(L.literal, 0)
L.fNumber = 0.0
L.base = base10
L.ident = nil
when defined(nimpretty):
L.commentOffsetA = 0
L.commentOffsetB = 0
proc openLexer*(lex: var Lexer, fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache; config: ConfigRef) =
openBaseLexer(lex, inputstream)
@@ -300,7 +323,8 @@ proc getNumber(L: var Lexer, result: var Token) =
# Used to get slightly human friendlier err messages.
const literalishChars = {'A'..'Z', 'a'..'z', '0'..'9', '_', '.', '\''}
var msgPos = L.bufpos
var t = Token(literal: "")
var t: Token
t.literal = ""
L.bufpos = startpos # Use L.bufpos as pos because of matchChars
matchChars(L, t, literalishChars)
# We must verify +/- specifically so that we're not past the literal
@@ -316,28 +340,6 @@ proc getNumber(L: var Lexer, result: var Token) =
L.bufpos = msgPos
lexMessage(L, msgKind, msg % t.literal)
proc checkBitWidth(L: var Lexer, base: NumericalBase, tokType: TokType,
numDigits: int, startpos: int) =
# Check bit width for non-base-10 literals
# Warn if the digit count exceeds what can fit in the target type
let bitsPerDigit = case base
of base2: 1
of base8: 3
of base16: 4
else: raiseAssert "unreachable"
let bitWidth = case tokType
of tkInt8Lit, tkUInt8Lit: 8
of tkInt16Lit, tkUInt16Lit: 16
of tkInt32Lit, tkUInt32Lit: 32
of tkInt64Lit, tkUIntLit, tkIntLit, tkUInt64Lit: 64
else: raiseAssert "unreachable"
# Maximum digits = ceil(bitWidth / bitsPerDigit) = (bitWidth + bitsPerDigit - 1) div bitsPerDigit
let maxDigits = (bitWidth + bitsPerDigit - 1) div bitsPerDigit
if numDigits > maxDigits:
lexMessageLitNum(L,
"number has " & $numDigits & " digits but type only supports " &
$maxDigits & " digits: '$1'", startpos, warnLongLiterals)
var
xi: BiggestInt
isBase10 = true
@@ -513,11 +515,6 @@ proc getNumber(L: var Lexer, result: var Token) =
setNumber result.fNumber, (cast[ptr float64](addr(xi)))[]
else: internalError(L.config, getLineInfo(L), "getNumber")
# Check bit width for non-base-10 literals
# Warn if the digit count exceeds what can fit in the target type
if result.base != base10 and result.tokType in {tkIntLit..tkUInt64Lit} and numDigits > 0:
checkBitWidth(L, result.base, result.tokType, numDigits, startpos)
# Bounds checks. Non decimal literals are allowed to overflow the range of
# the datatype as long as their pattern don't overflow _bitwise_, hence
# below checks of signed sizes against uint*.high is deliberate:
@@ -540,8 +537,8 @@ proc getNumber(L: var Lexer, result: var Token) =
of floatTypes:
result.fNumber = parseFloat(result.literal)
of tkUInt64Lit, tkUIntLit:
var iNumber: uint64 = uint64(0)
var len: int = 0
var iNumber: uint64
var len: int
try:
len = parseBiggestUInt(result.literal, iNumber)
except ValueError:
@@ -550,8 +547,8 @@ proc getNumber(L: var Lexer, result: var Token) =
raise newException(ValueError, "invalid integer: " & result.literal)
result.iNumber = cast[int64](iNumber)
else:
var iNumber: int64 = int64(0)
var len: int = 0
var iNumber: int64
var len: int
try:
len = parseBiggestInt(result.literal, iNumber)
except ValueError:
@@ -797,7 +794,7 @@ proc getString(L: var Lexer, tok: var Token, mode: StringMode) =
if mode != normal: tok.tokType = tkRStrLit
else: tok.tokType = tkStrLit
while true:
let c = L.buf[pos]
var c = L.buf[pos]
if c == '\"':
if mode != normal and L.buf[pos+1] == '\"':
inc(pos, 2)
@@ -823,7 +820,7 @@ proc getCharacter(L: var Lexer; tok: var Token) =
tokenBegin(tok, L.bufpos)
let startPos = L.bufpos
inc(L.bufpos) # skip '
let c = L.buf[L.bufpos]
var c = L.buf[L.bufpos]
case c
of '\0'..pred(' '), '\'':
lexMessage(L, errGenerated, "invalid character literal")
@@ -923,7 +920,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
tok.tokType = tkSymbol
else:
tok.tokType = TokType(tok.ident.id + ord(tkSymbol))
if suspicious and {optStyleHint, optStyleError, optStyleWarning} * L.config.globalOptions != {}:
if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}:
lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s)
L.bufpos = pos
@@ -941,7 +938,7 @@ proc getOperator(L: var Lexer, tok: var Token) =
tokenBegin(tok, pos)
var h: Hash = 0
while true:
let c = L.buf[pos]
var c = L.buf[pos]
if c in OpChars:
h = h !& ord(c)
inc(pos)
@@ -1009,6 +1006,22 @@ proc getPrecedence*(tok: Token): int =
of tkOr, tkXor, tkPtr, tkRef: result = 3
else: return -10
proc newlineFollows*(L: Lexer): bool =
var pos = L.bufpos
while true:
case L.buf[pos]
of ' ', '\t':
inc(pos)
of CR, LF:
result = true
break
of '#':
inc(pos)
if L.buf[pos] == '#': inc(pos)
if L.buf[pos] != '[': return true
else:
break
proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
isDoc: bool) =
var pos = start
@@ -1100,7 +1113,9 @@ proc scanComment(L: var Lexer, tok: var Token) =
toStrip = 0
else: # found first non-whitespace character
stripInit = true
var lastBackslash = -1
while L.buf[pos] notin {CR, LF, nimlexbase.EndOfFile}:
if L.buf[pos] == '\\': lastBackslash = pos+1
tok.literal.add(L.buf[pos])
inc(pos)
tokenEndIgnore(tok, pos)
@@ -1143,7 +1158,7 @@ proc skip(L: var Lexer, tok: var Token) =
inc(pos)
tok.spacing.incl(tsLeading)
of '\t':
lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
inc(pos)
of CR, LF:
tokenEndPrevious(tok, pos)
@@ -1211,7 +1226,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
L.previousToken.line = tok.line.uint16
L.previousToken.col = tok.col.int16
reset(tok)
fillToken(tok)
if L.indentAhead >= 0:
tok.indent = L.indentAhead
L.currLineIndent = L.indentAhead
@@ -1223,7 +1238,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
if tok.tokType == tkComment:
L.indentAhead = L.currLineIndent
return
let c = L.buf[L.bufpos]
var c = L.buf[L.bufpos]
tok.line = L.lineNumber
tok.col = getColNumber(L, L.bufpos)
if c in SymStartChars - {'r', 'R'} - UnicodeOperatorStartChars:
@@ -1349,7 +1364,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
of '-':
if L.buf[L.bufpos+1] in {'0'..'9'} and
(L.bufpos == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
(L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
# x)-23 # binary minus
# ,-23 # unary minus
# \n-78 # unary minus? Yes.
@@ -1379,9 +1394,9 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache; config: ConfigRef): int =
result = 0
var lex: Lexer = default(Lexer)
var tok: Token = default(Token)
var lex: Lexer
var tok: Token
initToken(tok)
openLexer(lex, fileIdx, inputstream, cache, config)
var prevToken = tkEof
while tok.tokType != tkEof:
@@ -1394,11 +1409,11 @@ proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
proc getPrecedence*(ident: PIdent): int =
## assumes ident is binary operator already
let
tokType =
if ident.id in ord(tokKeywordLow) - ord(tkSymbol)..ord(tokKeywordHigh) - ord(tkSymbol):
TokType(ident.id + ord(tkSymbol))
else: tkOpr
tok = Token(ident: ident, tokType: tokType)
var tok: Token
initToken(tok)
tok.ident = ident
tok.tokType =
if tok.ident.id in ord(tokKeywordLow) - ord(tkSymbol)..ord(tokKeywordHigh) - ord(tkSymbol):
TokType(tok.ident.id + ord(tkSymbol))
else: tkOpr
getPrecedence(tok)

View File

@@ -11,9 +11,8 @@
## (`=sink`, `=copy`, `=destroy`, `=deepCopy`, `=wasMoved`, `=dup`).
import modulegraphs, lineinfos, idents, ast, renderer, semdata,
sighashes, lowerings, options, types, msgs, magicsys, ccgutils
sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils
import std/tables
from trees import isCaseObj
when defined(nimPreviewSlimSystem):
@@ -49,17 +48,17 @@ 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)
op != nil and sfOverridden in op.flags
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
for i, a in t.ikids:
for i in 0..<t.len:
let lit = lowerings.newIntLit(c.g, x.info, i)
let b = if c.kind == attachedTrace: y else: y.at(lit, a)
fillBody(c, a, body, x.at(lit, a), b)
let b = if c.kind == attachedTrace: y else: y.at(lit, t[i])
fillBody(c, t[i], body, x.at(lit, t[i]), b)
proc dotField(x: PNode, f: PSym): PNode =
result = newNodeI(nkDotExpr, x.info, 2)
@@ -68,7 +67,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,10 +87,10 @@ 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)
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
proc genAddr(c: var TLiftCtx; x: PNode): PNode =
if x.kind == nkHiddenDeref:
@@ -105,7 +104,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 +126,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)
@@ -139,21 +138,21 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
result.add minusExpr
proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
var destroy = newNodeIT(nkCall, x.info, op.typ.returnType)
var destroy = newNodeIT(nkCall, x.info, op.typ[0])
destroy.add(newSymNode(op))
if op.typ.firstParamType.kind != tyVar:
if op.typ[1].kind != tyVar:
destroy.add x
else:
destroy.add genAddr(c, x)
if sfNeverRaises notin op.flags:
c.canRaise = true
if c.addMemReset:
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "wasMoved", x))
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "`=wasMoved`", x))
else:
result = destroy
proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ.returnType)
result = newNodeIT(nkCall, x.info, op.typ[0])
result.add(newSymNode(op))
result.add genAddr(c, x)
@@ -168,7 +167,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
defaultOp(c, f.typ, body, x.dotField(f), b)
else:
if enforceWasMoved:
body.add genBuiltin(c, mWasMoved, "wasMoved", x.dotField(f))
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
fillBody(c, f.typ, body, x.dotField(f), b)
of nkNilLit: discard
of nkRecCase:
@@ -229,17 +228,17 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
template fillBase =
if t.baseClass != nil:
let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
if t.len > 0 and t[0] != nil:
let dest = newNodeIT(nkHiddenSubConv, c.info, t[0])
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 = newNodeIT(nkHiddenSubConv, c.info, t[0])
src.add newNodeI(nkEmpty, c.info)
src.add y
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
fillBody(c, skipTypes(t[0], abstractPtrs), body, dest, src)
template fillFields =
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
@@ -254,8 +253,8 @@ proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
var hasCase = isCaseObj(t.n)
var obj = t
while obj.baseClass != nil:
obj = skipTypes(obj.baseClass, abstractPtrs)
while obj.len > 0 and obj[0] != nil:
obj = skipTypes(obj[0], abstractPtrs)
hasCase = hasCase or isCaseObj(obj.n)
if hasCase and c.kind in {attachedAsgn, attachedDeepCopy}:
@@ -280,7 +279,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
@@ -292,8 +291,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
#body.add newAsgnStmt(blob, x)
var wasMovedCall = newNodeI(nkCall, c.info)
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "`=wasMoved`", mWasMoved)))
wasMovedCall.add x # mWasMoved does not take the address
body.add wasMovedCall
@@ -312,11 +310,11 @@ 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}
if c.fn.typ.len == 3 + ord(c.kind == attachedAsgn):
if c.fn.typ.len == 4:
result = c.fn.typ.n.lastSon
assert result.kind == nkSym
assert result.sym.name.s == "cyclic"
@@ -330,35 +328,27 @@ proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
result.add newSymNode(op)
if sfNeverRaises notin op.flags:
c.canRaise = true
if op.typ.firstParamType.kind == tyVar:
if op.typ.sons[1].kind == tyVar:
result.add genAddr(c, x)
else:
result.add x
if y != nil:
result.add y
if op.typ.signatureLen == 4:
if op.typ.len == 4:
assert y != nil
if c.fn.typ.signatureLen == 4:
if c.fn.typ.len == 4:
result.add getCycleParam(c)
else:
# assume the worst: A cycle is created:
result.add boolLit(c.g, y.info, true)
proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ.returnType)
result = newNodeIT(nkCall, x.info, op.typ[0])
result.add(newSymNode(op))
result.add x
if sfNeverRaises notin op.flags:
c.canRaise = true
if c.kind == attachedDup and op.typ.len == 3:
assert x != nil
if c.fn.typ.len == 3:
result.add getCycleParam(c)
else:
# assume the worst: A cycle is created:
result.add boolLit(c.g, x.info, true)
proc newDeepCopyCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
result = newAsgnStmt(x, newOpCall(c, op, y))
@@ -380,10 +370,6 @@ proc requiresDestructor(c: TLiftCtx; t: PType): bool {.inline.} =
proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
if c.c != nil and typeInst != nil:
result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
elif typeInst != nil and getAttachedOp(c.g, typeInst, c.kind) != nil:
# c.c == nil in lambdalifting
# hooks are already insted
result = getAttachedOp(c.g, typeInst, c.kind)
else:
localError(c.g.config, c.info,
"cannot generate destructor for generic type: " & typeToString(t))
@@ -407,8 +393,6 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
body.add newHookCall(c, op, x, y)
result = true
else:
result = false
elif tfHasAsgn in t.flags:
var op: PSym
if sameType(t, c.asgnForType):
@@ -438,8 +422,6 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
assert op.ast[genericParamsPos].kind == nkEmpty
body.add newHookCall(c, op, x, y)
result = true
else:
result = false
proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
let t = orig.skipTypes(abstractInst - {tyDistinct})
@@ -479,8 +461,6 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
onUse(c.info, op)
body.add destructorCall(c, op, x)
result = true
else:
result = false
#result = addDestructorCall(c, t, body, x)
of attachedAsgn, attachedSink, attachedTrace:
var op = getAttachedOp(c.g, t, c.kind)
@@ -501,8 +481,6 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
onUse(c.info, op)
body.add newDeepCopyCall(c, op, x, y)
result = true
else:
result = false
of attachedWasMoved:
var op = getAttachedOp(c.g, t, attachedWasMoved)
@@ -517,8 +495,6 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
onUse(c.info, op)
body.add genWasMovedCall(c, op, x)
result = true
else:
result = false
of attachedDup:
var op = getAttachedOp(c.g, t, attachedDup)
@@ -533,8 +509,6 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
onUse(c.info, op)
body.add newDupCall(c, op, x, y)
result = true
else:
result = false
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
@@ -565,18 +539,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)
@@ -585,7 +559,7 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let counterIdx = body.len
let i = declareCounter(c, body, toInt64(firstOrd(c.g.config, t)))
let whileLoop = genWhileLoop(c, i, x)
let elemType = t.elementType
let elemType = t.lastSon
let b = if c.kind == attachedTrace: y else: y.at(i, elemType)
fillBody(c, elemType, whileLoop[1], x.at(i, elemType), b)
if whileLoop[1].len > 0:
@@ -599,37 +573,14 @@ 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 genBulkCopySeq(c: var TLiftCtx; t: PType; body, x, y: PNode) =
## Generates a call to nimCopySeqPayload for bulk memcpy of seq data.
let elemType = t.elementType
let sym = magicsys.getCompilerProc(c.g, "nimCopySeqPayload")
if sym == nil:
localError(c.g.config, c.info, "system module needs: nimCopySeqPayload")
return
var sizeOf = genBuiltin(c, mSizeOf, "sizeof", newNodeIT(nkType, c.info, elemType))
sizeOf.typ = getSysType(c.g, c.info, tyInt)
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
alignOf.typ = getSysType(c.g, c.info, tyInt)
let call = newNodeI(nkCall, c.info)
call.add newSymNode(sym)
call.add newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x)
call.add newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
call.add sizeOf
call.add alignOf
call.typ = sym.typ.returnType
body.add call
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
of attachedDup:
body.add setLenSeqCall(c, t, x, y)
if supportsCopyMem(t.elementType):
genBulkCopySeq(c, t, body, x, y)
else:
forallElements(c, t, body, x, y)
forallElements(c, t, body, x, y)
of attachedAsgn, attachedDeepCopy:
# we generate:
# if x.p == y.p:
@@ -638,13 +589,9 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# var i = 0
# while i < y.len: dest[i] = y[i]; inc(i)
# This is usually more efficient than a destroy/create pair.
# For trivially copyable types, use bulk copyMem instead of element loop.
checkSelfAssignment(c, t, body, x, y)
body.add setLenSeqCall(c, t, x, y)
if supportsCopyMem(t.elementType):
genBulkCopySeq(c, t, body, x, y)
else:
forallElements(c, t, body, x, y)
forallElements(c, t, body, x, y)
of attachedSink:
let moveCall = genBuiltin(c, mMove, "move", x)
moveCall.add y
@@ -659,7 +606,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if canFormAcycle(c.g, t.elemType):
# follow all elements:
forallElements(c, t, body, x, y)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
@@ -697,7 +644,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if op == nil:
return # protect from recursion
body.add newHookCall(c, op, x, y)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedDup:
# XXX: replace these with assertions.
let op = getAttachedOp(c.g, t, c.kind)
@@ -719,11 +666,11 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genBuiltin(c, mDestroy, "destroy", x)
of attachedTrace:
discard "strings are atomic and have no inner elements that are to trace"
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
proc cyclicType*(g: ModuleGraph, t: PType): bool =
case t.kind
of tyRef: result = types.canFormAcycle(g, t.elementType)
of tyRef: result = types.canFormAcycle(g, t.lastSon)
of tyProc: result = t.callConv == ccClosure
else: result = false
@@ -748,7 +695,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
]#
var actions = newNodeI(nkStmtList, c.info)
let elemType = t.elementType
let elemType = t.lastSon
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(c.g, elemType)
@@ -767,7 +714,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))
@@ -777,7 +724,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)
@@ -785,7 +732,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:
@@ -812,13 +759,13 @@ 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
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
#echo "can follow ", elemType, " static ", isFinal(elemType)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
of attachedDup:
if isCyclic:
body.add newAsgnStmt(x, y)
@@ -833,7 +780,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 =
@@ -849,7 +796,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:
@@ -861,7 +808,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)
@@ -873,7 +820,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)))
@@ -885,7 +832,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace:
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
@@ -913,19 +860,19 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.sons.insert(des, 0)
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
var actions = newNodeI(nkStmtList, c.info)
let elemType = t.skipModifier
let elemType = t.lastSon
#fillBody(c, elemType, actions, genDeref(x), genDeref(y))
#var disposeCall = genBuiltin(c, mDispose, "dispose", x)
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))
@@ -941,21 +888,21 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genIf(c, x, actions)
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if c.kind == attachedDeepCopy:
# 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
@@ -964,13 +911,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:
@@ -981,11 +928,11 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.sons.insert(des, 0)
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
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)
@@ -999,7 +946,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genIf(c, xx, actions)
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case t.kind
@@ -1049,13 +996,9 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# 'selectedGC' here to determine if we have the new runtime.
discard considerUserDefinedOp(c, t, body, x, y)
elif tfHasAsgn in t.flags:
# seqs with elements using custom hooks in refc
if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
body.add newSeqCall(c, x, y)
if c.kind == attachedWasMoved:
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
else:
forallElements(c, t, body, x, y)
forallElements(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyString:
@@ -1072,11 +1015,9 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of {attachedAsgn, attachedSink, attachedDup}:
body.add newAsgnStmt(x, y)
of attachedWasMoved:
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
else:
fillBodyObjT(c, t, body, x, y)
elif tfUnion in t.flags: # bug #25236
defaultOp(c, t, body, x, y)
else:
if c.kind == attachedDup:
var op2 = getAttachedOp(c.g, t, attachedAsgn)
@@ -1090,7 +1031,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
fillBodyObjT(c, t, body, x, y)
of tyDistinct:
if not considerUserDefinedOp(c, t, body, x, y):
fillBody(c, t.elementType, body, x, y)
fillBody(c, t[0], body, x, y)
of tyTuple:
fillBodyTup(c, t, body, x, y)
of tyVarargs, tyOpenArray:
@@ -1099,7 +1040,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
else:
discard "cannot copy openArray"
of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass,
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
@@ -1107,14 +1048,14 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
discard
of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyAlias, tySink:
fillBody(c, skipModifier(t), body, x, y)
of tyConcept, tyIterable: raiseAssert "unreachable"
fillBody(c, lastSon(t), body, x, y)
of tyConcept, tyIterable: doAssert false
proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
kind: TTypeAttachedOp; info: TLineInfo;
idgen: IdGenerator): PSym =
assert typ.kind == tyDistinct
let baseType = typ.elementType
let baseType = typ[0]
if getAttachedOp(g, baseType, kind) == nil:
discard produceSym(g, c, baseType, kind, info, idgen)
result = getAttachedOp(g, baseType, kind)
@@ -1130,7 +1071,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache
res.typ = typ
src.typ = typ
result.typ = newType(tyProc, idgen, owner)
result.typ = newType(tyProc, nextTypeId idgen, owner)
result.typ.n = newNodeI(nkFormalParams, info)
rawAddSon(result.typ, res.typ)
result.typ.n.add newNodeI(nkEffectList, info)
@@ -1164,7 +1105,6 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
idgen, result, info)
if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence})):
dest.typ = typ
@@ -1176,7 +1116,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
else:
src.typ = typ
result.typ = newProcType(info, idgen, owner)
result.typ = newProcType(info, nextTypeId(idgen), owner)
result.typ.addParam dest
if kind notin {attachedDestructor, attachedWasMoved}:
result.typ.addParam src
@@ -1203,8 +1143,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;
@@ -1220,7 +1160,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
fn: result)
let dest = if kind == attachedDup: result.ast[resultPos].sym else: result.typ.n[1].sym
let d = if result.typ.firstParamType.kind == tyVar: newDeref(newSymNode(dest)) else: newSymNode(dest)
let d = if result.typ[1].kind == tyVar: newDeref(newSymNode(dest)) else: newSymNode(dest)
let src = case kind
of {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
of attachedDup: newSymNode(result.typ.n[1].sym)
@@ -1233,14 +1173,12 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
## compiler can use a combination of `=destroy` and memCopy for sink op
dest.flags.incl sfCursor
let op = getAttachedOp(g, typ, attachedDestructor)
result.ast[bodyPos].add newOpCall(a, op, if op.typ.firstParamType.kind == tyVar: d[0] else: d)
result.ast[bodyPos].add newOpCall(a, op, if op.typ[1].kind == tyVar: d[0] else: d)
result.ast[bodyPos].add newAsgnStmt(d, src)
else:
var tk: TTypeKind
var skipped: PType = nil
if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
skipped = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink})
tk = skipped.kind
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
else:
tk = tyNone # no special casing for strings and seqs
case tk
@@ -1250,7 +1188,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
fillStrOp(a, typ, result.ast[bodyPos], d, src)
else:
fillBody(a, typ, result.ast[bodyPos], d, src)
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(skipped):
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(typ):
# bug #19205: Do not forget to also copy the hidden type field:
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
@@ -1321,7 +1259,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
else:
localError(g.config, info, "unresolved generic parameter")
proc isTrivial*(s: PSym): bool {.inline.} =
proc isTrival*(s: PSym): bool {.inline.} =
s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
@@ -1331,10 +1269,6 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
## The later 'injectdestructors' pass depends on it.
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
incl orig.flags, tfCheckedForDestructor
# for user defined generic destructors:
let origRoot = genericRoot(orig)
if origRoot != nil:
incl origRoot.flags, tfGenericHasDestructor
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
@@ -1360,7 +1294,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
# bug #15122: We need to produce all prototypes before entering the
# mind boggling recursion. Hacks like these imply we should rewrite
# this module.
var generics: array[attachedWasMoved..attachedTrace, bool] = default(array[attachedWasMoved..attachedTrace, bool])
var generics: array[attachedWasMoved..attachedTrace, bool]
for k in attachedWasMoved..lastAttached:
generics[k] = getAttachedOp(g, canon, k) != nil
if not generics[k]:
@@ -1376,8 +1310,8 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
if canon != orig:
setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
if not isTrivial(getAttachedOp(g, orig, attachedDestructor)):
#or not isTrivial(orig.assignment) or
# not isTrivial(orig.sink):
if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
#or not isTrival(orig.assignment) or
# not isTrival(orig.sink):
orig.flags.incl tfHasAsgn
# ^ XXX Breaks IC!

View File

@@ -10,11 +10,9 @@
## This module implements the '.liftLocals' pragma.
import
options, ast, msgs,
strutils, options, ast, msgs,
idents, renderer, types, lowerings, lineinfos
import std/strutils
from pragmas import getPragmaVal
from wordrecg import wLiftLocals
@@ -32,12 +30,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]
@@ -51,7 +49,6 @@ proc liftLocals(n: PNode; i: int; c: var Ctx) =
liftLocals(it, i, c)
proc lookupParam(params, dest: PNode): PSym =
result = nil
if dest.kind != nkIdent: return nil
for i in 1..<params.len:
if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id:

View File

@@ -7,11 +7,10 @@
# distribution, for details about the copyright.
#
## This module contains the `TMsgKind` enum as well as the
## `TLineInfo` object.
## This module contains the ``TMsgKind`` enum as well as the
## ``TLineInfo`` object.
import ropes, pathutils
import std/[hashes, tables]
import ropes, tables, pathutils, hashes
const
explanationsBaseUrl* = "https://nim-lang.github.io/Nim"
@@ -93,14 +92,7 @@ type
warnBareExcept = "BareExcept",
warnImplicitDefaultValue = "ImplicitDefaultValue",
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
warnStdPrefix = "StdPrefix",
warnUnknownNotes = "UnknownNotes",
warnLongLiterals = "LongLiterals",
warnUser = "User",
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
warnImplicitRangeConversion = "ImplicitRangeConversion",
warnSystemRangeConversion = "SystemRangeConversion",
warnInvalidCmpOp = "InvalidCmpOp",
# hints
hintSuccess = "Success", hintSuccessX = "SuccessX",
hintCC = "CC",
@@ -114,9 +106,9 @@ type
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
hintUnknownRaises = "UnknownRaises",
hintMsgOrigin = "MsgOrigin", # since 1.3.5
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
hintUnknownHint = "UnknownHint"
const
MsgKindToStr*: array[TMsgKind, string] = [
@@ -204,14 +196,7 @@ const
warnBareExcept: "$1",
warnImplicitDefaultValue: "$1",
warnIgnoredSymbolInjection: "$1",
warnStdPrefix: "$1 needs the 'std' prefix",
warnUnknownNotes: "$1",
warnLongLiterals: "$1",
warnUser: "$1",
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
warnImplicitRangeConversion: "implicit range conversion $1",
warnSystemRangeConversion: "implicit range conversion $1",
warnInvalidCmpOp: "$1",
hintSuccess: "operation successful: $#",
# keep in sync with `testament.isSuccess`
hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
@@ -245,9 +230,9 @@ const
hintUser: "$1",
hintUserRaw: "$1",
hintExtendedContext: "$1",
hintUnknownRaises: "$1 is a forward declaration without explicit .raises, assuming it can raise anything",
hintMsgOrigin: "$1",
hintDeclaredLoc: "$1"
hintDeclaredLoc: "$1",
hintUnknownHint: "unknown hint: $1"
]
const
@@ -265,10 +250,9 @@ type
TNoteKinds* = set[TNoteKind]
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
result = default(array[0..3, TNoteKinds])
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix, warnSystemRangeConversion}
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept}
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
result[1] = result[2] - {warnImplicitRangeConversion, warnProveField, warnProveIndex,
result[1] = result[2] - {warnProveField, warnProveIndex,
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
@@ -278,7 +262,6 @@ 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
@@ -329,7 +312,7 @@ proc `==`*(a, b: FileIndex): bool {.borrow.}
proc hash*(i: TLineInfo): Hash =
hash (i.line.int, i.col.int, i.fileIndex.int)
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
proc raiseRecoverableError*(msg: string) {.noinline.} =
raise newException(ERecoverableError, msg)
const
@@ -360,8 +343,9 @@ type
proc initMsgConfig*(): MsgConfig =
result = MsgConfig(msgContext: @[], lastError: unknownLineInfo,
filenameToIndexTbl: initTable[string, FileIndex](),
fileInfos: @[], errorOutputs: {eStdOut, eStdErr}
)
result.msgContext = @[]
result.lastError = unknownLineInfo
result.filenameToIndexTbl = initTable[string, FileIndex]()
result.fileInfos = @[]
result.errorOutputs = {eStdOut, eStdErr}
result.filenameToIndexTbl["???"] = FileIndex(-1)

View File

@@ -19,12 +19,10 @@ const
Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
proc identLen*(line: string, start: int): int =
result = 0
while start+result < line.len and line[start+result] in Letters:
inc result
proc `=~`(s: string, a: openArray[string]): bool =
result = false
for x in a:
if s.startsWith(x): return true
@@ -95,7 +93,7 @@ proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) =
## Check symbol definitions adhere to NEP1 style rules.
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
{optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # check only if hint/error/warning is enabled
{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.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
@@ -136,7 +134,7 @@ proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
## Check symbol uses match their definition's style.
if {optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
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.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
sym.kind != skTemp and # ignore temporary variables created by the compiler
@@ -152,7 +150,7 @@ proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragm
template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
## Check builtin pragma uses match their definition's style.
## Note: This only applies to builtin pragmas, not user pragmas.
if {optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
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.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)): # ignore foreign packages
checkPragmaUseImpl(ctx.config, info, w, pragmaName)

View File

@@ -11,7 +11,7 @@
import
pathutils
import std/strutils
when defined(nimPreviewSlimSystem):
import std/syncio
@@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem):
const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and
not defined(windows)
when hasRstdin: import std/rdstdin
when hasRstdin: import rdstdin
type
TLLRepl* = proc (s: PLLStream, buf: pointer, bufLen: int): int
@@ -40,22 +40,33 @@ type
PLLStream* = ref TLLStream
proc llStreamOpen*(data: sink string): PLLStream =
PLLStream(kind: llsString, s: data)
proc llStreamOpen*(data: string): PLLStream =
new(result)
result.s = data
result.kind = llsString
proc llStreamOpen*(f: File): PLLStream =
PLLStream(kind: llsFile, f: f)
new(result)
result.f = f
result.kind = llsFile
proc llStreamOpen*(filename: AbsoluteFile, mode: FileMode): PLLStream =
result = PLLStream(kind: llsFile)
new(result)
result.kind = llsFile
if not open(result.f, filename.string, mode): result = nil
proc llStreamOpen*(): PLLStream =
PLLStream(kind: llsNone)
new(result)
result.kind = llsNone
proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int
proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin, onPrompt: OnPrompt = nil): PLLStream =
PLLStream(kind: llsStdIn, s: "", lineOffset: -1, repl: r, onPrompt: onPrompt)
new(result)
result.kind = llsStdIn
result.s = ""
result.lineOffset = -1
result.repl = r
result.onPrompt = onPrompt
proc llStreamClose*(s: PLLStream) =
case s.kind
@@ -68,7 +79,6 @@ 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")
@@ -79,54 +89,11 @@ proc endsWith*(x: string, s: set[char]): bool =
while i >= 0 and x[i] == ' ': dec(i)
if i >= 0 and x[i] in s:
result = true
else:
result = false
const
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
'|', '%', '&', '$', '@', '~', ','}
AdditionalLineContinuationOprs = {'#', ':', '='}
LineContinuationTokens = [
"let", "var", "const", "type", # section
"object", "tuple",
# from ./layouter.oprSet
"div", "mod", "shl", "shr", "in", "notin", "is",
"isnot", "not", "of", "as", "from", "..", "and", "or", "xor",
] # must be all `nimIdentNormalized`-ed
proc eqIdent(a, bNormalized: string): bool =
a.nimIdentNormalize == bNormalized
proc endsWithIdent(s, subs: string): bool =
let le = subs.len
if le > s.len: return false
s[^le .. ^1].eqIdent subs
proc continuesWithIdent(s, subs: string, start: int): bool =
s.substr(start, start+subs.high).eqIdent subs
proc endsWithIdent(s, subs: string, endIdx: var int): bool =
endIdx.dec subs.len
result = s.continuesWithIdent(subs, endIdx+1)
proc containsObjectOf(x: string): bool =
const sep = ' '
var idx = x.rfind(sep)
if idx == -1: return
template eatWord(word) =
while x[idx] == sep: idx.dec
result = x.endsWithIdent(word, idx)
if not result: return
eatWord "of"
eatWord "object"
result = true
proc endsWithLineContinuationToken(x: string): bool =
result = false
for tok in LineContinuationTokens:
if x.endsWithIdent(tok):
return true
result = x.containsObjectOf
proc endsWithOpr*(x: string): bool =
result = x.endsWith(LineContinuationOprs)
@@ -134,12 +101,9 @@ proc endsWithOpr*(x: string): bool =
proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
result = inTripleString or line.len > 0 and (
line[0] == ' ' or
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) or
line.endsWithLineContinuationToken()
)
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
proc countTriples(s: string): int =
result = 0
var i = 0
while i+2 < s.len:
if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"':
@@ -152,10 +116,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
s.rd = 0
var line = newStringOfCap(120)
var triples = 0
while true:
if not readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
# now readLineFromStdin meets EOF (ctrl-D/Z) or ctrl-C
quit()
while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
s.s.add(line)
s.s.add("\n")
inc triples, countTriples(line)

View File

@@ -14,10 +14,8 @@ when defined(nimPreviewSlimSystem):
import std/assertions
import
ast, astalgo, idents, semdata, types, msgs, options,
renderer, lineinfos, modulegraphs, astmsgs, wordrecg
import std/[intsets, sets]
intsets, ast, astalgo, idents, semdata, types, msgs, options,
renderer, lineinfos, modulegraphs, astmsgs, sets, wordrecg
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
@@ -58,11 +56,13 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
else: handleError(n, origin)
result = getIdent(c.cache, id)
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
if n[0].kind == nkSym:
result = n[0].sym.name
else:
handleError(n, origin)
of nkOpenSym:
result = considerQuotedIdent(c, n[0], origin)
else:
handleError(n, origin)
@@ -74,14 +74,11 @@ proc addUniqueSym*(scope: PScope, s: PSym): PSym =
proc openScope*(c: PContext): PScope {.discardable.} =
result = PScope(parent: c.currentScope,
symbols: initStrTable(),
depthLevel: c.scopeDepth + 1,
optionStackLen: c.optionStack.len)
symbols: newStrTable(),
depthLevel: c.scopeDepth + 1)
c.currentScope = result
proc rawCloseScope*(c: PContext) =
if c.currentScope.optionStackLen >= 1:
c.optionStack.setLen(c.currentScope.optionStackLen)
c.currentScope = c.currentScope.parent
proc closeScope*(c: PContext) =
@@ -222,14 +219,7 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
if i == limit: return
inc i
proc searchImportsAll*(c: PContext, s: PIdent, filter: TSymKinds, holding: var seq[PSym]) =
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s, c.graph):
if s.kind in filter:
holding.add s
proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = @[]
for scope in allScopes(c.currentScope):
var ti: TIdentIter = default(TIdentIter)
@@ -239,12 +229,14 @@ proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result.add candidate
candidate = nextIdentIter(ti, scope.symbols)
proc searchScopesAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = searchScopes(c,s,filter)
if result.len == 0:
searchImportsAll(c, s, filter, result)
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s, c.graph):
if s.kind in filter:
result.add s
proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = @[]
block outer:
for scope in allScopes(c.currentScope):
@@ -258,7 +250,11 @@ proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PS
candidate = nextIdentIter(ti, scope.symbols)
if result.len == 0:
searchImportsAll(c, s, filter, result)
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s, c.graph):
if s.kind in filter:
result.add s
proc cmpScopes*(ctx: PContext, s: PSym): int =
# Do not return a negative number
@@ -354,7 +350,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
var it: TTabIter = default(TTabIter)
var s = initTabIter(it, scope.symbols)
var missingImpls = 0
var unusedSyms: seq[tuple[sym: PSym, key: string]] = @[]
var unusedSyms: seq[tuple[sym: PSym, key: string]]
while s != nil:
if sfForward in s.flags and s.kind notin {skType, skModule}:
# too many 'implementation of X' errors are annoying
@@ -388,7 +384,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
if sym.name.id == ord(wUnderscore): return
let conflict = scope.addUniqueSym(sym)
if conflict != nil:
if sym.kind == skModule and conflict.kind == skModule and not c.config.isDefined("nimPreviewDuplicateModuleError"):
if sym.kind == skModule and conflict.kind == skModule:
# e.g.: import foo; import foo
# xxx we could refine this by issuing a different hint for the case
# where a duplicate import happens inside an include.
@@ -460,18 +456,9 @@ proc openShadowScope*(c: PContext) =
## opens a shadow scope, just like any other scope except the depth is the
## same as the parent -- see `isShadowScope`.
c.currentScope = PScope(parent: c.currentScope,
symbols: initStrTable(),
symbols: newStrTable(),
depthLevel: c.scopeDepth)
proc rememberShadowDefs*(c: PContext) =
## bug #25693: a template/macro operand's local definitions are sem-checked in
## a shadow scope that is then discarded. Record those definitions so that a
## later re-emission (e.g. a captured `typed` fragment expanded more than once)
## can be detected as a redefinition rather than silently miscompiled.
for s in c.currentScope.symbols:
if s.kind in {skVar, skLet, skForVar} and {sfGenSym, sfWasGenSym} * s.flags == {}:
c.shadowDiscardedDefs.incl s.id
proc closeShadowScope*(c: PContext) =
## closes the shadow scope, but doesn't merge any of the symbols
## Does not check for unused symbols or missing forward decls since a macro
@@ -494,7 +481,7 @@ proc mergeShadowScope*(c: PContext) =
c.addInterfaceDecl(sym)
import std/[editdistance, heapqueue]
import std/editdistance, heapqueue
type SpellCandidate = object
dist: int
@@ -523,7 +510,7 @@ proc fixSpelling(c: PContext, ident: PIdent, result: var string) =
for (sym, depth, isLocal) in allSyms(c):
let depth = -depth - 1
let dist = editDistance(name0, sym.name.s.nimIdentNormalize)
var msg: string = ""
var msg: string
msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s]
list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym)
@@ -553,7 +540,6 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PS
var err = "ambiguous identifier: '" & s.name.s & "'"
var i = 0
var ignoredModules = 0
result = nil
for candidate in importedItems(c, s.name):
if i == 0: err.add " -- use one of the following:\n"
else: err.add "\n"
@@ -574,33 +560,23 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
var amb: bool = false
discard errorUseQualifier(c, info, s, amb)
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 & "'"
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
var i = 0
for candidate in candidates:
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)
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)
inc i
localError(c.config, info, errGenerated, err)
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates))
proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string =
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
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
result = ambiguousIdentifierMsg(candidates, prefix, indent)
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices))
errorUseQualifier(c, info, candidates, prefix)
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
var err: string
@@ -653,10 +629,9 @@ type
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
includePureEnum = false): seq[PSym] =
result = selectFromScopesElseAll(c, ident, filter)
if skEnumField in filter and (result.len == 0 or includePureEnum):
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind]): seq[PSym] =
result = searchInScopesFilterBy(c, ident, filter)
if result.len == 0:
result.add allPureEnumFields(c, ident)
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
@@ -696,26 +671,19 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
if m != nil and m.kind == skModule:
var ident: PIdent = nil
if n[1].kind == nkAccQuoted:
if n[1].kind == nkIdent:
ident = n[1].ident
elif n[1].kind == nkAccQuoted:
ident = considerQuotedIdent(c, n[1])
else:
# this includes sym and symchoice nodes, but since we are looking in
# a module, it shouldn't matter what was captured
ident = n[1].getPIdent
if ident != nil:
if m == c.module:
var ti: TIdentIter = default(TIdentIter)
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
result = strTableGet(c.topLevelScope.symbols, ident)
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 = someSymAmb(c.graph, m, ident, amb)
if amb: c.isAmbiguous = true
result = someSym(c.graph, m, ident)
if result == nil and checkUndeclared in flags:
result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
elif n[1].kind == nkSym:
@@ -736,13 +704,12 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if n.kind == nkOpenSym:
# maybe the logic in semexprs should be mirrored here instead
# for now it only seems this is called for `pickSym` in `getTypeIdent`
# for now it only seems this is called for `pickSym` in `getTypeIdent`
return initOverloadIter(o, c, n[0])
o.importIdx = -1
o.marked = initIntSet()
case n.kind
of nkIdent, nkAccQuoted:
result = nil
var ident = considerQuotedIdent(c, n)
var scope = c.currentScope
o.mode = oimNoQualifier
@@ -766,7 +733,6 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
result = n.sym
o.mode = oimDone
of nkDotExpr:
result = nil
o.mode = oimOtherModule
o.m = qualifiedLookUp(c, n[0], {checkUndeclared, checkModule})
if o.m != nil and o.m.kind == skModule:
@@ -796,7 +762,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
o.symChoiceIndex = 1
o.marked = initIntSet()
incl(o.marked, result.id)
else: result = nil
else: discard
when false:
if result != nil and result.kind == skStub: loadStub(result)
@@ -811,7 +777,6 @@ proc lastOverloadScope*(o: TOverloadIter): int =
else: result = -1
proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym =
result = nil
assert o.currentScope == nil
var idx = o.importIdx+1
o.importIdx = c.imports.len # assume the other imported modules lack this symbol too
@@ -824,7 +789,6 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
inc idx
proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym =
result = nil
assert o.currentScope == nil
while o.importIdx < c.imports.len:
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph)
@@ -887,8 +851,6 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
break
if result != nil:
incl o.marked, result.id
else:
result = nil
of oimSymChoiceLocalLookup:
if o.currentScope != nil:
result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked)
@@ -912,16 +874,13 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if result == nil:
inc o.importIdx
result = symChoiceExtension(o, c, n)
else:
result = nil
when false:
if result != nil and result.kind == skStub: loadStub(result)
proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind];
flags: TSymFlags = {}): PSym =
result = nil
var o: TOverloadIter = default(TOverloadIter)
var o: TOverloadIter
var a = initOverloadIter(o, c, n)
while a != nil:
if a.kind in kinds and flags <= a.flags:

View File

@@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem):
import std/assertions
proc newDeref*(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.elementType)
result = newNodeIT(nkHiddenDeref, n.info, n.typ[0])
result.add n
proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
@@ -144,7 +144,7 @@ proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNod
result.add newFastAsgnStmt(n[2], tempAsNode)
proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType =
result = newType(tyObject, idgen, owner)
result = newType(tyObject, nextTypeId(idgen), owner)
if final:
rawAddSon(result, nil)
incl result.flags, tfFinal
@@ -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,19 +250,19 @@ 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)[0]
var t = deref.typ.skipTypes(abstractInst)
var field: PSym
while true:
assert t.kind == tyObject
field = lookupInRecord(t.n, b)
if field != nil: break
t = t.baseClass
t = t[0]
if t == nil: break
t = t.skipTypes(skipPtrs)
#if field == nil:
@@ -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)[0]
var t = deref.typ.skipTypes(abstractInst)
var field: PSym
let bb = getIdent(cache, b)
@@ -286,7 +286,7 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): P
assert t.kind == tyObject
field = getSymFromList(t.n, bb)
if field != nil: break
t = t.baseClass
t = t[0]
if t == nil: break
t = t.skipTypes(skipPtrs)
#if field == nil:
@@ -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
@@ -306,7 +306,7 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
assert t.kind == tyObject
result = lookupInRecord(t.n, v.itemId)
if result != nil: break
t = t.baseClass
t = t[0]
if t == nil: break
t = t.skipTypes(skipPtrs)
@@ -320,12 +320,12 @@ 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, nextTypeId(idgen), n.typ.owner)
result.typ.rawAddSon(n.typ)
proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
result = newNodeIT(k, n.info,
n.typ.skipTypes(abstractInst).elementType)
n.typ.skipTypes(abstractInst)[0])
result.add n
proc callCodegenProc*(g: ModuleGraph; name: string;
@@ -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[0]
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

@@ -11,14 +11,14 @@
import
ast, astalgo, msgs, platform, idents,
modulegraphs, lineinfos, types
modulegraphs, lineinfos
export createMagic
proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt]
proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
result = newType(kind, g.idgen, g.systemModule)
result = newType(kind, nextTypeId(g.idgen), g.systemModule)
result.size = size
result.align = size.int16
@@ -27,20 +27,19 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
if result == nil:
localError(g.config, info, "system module needs: " & name)
result = newSym(skError, getIdent(g.cache, name), g.idgen, g.systemModule, g.systemModule.info, {})
result.typ = newType(tyError, g.idgen, g.systemModule)
result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
result = nil
let id = getIdent(g.cache, name)
for r in systemModuleSyms(g, id):
if r.magic == m:
# prefer the tyInt variant:
if r.typ.returnType != nil and r.typ.returnType.kind == tyInt: return r
if r.typ[0] != nil and r.typ[0].kind == tyInt: return r
result = r
if result != nil: return result
localError(g.config, info, "system module needs: " & name)
result = newSym(skError, id, g.idgen, g.systemModule, g.systemModule.info, {})
result.typ = newType(tyError, g.idgen, g.systemModule)
result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
result = getSysSym(g, info, name).typ
@@ -81,8 +80,8 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
proc resetSysTypes*(g: ModuleGraph) =
g.systemModule = nil
g.compilerprocs = initStrTable()
g.exposed = initStrTable()
initStrTable(g.compilerprocs)
initStrTable(g.exposed)
for i in low(g.sysTypes)..high(g.sysTypes):
g.sysTypes[i] = nil
@@ -93,23 +92,16 @@ proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} =
if t.n != nil and t.kind in {tyInt, tyFloat}:
result = copyType(t, id, t.owner)
result = copyType(t, nextTypeId(id), t.owner)
result.n = nil
else:
result = t
proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) =
let s = son.skipIntLit(id)
father.add(s)
father.sons.add(s)
propagateToOwner(father, s)
proc makeVarType*(owner: PSym; baseType: PType; idgen: IdGenerator; kind = tyVar): PType =
if baseType.kind == kind:
result = baseType
else:
result = newType(kind, idgen, owner)
addSonSkipIntLit(result, baseType, idgen)
proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
let ident = getIdent(g.cache, name)
result = strTableGet(g.compilerprocs, ident)
@@ -131,10 +123,10 @@ proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) =
proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
strTableGet(g.exposed, getIdent(g.cache, name))
proc resetNimScriptSymbols*(g: ModuleGraph) = g.exposed = initStrTable()
proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)
proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
case t.skipTypes(abstractRange).kind
case t.kind
of tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64:
result = getSysMagic(g, info, "==", mEqI)
@@ -153,17 +145,7 @@ proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
of tyProc:
result = getSysMagic(g, info, "==", mEqProc)
else:
result = nil
globalError(g.config, info,
"can't find magic equals operator for type kind " & $t.kind)
proc makePtrType*(baseType: PType; idgen: IdGenerator): PType =
result = newType(tyPtr, idgen, baseType.owner)
addSonSkipIntLit(result, baseType, idgen)
proc makeAddr*(n: PNode; idgen: IdGenerator): PNode =
if n.kind == nkHiddenAddr:
result = n
else:
result = newTree(nkHiddenAddr, n)
result.typ() = makePtrType(n.typ.skipTypes({tySink}), idgen)

View File

@@ -26,7 +26,8 @@ import
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
import ic / [cbackend, integrity, navigator, ic]
import ic / [cbackend, integrity, navigator]
from ic / ic import rodViewer
import ../dist/checksums/src/checksums/sha1
@@ -55,7 +56,7 @@ proc writeCMakeDepsFile(conf: ConfigRef) =
for it in conf.toCompile: cfiles.add(it.cname.string)
let fileset = cfiles.toCountTable()
# read old cfiles list
var fl: File = default(File)
var fl: File
var prevset = initCountTable[string]()
if open(fl, fname.string, fmRead):
for line in fl.lines: prevset.inc(line)
@@ -114,7 +115,7 @@ when not defined(leanCompiler):
setPipeLinePass(graph, Docgen2JsonPass)
of HtmlExt:
setPipeLinePass(graph, Docgen2Pass)
else: raiseAssert $ext
else: doAssert false, $ext
compilePipelineProject(graph)
proc commandCompileToC(graph: ModuleGraph) =
@@ -196,6 +197,7 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
var
L: Lexer = default(Lexer)
tok: Token = default(Token)
initToken(tok)
openLexer(L, f, stream, cache, config)
while true:
rawGetTok(L, tok)
@@ -265,7 +267,7 @@ proc mainCommand*(graph: ModuleGraph) =
# and it has added this define implictly, so we must undo that here.
# A better solution might be to fix system.nim
undefSymbol(conf.symbols, "useNimRtl")
of backendInvalid: raiseAssert "unreachable"
of backendInvalid: doAssert false
proc compileToBackend() =
customizeForBackend(conf.backend)
@@ -275,7 +277,7 @@ proc mainCommand*(graph: ModuleGraph) =
of backendCpp: commandCompileToC(graph)
of backendObjc: commandCompileToC(graph)
of backendJs: commandCompileToJS(graph)
of backendInvalid: raiseAssert "unreachable"
of backendInvalid: doAssert false
template docLikeCmd(body) =
when defined(leanCompiler):
@@ -295,18 +297,14 @@ proc mainCommand*(graph: ModuleGraph) =
# so by default should not end up in $PWD nor in $projectPath.
var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
else: conf.projectPath
if not ret.string.isAbsolute: # `AbsoluteDir` is not a real guarantee
rawMessage(conf, errCannotOpenFile, ret.string & "/")
doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex}:
ret = ret / htmldocsDir
conf.outDir = ret
## process all commands
case conf.cmd
of cmdBackends:
compileToBackend()
when BenchIC:
echoTimes graph.packed
of cmdBackends: compileToBackend()
of cmdTcc:
when hasTinyCBackend:
extccomp.setCC(conf, "tcc", unknownLineInfo)
@@ -402,10 +400,6 @@ proc mainCommand*(graph: ModuleGraph) =
for it in conf.searchPaths: msgWriteln(conf, it.string)
of cmdCheck:
commandCheck(graph)
of cmdM:
graph.config.symbolFiles = v2Sf
setUseIc(graph.config.symbolFiles != disabledSf)
commandCheck(graph)
of cmdParse:
wantMainModule(conf)
discard parseFile(conf.projectMainIdx, cache, conf)

View File

@@ -1,59 +0,0 @@
import std/strutils
import ast, modulegraphs
proc mangle*(name: string): string =
result = newStringOfCap(name.len)
var start = 0
if name[0] in Digits:
result.add("X" & name[0])
start = 1
var requiresUnderscore = false
template special(x) =
result.add x
requiresUnderscore = true
for i in start..<name.len:
let c = name[i]
case c
of 'a'..'z', '0'..'9', 'A'..'Z':
result.add(c)
of '_':
# we generate names like 'foo_9' for scope disambiguations and so
# disallow this here:
if i > 0 and i < name.len-1 and name[i+1] in Digits:
discard
else:
result.add(c)
of '$': special "dollar"
of '%': special "percent"
of '&': special "amp"
of '^': special "roof"
of '!': special "emark"
of '?': special "qmark"
of '*': special "star"
of '+': special "plus"
of '-': special "minus"
of '/': special "slash"
of '\\': special "backslash"
of '=': special "eq"
of '<': special "lt"
of '>': special "gt"
of '~': special "tilde"
of ':': special "colon"
of '.': special "dot"
of '@': special "at"
of '|': special "bar"
else:
result.add("X" & toHex(ord(c), 2))
requiresUnderscore = true
if requiresUnderscore:
result.add "_"
proc mangleParamExt*(s: PSym): string =
result = "_p"
result.addInt s.position
proc mangleProcNameExt*(graph: ModuleGraph, s: PSym): string =
result = "__"
result.add graph.ifaces[s.itemId.module].uniqueName
result.add "_u"
result.addInt s.itemId.item # s.disamb #

View File

@@ -11,12 +11,11 @@
## 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, os, strutils, parseutils]
import std/[intsets, tables, hashes, strtabs]
import ../dist/checksums/src/checksums/md5
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
import ic / [packed_ast, ic]
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -55,6 +54,11 @@ type
concreteTypes*: seq[FullId]
inst*: PInstantiation
SymInfoPair* = object
sym*: PSym
info*: TLineInfo
isDecl*: bool
PipelinePass* = enum
NonePass
SemPass
@@ -75,9 +79,8 @@ type
typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
methodsPerGenericType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual,member and ctor so far).
initializersPerType*: Table[ItemId, PNode] # Type ID, AST call to the default ctor (c++ only)
methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual and ctor so far)
enumToStringProcs*: Table[ItemId, LazySym]
emittedTypeInfo*: Table[string, FileIndex]
@@ -87,8 +90,6 @@ type
importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
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
@@ -98,18 +99,12 @@ type
cache*: IdentCache
vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will
# be clarified in later compiler implementations.
repl*: RootRef # REPL state is shared project-wise.
doStopCompile*: proc(): bool {.closure.}
usageSym*: PSym # for nimsuggest
owners*: seq[PSym]
suggestSymbols*: SuggestSymbolDatabase
suggestSymbols*: Table[FileIndex, seq[SymInfoPair]]
suggestErrors*: Table[FileIndex, seq[Suggest]]
methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
bucketTable*: CountTable[ItemId]
objectTree*: Table[ItemId, seq[tuple[depth: int, value: PType]]]
methodsPerType*: Table[ItemId, seq[LazySym]]
dispatchers*: seq[LazySym]
systemModule*: PSym
sysTypes*: array[TTypeKind, PType]
compilerprocs*: TStrTable
@@ -136,8 +131,6 @@ type
cachedFiles*: StringTableRef
procGlobals*: seq[PNode]
TPassContext* = object of RootObj # the pass's context
idgen*: IdGenerator
PPassContext* = ref TPassContext
@@ -152,15 +145,13 @@ type
isFrontend: bool]
proc resetForBackend*(g: ModuleGraph) =
g.compilerprocs = initStrTable()
initStrTable(g.compilerprocs)
g.typeInstCache.clear()
g.procInstCache.clear()
for a in mitems(g.attachedOps):
a.clear()
g.methodsPerGenericType.clear()
g.enumToStringProcs.clear()
g.dispatchers.setLen(0)
g.methodsPerType.clear()
g.enumToStringProcs.clear()
const
cb64 = [
@@ -208,8 +199,8 @@ template semtabAll*(g: ModuleGraph, m: PSym): TStrTable =
g.ifaces[m.position].interfHidden
proc initStrTables*(g: ModuleGraph, m: PSym) =
semtab(g, m) = initStrTable()
semtabAll(g, m) = initStrTable()
initStrTable(semtab(g, m))
initStrTable(semtabAll(g, m))
proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) =
strTableAdd(semtab(g, m), s)
@@ -218,10 +209,10 @@ proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) =
proc isCachedModule(g: ModuleGraph; module: int): bool {.inline.} =
result = module < g.packed.len and g.packed[module].status == loaded
proc isCachedModule*(g: ModuleGraph; m: PSym): bool {.inline.} =
proc isCachedModule(g: ModuleGraph; m: PSym): bool {.inline.} =
isCachedModule(g, m.position)
proc simulateCachedModule(g: ModuleGraph; moduleSym: PSym; m: PackedModule) =
proc simulateCachedModule*(g: ModuleGraph; moduleSym: PSym; m: PackedModule) =
when false:
echo "simulating ", moduleSym.name.s, " ", moduleSym.position
simulateLoadedModule(g.packed, g.config, g.cache, moduleSym, m)
@@ -277,25 +268,6 @@ 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)
@@ -331,7 +303,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)
@@ -350,7 +322,6 @@ iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
for t in mitems(x[]):
yield resolveInst(g, t)
proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
## returns the requested attached operation for type `t`. Can return nil
## if no such operation exists.
@@ -374,27 +345,6 @@ proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttached
toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
#storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk)
iterator getDispatchers*(g: ModuleGraph): PSym =
for i in g.dispatchers.mitems:
yield resolveSym(g, i)
proc addDispatchers*(g: ModuleGraph, value: PSym) =
# TODO: add it for packed modules
g.dispatchers.add LazySym(sym: value)
iterator resolveLazySymSeq(g: ModuleGraph, list: var seq[LazySym]): PSym =
for it in list.mitems:
yield resolveSym(g, it)
proc setMethodsPerType*(g: ModuleGraph; id: ItemId, methods: seq[LazySym]) =
# TODO: add it for packed modules
g.methodsPerType[id] = methods
iterator getMethodsPerType*(g: ModuleGraph; t: PType): PSym =
if g.methodsPerType.contains(t.itemId):
for it in mitems g.methodsPerType[t.itemId]:
yield resolveSym(g, it)
proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
result = resolveSym(g, g.enumToStringProcs[t.itemId])
assert result != nil
@@ -403,22 +353,16 @@ proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
g.enumToStringProcs[t.itemId] = LazySym(sym: value)
iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) =
if g.methodsPerGenericType.contains(t.itemId):
for it in mitems g.methodsPerGenericType[t.itemId]:
if g.methodsPerType.contains(t.itemId):
for it in mitems g.methodsPerType[t.itemId]:
yield (it[0], resolveSym(g, it[1]))
proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) =
g.methodsPerGenericType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
proc hasDisabledOp(g: ModuleGraph; t: PType; kind: TTypeAttachedOp): bool =
let op = getAttachedOp(g, t, kind)
result = op != nil and sfError in op.flags
g.methodsPerType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool =
result = hasDisabledOp(g, t, attachedAsgn)
proc hasDisabledDup*(g: ModuleGraph; t: PType): bool =
result = hasDisabledOp(g, t, attachedDup)
let op = getAttachedOp(g, t, attachedAsgn)
result = op != nil and sfError in op.flags
proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
for k in low(TTypeAttachedOp)..high(TTypeAttachedOp):
@@ -427,11 +371,10 @@ proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
setAttachedOp(g, module, dest, k, op)
proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
result = nil
if g.config.symbolFiles == disabledSf: return nil
# slow, linear search, but the results are cached:
for module in 0..<len(g.packed):
for module in 0..high(g.packed):
#if isCachedModule(g, module):
let x = searchForCompilerproc(g.packed[module], name)
if x >= 0:
@@ -462,10 +405,10 @@ template getPContext(): untyped =
else: c.c
when defined(nimsuggest):
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
template onUse*(info: TLineInfo; s: PSym) = discard
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
else:
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
template onUse*(info: TLineInfo; s: PSym) = discard
template onDef*(info: TLineInfo; s: PSym) = discard
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
@@ -480,49 +423,6 @@ 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
@@ -531,10 +431,10 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
setLen(g.ifaces, m.position + 1)
if m.position >= g.packed.len:
setLen(g.packed.pm, m.position + 1)
setLen(g.packed, m.position + 1)
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
uniqueName: rope(uniqueModuleName(g.config, m)))
uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position))))
initStrTables(g, m)
proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
@@ -543,39 +443,37 @@ proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
proc initOperators*(g: ModuleGraph): Operators =
# These are safe for IC.
# Public because it's used by DrNim.
result = Operators(
opLe: createMagic(g, "<=", mLeI),
opLt: createMagic(g, "<", mLtI),
opAnd: createMagic(g, "and", mAnd),
opOr: createMagic(g, "or", mOr),
opIsNil: createMagic(g, "isnil", mIsNil),
opEq: createMagic(g, "==", mEqI),
opAdd: createMagic(g, "+", mAddI),
opSub: createMagic(g, "-", mSubI),
opMul: createMagic(g, "*", mMulI),
opDiv: createMagic(g, "div", mDivI),
opLen: createMagic(g, "len", mLengthSeq),
opNot: createMagic(g, "not", mNot),
opContains: createMagic(g, "contains", mInSet)
)
result.opLe = createMagic(g, "<=", mLeI)
result.opLt = createMagic(g, "<", mLtI)
result.opAnd = createMagic(g, "and", mAnd)
result.opOr = createMagic(g, "or", mOr)
result.opIsNil = createMagic(g, "isnil", mIsNil)
result.opEq = createMagic(g, "==", mEqI)
result.opAdd = createMagic(g, "+", mAddI)
result.opSub = createMagic(g, "-", mSubI)
result.opMul = createMagic(g, "*", mMulI)
result.opDiv = createMagic(g, "div", mDivI)
result.opLen = createMagic(g, "len", mLengthSeq)
result.opNot = createMagic(g, "not", mNot)
result.opContains = createMagic(g, "contains", mInSet)
proc initModuleGraphFields(result: ModuleGraph) =
# A module ID of -1 means that the symbol is not attached to a module at all,
# but to the module graph:
result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)
result.packageSyms = initStrTable()
initStrTable(result.packageSyms)
result.deps = initIntSet()
result.importDeps = initTable[FileIndex, seq[FileIndex]]()
result.ifaces = @[]
result.importStack = @[]
result.inclToMod = initTable[FileIndex, FileIndex]()
result.owners = @[]
result.suggestSymbols = initTable[FileIndex, SuggestFileSymbolDatabase]()
result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]()
result.suggestErrors = initTable[FileIndex, seq[Suggest]]()
result.methods = @[]
result.compilerprocs = initStrTable()
result.exposed = initStrTable()
result.packageTypes = initStrTable()
initStrTable(result.compilerprocs)
initStrTable(result.exposed)
initStrTable(result.packageTypes)
result.emptyNode = newNode(nkEmpty)
result.cacheSeqs = initTable[string, PNode]()
result.cacheCounters = initTable[string, BiggestInt]()
@@ -593,7 +491,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
initModuleGraphFields(result)
proc resetAllModules*(g: ModuleGraph) =
g.packageSyms = initStrTable()
initStrTable(g.packageSyms)
g.deps = initIntSet()
g.ifaces = @[]
g.importStack = @[]
@@ -601,12 +499,11 @@ proc resetAllModules*(g: ModuleGraph) =
g.usageSym = nil
g.owners = @[]
g.methods = @[]
g.compilerprocs = initStrTable()
g.exposed = initStrTable()
initStrTable(g.compilerprocs)
initStrTable(g.exposed)
initModuleGraphFields(g)
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
result = nil
if fileIdx.int32 >= 0:
if isCachedModule(g, fileIdx.int32):
result = g.packed[fileIdx.int32].module
@@ -712,7 +609,6 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
proc needsCompilation*(g: ModuleGraph): bool =
# every module that *depends* on this file is also dirty:
result = false
for i in 0i32..<g.ifaces.len.int32:
let m = g.ifaces[i].module
if m != nil:
@@ -720,7 +616,6 @@ proc needsCompilation*(g: ModuleGraph): bool =
return true
proc needsCompilation*(g: ModuleGraph, fileIdx: FileIndex): bool =
result = false
let module = g.getModule(fileIdx)
if module != nil and g.isDirty(module):
return true
@@ -742,12 +637,12 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
## Returns 'nil' if the module needs to be recompiled.
if g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
result = moduleFromRodFile(g.packed, g.config, g.cache, fileIdx, cachedModules)
else:
result = nil
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")
@@ -773,14 +668,16 @@ func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
## Check if symbol belongs to the 'stdlib' package.
sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId
proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): SuggestFileSymbolDatabase =
result = graph.suggestSymbols.getOrDefault(fileIdx, newSuggestFileSymbolDatabase(fileIdx, optIdeExceptionInlayHints in graph.config.globalOptions))
doAssert(result.fileIndex == fileIdx)
proc `==`*(a, b: SymInfoPair): bool =
result = a.sym == b.sym and a.info.exactEquals(b.info)
proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): seq[SymInfoPair] =
result = graph.suggestSymbols.getOrDefault(fileIdx, @[])
iterator suggestSymbolsIter*(g: ModuleGraph): SymInfoPair =
for xs in g.suggestSymbols.values:
for i in xs.lineInfo.low..xs.lineInfo.high:
yield xs.getSymInfoPair(i)
for x in xs:
yield x
iterator suggestErrorsIter*(g: ModuleGraph): Suggest =
for xs in g.suggestErrors.values:

View File

@@ -7,11 +7,9 @@
# distribution, for details about the copyright.
#
import ast, renderer, msgs, options, idents, lineinfos,
import ast, renderer, strutils, msgs, options, idents, os, lineinfos,
pathutils
import std/[strutils, os]
proc getModuleName*(conf: ConfigRef; n: PNode): string =
# This returns a short relative module name without the nim extension
# e.g. like "system", "importer" or "somepath/module"
@@ -79,10 +77,6 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
else:
result = fileInfoIdx(conf, fullPath)
type
SelectedBase = enum
FromProject, FromSearchPath, FromNimblePath
proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
## Mangle a relative module path to avoid path and symbol collisions.
##
@@ -91,27 +85,9 @@ proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
##
## Example:
## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar`
var best = relativeTo(path, conf.projectPath).string
var selectedBase = FromProject
for x in conf.searchPaths:
let other = relativeTo(path, x).string
if other.len < best.len:
best = other
selectedBase = FromSearchPath
for x in conf.nimblePaths:
let other = relativeTo(path, x).string
if other.len < best.len:
best = other
selectedBase = FromNimblePath
let prefix =
case selectedBase
of FromProject: "@m"
of FromSearchPath: "@p"
of FromNimblePath: "@n"
prefix & best.multiReplace(
"@m" & relativeTo(path, conf.projectPath).string.multiReplace(
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
proc demangleModuleName*(path: string): string =
## Demangle a relative module path.
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@p": "", "@n": "", "@c": ":"})
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})

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)
setOwner(result, packSym)
result.owner = packSym
result.position = int fileIdx
proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =

View File

@@ -61,12 +61,14 @@ proc makeCString*(s: string): Rope =
result.add('\"')
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
result = TFileInfo(fullPath: fullPath, projPath: projPath,
shortName: fullPath.extractFilename,
quotedFullName: fullPath.string.makeCString,
lines: @[]
)
result.fullPath = fullPath
#shallow(result.fullPath)
result.projPath = projPath
#shallow(result.projPath)
result.shortName = fullPath.extractFilename
result.quotedName = result.shortName.makeCString
result.quotedFullName = fullPath.string.makeCString
result.lines = @[]
when defined(nimpretty):
if not result.fullPath.isEmpty:
try:
@@ -134,7 +136,7 @@ proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex =
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy)
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
result = TLineInfo(fileIndex: fileInfoIdx)
result.fileIndex = fileInfoIdx
if line < int high(uint16):
result.line = uint16(line)
else:
@@ -292,11 +294,9 @@ proc toColumn*(info: TLineInfo): int {.inline.} =
result = info.col
proc toFileLineCol(info: InstantiationInfo): string {.inline.} =
result = ""
result.toLocation(info.filename, info.line, info.column + ColOffset)
proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
result = ""
result.toLocation(toMsgFilename(conf, info), info.line.int, info.col.int + ColOffset)
proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
@@ -408,7 +408,7 @@ proc getMessageStr(msg: TMsgKind, arg: string): string = msgKindToString(msg) %
type TErrorHandling* = enum doNothing, doAbort, doRaise
proc log*(s: string) =
var f: File = default(File)
var f: File
if open(f, getHomeDir() / "nimsuggest.log", fmAppend):
f.writeLine(s)
close(f)
@@ -507,8 +507,6 @@ proc getSurroundingSrc(conf: ConfigRef; info: TLineInfo): string =
result = "\n" & indent & $sourceLine(conf, info)
if info.col >= 0:
result.add "\n" & indent & spaces(info.col) & '^'
else:
result = ""
proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string =
let title = case msg
@@ -629,7 +627,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 in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return
if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
writeContext(conf, info)
liMessage(conf, info, errInternal, errMsg, doAbort, info2)
@@ -648,9 +646,7 @@ template internalAssert*(conf: ConfigRef, e: bool) =
template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") =
let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg]
let msg = if optStyleError in conf.globalOptions: errGenerated
elif optStyleWarning in conf.globalOptions: warnUser
else: hintName
let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
liMessage(conf, info, msg, m, doNothing, instLoc())
proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope =
@@ -671,6 +667,31 @@ 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"
@@ -709,8 +730,6 @@ proc genSuccessX*(conf: ConfigRef) =
elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends:
# for some cmd we expect a valid absOutFile
output = "unknownOutput"
elif optStdout in conf.globalOptions:
output = "stdout"
else:
output = $conf.absOutFile
if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename

52
compiler/ndi.nim Normal file
View File

@@ -0,0 +1,52 @@
#
#
# The Nim Compiler
# (c) Copyright 2017 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the generation of ``.ndi`` files for better debugging
## support of Nim code. "ndi" stands for "Nim debug info".
import ast, msgs, ropes, options, pathutils
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
type
NdiFile* = object
enabled: bool
f: File
buf: string
filename: AbsoluteFile
syms: seq[PSym]
proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
f.buf.setLen 0
f.buf.addInt s.info.line.int
f.buf.add "\t"
f.buf.addInt s.info.col.int
f.f.write(s.name.s, "\t")
f.f.writeRope(s.loc.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

@@ -7,8 +7,8 @@
# distribution, for details about the copyright.
#
import ast, renderer, msgs, options, lineinfos, idents, treetab
import std/[intsets, tables, sequtils, strutils, sets, strformat, hashes]
import ast, renderer, intsets, tables, msgs, options, lineinfos, strformat, idents, treetab, hashes
import sequtils, strutils, sets
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -183,6 +183,12 @@ func `<`*(a: ExprIndex, b: ExprIndex): bool =
func `<=`*(a: ExprIndex, b: ExprIndex): bool =
a.int16 <= b.int16
func `>`*(a: ExprIndex, b: ExprIndex): bool =
a.int16 > b.int16
func `>=`*(a: ExprIndex, b: ExprIndex): bool =
a.int16 >= b.int16
func `==`*(a: ExprIndex, b: ExprIndex): bool =
a.int16 == b.int16
@@ -303,7 +309,6 @@ proc symbol(n: PNode): Symbol =
# echo "symbol ", n, " ", n.kind, " ", result.int
func `$`(map: NilMap): string =
result = ""
var now = map
var stack: seq[NilMap] = @[]
while not now.isNil:
@@ -411,7 +416,7 @@ proc moveOut(ctx: NilCheckerContext, map: NilMap, target: PNode) =
if targetSetIndex != noSetIndex:
var targetSet = map.sets[targetSetIndex]
if targetSet.len > 1:
var other: ExprIndex = default(ExprIndex)
var other: ExprIndex
for element in targetSet:
if element.ExprIndex != targetIndex:
@@ -492,7 +497,7 @@ proc checkCall(n, ctx, map): Check =
# check args and handle possible mutations
var isNew = false
result = Check(map: map)
result.map = map
for i, child in n:
discard check(child, ctx, map)
@@ -501,7 +506,7 @@ proc checkCall(n, ctx, map): Check =
# as it might have been mutated
# TODO similar for normal refs and fields: find dependent exprs: brackets
if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ.elementType.kind == tyRef:
if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ[0].kind == tyRef:
if not isNew:
result.map = newNilMap(map)
isNew = true
@@ -556,7 +561,7 @@ proc derefWarning(n, ctx, map; kind: Nilability) =
if n.info in ctx.warningLocations:
return
ctx.warningLocations.incl(n.info)
var a: seq[History] = @[]
var a: seq[History]
if n.kind == nkSym:
a = history(map, ctx.index(n))
var res = ""
@@ -747,7 +752,6 @@ proc checkReturn(n, ctx, map): Check =
proc checkIf(n, ctx, map): Check =
## check branches based on condition
result = default(Check)
var mapIf: NilMap = map
# first visit the condition
@@ -761,7 +765,7 @@ proc checkIf(n, ctx, map): Check =
# the state of the conditions: negating conditions before the current one
var layerHistory = newNilMap(mapIf)
# the state after branch effects
var afterLayer: NilMap = nil
var afterLayer: NilMap
# the result nilability for expressions
var nilability = Safe
@@ -820,7 +824,7 @@ proc checkFor(n, ctx, map): Check =
var check2 = check(n.sons[2], ctx, m)
var map2 = check2.map
result = Check(map: ctx.union(map0, m))
result.map = ctx.union(map0, m)
result.map = ctx.union(result.map, map2)
result.nilability = Safe
@@ -848,7 +852,7 @@ proc checkWhile(n, ctx, map): Check =
var check2 = check(n.sons[1], ctx, m)
var map2 = check2.map
result = Check(map: ctx.union(map0, map1))
result.map = ctx.union(map0, map1)
result.map = ctx.union(result.map, map2)
result.nilability = Safe
@@ -858,10 +862,9 @@ proc checkInfix(n, ctx, map): Check =
## a or b : map is an union of a and b's
## a == b : use checkCondition
## else: no change, just check args
result = default(Check)
if n[0].kind == nkSym:
var mapL: NilMap = nil
var mapR: NilMap = nil
var mapL: NilMap
var mapR: NilMap
if n[0].sym.magic notin {mAnd, mEqRef}:
mapL = checkCondition(n[1], ctx, map, false, false)
mapR = checkCondition(n[2], ctx, map, false, false)
@@ -894,7 +897,7 @@ proc checkInfix(n, ctx, map): Check =
proc checkIsNil(n, ctx, map; isElse: bool = false): Check =
## check isNil calls
## update the map depending on if it is not isNil or isNil
result = Check(map: newNilMap(map))
result.map = newNilMap(map)
let value = n[1]
result.map.store(ctx, ctx.index(n[1]), if not isElse: Nil else: Safe, TArg, n.info, n)
@@ -913,7 +916,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, nextTypeId ctx.idgen, nil)
proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
var cache = newIdentCache()
@@ -923,7 +926,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, nextTypeId ctx.idgen, nil)
proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
infix(ctx, l, r, mEqRef)
@@ -942,9 +945,9 @@ proc checkCase(n, ctx, map): Check =
# c2
# also a == true is a , a == false is not a
let base = n[0]
result = Check(map: map.copyMap())
result.map = map.copyMap()
result.nilability = Safe
var a: PNode = nil
var a: PNode
for child in n:
case child.kind:
of nkOfBranch:
@@ -1214,12 +1217,12 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
result = check(n.sons[1], ctx, map)
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkElse:
result = Check(map: map)
result.map = map
if n.kind in {nkObjConstr, nkTupleConstr}:
# TODO deeper nested elements?
# A(field: B()) #
# field: Safe ->
var elements: seq[(PNode, Nilability)] = @[]
var elements: seq[(PNode, Nilability)]
for i, child in n:
result = check(child, ctx, result.map)
if i > 0:
@@ -1241,10 +1244,10 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
result = checkIf(n, ctx, map)
of nkAsgn, nkFastAsgn, nkSinkAsgn:
result = checkAsgn(n[0], n[1], ctx, map)
of nkVarSection, nkLetSection:
result = Check(map: map)
of nkVarSection:
result.map = map
for child in n:
result = checkAsgn(child[0].skipPragmaExpr, child[2], ctx, result.map)
result = checkAsgn(child[0], child[2], ctx, result.map)
of nkForStmt:
result = checkFor(n, ctx, map)
of nkCaseStmt:
@@ -1268,7 +1271,8 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
else:
var elementMap = map.copyMap()
var elementCheck = Check(map: elementMap)
var elementCheck: Check
elementCheck.map = elementMap
for element in n:
elementCheck = check(element, ctx, elementCheck.map)
@@ -1329,7 +1333,7 @@ proc preVisit(ctx: NilCheckerContext, s: PSym, body: PNode, conf: ConfigRef) =
ctx.symbolIndices = {resultId: resultExprIndex}.toTable()
var cache = newIdentCache()
ctx.expressions = SeqOfDistinct[ExprIndex, PNode](@[newIdentNode(cache.getIdent("result"), s.ast.info)])
var emptySet: IntSet = initIntSet() # set[ExprIndex]
var emptySet: IntSet # set[ExprIndex]
ctx.dependants = SeqOfDistinct[ExprIndex, IntSet](@[emptySet])
for i, arg in s.typ.n.sons:
if i > 0:
@@ -1361,7 +1365,7 @@ proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) =
continue
map.store(context, context.index(child), typeNilability(child.typ), TArg, child.info, child)
map.store(context, resultExprIndex, if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef: Nil else: Safe, TResult, s.ast.info)
map.store(context, resultExprIndex, if not s.typ[0].isNil and s.typ[0].kind == tyRef: Nil else: Safe, TResult, s.ast.info)
# echo "checking ", s.name.s, " ", filename
@@ -1377,5 +1381,5 @@ proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) =
# (ANotNil, BNotNil) :
# do we check on asgn nilability at all?
if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef and tfNotNil in s.typ.returnType.flags:
if not s.typ[0].isNil and s.typ[0].kind == tyRef and tfNotNil in s.typ[0].flags:
checkResult(s.ast, context, res.map)

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