Compare commits

...

558 Commits

Author SHA1 Message Date
narimiran
f7145dd26e Revert "leave type section symbols unchanged on resem, fix overly general double semcheck for forward types (#24888)"
This reverts commit cfe89097e7.
2025-04-21 23:07:52 +02:00
narimiran
1db543e8b2 bump NimVersion to 2.2.4 2025-04-21 19:10:12 +02:00
metagn
238a4db3d9 update proc type recursion errors after merge (#24897)
refs #24893, refs #24888

(cherry picked from commit dc100c5caa)
2025-04-21 19:09:52 +02:00
metagn
a19d06e1f7 generally disallow recursive structural types, check proc param types (#24893)
fixes #5631, fixes #8938, fixes #18855, fixes #19271, fixes #23885,
fixes #24877

`isTupleRecursive`, previously only called to give an error for illegal
recursions for:

* tuple fields
* types declared in type sections
* explicitly instantiated generic types

did not check for recursions in proc types. It now does, meaning proc
types now need a nominal type layer to recurse over themselves. It is
renamed to `isRecursiveStructuralType` to better reflect what it does,
it is different from a recursive type that cannot exist due to a lack of
pointer indirection which is possible for nominal types.

It is now also called to check the param/return types of procs, similar
to how tuple field types are checked. Pointer indirection checks are not
needed since procs are pointers.

I wondered if this would lead to a slowdown in the compiler but since it
only skips structural types it shouldn't take too many iterations, not
to mention only proc types are newly considered and aren't that common.
But maybe something in the implementation could be inefficient, like the
cycle detector using an IntSet.

Note: The name `isRecursiveStructuralType` is not exactly correct
because it still checks for `distinct` types. If it didn't, then the
compiler would accept this:

```nim
type
  A = distinct B
  B = ref A
```

But this breaks when attempting to write `var x: A`. However this is not
the case for:

```nim
type
  A = object
    x: B
  B = ref A
```

So a better description would be "types that are structural on the
backend".

A future step to deal with #14015 and #23224 might be to check the
arguments of `tyGenericInst` as well but I don't know if this makes
perfect sense.

(cherry picked from commit 7f0e07492f)
2025-04-21 19:09:45 +02:00
metagn
beb54a5a75 consider proc return type as weak reference in codegen (#24894)
fixes #7706

(cherry picked from commit 9c2593444a)
2025-04-21 17:34:17 +02:00
metagn
cfe89097e7 leave type section symbols unchanged on resem, fix overly general double semcheck for forward types (#24888)
fixes #24887 (really just this [1 line
commit](632c7b3397)
would have been enough to fix the issue but it would ignore the general
problem)

When a type definition is encountered where the symbol already has a
type (not a forward type), the type is left alone (not reset to
`tyForward`) and the RHS is handled differently: The RHS is still
semchecked, but the type of the symbol is not updated, and nominal type
nodes are ignored entirely (specifically if they are the same kind as
the symbol's existing type but this restriction is not really needed).
If the existing type of the symbol is an enum and and the RHS has a
nominal enum type node, the enum fields of the existing type are added
to scope rather than creating a new type from the RHS and adding its
symbols instead.

The goal is to prevent any incompatible nominal types from being
generated during resem as in #24887. But it also restricts what macros
can do if they generate type section AST, for example if we have:

```nim
type Foo = int
```

and a macro modifies the type section while keeping the symbol node for
`Foo` like:

```nim
type Foo = float
```

Then the type of `Foo` will still remain `int`, while it previously
became `float`. While we could maybe allow this and make it so only
nominal types cannot be changed, it gets even more complex when
considering generic params and whether or not they get updated. So to
keep it as simple as possible the rule is that the symbol type does not
change, but maybe this behavior was useful for macros.

Only nominal type nodes are ignored for semchecking on the RHS, so that
cases like this do not cause a regression:

```nim
template foo(): untyped =
  proc bar() {.inject.} = discard
  int

type Foo = foo()
bar() # normally works
```

However this specific code exposed a problem with forward type handling:

---

In specific cases, when the type section is undergoing the final pass,
if the type fits some overly general criteria (it is not an object,
enum, alias or a sink type and its node is not a nominal type node), the
entire RHS is semchecked for a 2nd time as a standalone type (with `nil`
prev) and *maybe* reassigned to the new semchecked type, depending on
its type kind. (for some reason including nominal types when we excluded
them before?) This causes a redefinition error if the RHS defines a
symbol.

This code goes all the way back to the first commit and I could not find
the reason why it was there, but removing it showed a failure in
`thard_tyforward`: If a generic forward type is invoked, it is left as
an unresolved `tyGenericInvocation` on the first run. Semchecking it
again at the end turns it into a `tyGenericInst`. So my understanding is
that it exists to handle these loose forward types, but it is way too
general and there is a similar mechanism `c.skipTypes` which is supposed
to do the same thing but doesn't.

So this is no longer done, and `c.skipTypes` is revamped (and renamed):
It is now a list of types and the nodes that are supposed to evaluate to
them, such that types needing to be updated later due to containing
forward types are added to it along with their nodes. When finishing the
type section, these types are reassigned to the semchecked value of
their nodes so that the forward types in them are fully resolved. The
"reassigning" here works due to updating the data inside the type
pointer directly, and is how forward types work by themselves normally
(`tyForward` types are modified in place as `s.typ`).

For example, as mentioned before, generic invocations of forward types
are first created as `tyGenericInvocation` and need to become
`tyGenericInst` later. So they are now added to this list along with
their node. Object types with forward types as their base types also
need to be updated later to check that the base type is correct/inherit
fields from it: For this the entire object type and its node are added
to the list. Similarly, any case where whether a component type is
`tyGenericInst` or `tyGenericInvocation` matters also needs to cascade
this (`set` does presumably to check the instantiated type).

This is not complete: Generic invocations with forward types only check
that their base type is a forward type, but not any of their arguments,
which causes #16754 and #24133. The generated invocations also need to
cascade properly: `Foo[Bar[ForwardType]]` for example would see that
`Bar[ForwardType]` is a generic invocation and stay as a generic
invocation itself, but it might not queue itself to be updated later.
Even if it did, only the entire type `Foo[Bar[ForwardType]]` needs to be
queued, updating `Bar[ForwardType]` by itself would be redundant or it
would not change anything at all. But these can be done later.

(cherry picked from commit 525d64fe88)
2025-04-21 17:28:11 +02:00
lit
b5ee86b43f fix(docgen): export for imported symbols missing; closes #24890 (#24891)
(cherry picked from commit 8bc8d40778)
2025-04-21 17:27:59 +02:00
metagn
1227799b84 implement parser for new case objects (#24885)
refs https://github.com/nim-lang/RFCs/issues/559

Parses as an `nkIdentDefs` with an `nkEmpty` name. Pragma is allowed,
can remove this if necessary.

Fine to close and postpone for later

(cherry picked from commit 032da90ed1)
2025-04-18 12:50:19 +02:00
metagn
545058a4ea account for invalid data in enum $ on arc/orc (#24886)
closes #24875

Refc gives `0 (invalid data!)`, but since enum `$` procs on arc are
generated during enum declarations we might not have access to string
concatenation and integer `$`, so it generates a static string. Just
chose an empty string for this.

(cherry picked from commit 5aaba213d4)
2025-04-18 12:50:05 +02:00
ringabout
403b24faeb 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-04-17 19:09:22 +02:00
metagn
aa8715afde fix stmtlist expression indent regression (#24883)
follows up #24855

Before #24855, the test would work because the indentation of the `;`
token would be passed to `semiStmtList` and so its indentation of `-1`
would be used. Now the `;` token is skipped and the indentation of the
first `discard` is used which is > -1. However the second discard has an
indentation of -1 because it's on the same line: this fails the
`sameInd(p) or realInd(p)` check since -1 is never >= the indent of the
first discard.

For compatibility with the parser up to this point this indent check is
entirely removed, meaning the indent is ignored. Because the `;` is
basically never on a separate line, this was already the case for
basically every use. `semiStmtList` is wrapped in a `withInd` anyway
which resets the indent after it's done, since the entire statement list
is wrapped in a `()`. To disallow dedents, the above check could be
fixed to use `sameOrNoInd` instead of `sameInd`, which is done in the
commented version of this check.

(cherry picked from commit 3d14381473)
2025-04-17 17:33:04 +02:00
ringabout
397eb361e9 fixes #24879; Data getting wiped on copy with iterators and =copy on refc (#24880)
fixes #24879

(cherry picked from commit 9f359e8d6d)
2025-04-17 17:32:57 +02:00
ringabout
ea4df85f34 fixes nimsugget with Checksums deps (#24882)
ref https://github.com/nim-lang/Nim/issues/24881

(cherry picked from commit 3f9c269013)
2025-04-17 17:32:48 +02:00
Miran
e3a2af00ea update the tooling versions (#24878)
(cherry picked from commit 11e4bd668c)
2025-04-17 17:32:16 +02:00
Juan M Gómez
349ee54838 Fixes a nimsuggest crash (#24873)
(cherry picked from commit e7f73bfebe)
2025-04-17 17:32:01 +02:00
metagn
a8d87c041c don't traverse inner procs to lift locals in closure iters (#24876)
fixes #24863, refs #23787 and #24316

Working off the minimized example, my understanding of the issue is: `n`
captures `r` as `:envP.r1` where `:envP` is the environment of `b`, then
`proc () = n()` does the lambda lifting of `n` again (which isn't done
if the `proc ()` is marked `{.closure.}`, hence the workaround) which
then captures the `:envP` as another field inside the `:envP`, so it
generates `:envP.:envP_2.r1` but the `.:envP_2` field is `nil`, so it
causes a segfault.

The problem is that the capture of `r` in `n` is done inside
`detectCapturedVars` for the surrounding closure iterator: inner procs
are not special cased and traversed as regular nodes, so it thinks it's
inside the iterator and generates a field access of `:envP` freely. The
lambda lifting version of `detectCapturedVars` ignores inner procs and
works off of symbol uses (anonymous iterator and lambda declarations
pretend their symbol is used).

As a naive solution, closure iterators now also ignore inner proc
declarations same as `lambdalifting.detectCapturedVars`, but unlike it
they also don't do anything for the inner proc symbols. Lambdalifting
seems to properly handle the lifted variables but in the worst case we
can also make sure `closureiters.detectCapturedVars` traverses inner
procs by marking every local of the closure iter used in them as needing
lifting (but not doing the lifting). This does not seem necessary for
now so it's not done (was done and reverted in [this
commit](9bb39a9259)),
but regressions are still possible

(cherry picked from commit c06bb6cc03)
2025-04-16 09:09:08 +02:00
narimiran
e7244c0d28 remove wrong import 2025-04-14 11:27:15 +02:00
metagn
0c8cefcbef fix field setter fallback that never worked (#24871)
refs https://forum.nim-lang.org/t/12785, refs #4711

The code was already there that when `propertyWriteAccess` returns `nil`
(i.e. cannot find a setter), `semAsgn` turns the [LHS into a call and
semchecks
it](1ef9a656d2/compiler/semexprs.nim (L1941-L1948)),
meaning if a setter cannot be found a getter will be assigned to
instead. However `propertyWriteAccess` never returned nil, because
`semOverloadedCallAnalyseEffects` was not called with `efNoUndeclared`
and so produced an error directly. So `efNoUndeclared` is passed to this
call so this code works as intended.

This fixes the issue described in #4711 which was closed because
subscripts do not have the same behavior implemented. However we can
implement this for subscripts as well (I have an implementation ready),
it just changes the error message from the failed overloads of `[]=` to
the failed overloads of `[]` for the LHS, which might be misleading but
is consistent with the error messages for any other assignment. I can do
this in this PR or another one.

(cherry picked from commit 4d9e5e8b6d)
2025-04-14 10:53:07 +02:00
metagn
94497c790b allow setting arbitrary size for importc types (#24868)
split from #24204, closes #7674

The `{.size.}` pragma no longer restricts the given size to 1, 2, 4 or 8
if it is used for an imported type. This is not tested very thoroughly
but there's no obvious reason to disallow it.

(cherry picked from commit 1ef9a656d2)
2025-04-14 10:53:01 +02:00
metagn
74f4042f89 isolate and rematch generic converters to get bindings (#24867)
fixes #4554, fixes #10900, fixes #13843, fixes #19471, fixes #19517

Instead of matching generic converters to their arguments using the full
call match bindings, a new match is created for them (from which the
bindings are used to instantiate the converter return type). Then when
instantiating generic converters, they are matched to their argument
again to get their bindings again instead of using the call bindings.
This prevents generic converters which match more than once from
interfering with each other's bindings.

(cherry picked from commit 334f96c05a)
2025-04-14 10:52:56 +02:00
Jake Leahy
ec1d68fc64 Allow specifiying path to use for stdin error messages (#24595)
Implements #24569

Adds `--stdinfile` flag for specifying the file to use in place of
`stdinfile.nim` in error messages. Will enable easier integration of
tooling with nim check

(cherry picked from commit 0cba752c8a)
2025-04-14 10:52:50 +02:00
metagn
20ff258a08 clean up opensym encounters in compiler (#24866)
To protect against crashes when this stops being experimental, in most
places handled the exact same as normal symchoices (not encountered in
typed ast)

(cherry picked from commit 4d075dc301)
2025-04-14 10:52:41 +02:00
metagn
c7dc4ae86d add bit type overloads of $ and repr (#24865)
fixes #24864

(cherry picked from commit 97d819a251)
2025-04-14 10:52:35 +02:00
握猫猫
2d872329ae Update winlean.nim, import AddrInfo from ws2tcpip.h (#24828)
[ADDRINFOA](https://learn.microsoft.com/en-us/windows/win32/api/ws2def/ns-ws2def-addrinfoa#remarks).

(cherry picked from commit b961ee69aa)
2025-04-14 10:52:23 +02:00
ringabout
96f5b693ba fixes #24764; cross-module sink analysis broken (#24862)
fixes  #24764

It now consumes the `conv(x)` arg for the explicit sinking. So the
explicit sinking is kept as it is.

Follows up https://github.com/nim-lang/Nim/pull/20585

Related issues: https://github.com/nim-lang/Nim/issues/20572

Probably the same needs to be applied to explicit `copy` to prevent a
copy turning into a sink

(cherry picked from commit 42df731a2d)
2025-04-14 10:52:13 +02:00
Ryan McConnell
3c8be5b63f split nativesockets bindAddr into two procs (#24860)
#24858

(cherry picked from commit 520bbaf384)
2025-04-14 10:51:58 +02:00
metagn
380697d3ff ignore typeof in closure iterators (#24861)
fixes #24859

(cherry picked from commit f58cd51fc4)
2025-04-14 10:51:51 +02:00
metagn
0cd5307633 fix array/set/tuple literals with generic expression elements (#24497)
fixes #24484, fixes #24672

When an array, set or tuple constructor has an element that resolves to
`tyFromExpr`, the type of the entire literal is now set to `tyFromExpr`
and the subsequent elements are not matched to any type.

The remaining expressions are still typed (a version of the PR before
this called `semGenericStmt` on them instead), however elements with int
literal types have their types set to `nil`, since generic instantiation
removes int literal types and the int literal type is required for
implicitly converting the int literal element to the set type. Tuples
should not really need this but it is done for them anyway in case it
messes up some type inference

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 897126a711)
2025-04-14 10:51:41 +02:00
Ryan McConnell
ee44fe197b new-style concept bugfix (#24858)
Combining two small PRs in one here. The test case explains what was
wrong with the concepts and for naitivesockets, it's typical to adjust
`ai_flags` so I opened that up.

(cherry picked from commit d4098e6ca0)
2025-04-14 10:51:31 +02:00
metagn
72190536cb skip semicolon in stmtlist expr parsing (#24855)
Previously it would try to parse the semicolon as its own statement and
produce an `nkEmpty` node

Also more than 1 semicolon in an expression list i.e. `(a;; b)` gives an
"expression expected" error in `semiStmtList` when multiple semicolons
are allowed in normal statements, this could be fixed by changing the
`if tok.kind == tokSemicolon` check to a `while` but it does not match
the grammar so not done here.

(cherry picked from commit 918f972369)
2025-04-14 10:51:23 +02:00
ringabout
7842428261 fixes =copy is transformed into nkFastAsgn and unify mAsgn handling (#24857)
`=copy` should be treated like `=` instead of `shallowCopy`, i.e.,
`nkFastAsgn` by default. `mAsgn` is treated similar in sempass2 too

(cherry picked from commit 51166ab382)
2025-04-14 10:51:15 +02:00
ringabout
0dd198278e overhaul hook injections (#24841)
ref https://github.com/nim-lang/Nim/issues/24764

To keep destructors injected consistently, we need to transform `mAsgn`
properly into `nkSinkAsgn` and `nkAsgn`. This PR is the first step
towards overhauling hook injections.

In this PR, hooks (except mAsgn) are treated consistently whether it is
resolved in matching or instantiated by sempass2. It also fixes a
spelling `=wasMoved` to its normalized version, which caused no
replacing generic hook calls with lifted hook calls.

(cherry picked from commit 40a1ec21d7)
2025-04-14 10:51:08 +02:00
ringabout
c452d706ae 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:54:44 +02:00
metagn
741411e0dc 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:54:34 +02:00
ringabout
706011a21c bump to windows 2025 (#24853)
(cherry picked from commit 052ceca3c1)
2025-04-09 07:54:01 +02:00
Miran
018e63b46c test stint more thoroughly (#24832)
(cherry picked from commit 10c9ebad93)
2025-04-04 10:09:00 +02:00
ringabout
093f5a1de5 Makes except: panics on Defect (#24821)
implements https://github.com/nim-lang/RFCs/issues/557

It inserts defect handing into a bare except branch

```nim
try:
  raiseAssert "test"
except:
  echo "nope"
```

=>

```nim
try:
  raiseAssert "test"
except:
  # New behaviov, now well-defined: **never** catches the assert, regardless of panic mode
  raiseDefect()
  echo "nope"
```

In this way, `except` still catches foreign exceptions, but panics on
`Defect`. Probably when Nim has `except {.foreign.}`, we can extend
`raiseDefect` to foreign exceptions as well. That's supposed to be a
small use case anyway.

 `--legacy:noPanicOnExcept` is provided for a transition period.

(cherry picked from commit 26b86c8f4d)
2025-04-04 10:08:49 +02:00
la.panon.
975e8576ec Make loadConfig available from NimScript (#24840)
fixes #24837

I really wanted to name the variable just `stream` and leave `defer:
...` and `result =...` out, but the compiler says the variable is
redefined, so this is the form.

(cherry picked from commit 2ed45eb848)
2025-04-04 10:08:41 +02:00
ringabout
2de409cd0c fixes #24806; don't elide wasMoved when syms are used in blocks (#24831)
fixes #24806

Blocks don't merge symbols that are used before destruction to the
parent scope, which causes `wasMoved; destroy` to elide incorrectly

(cherry picked from commit 73aeac81d1)
2025-04-04 10:08:30 +02:00
metagn
23ca21a9c4 fix infinite recursion with pushed user pragmas (#24839)
fixes #24838

(cherry picked from commit 5bcd9a329a)
2025-04-04 10:08:19 +02:00
ringabout
31effe8c75 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:08:06 +02:00
ringabout
a2a6565e23 fixes lastRead uses the when nimvm branch (#24834)
```nim
proc foo =
  var x = "1234"
  var y = x
  when nimvm:
    discard
  else:
    var s = x
    doAssert s == "1234"
  doAssert y == "1234"

static: foo()
foo()
```
`dfa` chooses the `nimvm` branch, `x` is misread as a last read and
`wasMoved`.

`injectDestructor` is used for codegen and is not used for vmgen. It's
reasonable to choose the codegen path instead of the `nimvm` path so the
code works for codegen. Though the problem is often hidden by
`cursorinference` or `optimizer`.

found in https://github.com/nim-lang/Nim/pull/24831

(cherry picked from commit 3617d2e077)
2025-04-02 09:43:12 +02:00
ringabout
01389b5eb9 conv needs to be picky about aliases and introduces a temp for addr conv (#24818)
ref https://github.com/nim-lang/Nim/pull/24817
ref https://github.com/nim-lang/Nim/pull/24815
ref https://github.com/status-im/nim-eth/pull/784

```nim
{.emit:"""
void foo(unsigned long long* x)
{
}
""".}

proc foo(x: var culonglong) {.importc: "foo", nodecl.}

proc main(x: var uint64) =
  # var s: culonglong = u # TODO:
  var m = uint64(12)
  # var s = culonglong(m)
  foo(culonglong m)

var u = uint64(12)
main(u)
```
Notes that this code gives incompatible errors in 2.0.0, 2.2.0 and the
devel branch. With this PR, `conv` is kept, but it seems to go back to
https://github.com/nim-lang/Nim/pull/24807

(cherry picked from commit f9c8775783)
2025-04-02 09:42:57 +02:00
James
210f747596 Add withValue for immutable tables (#24825)
This change adds `withValue` templates for the `Table` type that are
able to operate on immutable table values -- the existing implementation
requires a `var`.

This is needed for situations where performance is sensitive. There are
two goals with my implementation:

1. Don't create a copy of the value in the table. That's why I need the
`cursor` pragma. Otherwise, it would copy the value
2. Don't double calculate the hash. That's kind of intrinsic with this
implementation. But the only way to achieve this without this PR is to
first check `if key in table` then to read `table[key]`

I brought this up in the discord and a few folks tried to come up with
options that were as fast as this, but nothing quite matched the
performance here. Thread starts here:
https://discord.com/channels/371759389889003530/371759389889003532/1355206546966974584

(cherry picked from commit 0f5732bc8c)
2025-03-31 14:00:48 +02:00
Jake Leahy
65a0ec3964 Fix nim-gdb.py script (#24824)
Script wasn't working on my machine with GDB 16.2
Main issues
 - `gdb.types` wasn't imported, leading to import error on initial load
 - dollar function didn't work with the new mangling scheme

Fixes them, also updates the test script to work with some new mangling
changes.

Test evidence

![image](https://github.com/user-attachments/assets/450b020f-1665-4ed2-9073-d02537150914)

(cherry picked from commit e0a4876981)
2025-03-31 14:00:39 +02:00
Zoom
1d0e1679a8 Mark system.newStringUninit sideeffect-free (#24813)
- Allows using with `--experimental:strictFuncs`
- `{.cast(noSideEffect).}:` inside the proc was required to mutate
`s.len`, same as used in `newSeqImpl`.
- Removed now unnecessary `noSideEffect` casts in `system.nim`
-
Closes #24811

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
(cherry picked from commit ecdcffed4b)
2025-03-31 14:00:32 +02:00
ringabout
2c7577745b fixes implicitConv discarding flags (#24817)
follow up https://github.com/nim-lang/Nim/pull/24809
ref https://github.com/nim-lang/Nim/pull/24815

(cherry picked from commit 58b1f28177)
2025-03-31 14:00:10 +02:00
narimiran
6864337dc2 Revert "fixes #24800; Invalid C code generation with a method, case object in refc (#24809)"
This reverts commit 3a8b7d987b.
2025-03-26 17:06:41 +01:00
Zoom
ce67056f80 stdlib: substr uses copymem if available, improve docs (#24792)
- `system.substr` now uses `copymem` when available, introducing a small
template for nimvm detection (#12517 #12518)
- Docs are updated to clarify behaviour on out-of-bounds input
- Runnable examples cover more edge cases and do not repeat between
overloads
- Docs now explain the difference between overloads

What bothers me is that the `substr*(a: openArray[char]): string =`
which was added by @beef331 is practically an implementation of #14810,
which is just a conversion from `openArray` to `string` but somehow it
ended up being a `substr` overload, even though its behaviour is totally
different, _the "substringing" is performed by a previous step_
(conversion to openArray) and the bounds are not checked. I'm not sure
it's that great for overloads to differ in subtle ways so much.

What are the cases that `substr` covers now, that prohibit renaming it
to `toString` (or something like that)?

(cherry picked from commit b82d7e8ba1)
2025-03-26 07:48:22 +01:00
ringabout
3a8b7d987b fixes #24800; Invalid C code generation with a method, case object in refc (#24809)
fixes #24800

This PR avoids a conversion from `sink T` to `T`

I will add a test case

(cherry picked from commit ddd83f8d8a)
2025-03-26 07:48:10 +01:00
握猫猫
f8ab76ba61 Update nativesockets.nim, namelen should be the len of name (#24810)
In other places where `getsockname` is called, the size of the 'name' is
used.

d573578b28/lib/pure/nativesockets.nim (L347-L351)

d573578b28/lib/pure/nativesockets.nim (L585-L595)

d573578b28/lib/pure/nativesockets.nim (L622-L624)

d573578b28/lib/pure/nativesockets.nim (L347-L350)

I have checked the [Windows
documentation](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockname#remarks),
and it describes it like this: "On call, the namelen parameter contains
the size of the name buffer, in bytes. On return, the namelen parameter
contains the actual size in bytes of the name parameter."

[https://www.man7.org/linux/man-pages/man2/getsockname.2.html](https://www.man7.org/linux/man-pages/man2/getsockname.2.html)
say:
The addrlen argument should be initialized to indicate the amount of
space (in bytes) pointed to by addr.

(cherry picked from commit 8e36fb0fec)
2025-03-26 07:47:56 +01:00
narimiran
5c9aea9c69 Revert "fixes #24721; Table add missing sink (#24724)"
This reverts commit 20362cc0d2.
2025-03-25 13:09:02 +01:00
lit
3a9920d8fd repl: support eof, define object with fields (#24784)
For `nim secret`:

- **fix(repl): eof(ctrl-D/Z) and ctrl-C were ignored**
- **feat(repl): continueLine  figures section, constr, bool ops**

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
(cherry picked from commit d573578b28)
2025-03-25 09:44:55 +01:00
Zoom
81eabe3b9e [feature] stdlib: strutils.multiReplace for character sets (#24805)
Multiple replacements based on character sets in a single pass. Useful
for string sanitation. Follows existing `multiReplace` semantics.

Note: initially copied the substring version logic with a `while` and a
named block break, but Godbolt showed it had produced slightly larger
assembly using higher registers than the final version.

- [x] Tests
- [x] changelog.md

(cherry picked from commit 909f3b8b79)
2025-03-25 09:44:49 +01:00
ringabout
e68a91c8df fixes usenimrtl with useMalloc (#24804)
Follow up https://github.com/nim-lang/Nim/pull/19512

ref https://github.com/nim-lang/Nim/issues/24794

Otherwise, `/Users/blue/Desktop/Nim/lib/system/mm/malloc.nim(4, 1)
Error: redefinition of 'allocImpl'; previous declaration here:
/Users/blue/Desktop/Nim/lib/system/memalloc.nim(51, 8)`

In `proc allocImpl*(size: Natural): pointer {.noconv, rtl, tags: [],
benign, raises: [].}`, `rtl` means it is an `importc` function instead
of a proc forward decl.

(cherry picked from commit d15705e05b)
2025-03-25 09:43:51 +01:00
ringabout
346b989b5d disable implicit sinkinference for stdlibs (#24803)
ref https://github.com/nim-lang/Nim/issues/24794

(cherry picked from commit 0b9ed84d32)
2025-03-25 09:43:44 +01:00
metagn
8dcb6ddc89 disable "dest register is set" for vm statements (#24797)
closes #24780

This proc `genStmt` is only called to run the VM in `vm.evalStmt`,
otherwise it's not used in vmgen. Now it acts the same as `proc
gen(PCtx, PNode)`, used by `discard` statements, which just calls
`freeTemp` on the dest if it was set rather than erroring.

(cherry picked from commit fcba14707a)
2025-03-25 09:42:08 +01:00
ringabout
20362cc0d2 fixes #24721; Table add missing sink (#24724)
fixes #24721

(cherry picked from commit 482662d198)
2025-03-25 09:41:55 +01:00
Esteban C Borsani
e799d2fca0 Fix SIGSEGV when closing SSL async socket while sending/receiving (#24795)
Async SSL socket SIGSEGV's sometimes when calling socket.close() while
send/recv. The issue was found here
https://github.com/nitely/nim-hyperx/pull/59.

Possibly related: #24024

This can occur when closing the socket while sending or receiving,
because `socket.sslHandle` is freed. The sigsegv can also occur on calls
that require `socket.bioIn` or `socket.bioOut` because those use
`socket.sslHandle` internally. This PR checks sslHandle is set before
doing any operation that requires it.

(cherry picked from commit 9ace1f97ac)
2025-03-25 09:41:50 +01:00
Angus Gibson
4d41384f09 Allow parsing year "00" with "yy" pattern (#24785)
The "yy" pattern is relative to the current century, so year "00" should
be valid.

(cherry picked from commit 1d32607575)
2025-03-25 09:41:41 +01:00
ringabout
6032a14f26 fixes #10625; setjmp on linux mangles ebp leading to early collection (#24787)
fixes #10625

(cherry picked from commit 7c5d005510)
2025-03-25 09:41:23 +01:00
narimiran
bfd25121f9 Revert "fixes move for getPotentialWrites (#24753)"
This reverts commit ed57499427.
2025-03-18 15:17:49 +01:00
narimiran
faa4042e26 Revert "implements internal sink copy (#24747)"
This reverts commit 6651c40ba0.
2025-03-18 08:49:45 +01:00
narimiran
2aa2ac354f Revert "remove special treatments of sinking const sequences (#24763)"
This reverts commit 1c7ffece0a.
2025-03-18 08:48:57 +01:00
Ryan McConnell
9c64374599 new-style concepts - small bugfix (#24778)
(cherry picked from commit 2b699bca53)
2025-03-17 20:29:47 +01:00
metagn
44c1b2a6df fix compound inheritance penalty (#24775)
fixes #24773

`c.inheritancePenalty` is supposed to be used for the entire match, but
in these places the inheritance penalty of a single argument overrides
the entire match penalty. The `+ ord(c.inheritancePenalty < 0)` is
copied from other places that use the same idiom, the intent is that the
existing penalty changes from -1 to 0 first to mark that it participates
in inheritance before adding the inheritance depth.

---------

Co-authored-by: Andreas Rumpf <araq4k@proton.me>
(cherry picked from commit fb93295344)
2025-03-13 12:28:15 +01:00
ringabout
903ce6db28 fixes generic types sink T cannot be inferred for passed arguments (#24761)
Otherwise, `sink T` is kept as it is. This PR treats sink types as its
base types for the arguments. So the concept would match both cases

Required by https://github.com/nim-lang/Nim/pull/24724

(cherry picked from commit 9ebfa7973a)
2025-03-13 12:28:10 +01:00
lit
56bde37add fixes #24772: system.NaN was negative when C (#24774)
fixes #24772

The old implementation was said to copied  from Windows SDK,

but you can find the newer SDK's definition is updated and the sign is
reversed compared to the old.

Also, `__builtin_nanf("")` is used if available,
which is more efficient than previous (In x86_64 gcc, latter produces
32B code but former just 8B).

(cherry picked from commit 4f32624641)
2025-03-13 12:28:01 +01:00
ringabout
9f4fe7fd7a 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:27:51 +01:00
metagn
ee1ecbd51e give hint for forward declarations with unknown raises effects (#24767)
refs #24766

Detect when we track a call to a forward declaration without explicit
`raises` effects, then when the `raises` check fails for the proc, give
a hint that this forward declaration was tracked as potentially raising
any exception.

(cherry picked from commit 82891e6850)
2025-03-13 12:27:44 +01:00
Ryan McConnell
05beb32d07 folding const expressions with branching logic (#24689)
motivating example:
```nim
iterator p(a: openArray[char]): int =
  if a.len != 0:
    if a[0] != '/':
      discard
for t in p(""): discard
```
The compiler wants to evaluate `a[0]` at compile time even though it is
protected by the if statement above it. Similarly expressions like
`a.len != 0 and a[0] == '/'` have problems. It seems like the logic in
semfold needs to be more aware of branches to positively identify when
it is okay to fail compilation in these scenarios. It's a bit tough
though because it may be the case that non-constant expressions in
branching logic can properly protect some constant expressions.

(cherry picked from commit 850f327713)
2025-03-13 12:27:37 +01:00
metagn
6af8b33485 fix tuple nodes from VM inserting hidden conv to keep old type (#24756)
fixes #24755, refs #24710

Instead of using the node from `indexTypesMatch` which inserts a hidden
conv node, just change the type of the node back to the old type
directly

(cherry picked from commit 38ad336c69)
2025-03-13 12:27:31 +01:00
ringabout
8f563f2cc9 fixes #24754; {.gcsafe.} block breaks move analysis (#24757)
fixes #24754

(cherry picked from commit a7711d452d)
2025-03-13 12:27:15 +01:00
metagn
ae8ae8fa95 fix canRaise for non-proc calls (#24752)
fixes #24751

`typeof` leaves the object constructor as a call node for some reason,
in this case it tries to access the first child of the type node but the
object has no fields so the type field is empty. Alternatively the
optimizer can stop looking into `typeof`

(cherry picked from commit e2e7790779)
2025-03-13 12:26:23 +01:00
Michael Lee
1d8fed5f6b Add linking options for tinycc backend (#24750)
### Issue

When using `tcc` as backend to compile a trivial program

```
nim c  --cc:tcc  --skipCfg a.nim
```

, errors reported:

```
tcc: error: undefined symbol 'fabs'
```

### Solution

`fabs` belongs to libm. With these two options added, one can compile
with an additional clib option:

```
nim c  --cc:tcc  --skipCfg --clib:m a.nim
```

(cherry picked from commit dfd2987118)
2025-03-13 12:26:13 +01:00
ringabout
ed57499427 fixes move for getPotentialWrites (#24753)
`move` would modify parameters as well

(cherry picked from commit e2d4791229)
2025-03-13 12:26:06 +01:00
ringabout
1c7ffece0a remove special treatments of sinking const sequences (#24763)
(cherry picked from commit ccb40024c6)
2025-03-13 12:26:00 +01:00
Laylie
cd9c47140e Fix scanTuple undeclared identifier 'scanf' (#24759)
Without this fix, trying to use `scanTuple` in a generic proc imported
from a different module fails to compile (`undeclared identifier:
'scanf'`):

```nim
# module.nim
import std/strscans

proc scan*[T](s: string): (bool, string) =
  s.scanTuple("$+")
```

```nim
# main.nim
import ./module

echo scan[int]("foo")
```

Workaround is to `export scanf` in `module.nim` or `import std/strscans`
in `main.nim`.

(cherry picked from commit f8294ce06e)
2025-03-13 12:25:54 +01:00
ringabout
6651c40ba0 implements internal sink copy (#24747)
TODO:

- [x] other value types (arrays, strings, seqs, objects)
- [x] replaces https://github.com/nim-lang/Nim/pull/24731
- [x] improve code shape
- [ ] revert https://github.com/nim-lang/Nim/issues/24175
- [x] if possible, revert https://github.com/nim-lang/Nim/pull/23685
- [ ] if possible, revert https://github.com/nim-lang/Nim/pull/22229 and
https://github.com/nim-lang/Nim/pull/23764
- [ ] if possible, remove `if n.containsConstSeq:`
- [ ] if possible, always pass value (arrays, strings, seqs, tuples, or
even objects without custom hooks (?)) sinks by ref because this PR
should ensure these value types are not modified without a copy
- [x] fixes `say a, (b = move a; a)` for potential writes
https://github.com/nim-lang/Nim/pull/24753

(cherry picked from commit b8302cdd97)
2025-03-13 12:25:46 +01:00
narimiran
9cf0d07b9e Revert "fixes #12340; enable refc with move analyzer (#23782)"
This reverts commit 8038ad4e58.
2025-03-10 10:55:14 +01:00
Ryan McConnell
82974d91ce new-style concepts adjusments (#24697)
Yet another one of these. Multiple changes piled up in this one. I've
only minimally cleaned it for now (debug code is still here etc). Just
want to start putting this up so I might get feedback. I know this is a
lot and you all are busy with bigger things. As per my last PR, this
might just contain changes that are not ready.

### concept instantiation uniqueness
It has already been said that concepts like `ArrayLike[int]` is not
unique for each matching type of that concept. Likewise the compiler
needs to instantiate a new proc for each unique *bound* type not each
unique invocation of `ArrayLike`

### generic parameter bindings
Couple of things here. The code in sigmatch has to give it's bindings to
the code in concepts, else the information is lost in that step. The
code that prepares the generic variables bound in concepts was also
changed slightly. Net effect is that it works better.
I did choose to use the `LayedIdTable` instead of the `seq`s in
`concepts.nim`. This was mostly to avoid confusing myself. It also
avoids some unnecessary movings around. I wouldn't doubt this is
slightly less performant, but not much in the grand scheme of things and
I would prefer to keep things as easy to understand as possible for as
long as possible because this stuff can get confusing.

### various fixes in the matching logic
Certain forms of modifiers like `var` and generic types like
`tyGenericInst` and `tyGenericInvocation` have logic adjustments based
on my testing and usage

### signature matching method adjustment
This is the weird one, like my last PR. I thought a lot about the
feedback from my last attempt and this is what I came up with. Perhaps
unfortunately I am preoccupied with a slight grey area. consider the
follwing:
```nim
type
  C1 = concept
    proc p[T](s: Self; x: T)
  C2[T] = concept
    proc p(s: Self; x: T)
```
It would be temping to say that these are the same, but I don't think
they are. `C2` makes each invocation distinct, and this has important
implications in the type system. eg `C2[int]` is not the same type as
`C2[string]` and this means that signatures are meant to accept a type
that only matches `p` for a single type per unique binding. For `C1` all
are the same and the binding `p` accepts multiple types. There are
multiple variations of this type classes, `tyAnything` and the like.

The make things more complicated, an implementation might match:
```nim
type
  A = object
  C3 = concept
    proc p(s: Self; x:  A)
```
if the implementation defines:
```nim
proc p(x: Impl; y: object)
```

while a concept that fits `C2` may be satisfied by something like:
```nim
proc p(x: Impl; y: int)
proc spring[T](x: C2[T])
```
it just depends. None of this is really a problem, it just seems to
provoke some more logic in `concepts.nim` that makes all of this (appear
to?) work. The logic checks for both kinds of matches with a couple of
caveats. The fist is that some unbind-able arrangements may be matched
during overload resolution. I don't think this is avoidable and I
actually think this is a good way to get a failed compilation. So, first
note imo is that failing during binding is preferred to forcing the
programming to write annoying stub procs and putting insane gymnastics
in the compiler. Second thing is: I think this logic is way to accepting
for some parts of overload resolutions. Particularly in `checkGeneric`
when disambiguation is happening. Things get hard to understand for me
here. ~~I made it so the implicit bindings to not count during
disambiguation~~. I still need to test this more, but the thought is
that it would help curb excessive ambiguity errors.

Again, I'm sorry for this being so many changes. It's probably
inconvenient.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit dfab30734b)
2025-03-10 09:51:05 +01:00
metagn
937801e3aa generate tyFromExpr for typeof static param with generic base type (#24745)
fixes #24743, refs #24718

We cannot do this in general for any expression with generic type
because the `typeof` logic is called for things like `type Foo` in:

```nim
type Foo[T] = object

proc init(_: type Foo) = discard
```

We also cannot use `containsUnresolvedType` to work around this specific
case because the base type of `static[auto]` is not unresolved, it is a
typeclass that isn't lifted to a parameter. The behavior of generating
`tyFromExpr` is also consistent with pre-2.0, so we do this in this
special case of `static`.

(cherry picked from commit 569d02e212)
2025-03-10 09:50:51 +01:00
narimiran
58f1e22db3 Revert "sink tuples by values (#24731)"
This reverts commit b9d3348dab.
2025-03-03 20:32:56 +01:00
metagn
6bc07c7e3f handle ranges in annotateType for set constructors (#24737)
fixes #24736

The VM can produce integer nodes with no types as set elements, which
are later reannotated in `semmacrosanity.annotateType`. However the case
of ranges was not handled properly. Not sure why this is a regression,
probably unrelated but will have to see the bisect result to make sure.

Note. Originally tried to fix this in `opcInclRange`, generated for and
only for range expressions in set constructors, this seems to add the
range node directly to the set node without checking if it has overlap
with the existing elements by calling `nimsets` so an expression like
`{cctNone, cctNone..cctHeader}` can produce `{0, 0..5}`. Doesn't seem to
cause problems but `opcIncl` for single elements does check for overlap.

Something else to note is that integer nodes produced by `nimsets` have
proper types, so another option instead of relying on semmacrosanity to
fix this would be to make `opcIncl` and `opcInclRange` call `nimsets` to
add to the set node, but this might lose performance.

(cherry picked from commit e39d152b89)
2025-03-03 14:11:32 +01:00
ringabout
b9d3348dab sink tuples by values (#24731)
A reduced case
```nim
type AnObject = tuple
  a: string
  b: int
  c: int

proc mutate(a: sink AnObject) =
  `=wasMoved`(a)
  echo 1

# echo "Value is: ", obj.value
proc bar =
  mutate(("1.2", 0, 0))

bar()
```

(cherry picked from commit 7e8a650729)
2025-03-03 14:11:23 +01:00
ringabout
66e2352bf9 fixes #24339; underscores used with fields and fieldPairs (#24341)
fixes #24339

(cherry picked from commit 7ecb35115b)
2025-03-03 14:10:49 +01:00
ringabout
f20e6ef901 fixes #24705; encode static parameters into function names for debugging (#24707)
fixes #24705

```nim
proc xxx(v: static int) =
  echo v
xxx(10)
xxx(20)
```

They are mangled as `_ZN14titaniummangle7xxx_s10E` and
`_ZN14titaniummangle7xxx_s20E` with `--debugger:native`. Static
parameters are prefixed with `_s` to distinguish simple cases like
`xxx(10, 15)` and `xxx(101, 5)` if `xxx` supports two `static[int]`
parameters

(cherry picked from commit c452275e29)
2025-03-03 14:07:29 +01:00
metagn
dac77cc97e don't try to infer array range to unresolved range (#24709)
fixes #24708

(cherry picked from commit a18dcca744)
2025-03-03 14:07:15 +01:00
metagn
4143bb32f7 convert tuple constructors from VM back to original types (#24710)
fixes #24698

The same aim as #24224 but for tuple constructors. The difference here
is that the type of a tuple constructor is always going to be valid
unlike array constructors which can have `seq` etc types, so we can just
generate a conversion again. If the conversion fails, it is ignored
similar to #24611, this is to protect against modified typed nodes in
macros.

Also #24611 was only adapted to `semTupleFieldsConstr` and not
`semTuplePositionsConstr`, this is now fixed.

(cherry picked from commit 49dfc3a0d4)
2025-03-03 14:07:07 +01:00
Michael Lee
34de654aa7 Improve bash completion support (#24692)
Following https://github.com/nim-lang/nimble/pull/1347, this patch adds
bash completion support for `nim`, `nimgrep`, `nimpretty`, `nimsuggest`.

(cherry picked from commit 16280d4e49)
2025-03-03 14:07:00 +01:00
ringabout
8038ad4e58 fixes #12340; enable refc with move analyzer (#23782)
fixes https://github.com/nim-lang/Nim/issues/12340

(cherry picked from commit a7a8e364ea)
2025-03-03 14:06:51 +01:00
Ryan McConnell
3a9c88239b Make koch friendlier to offline environments (#24713)
(cherry picked from commit d94e535145)
2025-03-03 14:06:42 +01:00
metagn
fc587256c3 always skip static types for result of typeof (#24718)
fixes #24715

In generic typechecking, unresolved static param symbols (i.e.
`skGenericParam`) have [the static type
itself](1f8da3835f/compiler/semexprs.nim (L1483-L1485))
as their type when used in an expression. This is not the case when the
static param is resolved (the type is wrapped in static when necessary),
but semchecking of types and generic typechecking expects the type of
the value to be wrapped in `static` (at least `array[N, int]` breaks).
So for now, to solve the issue, `typeof` just skips static types.

(cherry picked from commit 514a25c9a2)
2025-03-03 14:06:33 +01:00
ringabout
7f902217a1 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-03-03 14:06:19 +01:00
ringabout
881d1dfdb6 undeprecates var T destructors (#24716)
Both cases are now valid. Though, it could be problematic to mix two
cases together as built-in types have non var T destructors

(cherry picked from commit 93fb219f10)
2025-03-03 14:06:13 +01:00
metagn
d3780bb7bd keep param pragmas in typed proc AST (#24711)
fixes #24702

(cherry picked from commit 1f8da3835f)
2025-03-03 14:06:03 +01:00
ringabout
51edd9bd60 always mangle local variables (#24681)
ref #24677

(cherry picked from commit 1af88a2d20)
2025-03-03 14:05:46 +01:00
lit
c8a030a902 fix(dollar): $NaN -> "NaN", $Inf -> "Infinity" only when js (#24695)
ref nimpylib/pylib#44

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
(cherry picked from commit 91e8e605d0)
2025-03-03 14:03:49 +01:00
ringabout
1380084f57 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-03-03 14:03:27 +01:00
ringabout
5584885226 fixes #24664; always sets the \0 terminator in appendString (#24703)
fixes #24664

```nim
proc main() =
    for i in 0..1:
        var s = "12345"
        s.add s
        echo s

main()
```
In the given example, `add` contains two steps: `prepareAdd` and
`appendString`. In the first step, a new buffer is created in order to
store the final doubled string. But it doesn't copy the null terminator,
neither zeromem the left unused spaces. It causes a problem because
`appendString` will copy itself which doesn't end with `\0` properly so
contaminated memory is copied instead.

```
var s = 12345\0

prepareAdd:

var s = 12345xxxxx\0

appendString:

var s = 1234512345x
```

(cherry picked from commit 1f07fdd2dc)
2025-03-03 14:03:17 +01:00
metagn
26d3b4c3ab adapt generic matches to inheritance penalty of final objects (#24691)
Applies #24144 to the equivalent matches of generic types, and adds the
behavior to matches of generic invocations to generic invocations. Not
encountered in many cases so it's hard to come up with tests but an
example is the test code in #24688, the match to the generic body never
sets the inheritance penalty leaving it at -1, but the match to the
generic invocation sets it to 0 which matches worse, when it should set
it to -1 because the object does not participate in inheritance.

(cherry picked from commit ebeef1067f)
2025-03-03 14:03:09 +01:00
ringabout
130e7182c4 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-03-03 14:02:48 +01:00
Ryan McConnell
f5026570c2 Add terminal colors back to unittest under nimPreviewSlimSystem (#24694)
(cherry picked from commit b7d8896d00)
2025-03-03 14:02:20 +01:00
metagn
bcecce885f 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-03-03 14:02:03 +01:00
ringabout
b740e8cca8 fixes #24673; divmod errors for ranges (#24679)
fixes #24673

The problem is that there is no way to distinguish `cint`, `cint`, etc
ctypes with Nim types. So `when T is cint | clong | clonglong:` is true
for types derived from `int`, `int32` and `int64`. In this PR, it fixes
the branch to avoid erros for `Natural`

(cherry picked from commit b211ada273)
2025-03-03 14:01:55 +01:00
Mads Hougesen
1bae14aa25 feat(nimpretty): support formatting code from stdin (#24676)
This pr adds support for running `nimpretty` on stdin as described in
#24622.

I tested `:%!nimpretty -` and `:%!nimpretty --stdin` in neovim and both
seems to work without issues.

(cherry picked from commit 1a7bc6d878)
2025-03-03 14:01:49 +01:00
ringabout
ce8d3e02f5 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-03-03 14:01:41 +01:00
narimiran
a5e595d8ad bump NimVersion to 2.2.3 2025-03-03 13:59:39 +01:00
ringabout
6c34f62785 fixes #24666; Compilation error when formatting a complex number (#24667)
fixes #24666

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

echo "found: ", nfound
```

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

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

This properly fixes #19414

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

```nim
import std/strscans

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

it gave this warning before

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

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

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

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

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

The old implementation generates

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

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

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

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

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

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

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

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

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

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

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

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Can also be backported to 2.0 if necessary.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  for oneDindex, innerArray in myPairsInline(my2dArray):
    discard

innerTestTotalMem()
```

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

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

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

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

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

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

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

To fix this, we only skip the conversion if:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Diff follows #24276.

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

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

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

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

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

use(suiteName)
```

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

```nim
block:
  type
    Foo = ref int

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Possible directions:

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

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

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

---------

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

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

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

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

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

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

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

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

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

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

Notes:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Package PRs, both merged:

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

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

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

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

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

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

- confutils
- eth
- metrics
- nat_traversal
- toml_serialization

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

----

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

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

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

it was added in 1.6.0

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

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

---

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

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

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

---

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

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

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

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

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

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

Macos 12 gives a warning:

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

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

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

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

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

Thank you.

---------

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

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

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

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

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

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

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

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

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

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

(cherry picked from commit f7cb0322c2)
2025-01-14 07:28:10 +01:00
Miran
78983f1876 update the changelog (#24213) 2024-10-01 22:22:30 +02:00
ringabout
0e1df88f7e check fileExists for outputFile (#24211)
ref
a5f46a72ba (commitcomment-147402726)
2024-10-01 08:11:07 +02:00
Miran
a5f46a72ba bump NimVersion to 2.2.0 (#24210) 2024-09-30 20:59:38 +02:00
ringabout
4974baf7fa fixes #24008; triggers a recompilation on output executables changes when switching release/debug modes (#24193)
fixes #24008

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

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

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

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

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

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

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

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

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

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

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

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

A minimization that is probably the same issue is:

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

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

import macros

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

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

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

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

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

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

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

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

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

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

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

fixes #23757

---------

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

Notes for the future:

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

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

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

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

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

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

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

Avoid this issue by ignoring ENOENT errors returned from kqueue.

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

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

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

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

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

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

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

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

Other minor bugfixes:

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

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

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

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

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

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

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

---------

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

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

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

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

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

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

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

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

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

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

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

This has several problems:

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

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

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

Here is how this PR deals with these problems:

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

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

Additional changes:

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

Package PRs:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

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

Instead, int literals with range type now:

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

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

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

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

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

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

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

foo()
```

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

---------

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

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

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

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

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

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

After (this PR and stable)


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

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

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

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

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

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

---------

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

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

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

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

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

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

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

However,

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

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

---

This PR:

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

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

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

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

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

Before:


raiseNilAccess__OOZOOZOnimbleZpkgs2Zthreading450O2O045288108d1dfa34d5ade5ce4d922af51909c83cebfZthreadingZsmartptrs_u4

After:

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

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

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

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


For `threading-0.2.0-288108d1dfa34d5ade5ce4d922af51909c83cebf`

Before: 


raiseNilAccess__OOZOOZOnimbleZpkgs50Zthreading4548O50O4845505656494856d49dfa5152d53ade53ce52d575050af5349574857c5651cebfZthreadingZsmartptrs_u4


After:


raiseNilAccess__OOZOOZOnimbleZpkgs2Zthreading450O2O045288108d1dfa34d5ade5ce4d922af51909c83cebfZthreadingZsmartptrs_u4


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


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

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

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

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

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

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

On Vscode wsl2

Before:


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

After


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

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

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

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

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

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

Unfortunately at the moment I'm in a conundrum.

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

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

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

---------

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

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

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

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

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

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

CC @alex65536
2024-07-12 15:25:18 +02:00
Andreas Rumpf
6d7ab08dee refactor: The popular 'r' field is now named 'snippet' (#23829) 2024-07-12 15:23:09 +02:00
ringabout
570deb10d3 deprecate owner from std/macros (#23828) 2024-07-12 13:33:54 +02:00
Ryan McConnell
22ba5abd63 fixes 23823; array static overload - again (#23824)
#23823
2024-07-11 22:57:17 +02:00
ringabout
173b8a8c58 fixes #3011; handles meta fields defined in the ref object (#23818)
fixes #3011

In https://github.com/nim-lang/Nim/pull/23532, meta fields that defined
in the object are handled.

In this PR, RefObjectTy is handled as well:
```nim
type
  Type = ref object 
    context: ref object
```
Ref alias won't trigger mata fields checking so there won't have
cascaded errors on `TypeBase`.

```nim
type
  TypeBase = object 
    context: ref object
  Type = ref TypeBase 
    context: ref object
```
2024-07-11 15:39:44 +02:00
ringabout
9092244f87 closes #22095; adds a test case (#23822)
closes #22095
2024-07-11 15:38:32 +02:00
ringabout
e53a2ed19b fixes #20865; fixes #20987; Missing bounds check in array slicing (#23814)
fixes #20865
fixes #20987
2024-07-10 17:25:34 +02:00
ringabout
5c5e7a9b6e fixes #22389; fixes #19840; don't fold paths containing addr (#23807)
fixes #22389;
fixes #19840
2024-07-09 12:59:21 +02:00
quimt
96c165304d conditional compilation of gcd(SomeInteger,SomeInteger) in std/math (#23773)
The most specific version of `gcd(int,int)` in `std/math` uses bitwise
comparisons from C compilers, which can't be borrowed on the js platform
in the web browser. Conditional compilation here should fix the issue
for this and downstream libraries such as `std/rationals` when compiling
to browser js as the backend.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2024-07-09 12:59:10 +02:00
ringabout
732f7752a9 remove nir; succeeded by nif (#23809)
ref https://github.com/nim-lang/nif
2024-07-09 09:29:45 +02:00
Juan M Gómez
14f86b3965 Bumps nimble so the next Nim release includes the latest changes (#23808) 2024-07-09 06:03:24 +02:00
Andrew Brower
dc46350fa1 Add support for nvcc & hipcc (cuda/rocm) (#23805)
I've been working on making some basic cuda examples work, both with
cuda (nvcc) and with AMD HIP (hipcc) https://github.com/monofuel/hippo

- hipcc is just a drop-in replacement for clang and works out of the box
with clang settings in Nim. hipcc is capable of compiling for AMD ROCm
or to CUDA, depending on how HIP_PLATFORM is set.
- nvcc is a little quirky. we can use `-x cu` to tell it to handle nim's
`.cpp` files as if they were `.cu` files. nvcc expects all backend
compiler flags to be wrapped with a special `-Xcompiler=""` flag when
compiling and also when linking.

I manually tested on a linux desktop with amd and a laptop with nvidia.
2024-07-08 11:17:04 +02:00
SirOlaf
3f5016f60e Adjust the correct chunk's free space in allocator (#23795)
Fixes #23788
2024-07-08 11:15:53 +02:00
c-blake
4faa15f3ad Replacement PR for https://github.com/nim-lang/Nim/pull/23779 that (#23793)
makes new hash the default, with an opt-out (& js-no-big-int) define.
Also update changelog (& fix one typo).

Only really expect the chronos hash-order sensitive test to fail until
they merge that PR and tag a new release.
2024-07-07 12:51:42 +02:00
Alexander Kernozhitsky
1dcc364cd2 [backport] fixes #23796; remove extra indirection for args in importc'ed functions in cpp (#23800)
fixes #23796
2024-07-06 23:10:15 +02:00
Leon Lysak
ce75042a9d Update documentation for parseEnum in strutils.nim (#23804)
added small note regarding style insensitivity for parsing enums. the
casing of the first letter is still taken into account for this
function. was confused a little at first because when I read "style
insensitive manner" I thought it meant casing as well and ran into a
couple of `ValueError`'s because of it.
2024-07-06 22:51:15 +02:00
Alexander Kernozhitsky
841d30a213 fixes #23790; roll back instCounter properly in case of exceptions (#23802)
fixes #23790
2024-07-06 22:50:46 +02:00
Yuriy Glukhov
05df263b84 Optimize closure iterator locals (#23787)
This pr redefines the relation between lambda lifting and closureiter
transformation.

Key takeaways:
- Lambdalifting now has less distinction between closureiters and
regular closures. Namely instead of lifting _all_ closureiter variables,
it lifts only those variables it would also lift for simple closure,
i.e. those not owned by the closure.
- It is now closureiter transformation's responsibility to lift all the
locals that need lifting and are not lifted by lambdalifting. So now we
lift only those locals that appear in more than one state. The rest
remains on stack, yay!
- Closureiter transformation always relies on the closure env param
created by lambdalifting. Special care taken to make lambdalifting
create it even in cases when it's "too early" to lift.
- Environments created by lambdalifting will contain `:state` only for
closureiters, whereas previously any closure env contained it.

IMO this is a more reasonable approach as it simplifies not only
lambdalifting, but transf too (e.g. freshVarsForClosureIters is now gone
for good).

I tried to organize the changes logically by commits, so it might be
easier to review this on per commit basis.

Some ugliness:
- Adding lifting to closureiters transformation I had to repeat this
matching of `return result = value` node. I tried to understand why it
is needed, but that was just another rabbit hole, so I left it for
another time. @Araq your input is welcome.
- In the last commit I've reused currently undocumented `liftLocals`
pragma for symbols so that closureiter transformation will forcefully
lift those even if they don't require lifting otherwise. This is needed
for [yasync](https://github.com/yglukhov/yasync) or else it will be very
sad.

Overall I'm quite happy with the results, I'm seeing some noticeable
code size reductions in my projects. Heavy closureiter/async users,
please give it a go.
2024-07-03 22:49:30 +02:00
ringabout
051a536275 fixes #23784; don't allow fold paths containing nkAddr (#23792)
fixes #23784

notes that before https://github.com/nim-lang/Nim/pull/23477, it didn't
fold paths containing `addr`/`unsafeAddr` because it retained the form
of the magic function: `mAddr`.
2024-07-03 22:48:19 +02:00
c-blake
903b1b1016 This test for issue 9739 never needed to depend upon hash order (#23791)
(for `string` or any other key type). Independence is nice to ever
change orders. So, change it to just `len` & a `doAssert` like the other
test in the same file.
2024-07-03 16:33:26 +02:00
ringabout
fe3039410f fixes #23775; injectdestructors now handles discardable statements (#23780)
fixes #23775
2024-07-02 08:49:37 +02:00
Gianmarco
96aba18963 Fixed issues when using std/parseopt in miscripts with cmdline = "" (#23785)
Using initOptParser with an empty cmdline (so that it gets the cmdline
from the command line) in nimscripts does not yield the expected
results.

Fixes #23774.
2024-07-02 08:49:21 +02:00
lit
43ee545789 Fix doc: '\c' '\L' in lexbase.nim (#23781)
- In lexbase.nim, `\c` `\L` were rendered as `c` `L`.
2024-07-01 20:47:39 +02:00
lit
a557e5a341 refine: strmisc.expandTabs better code structure (#23783)
After this pr, for a string with just 20 length and 6 `'\t'`, the time reduces by about 1.5%[^t].

Also, the code is clearer than the previous at some places.


[^t]: Generally speaking, this rate increases with length. I may test
for longer string later.
2024-07-01 20:47:08 +02:00
Ryan McConnell
27abcdd57f fixes #23755; array static inference during overload resolution (#23760)
#23755

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2024-07-01 14:39:16 +02:00
Andreas Rumpf
d78ccbc27c Revert "Document move limitations" (#23778)
Reverts nim-lang/Nim#23763

Too vague and fear inducing.
2024-07-01 13:06:09 +02:00
Mark Leyva
288d5c4ac3 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
2024-07-01 11:42:11 +02:00
Alexander Kernozhitsky
4202b606b1 [backport] fixes #23748; do not skip materializing temporaries for proc arguments (#23769)
fixes #23748
2024-06-30 14:10:10 +02:00
Alexander Kernozhitsky
c88894bf76 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.
2024-06-29 20:49:54 +02:00
Juan Carlos
179897e55f Document move limitations (#23763)
- See
https://github.com/nim-lang/Nim/issues/23759#issuecomment-2192123783
2024-06-29 10:44:57 +02:00
Juan M Gómez
9f74baa49d Bumps nimble to entryPoints commit (#23766) 2024-06-29 10:44:18 +02:00
ringabout
56ed4e0bb9 fixes #23759; rework move for refc (#23764)
fixes #23759
2024-06-29 10:43:41 +02:00
852 changed files with 21220 additions and 12726 deletions

View File

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

View File

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

View File

@@ -4,6 +4,7 @@ on:
push:
branches:
- 'devel'
- 'version-2-2'
- 'version-2-0'
- 'version-1-6'
- 'version-1-2'
@@ -17,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-12]
os: [ubuntu-22.04, macos-13]
cpu: [amd64]
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
@@ -48,6 +49,7 @@ jobs:
- name: 'Install dependencies (macOS)'
if: runner.os == 'macOS'
run: brew install boehmgc make sfml gtk+3
# XXX can't find boehm and gtk on macos 13
- name: 'Install dependencies (Windows)'
if: runner.os == 'Windows'
shell: bash

View File

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

View File

@@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
Linux_amd64:
vmImage: 'ubuntu-20.04'
vmImage: 'ubuntu-24.04'
CPU: amd64
# regularly breaks, refs bug #17325
# Linux_i386:
@@ -29,23 +29,23 @@ jobs:
# vmImage: 'ubuntu-18.04'
# CPU: i386
OSX_amd64:
vmImage: 'macOS-12'
vmImage: 'macOS-13'
CPU: amd64
OSX_amd64_cpp:
vmImage: 'macOS-12'
vmImage: 'macOS-13'
CPU: amd64
NIM_COMPILE_TO_CPP: true
Windows_amd64_batch0_3:
vmImage: 'windows-2019'
vmImage: 'windows-2025'
CPU: amd64
# see also: `NIM_TEST_PACKAGES`
NIM_TESTAMENT_BATCH: "0_3"
Windows_amd64_batch1_3:
vmImage: 'windows-2019'
vmImage: 'windows-2025'
CPU: amd64
NIM_TESTAMENT_BATCH: "1_3"
Windows_amd64_batch2_3:
vmImage: 'windows-2019'
vmImage: 'windows-2025'
CPU: amd64
NIM_TESTAMENT_BATCH: "2_3"
@@ -80,10 +80,12 @@ jobs:
- bash: |
set -e
. ci/funs.sh
echo_run sudo apt-fast update -qq
echo_run sudo add-apt-repository universe
echo_run sudo apt-get update -qq
DEBIAN_FRONTEND='noninteractive' \
echo_run sudo apt-fast install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
echo_run sudo apt-get install --no-install-recommends -yq \
gcc-14 g++-14 libpcre3 liblapack-dev libpcre3 liblapack-dev libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
echo_run sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
displayName: 'Install dependencies (amd64 Linux)'
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
@@ -100,15 +102,16 @@ jobs:
Pin-Priority: 1001
EOF
# echo_run sudo apt-fast update -qq
echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
# echo_run sudo apt-get update -qq
echo_run sudo apt-get update -qq || echo "failed, see bug #17343"
# `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
# `could not load: libffi.so` during dynamic loading.
DEBIAN_FRONTEND='noninteractive' \
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
echo_run sudo apt-get install --no-install-recommends --allow-downgrades -yq \
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
cat << EOF > bin/gcc
#!/bin/bash
@@ -130,6 +133,7 @@ jobs:
- bash: brew install boehmgc make sfml
displayName: 'Install dependencies (OSX)'
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
# XXX can't find boehm on macos 13
- bash: |
set -e

View File

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

View File

@@ -1,115 +1,83 @@
# v2.2.0 - yyyy-mm-dd
# v2.x.x - yyyy-mm-dd
## Changes affecting backward compatibility
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to be in the same module where their type has been defined.
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
```nim
var a, b: int
(a, b) = (1, 2, 3, 4)
```
will no longer compile.
- `internalNew` is removed from system, use `new` instead.
- `-d:nimPreviewFloatRoundtrip` becomes the default. `system.addFloat` and `system.$` now can produce string representations of
floating point numbers that are minimal in size and possess round-trip and correct
rounding guarantees (via the
[Dragonbox](https://raw.githubusercontent.com/jk-jeon/dragonbox/master/other_files/Dragonbox.pdf) algorithm). Use `-d:nimLegacySprintf` to emulate old behaviors.
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
- The `default` parameter of `tables.getOrDefault` has been renamed to `def` to
avoid conflicts with `system.default`, so named argument usage for this
parameter like `getOrDefault(..., default = ...)` will have to be changed.
- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behavior.
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
- 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.
## Standard library additions and changes
[//]: # "Changes:"
- Changed `std/osfiles.copyFile` to allow to specify `bufferSize` instead of a hardcoded one.
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
[//]: # "Additions:"
- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
- Added `setLenUninit` to system, which doesn't 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`.
- `nimPreviewHashFarm` has been added to `lib/pure/hashes.nim` to default to a
64-bit string `Hash` (based upon Google's Farm Hash) which is also faster than
the present one. At present, this is incompatible with `--jsbigint=off` mode.
- `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.
[//]: # "Deprecations:"
[//]: # "Changes:"
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
[//]: # "Removals:"
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
- `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`.
## Language changes
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.htm#constructor-initializer
- `member` can be used to attach a procedure to a C++ type.
- C++ `constructor` now reuses `result` instead creating `this`.
- Tuple unpacking changes:
- Tuple unpacking assignment now supports using underscores to discard values.
```nim
var a, c: int
(a, _, c) = (1, 2, 3)
```
- Tuple unpacking variable declarations now support type annotations, but
only for the entire tuple.
```nim
let (a, b): (int, int) = (1, 2)
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
```
- An experimental option `genericsOpenSym` has been added to allow captured
symbols in generic routine bodies to be replaced by symbols injected locally
by templates/macros at instantiation time. `bind` may be used to keep the
captured symbols over the injected ones regardless of enabling the option.
Since this change may affect runtime behavior, the experimental switch
`genericsOpenSym` needs to be enabled, and a warning is given in the case
where an injected symbol would replace a captured symbol not bound by `bind`
and the experimental switch isn't enabled.
- An experimental option `--experimental:typeBoundOps` has been added that
implements the RFC https://github.com/nim-lang/RFCs/issues/380.
This makes the behavior of interfaces like `hash`, `$`, `==` etc. more
reliable for nominal types across indirect/restricted imports.
```nim
const value = "captured"
template foo(x: int, body: untyped) =
let value {.inject.} = "injected"
body
# objs.nim
import std/hashes
proc old[T](): string =
foo(123):
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:genericsOpenSym`
echo old[int]() # "captured"
type
Obj* = object
x*, y*: int
z*: string # to be ignored for equality
{.experimental: "genericsOpenSym".}
proc `==`*(a, b: Obj): bool =
a.x == b.x and a.y == b.y
proc bar[T](): string =
foo(123):
return value
assert bar[int]() == "injected" # previously it would be "captured"
proc baz[T](): string =
bind value
foo(123):
return value
assert baz[int]() == "captured"
proc hash*(a: Obj): Hash =
$!(hash(a.x) &! hash(a.y))
```
```nim
# main.nim
{.experimental: "typeBoundOps".}
from objs import Obj # objs.hash, objs.`==` not imported
import std/tables
var t: Table[Obj, int]
t[Obj(x: 3, y: 4, z: "debug")] = 34
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
```
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#typeminusbound-overloads)
for more information.
## Compiler changes
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
## Tool changes
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.
- Added `--stdinfile` flag to name of the file used when running program from stdin (defaults to `stdinfile.nim`)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -41,7 +41,7 @@ type
TNodeKinds* = set[TNodeKind]
type
TSymFlag* = enum # 52 flags!
TSymFlag* = enum # 63 flags!
sfUsed, # read access of sym (for warnings) or simply used
sfExported, # symbol is exported from module
sfFromGeneric, # symbol is instantiation of a generic; this is needed
@@ -93,7 +93,7 @@ type
sfNamedParamCall, # symbol needs named parameter call syntax in target
# language; for interfacing with Objective C
sfDiscardable, # returned value may be discarded implicitly
sfOverridden, # proc is overridden
sfOverridden, # proc is overridden
sfCallsite # A flag for template symbols to tell the
# compiler it should use line information from
# the calling side of the macro, not from the
@@ -127,24 +127,24 @@ type
sfMember # proc is a C++ member of a type
sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl
sfWasGenSym # symbol was 'gensym'ed
sfForceLift # variable has to be lifted into closure environment
sfDirty # template is not hygienic (old styled template) module,
# compiled from a dirty-buffer
sfCustomPragma # symbol is custom pragma template
sfBase, # a base method
sfGoto # var is used for 'goto' code generation
sfAnon, # symbol name that was generated by the compiler
# the compiler will avoid printing such names
# in user messages.
sfAllUntyped # macro or template is immediately expanded in a generic context
sfTemplateRedefinition # symbol is a redefinition of an earlier template
TSymFlags* = set[TSymFlag]
const
sfNoInit* = sfMainModule # don't generate code to init the variable
sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
# in a generic context
sfDirty* = sfPure
# template is not hygienic (old styled template)
# module, compiled from a dirty-buffer
sfAnon* = sfDiscardable
# symbol name that was generated by the compiler
# the compiler will avoid printing such names
# in user messages.
sfNoForward* = sfRegister
# forward declarations are not required (per module)
sfReorder* = sfForward
@@ -153,12 +153,8 @@ const
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
sfExperimental* = sfOverridden # module uses the .experimental switch
sfGoto* = sfOverridden # var is used for 'goto' code generation
sfWrittenTo* = sfBorrow # param is assigned to
# currently unimplemented
sfBase* = sfDiscriminant
sfCustomPragma* = sfRegister # symbol is custom pragma template
sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template
sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition
const
@@ -218,7 +214,8 @@ type
tyUncheckedArray
# An array with boundaries [0,+∞]
tyProxy # used as errornous type (for idetools)
tyError # used as erroneous type (for idetools)
# as an erroneous node should match everything
tyBuiltInTypeClass
# Type such as the catch-all object, tuple, seq, etc
@@ -280,13 +277,9 @@ static:
const
tyPureObject* = tyTuple
GcTypeKinds* = {tyRef, tySequence, tyString}
tyError* = tyProxy # as an errornous node should match everything
tyUnknown* = tyFromExpr
tyUnknownTypes* = {tyError, tyFromExpr}
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
tyUserTypeClass, tyUserTypeClassInst,
tyUserTypeClass, tyUserTypeClassInst, tyConcept,
tyAnd, tyOr, tyNot, tyAnything}
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
@@ -329,7 +322,9 @@ type
nfFirstWrite # this node is a first write
nfHasComment # node has a comment
nfSkipFieldChecking # node skips field visable checking
nfOpenSym # node is a captured sym but can be overriden by local symbols
nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot
# because openSym experimental switch is disabled
# gives warning instead
TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47)
@@ -450,6 +445,10 @@ const
tfGcSafe* = tfThread
tfObjHasKids* = tfEnumHasHoles
tfReturnsNew* = tfInheritable
tfNonConstExpr* = tfExplicitCallConv
## tyFromExpr where the expression shouldn't be evaluated as a static value
tfGenericHasDestructor* = tfExplicitCallConv
## tyGenericBody where an instance has a generated destructor
skError* = skUnknown
var
@@ -494,7 +493,7 @@ type
mAnd, mOr,
mImplies, mIff, mExists, mForall, mOld,
mEqStr, mLeStr, mLtStr,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
mConStrStr, mSlice,
mDotDot, # this one is only necessary to give nice compile time warnings
mFields, mFieldPairs, mOmpParFor,
@@ -562,7 +561,7 @@ const
mStrToStr, mEnumToStr,
mAnd, mOr,
mEqStr, mLeStr, mLtStr,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInSet, mRepr, mOpenArrayToSeq}
@@ -594,7 +593,7 @@ type
TNode*{.final, acyclic.} = object # on a 32bit machine, this takes 32 bytes
when defined(useNodeIds):
id*: int
typ*: PType
typField: PType
info*: TLineInfo
flags*: TNodeFlags
case kind*: TNodeKind
@@ -655,7 +654,7 @@ type
storage*: TStorageLoc
flags*: TLocFlags # location's flags
lode*: PNode # Node where the location came from; can be faked
r*: Rope # rope value of location (code generators)
snippet*: Rope # C code snippet of location (code generators)
# ---------------- end of backend information ------------------------------
@@ -709,7 +708,7 @@ type
when defined(nimsuggest):
endInfo*: TLineInfo
hasUserSpecifiedType*: bool # used for determining whether to display inlay type hints
owner*: PSym
ownerField: PSym
flags*: TSymFlags
ast*: PNode # syntax tree of proc, iterator, etc.:
# the whole proc including header; this is used
@@ -778,7 +777,7 @@ type
# formal param list
# for concepts, the concept body
# else: unused
owner*: PSym # the 'owner' of the type
ownerField: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them
# it is used for converting types to strings
size*: BiggestInt # the size of the type in bytes
@@ -817,6 +816,15 @@ type
template nodeId(n: PNode): int = cast[int](n)
template typ*(n: PNode): PType =
n.typField
proc owner*(s: PSym|PType): PSym {.inline.} =
result = s.ownerField
proc setOwner*(s: PSym|PType, owner: PSym) {.inline.} =
s.ownerField = owner
type Gconfig = object
# we put comments in a side channel to avoid increasing `sizeof(TNode)`, which
# reduces memory usage given that `PNode` is the most allocated type by far.
@@ -884,7 +892,7 @@ const
nfFromTemplate, nfDefaultRefsParam,
nfExecuteOnReload, nfLastRead,
nfFirstWrite, nfSkipFieldChecking,
nfOpenSym}
nfDisabledOpenSym}
namePos* = 0
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2
@@ -898,7 +906,7 @@ const
nfAllFieldsSet* = nfBase2
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
nkClosedSymChoice}
nkClosedSymChoice, nkOpenSym}
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
nkLiterals* = {nkCharLit..nkTripleStrLit}
@@ -925,7 +933,7 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
case a.kind
of nkSym: a.sym.name
of nkIdent: a.ident
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: a.sons[0].sym.name
else: nil
const
@@ -1059,8 +1067,11 @@ proc getDeclPragma*(n: PNode): PNode =
proc extractPragma*(s: PSym): PNode =
## gets the pragma node of routine/type/var/let/const symbol `s`
if s.kind in routineKinds:
result = s.ast[pragmasPos]
if s.kind in routineKinds: # bug #24167
if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
result = s.ast[pragmasPos]
else:
result = nil
elif s.kind in {skType, skVar, skLet, skConst}:
if s.ast != nil and s.ast.len > 0:
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
@@ -1131,7 +1142,7 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
## new node with line info, type, and no children
result = newNode(kind)
result.info = info
result.typ = typ
result.typ() = typ
proc newNode*(kind: TNodeKind, info: TLineInfo): PNode =
## new node with line info, no type, and no children
@@ -1196,7 +1207,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
assert not name.isNil
let id = nextSymId idgen
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
options: options, owner: owner, offset: defaultOffset,
options: options, ownerField: owner, offset: defaultOffset,
disamb: getOrDefault(idgen.disambTable, name).int32)
idgen.disambTable.inc name
when false:
@@ -1277,15 +1288,18 @@ proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode =
proc newSymNode*(sym: PSym): PNode =
result = newNode(nkSym)
result.sym = sym
result.typ = sym.typ
result.typ() = sym.typ
result.info = sym.info
proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
result = newNode(nkSym)
result.sym = sym
result.typ = sym.typ
result.typ() = sym.typ
result.info = info
proc newOpenSym*(n: PNode): PNode {.inline.} =
result = newTreeI(nkOpenSym, n.info, n)
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
result = newNode(kind)
result.intVal = intVal
@@ -1360,7 +1374,7 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
result = newNode(nkIntLit)
else: raiseAssert $kind
result.intVal = intVal
result.typ = typ
result.typ() = typ
proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
# XXX: introduce range check
@@ -1499,7 +1513,7 @@ iterator signature*(t: PType): PType =
proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType =
let id = nextTypeId idgen
result = PType(kind: kind, owner: owner, size: defaultSize,
result = PType(kind: kind, ownerField: owner, size: defaultSize,
align: defaultAlignment, itemId: id,
uniqueId: id, sons: @[])
if son != nil: result.sons.add son
@@ -1510,13 +1524,14 @@ proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType
proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons
proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son]
proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len)
proc mergeLoc(a: var TLoc, b: TLoc) =
if a.k == low(typeof(a.k)): a.k = b.k
if a.storage == low(typeof(a.storage)): a.storage = b.storage
a.flags.incl b.flags
if a.lode == nil: a.lode = b.lode
if a.r == "": a.r = b.r
if a.snippet == "": a.snippet = b.snippet
proc newSons*(father: PNode, length: int) =
setLen(father.sons, length)
@@ -1553,7 +1568,7 @@ proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
result.sym = t.sym # backend-info should not be copied
proc exactReplica*(t: PType): PType =
result = PType(kind: t.kind, owner: t.owner, size: defaultSize,
result = PType(kind: t.kind, ownerField: t.owner, size: defaultSize,
align: defaultAlignment, itemId: t.itemId,
uniqueId: t.uniqueId)
assignType(result, t)
@@ -1631,7 +1646,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
if mask != {} and propagateHasAsgn:
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
if o2.kind in {tyTuple, tyObject, tyArray,
tySequence, tySet, tyDistinct}:
tySequence, tyString, tySet, tyDistinct}:
o2.flags.incl mask
owner.flags.incl mask
@@ -1661,7 +1676,7 @@ proc copyNode*(src: PNode): PNode =
return nil
result = newNode(src.kind)
result.info = src.info
result.typ = src.typ
result.typ() = src.typ
result.flags = src.flags * PersistentNodeFlags
result.comment = src.comment
when defined(useNodeIds):
@@ -1679,7 +1694,7 @@ proc copyNode*(src: PNode): PNode =
template transitionNodeKindCommon(k: TNodeKind) =
let obj {.inject.} = n[]
n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags)
n[] = TNode(kind: k, typField: n.typ, info: obj.info, flags: obj.flags)
# n.comment = obj.comment # shouldn't be needed, the address doesnt' change
when defined(useNodeIds):
n.id = obj.id
@@ -1702,7 +1717,7 @@ proc transitionNoneToSym*(n: PNode) =
template transitionSymKindCommon*(k: TSymKind) =
let obj {.inject.} = s[]
s[] = TSym(kind: k, itemId: obj.itemId, magic: obj.magic, typ: obj.typ, name: obj.name,
info: obj.info, owner: obj.owner, flags: obj.flags, ast: obj.ast,
info: obj.info, ownerField: obj.ownerField, flags: obj.flags, ast: obj.ast,
options: obj.options, position: obj.position, offset: obj.offset,
loc: obj.loc, annex: obj.annex, constraint: obj.constraint)
when hasFFI:
@@ -1730,7 +1745,7 @@ template copyNodeImpl(dst, src, processSonsStmt) =
dst.info = src.info
when defined(nimsuggest):
result.endInfo = src.endInfo
dst.typ = src.typ
dst.typ() = src.typ
dst.flags = src.flags * PersistentNodeFlags
dst.comment = src.comment
when defined(useNodeIds):
@@ -2073,14 +2088,16 @@ proc canRaise*(fn: PNode): bool =
result = false
elif fn.kind == nkSym and fn.sym.magic == mEcho:
result = true
else:
elif fn.typ != nil and fn.typ.kind == tyProc and fn.typ.n != nil:
# TODO check for n having sons? or just return false for now if not
if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym:
if fn.typ.n[0].kind == nkSym:
result = false
else:
result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
result = ((fn.typ.n[0].len < effectListLen) or
(fn.typ.n[0][exceptionEffects] != nil and
fn.typ.n[0][exceptionEffects].safeLen > 0))
else:
result = false
proc toHumanStrImpl[T](kind: T, num: static int): string =
result = $kind

View File

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

View File

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

121
compiler/cbuilder.nim Normal file
View File

@@ -0,0 +1,121 @@
type
Snippet = string
Builder = string
template newBuilder(s: string): Builder =
s
proc addField(obj: var Builder; name, typ: Snippet) =
obj.add('\t')
obj.add(typ)
obj.add(" ")
obj.add(name)
obj.add(";\n")
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool; initializer: Snippet) =
obj.add('\t')
if field.alignment > 0:
obj.add("NIM_ALIGN(")
obj.addInt(field.alignment)
obj.add(") ")
obj.add(typ)
if sfNoalias in field.flags:
obj.add(" NIM_NOALIAS")
obj.add(" ")
obj.add(name)
if isFlexArray:
obj.add("[SEQ_DECL_SIZE]")
if field.bitsize != 0:
obj.add(":")
obj.addInt(field.bitsize)
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc structOrUnion(t: PType): Snippet =
let t = t.skipTypes({tyAlias, tySink})
if tfUnion in t.flags: "union"
else: "struct"
proc ptrType(t: Snippet): Snippet =
t & "*"
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: string; body: typed) =
if tfPacked in typ.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add(structOrUnion(typ))
obj.add(" __attribute__((__packed__))")
else:
obj.add("#pragma pack(push, 1)\n")
obj.add(structOrUnion(typ))
else:
obj.add(structOrUnion(typ))
obj.add(" ")
obj.add(name)
type BaseClassKind = enum
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
var baseKind = bcNone
if typ.kind == tyObject:
if typ.baseClass == nil:
if lacksMTypeField(typ):
baseKind = bcNone
elif optTinyRtti in m.config.globalOptions:
baseKind = bcNoneTinyRtti
else:
baseKind = bcNoneRtti
elif m.compileToCpp:
baseKind = bcCppInherit
else:
baseKind = bcSupField
if baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
let currLen = obj.len
case baseKind
of bcNone:
# rest of the options add a field or don't need it due to inheritance,
# we need to add the dummy field for uncheckedarray ahead of time
# so that it remains trailing
if typ.itemId notin m.g.graph.memberProcsPerType and
typ.n != nil and typ.n.len == 1 and typ.n[0].kind == nkSym and
typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
# only consists of flexible array field, add *initial* dummy field
obj.addField(name = "dummy", typ = "char")
of bcCppInherit: discard
of bcNoneRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
of bcNoneTinyRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
of bcSupField:
obj.addField(name = "Sup", typ = baseType)
body
if baseKind == bcNone and currLen == obj.len and typ.itemId notin m.g.graph.memberProcsPerType:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = "char")
obj.add("};\n")
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
result.add("#pragma pack(pop)\n")
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
## adds a field with a `struct { ... }` type, building it according to `body`
obj.add('\t')
if tfPacked in parentTyp.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add("struct __attribute__((__packed__)) {\n")
else:
obj.add("#pragma pack(push, 1)\nstruct {")
else:
obj.add("struct {\n")
body
obj.add("} ")
obj.add(fieldName)
obj.add(";\n")
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
result.add("#pragma pack(pop)\n")
template addAnonUnion(obj: var Builder; body: typed) =
obj.add "union{\n"
body
obj.add("};\n")

View File

@@ -132,16 +132,16 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.r = pl
d.snippet = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone and p.splitDecls == 0:
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
d = getTempCpp(p, typ.returnType, pl)
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
var list = initLoc(locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
list.snippet = pl
genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
if canRaise: raiseExit(p)
elif isHarmlessStore(p, canRaise, d):
@@ -151,16 +151,16 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
d = getTemp(p, typ.returnType)
assert(d.t != nil) # generate an assignment to d:
var list = initLoc(locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, flags) # no need for deep copying
list.snippet = pl
genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying
if canRaise:
if not (useTemp and cleanupTemp(p, typ.returnType, d)):
raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var list = initLoc(locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, tmp, list, flags) # no need for deep copying
list.snippet = pl
genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying
if canRaise:
if not cleanupTemp(p, typ.returnType, tmp):
raiseExit(p)
@@ -170,7 +170,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
line(p, cpsStmts, pl)
if canRaise: raiseExit(p)
proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
proc reifiedOpenArray(n: PNode): bool {.inline.} =
var x = n
@@ -191,15 +191,15 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
var a = initLocExpr(p, q[1])
var b = initLocExpr(p, q[2])
var c = initLocExpr(p, q[3])
# but first produce the required index checks:
if optBoundsCheck in p.options:
genBoundsCheck(p, a, b, c)
if prepareForMutation:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
# bug #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 here
# in the `genDeref`. We need to skip these ptrs here
let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
# but first produce the required index checks:
if optBoundsCheck in p.options:
genBoundsCheck(p, a, b, c, ty)
if prepareForMutation:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
let dest = getTypeDesc(p.module, destType)
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
case ty.kind
@@ -274,7 +274,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
var t = TLoc(r: "(*$1)" % [a.rdLoc])
var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
dataFieldAccessor(p, "*" & a.rdLoc)]
@@ -286,7 +286,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
of tyPtr, tyRef:
case elementType(a.t).kind
of tyString, tySequence:
var t = TLoc(r: "(*$1)" % [a.rdLoc])
var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
dataFieldAccessor(p, "*" & a.rdLoc)]
@@ -307,7 +307,7 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
else:
result = a
proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
result = getTemp(p, a.lode.typ, needsInit=false)
genAssignment(p, result, a, {})
@@ -326,27 +326,37 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
(optByRef notin param.options or not p.module.compileToCpp):
a = initLocExpr(p, n)
if n.kind in {nkCharLit..nkNilLit}:
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
addAddrLoc(p.config, expressionsNeedsTmp(p, a), result)
else:
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
n.kind == nkHiddenAddr:
a = initLocExprSingleUse(p, n[0])
# bug #23748: we need to introduce a temporary here. The expression type
# will be a reference in C++ and we cannot create a temporary reference
# 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.flags.incl tfVarIsPtr
a = initLocExprSingleUse(p, n)
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
# means '*T'. See posix.nim for lots of examples that do that in the wild.
let callee = call[0]
if callee.kind == nkSym and
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and
needsIndirect:
addAddrLoc(p.config, a, result)
else:
addRdLoc(a, result)
else:
a = initLocExprSingleUse(p, n)
if param.typ.kind in abstractPtrs:
if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
let typ = skipTypes(param.typ, abstractPtrs)
if typ.sym != nil and sfImportc in typ.sym.flags:
a.r = "(($1) ($2))" %
if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
a.snippet = "(($1) ($2))" %
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
#assert result != nil
@@ -430,9 +440,11 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
if not needTmp[i - 1]:
needTmp[i - 1] = potentialAlias(n, potentialWrites)
getPotentialWrites(ri[i], false, potentialWrites)
if ri[i].kind in {nkHiddenAddr, nkAddr}:
# Optimization: don't use a temp, if we would only take the address anyway
needTmp[i - 1] = false
when false:
# this optimization is wrong, see bug #23748
if ri[i].kind in {nkHiddenAddr, nkAddr}:
# Optimization: don't use a temp, if we would only take the address anyway
needTmp[i - 1] = false
var oldLen = result.len
for i in 1..<ri.len:
@@ -520,9 +532,9 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
else:
@@ -530,9 +542,9 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, tmp, list, {})
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {})
@@ -715,7 +727,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var typ = skipTypes(ri[0].typ, abstractInst)
assert(typ.kind == tyProc)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.r
let pat = $ri[0].sym.loc.snippet
internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@', '\''}):
var pl = newRopeAppender()
@@ -728,13 +740,13 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.r = pl
d.snippet = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
list.r = pl
list.snippet = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add(";\n")
@@ -744,7 +756,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var argsCounter = 0
if 1 < ri.len:
genThisArg(p, ri, 1, typ, pl)
pl.add(op.r)
pl.add(op.snippet)
var params = newRopeAppender()
for i in 2..<ri.len:
genOtherArg(p, ri, i, typ, params, argsCounter)
@@ -759,12 +771,12 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
assert(typ.kind == tyProc)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.r
let pat = $ri[0].sym.loc.snippet
internalAssert p.config, pat.len > 0
var start = 3
if ' ' in pat:
start = 1
pl.add(op.r)
pl.add(op.snippet)
if ri.len > 1:
pl.add(": ")
genArg(p, ri[1], typ.n[1].sym, ri, pl)
@@ -773,7 +785,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
if ri.len > 1:
genArg(p, ri[1], typ.n[1].sym, ri, pl)
pl.add(" ")
pl.add(op.r)
pl.add(op.snippet)
if ri.len > 2:
pl.add(": ")
genArg(p, ri[2], typ.n[2].sym, ri, pl)
@@ -808,7 +820,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
if d.k == locNone: d = getTemp(p, typ.returnType)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, ri, OnUnknown)
list.r = pl
list.snippet = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add("];\n")

View File

@@ -224,7 +224,7 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
result = TLoc(k: locField,
storage: a.storage,
lode: lodeTyp t,
r: rdLoc(a) & "." & field
snippet: rdLoc(a) & "." & field
)
proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
@@ -254,9 +254,9 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
case t.kind
of nkSym:
let field = t.sym
if field.loc.r == "": fillObjectFields(p.module, typ)
genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r),
optAsgnLoc(src, field.typ, field.loc.r), newflags)
if field.loc.snippet == "": fillObjectFields(p.module, typ)
genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.snippet),
optAsgnLoc(src, field.typ, field.loc.snippet), newflags)
of nkRecList:
for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ)
else: discard
@@ -343,7 +343,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
of tyString:
if optSeqDestructors in p.config.globalOptions:
genGenericAsgn(p, dest, src, flags)
elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
elif ({needToCopy, needToCopySinkParam} * flags == {} and src.storage != OnStatic) or canMove(p, src.lode, dest):
genRefAssign(p, dest, src)
else:
if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
@@ -379,7 +379,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
elif not isObjLackingTypeField(ty):
genGenericAsgn(p, dest, src, flags)
elif containsGarbageCollectedRef(ty):
if ty[0].isNil and asgnComplexity(ty.n) <= 4:
if ty[0].isNil and asgnComplexity(ty.n) <= 4 and
needAssignCall notin flags: # calls might contain side effects
discard getTypeDesc(p.module, ty)
internalAssert p.config, ty.n != nil
genOptAsgnObject(p, dest, src, flags, ty.n, ty)
@@ -482,7 +483,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
if d.k != locNone:
var a: TLoc = initLoc(locData, n, OnStatic)
# need to generate an assignment here
a.r = r
a.snippet = r
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
else: genAssignment(p, d, a, {needToCopy})
else:
@@ -490,13 +491,13 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
# the flags field!
d.k = locData
d.lode = n
d.r = r
d.snippet = r
proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
if d.k != locNone:
# need to generate an assignment here
var a: TLoc = initLoc(locExpr, n, s)
a.r = r
a.snippet = r
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
else: genAssignment(p, d, a, {needToCopy})
else:
@@ -504,7 +505,7 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
# the flags field!
d.k = locExpr
d.lode = n
d.r = r
d.snippet = r
proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) =
if d.k != locNone: internalError(p.config, e.info, "binaryStmt")
@@ -589,7 +590,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
# skipping 'range' is correct here as we'll generate a proper range check
# later via 'chckRange'
let t = e.typ.skipTypes(abstractRange)
if optOverflowCheck notin p.options:
if optOverflowCheck notin p.options or (m in {mSucc, mPred} and t.kind in {tyUInt..tyUInt64}):
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
putIntoDest(p, d, e, res)
else:
@@ -757,7 +758,10 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
typ = typ.last
typ = typ.skipTypes(abstractInstOwned)
if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and
p.module.compileToCpp and e[0].kind == nkHiddenAddr and
# don't override existing location:
d.k == locNone:
d = initLocExprSingleUse(p, e[0][0])
return
else:
@@ -804,17 +808,28 @@ proc cowBracket(p: BProc; n: PNode) =
proc cow(p: BProc; n: PNode) {.inline.} =
if n.kind == nkHiddenAddr: cowBracket(p, n[0])
template ignoreConv(e: PNode): bool =
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
let srcType = e[1].typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
sameBackendTypePickyAliases(destType, srcType)
proc genAddr(p: BProc, e: PNode, d: var TLoc) =
# careful 'addr(myptrToArray)' needs to get the ampersand:
if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}:
var a: TLoc = initLocExpr(p, e[0])
putIntoDest(p, d, e, "&" & a.r, a.storage)
putIntoDest(p, d, e, "&" & a.snippet, a.storage)
#Message(e.info, warnUser, "HERE NEW &")
elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ):
expr(p, e[0], d)
# bug #19497
d.lode = e
else:
var a: TLoc = initLocExpr(p, e[0])
putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
if e[0].kind in {nkHiddenStdConv, nkHiddenSubConv, nkConv} and not ignoreConv(e[0]):
# addr (conv x) introduces a temp because `conv x` is not a rvalue
putIntoDest(p, d, e, addrLoc(p.config, expressionsNeedsTmp(p, a)), a.storage)
else:
putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
template inheritLocation(d: var TLoc, a: TLoc) =
if d.k == locNone: d.storage = a.storage
@@ -877,10 +892,10 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
else:
var rtyp: PType = nil
let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
if field.loc.r == "" and rtyp != nil: fillObjectFields(p.module, rtyp)
if field.loc.r == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
if field.loc.snippet == "" and rtyp != nil: fillObjectFields(p.module, rtyp)
if field.loc.snippet == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
r.add "."
r.add field.loc.r
r.add field.loc.snippet
putIntoDest(p, d, e, r, a.storage)
r.freeze
@@ -899,10 +914,10 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
test = initLoc(locNone, it, OnStack)
u = initLocExpr(p, it[1])
v = initLoc(locExpr, disc, OnUnknown)
v.r = newRopeAppender()
v.r.add obj
v.r.add(".")
v.r.add(disc.sym.loc.r)
v.snippet = newRopeAppender()
v.snippet.add obj
v.snippet.add(".")
v.snippet.add(disc.sym.loc.snippet)
genInExprAux(p, it, u, v, test)
var msg = ""
if optDeclaredLocs in p.config.globalOptions:
@@ -960,12 +975,12 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
var r = rdLoc(a)
let f = e[0][1].sym
let field = lookupFieldAgain(p, ty, f, r)
if field.loc.r == "": fillObjectFields(p.module, ty)
if field.loc.r == "":
if field.loc.snippet == "": fillObjectFields(p.module, ty)
if field.loc.snippet == "":
internalError(p.config, e.info, "genCheckedRecordField") # generate the checks:
genFieldCheck(p, e, r, field)
r.add(".")
r.add field.loc.r
r.add field.loc.snippet
putIntoDest(p, d, e[0], r, a.storage)
r.freeze
else:
@@ -1019,8 +1034,8 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
putIntoDest(p, d, n,
ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
let ty = skipTypes(arr.t, abstractVarRange)
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) =
let ty = arrTyp
case ty.kind
of tyOpenArray, tyVarargs:
if reifiedOpenArray(arr.lode):
@@ -1100,7 +1115,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if d.k == locNone: d.storage = OnHeap
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
a.r = ropecg(p.module, "(*$1)", [a.r])
a.snippet = ropecg(p.module, "(*$1)", [a.snippet])
if lfPrepareForMutation in d.flags and ty.kind == tyString and
optSeqDestructors in p.config.globalOptions:
@@ -1166,9 +1181,9 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
var tmpB = initLocExprSingleUse(p, e[2])
tmpB.k = locExpr
if m == mOr:
tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
tmpB.snippet = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
else:
tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
tmpB.snippet = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
if d.k == locNone:
d = tmpB
else:
@@ -1271,7 +1286,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
lens.add(lenExpr(p, a))
lens.add(" + ")
appends.add(ropecg(p.module, "#appendString($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.r, lens, L])
linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.snippet, lens, L])
p.s(cpsStmts).add appends
if d.k == locNone:
d = tmp
@@ -1317,7 +1332,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
[byRefLoc(p, dest), lens, L])
else:
call = initLoc(locCall, e, OnHeap)
call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
call.snippet = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
genAssignment(p, dest, call, {})
gcUsage(p.config, e)
p.s(cpsStmts).add appends
@@ -1332,12 +1347,12 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
var call = initLoc(locCall, e, OnHeap)
if not p.module.compileToCpp:
const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)"
call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
getTypeDesc(p.module, e[1].typ),
genTypeInfoV1(p.module, seqType, e.info)])
else:
const seqAppendPattern = "($2) #incrSeqV3($1, $3)"
call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
getTypeDesc(p.module, e[1].typ),
genTypeInfoV1(p.module, seqType, e.info)])
# emit the write barrier if required, but we can always move here, so
@@ -1347,8 +1362,8 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
var dest = initLoc(locExpr, e[2], OnHeap)
var tmpL = getIntTemp(p)
lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)])
dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)])
lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.snippet, rdLoc(a), lenField(p)])
dest.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.snippet, dataField(p)])
genAssignment(p, dest, b, {needToCopy})
gcUsage(p.config, e)
@@ -1368,10 +1383,10 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
if optTinyRtti in p.config.globalOptions:
if needsInit:
b.r = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
b.snippet = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
[getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
else:
b.r = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
b.snippet = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
[getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
genAssignment(p, a, b, {})
else:
@@ -1396,15 +1411,15 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
if p.config.selectedGC == gcGo:
# newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
# implement the write barrier
b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
[addrLoc(p.config, a), b.rdLoc])
else:
# use newObjRC1 as an optimization
b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
b.snippet = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else:
b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
genAssignment(p, a, b, {})
# set the object type:
genObjectInit(p, cpsStmts, bt, a, constructRefObj)
@@ -1430,18 +1445,18 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
if not lenIsZero:
if p.config.selectedGC == gcGo:
# we need the write barrier
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc])
else:
call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
call.snippet = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc])
else:
if lenIsZero:
call.r = rope"NIM_NIL"
call.snippet = rope"NIM_NIL"
else:
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
genAssignment(p, dest, call, {})
@@ -1484,7 +1499,7 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) =
if id == p.module.labels:
# expression not found in the cache:
inc(p.module.labels)
var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, t), d.r]
var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, t), d.snippet]
# bug #23627; when generating const object fields, it's likely that
# we need to generate type infos for the object, which may be an object with
# custom hooks. We need to generate potential consts in the hooks first.
@@ -1501,14 +1516,14 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) =
var tmp2 = TLoc(r: r)
let field = lookupFieldAgain(p, ty, nField.sym, tmp2.r)
if field.loc.r == "": fillObjectFields(p.module, ty)
if field.loc.r == "": internalError(p.config, info, "genFieldObjConstr")
var tmp2 = TLoc(snippet: r)
let field = lookupFieldAgain(p, ty, nField.sym, tmp2.snippet)
if field.loc.snippet == "": fillObjectFields(p.module, ty)
if field.loc.snippet == "": internalError(p.config, info, "genFieldObjConstr")
if check != nil and optFieldCheck in p.options:
genFieldCheck(p, check, r, field)
tmp2.r.add(".")
tmp2.r.add(field.loc.r)
tmp2.snippet.add(".")
tmp2.snippet.add(field.loc.snippet)
if useTemp:
tmp2.k = locTemp
tmp2.storage = if isRef: OnHeap else: OnStack
@@ -1566,6 +1581,10 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
discard getTypeDesc(p.module, t)
let ty = getUniqueType(t)
for i in 1..<e.len:
if nfPreventCg in e[i].flags:
# this is an object constructor node generated by the VM and
# this field is in an inactive case branch, don't generate assignment
continue
var check: PNode = nil
if e[i].len == 3 and optFieldCheck in p.options:
check = e[i][2]
@@ -1607,7 +1626,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
arr = initLoc(locExpr, n[i], OnHeap)
var lit = newRopeAppender()
intLiteral(i, lit)
arr.r = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)])
arr.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)])
arr.storage = OnHeap # we know that sequences are on the heap
expr(p, n[i], arr)
gcUsage(p.config, n)
@@ -1620,7 +1639,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
var elem, arr: TLoc
if n[1].kind == nkBracket:
n[1].typ = n.typ
n[1].typ() = n.typ
genSeqConstr(p, n[1], d)
return
if d.k == locNone:
@@ -1643,19 +1662,19 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
var lit = newRopeAppender()
intLiteral(i, lit)
elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)])
elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)])
elem.storage = OnHeap # we know that sequences are on the heap
arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), lit])
arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), lit])
genAssignment(p, elem, arr, {needToCopy})
else:
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, L])
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.snippet, L])
elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
elem.storage = OnHeap # we know that sequences are on the heap
arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
genAssignment(p, elem, arr, {needToCopy})
lineF(p, cpsStmts, "}$n", [])
@@ -1671,7 +1690,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
b = initLoc(locExpr, a.lode, OnHeap)
ti = genTypeInfo(p.config, p.module, refType, e.info)
p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
b.snippet = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
getTypeDesc(p.module, refType),
ti, getTypeDesc(p.module, skipTypes(refType.elementType, abstractRange))])
genAssignment(p, a, b, {}) # set the object type:
@@ -1836,7 +1855,7 @@ proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) =
template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
var a: TLoc = initLocExpr(p, n[1])
a.r = ropecg(p.module, frmt, [rdLoc(a)])
a.snippet = ropecg(p.module, frmt, [rdLoc(a)])
a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR)
if d.k == locNone: d = getTemp(p, n.typ)
genAssignment(p, d, a, {})
@@ -1855,7 +1874,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
var b = initLocExpr(p, a[2])
var c = initLocExpr(p, a[3])
if optBoundsCheck in p.options:
genBoundsCheck(p, m, b, c)
genBoundsCheck(p, m, b, c, skipTypes(m.t, abstractVarRange))
if op == mHigh:
putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)]))
else:
@@ -1890,8 +1909,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
var a = initLocExpr(p, e[1])
var x = lenExpr(p, a)
if op == mHigh: x = "($1-1)" % [x]
lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x])
putIntoDest(p, d, e, tmp.r)
lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.snippet, x])
putIntoDest(p, d, e, tmp.snippet)
of tyArray:
# YYY: length(sideeffect) is optimized away incorrectly?
if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ)))
@@ -1913,13 +1932,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
var call = initLoc(locCall, e, OnHeap)
if not p.module.compileToCpp:
const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)"
call.r = ropecg(p.module, setLenPattern, [
call.snippet = ropecg(p.module, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
else:
const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)"
call.r = ropecg(p.module, setLenPattern, [
call.snippet = ropecg(p.module, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
@@ -1935,7 +1954,7 @@ proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
var b = initLocExpr(p, e[2])
var call = initLoc(locCall, e, OnHeap)
call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
call.snippet = ropecg(p.module, "#setLengthStr($1, $2)", [
rdLoc(a), rdLoc(b)])
genAssignment(p, a, call, {})
gcUsage(p.config, e)
@@ -2012,23 +2031,23 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
a = initLocExpr(p, ea)
b = initLoc(locExpr, e, OnUnknown)
if e[1].len > 0:
b.r = rope("(")
b.snippet = rope("(")
for i in 0..<e[1].len:
let it = e[1][i]
if it.kind == nkRange:
x = initLocExpr(p, it[0])
y = initLocExpr(p, it[1])
b.r.addf("$1 >= $2 && $1 <= $3",
b.snippet.addf("$1 >= $2 && $1 <= $3",
[rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
else:
x = initLocExpr(p, it)
b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
if i < e[1].len - 1: b.r.add(" || ")
b.r.add(")")
b.snippet.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
if i < e[1].len - 1: b.snippet.add(" || ")
b.snippet.add(")")
else:
# handle the case of an empty set
b.r = rope("0")
putIntoDest(p, d, e, b.r)
b.snippet = rope("0")
putIntoDest(p, d, e, b.snippet)
else:
assert(e[1].typ != nil)
assert(e[2].typ != nil)
@@ -2038,7 +2057,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
const
lookupOpr: array[mLeSet..mMinusSet, string] = [
lookupOpr: array[mLeSet..mXorSet, string] = [
"for ($1 = 0; $1 < $2; $1++) { $n" &
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
" if (!$3) break;}$n",
@@ -2048,7 +2067,8 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
"if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n",
"&",
"|",
"& ~"]
"& ~",
"^"]
var a, b: TLoc
var i: TLoc
var setType = skipTypes(e[1].typ, abstractVar)
@@ -2079,6 +2099,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mMulSet: binaryExpr(p, e, d, "($1 & $2)")
of mPlusSet: binaryExpr(p, e, d, "($1 | $2)")
of mMinusSet: binaryExpr(p, e, d, "($1 & ~ $2)")
of mXorSet: binaryExpr(p, e, d, "($1 ^ $2)")
of mInSet:
genInOp(p, e, d)
else: internalError(p.config, e.info, "genSetOp()")
@@ -2106,7 +2127,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
var a = initLocExpr(p, e[1])
var b = initLocExpr(p, e[2])
putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size]))
of mMulSet, mPlusSet, mMinusSet:
of mMulSet, mPlusSet, mMinusSet, mXorSet:
# we inline the simple for loop for better code generation:
i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
a = initLocExpr(p, e[1])
@@ -2147,6 +2168,8 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
elif etyp.kind == tyBool and srcTyp.kind in IntegralTypes:
putIntoDest(p, d, e, "(($1) != 0)" % [rdCharLoc(a)], a.storage)
elif etyp.kind == tyProc and srcTyp.kind == tyProc and sameBackendType(etyp, srcTyp):
expr(p, e[1], d)
else:
if etyp.kind == tyPtr:
# generates the definition of structs for casts like cast[ptr object](addr x)[]
@@ -2166,7 +2189,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
inc(p.labels)
var lbl = p.labels.rope
var tmp: TLoc = default(TLoc)
tmp.r = "LOC$1.source" % [lbl]
tmp.snippet = "LOC$1.source" % [lbl]
let destsize = getSize(p.config, destt)
let srcsize = getSize(p.config, srct)
@@ -2231,12 +2254,15 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
raiseInstr(p, p.s(cpsStmts))
linefmt p, cpsStmts, "}$n", []
putIntoDest(p, d, n, "(($1) ($2))" %
if sameBackendTypeIgnoreRange(dest, n[0].typ):
# don't cast so an address can be taken for `var` conversions
putIntoDest(p, d, n, "($1)" % [rdCharLoc(a)], a.storage)
else:
putIntoDest(p, d, n, "(($1) ($2))" %
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
proc genConv(p: BProc, e: PNode, d: var TLoc) =
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
if sameBackendType(destType, e[1].typ):
if ignoreConv(e):
expr(p, e[1], d)
else:
genSomeCast(p, e, d)
@@ -2311,7 +2337,7 @@ proc genWasMoved(p: BProc; n: PNode) =
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
proc genMove(p: BProc; n: PNode; d: var TLoc) =
var a: TLoc = initLocExpr(p, n[1].skipAddr)
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref})
if n.len == 4:
# generated by liftdestructors:
var src: TLoc = initLocExpr(p, n[2])
@@ -2345,8 +2371,13 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
else:
linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)])
else:
let flags = if not canMove(p, n[1], d): {needToCopy} else: {}
genAssignment(p, d, a, flags)
if n[1].kind == nkSym and isSinkParam(n[1].sym):
var tmp = getTemp(p, n[1].typ.skipTypes({tySink}))
genAssignment(p, tmp, a, {needToCopySinkParam})
genAssignment(p, d, tmp, {})
resetLoc(p, tmp)
else:
genAssignment(p, d, a, {})
resetLoc(p, a)
proc genDestroy(p: BProc; n: PNode) =
@@ -2454,7 +2485,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
var call = initLoc(locCall, e, OnHeap)
var dest = initLocExpr(p, e[1])
var b = initLocExpr(p, e[2])
call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
call.snippet = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
genAssignment(p, dest, call, {})
of mAppendStrStr: genStrAppend(p, e, d)
of mAppendSeqElem:
@@ -2518,7 +2549,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
let member =
if t.kind == tyTuple:
"Field" & rope(dotExpr[1].sym.position)
else: dotExpr[1].sym.loc.r
else: dotExpr[1].sym.loc.snippet
putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member])
of mChr: genSomeCast(p, e, d)
of mOrd: genOrd(p, e, d)
@@ -2533,7 +2564,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mSetLengthStr: genSetLengthStr(p, e, d)
of mSetLengthSeq: genSetLengthSeq(p, e, d)
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
mInSet:
mInSet, mXorSet:
genSetOp(p, e, d, op)
of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat:
var opr = e[0].sym
@@ -2541,8 +2572,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
# - not sure, and it wouldn't work if the symbol behind the magic isn't
# somehow forward-declared from some other usage, but it is *possible*
if lfNoDecl notin opr.loc.flags:
let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r)
assert prc != nil, $opr.loc.r
let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.snippet)
assert prc != nil, $opr.loc.snippet
# HACK:
# Explicitly add this proc as declared here so the cgsym call doesn't
# add a forward declaration - without this we could end up with the same
@@ -2555,7 +2586,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id)
# Make the function behind the magic get actually generated - this will
# not lead to a forward declaration! The genCall will lead to one.
cgsym(p.module, $opr.loc.r)
cgsym(p.module, $opr.loc.snippet)
# make sure we have pointer-initialising code for hot code reloading
if not wasDeclared and p.hcrOn:
p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
@@ -2684,7 +2715,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
var it = n[i]
if it.kind == nkExprColonExpr: it = it[1]
rec = initLoc(locExpr, it, dest[].storage)
rec.r = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
rec.snippet = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
rec.flags.incl(lfEnforceDeref)
expr(p, it, rec)
@@ -2734,12 +2765,12 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
var lit = newRopeAppender()
intLiteral(i, lit)
arr.r = "$1[$2]" % [rdLoc(d), lit]
arr.snippet = "$1[$2]" % [rdLoc(d), lit]
expr(p, n[i], arr)
proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) =
requestConstImpl(p, sym)
assert((sym.loc.r != "") and (sym.loc.t != nil))
assert((sym.loc.snippet != "") and (sym.loc.t != nil))
putLocIntoDest(p, d, sym.loc)
template genStmtListExprImpl(exprOrStmt) {.dirty.} =
@@ -2871,24 +2902,24 @@ proc genConstSetup(p: BProc; sym: PSym): bool =
result = lfNoDecl notin sym.loc.flags
proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) =
if sym.loc.r == "":
if sym.loc.snippet == "":
if not genConstSetup(p, sym): return
assert(sym.loc.r != "", $sym.name.s & $sym.itemId)
assert(sym.loc.snippet != "", $sym.name.s & $sym.itemId)
if m.hcrOn:
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]);
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]);
m.initProc.procSec(cpsLocals).addf(
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(q, sym)])
else:
let headerDecl = "extern NIM_CONST $1 $2;$n" %
[getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]
[getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]
m.s[cfsData].add(headerDecl)
if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
p.module.g.generatedHeader.s[cfsData].add(headerDecl)
proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
# add a suffix for hcr - will later init the global pointer with this data
let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r
let actualConstName = if q.hcrOn: sym.loc.snippet & "_const" else: sym.loc.snippet
var data = newRopeAppender()
data.addf("N_LIB_PRIVATE NIM_CONST $1 $2 = ",
[getTypeDesc(q, sym.typ), actualConstName])
@@ -2897,16 +2928,16 @@ proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
q.s[cfsData].add data
if q.hcrOn:
# generate the global pointer with the real name
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.r])
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.snippet])
# register it (but ignore the boolean result of hcrRegisterGlobal)
q.initProc.procSec(cpsLocals).addf(
"\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
[getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)])
[getModuleDllPath(q, sym), sym.loc.snippet, rdLoc(sym.loc)])
# always copy over the contents of the actual constant with the _const
# suffix ==> this means that the constant is reloadable & updatable!
q.initProc.procSec(cpsLocals).add(ropecg(q,
"\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
[sym.loc.r, actualConstName, rdLoc(sym.loc)]))
[sym.loc.snippet, actualConstName, rdLoc(sym.loc)]))
proc genConstStmt(p: BProc, n: PNode) =
# This code is only used in the new DCE implementation.
@@ -2946,7 +2977,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
genProcPrototype(p.module, sym)
else:
genProc(p.module, sym)
if sym.loc.r == "" or sym.loc.lode == nil:
if sym.loc.snippet == "" or sym.loc.lode == nil:
internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
of skConst:
@@ -2956,7 +2987,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
putIntoDest(p, d, n, lit, OnStatic)
elif useAliveDataFromDce in p.module.flags:
genConstHeader(p.module, p.module, p, sym)
assert((sym.loc.r != "") and (sym.loc.t != nil))
assert((sym.loc.snippet != "") and (sym.loc.t != nil))
putLocIntoDest(p, d, sym.loc)
else:
genComplexConst(p, sym, d)
@@ -2971,14 +3002,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if sfCompileTime in sym.flags:
genSingleVar(p, sym, n, astdef(sym))
if sym.loc.r == "" or sym.loc.t == nil:
if sym.loc.snippet == "" or sym.loc.t == nil:
#echo "FAILED FOR PRCO ", p.prc.name.s
#echo renderTree(p.prc.ast, {renderIds})
internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
if sfThread in sym.flags:
accessThreadLocalVar(p, sym)
if emulatedThreadVars(p.config):
putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.snippet)
else:
putLocIntoDest(p, d, sym.loc)
else:
@@ -2986,17 +3017,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of skTemp:
when false:
# this is more harmful than helpful.
if sym.loc.r == "":
if sym.loc.snippet == "":
# we now support undeclared 'skTemp' variables for easier
# transformations in other parts of the compiler:
assignLocalVar(p, n)
if sym.loc.r == "" or sym.loc.t == nil:
if sym.loc.snippet == "" or sym.loc.t == nil:
#echo "FAILED FOR PRCO ", p.prc.name.s
#echo renderTree(p.prc.ast, {renderIds})
internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id)
putLocIntoDest(p, d, sym.loc)
of skParam:
if sym.loc.r == "" or sym.loc.t == nil:
if sym.loc.snippet == "" or sym.loc.t == nil:
# echo "FAILED FOR PRCO ", p.prc.name.s
# debug p.prc.typ.n
# echo renderTree(p.prc.ast, {renderIds})
@@ -3057,16 +3088,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkObjConstr: genObjConstr(p, n, d)
of nkCast: genCast(p, n, d)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d)
of nkHiddenAddr:
if n[0].kind == nkDerefExpr:
# addr ( deref ( x )) --> x
var x = n[0][0]
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
x.typ = n.typ
expr(p, x, d)
return
genAddr(p, n, d)
of nkAddr: genAddr(p, n, d)
of nkAddr, nkHiddenAddr: genAddr(p, n, d)
of nkBracketExpr: genBracketExpr(p, n, d)
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
of nkDotExpr: genRecordField(p, n, d)
@@ -3086,7 +3108,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkLambdaKinds:
var sym = n[namePos].sym
genProc(p.module, sym)
if sym.loc.r == "" or sym.loc.lode == nil:
if sym.loc.snippet == "" or sym.loc.lode == nil:
internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
of nkClosure: genClosure(p, n, d)
@@ -3097,6 +3119,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkConstSection:
if useAliveDataFromDce in p.module.flags:
genConstStmt(p, n)
else: # enforce addressable consts for exportc
let m = p.module
for it in n:
let symNode = skipPragmaExpr(it[0])
if symNode.kind == nkSym and sfExportc in symNode.sym.flags:
requestConstImpl(p, symNode.sym)
# else: consts generated lazily on use
of nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
of nkCaseStmt: genCase(p, n, d)
@@ -3117,7 +3145,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if ex.kind != nkEmpty:
genLineDir(p, n)
var a: TLoc = initLocExprSingleUse(p, ex)
line(p, cpsStmts, "(void)(" & a.r & ");\L")
line(p, cpsStmts, "(void)(" & a.snippet & ");\L")
of nkAsmStmt: genAsmStmt(p, n)
of nkTryStmt, nkHiddenTryStmt:
case p.config.exc

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@
import
ast, types, msgs, wordrecg,
platform, trees, options, cgendata, mangleutils
platform, trees, options, cgendata, mangleutils, renderer
import std/[hashes, strutils, formatfloat]
@@ -120,14 +120,14 @@ 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): string =
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string =
#Module::Type
var name = s.name.s
var name = s.name.s & extra
if makeUnique:
name = makeUnique(m, s, name)
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
proc encodeType*(m: BModule; t: PType): string =
proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
result = ""
var kindName = ($t.kind)[2..^1]
kindName[0] = toLower($kindName[0])[0]
@@ -138,10 +138,10 @@ proc encodeType*(m: BModule; t: PType): string =
result = encodeName(t[0].sym.name.s)
result.add "I"
for i in 1..<t.len - 1:
result.add encodeType(m, t[i])
result.add encodeType(m, t[i], staticLists)
result.add "E"
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
tyPtr, tyRef, tyVar, tyLent, tySink, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
result =
case t.kind:
of tySequence: encodeName("seq")
@@ -150,8 +150,13 @@ proc encodeType*(m: BModule; t: PType): string =
for i in 0..<t.len:
let s = t[i]
if s.isNil: continue
result.add encodeType(m, s)
result.add encodeType(m, s, staticLists)
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}:
@@ -164,7 +169,7 @@ proc encodeType*(m: BModule; t: PType): string =
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
result = encodeName(kindName)
of tyAlias, tyInferred, tyOwned:
result = encodeType(m, t.elementType)
result = encodeType(m, t.elementType, staticLists)
else:
assert false, "encodeType " & $t.kind

View File

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

View File

@@ -11,7 +11,7 @@
import
ast, ropes, options,
ndi, lineinfos, pathutils, modulegraphs
lineinfos, pathutils, modulegraphs
import std/[intsets, tables, sets]
@@ -88,7 +88,7 @@ type
options*: TOptions # options that should be used for code
# generation; this is the same as prc.options
# unless prc == nil
optionsStack*: seq[TOptions]
optionsStack*: seq[(TOptions, TNoteKinds)]
module*: BModule # used to prevent excessive parameter passing
withinLoop*: int # > 0 if we are within a loop
splitDecls*: int # > 0 if we are in some context for C++ that
@@ -172,7 +172,6 @@ type
# OpenGL wrapper
sigConflicts*: CountTable[SigHash]
g*: BModuleList
ndi*: NdiFile
template config*(m: BModule): ConfigRef = m.g.config
template config*(p: BProc): ConfigRef = p.module.g.config

View File

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

View File

@@ -36,13 +36,6 @@
# else:
# return
# The transformation should play well with lambdalifting, however depending
# on situation, it can be called either before or after lambdalifting
# transformation. As such we behave slightly differently, when accessing
# iterator state, or using temp variables. If lambdalifting did not happen,
# we just create local variables, so that they will be lifted further on.
# Otherwise, we utilize existing env, created by lambdalifting.
# Lambdalifting treats :state variable specially, it should always end up
# as the first field in env. Currently C codegen depends on this behavior.
@@ -151,7 +144,6 @@ type
Ctx = object
g: ModuleGraph
fn: PSym
stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
tmpResultSym: PSym # Used when we return, but finally has to interfere
unrollFinallySym: PSym # Indicates that we're unrolling finally states (either exception happened or premature return)
curExcSym: PSym # Current exception
@@ -168,18 +160,18 @@ type
nearestFinally: int # Index of the nearest finally block. For try/except it
# is their finally. For finally it is parent finally. Otherwise -1
idgen: IdGenerator
varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states
const
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
nkCommentStmt, nkMixinStmt, nkBindStmt, nkTypeOfExpr} + procDefs
emptyStateLabel = -1
localNotSeen = -1
localRequiresLifting = -2
proc newStateAccess(ctx: var Ctx): PNode =
if ctx.stateVarSym.isNil:
result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)),
getStateField(ctx.g, ctx.fn), ctx.fn.info)
else:
result = newSymNode(ctx.stateVarSym)
result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)),
getStateField(ctx.g, ctx.fn), ctx.fn.info)
proc newStateAssgn(ctx: var Ctx, toValue: PNode): PNode =
# Creates state assignment:
@@ -195,24 +187,17 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.idgen, ctx.fn, ctx.fn.info)
result.typ = typ
result.flags.incl sfNoInit
assert(not typ.isNil)
assert(not typ.isNil, "Env var needs a type")
if not ctx.stateVarSym.isNil:
# We haven't gone through labmda lifting yet, so just create a local var,
# it will be lifted later
if ctx.tempVars.isNil:
ctx.tempVars = newNodeI(nkVarSection, ctx.fn.info)
addVar(ctx.tempVars, newSymNode(result))
else:
let envParam = getEnvParam(ctx.fn)
# let obj = envParam.typ.lastSon
result = addUniqueField(envParam.typ.elementType, result, ctx.g.cache, ctx.idgen)
let envParam = getEnvParam(ctx.fn)
# let obj = envParam.typ.lastSon
result = addUniqueField(envParam.typ.elementType, result, ctx.g.cache, ctx.idgen)
proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
if ctx.stateVarSym.isNil:
result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), s, ctx.fn.info)
else:
result = newSymNode(s)
result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), s, ctx.fn.info)
proc newTempVarAccess(ctx: Ctx, s: PSym): PNode =
result = newSymNode(s, ctx.fn.info)
proc newTmpResultAccess(ctx: var Ctx): PNode =
if ctx.tmpResultSym.isNil:
@@ -255,9 +240,18 @@ proc addGotoOut(n: PNode, gotoOut: PNode): PNode =
if result.len == 0 or result[^1].kind != nkGotoState:
result.add(gotoOut)
proc newTempVar(ctx: var Ctx, typ: PType): PSym =
result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ)
proc newTempVarDef(ctx: Ctx, s: PSym, initialValue: PNode): PNode =
var v = initialValue
if v == nil:
v = ctx.g.emptyNode
newTree(nkVarSection, newTree(nkIdentDefs, newSymNode(s), ctx.g.emptyNode, v))
proc newTempVar(ctx: var Ctx, typ: PType, parent: PNode, initialValue: PNode = nil): PSym =
result = newSym(skVar, getIdent(ctx.g.cache, ":tmpSlLower" & $ctx.tempVarId), ctx.idgen, ctx.fn, ctx.fn.info)
inc ctx.tempVarId
result.typ = typ
assert(not typ.isNil, "Temp var needs a type")
parent.add(ctx.newTempVarDef(result, initialValue))
proc hasYields(n: PNode): bool =
# TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt.
@@ -317,12 +311,12 @@ proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode =
let curExc = ctx.newCurExcAccess()
curExc.info = info
let nilnode = newNode(nkNilLit)
nilnode.typ = curExc.typ
nilnode.typ() = curExc.typ
result = newTree(nkAsgn, curExc, nilnode)
proc newOr(g: ModuleGraph, a, b: PNode): PNode {.inline.} =
result = newTree(nkCall, newSymNode(g.getSysMagic(a.info, "or", mOr)), a, b)
result.typ = g.getSysType(a.info, tyBool)
result.typ() = g.getSysType(a.info, tyBool)
result.info = a.info
proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
@@ -340,7 +334,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
newSymNode(g.getSysMagic(c.info, "of", mOf)),
g.callCodegenProc("getCurrentException"),
c[i])
nextCond.typ = ctx.g.getSysType(c.info, tyBool)
nextCond.typ() = ctx.g.getSysType(c.info, tyBool)
nextCond.info = c.info
if cond.isNil:
@@ -429,21 +423,20 @@ proc exprToStmtList(n: PNode): tuple[s, res: PNode] =
result.res = n
proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
proc newTempVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
if isEmptyType(v.typ):
result = v
else:
result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v)
result = newTree(nkFastAsgn, ctx.newTempVarAccess(s), v)
result.info = v.info
proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
if input.kind == nkStmtListExpr:
let (st, res) = exprToStmtList(input)
output.add(st)
output.add(ctx.newEnvVarAsgn(sym, res))
output.add(ctx.newTempVarAsgn(sym, res))
else:
output.add(ctx.newEnvVarAsgn(sym, input))
output.add(ctx.newTempVarAsgn(sym, input))
proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode =
result = newNodeI(nkStmtList, exprBody.info)
@@ -451,11 +444,17 @@ proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode =
proc newNotCall(g: ModuleGraph; e: PNode): PNode =
result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e)
result.typ = g.getSysType(e.info, tyBool)
result.typ() = g.getSysType(e.info, tyBool)
proc boolLit(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
result = newIntLit(g, info, ord value)
result.typ = getSysType(g, info, tyBool)
result.typ() = getSysType(g, info, tyBool)
proc captureVar(c: var Ctx, s: PSym) =
if c.varStates.getOrDefault(s.itemId) != localRequiresLifting:
c.varStates[s.itemId] = localRequiresLifting # Mark this variable for lifting
let e = getEnvParam(c.fn)
discard addField(e.typ.elementType, s, c.g.cache, c.idgen)
proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
result = n
@@ -487,7 +486,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
result = newNodeI(nkStmtListExpr, n.info)
if n.typ.isNil: internalError(ctx.g.config, "lowerStmtListExprs: constr typ.isNil")
result.typ = n.typ
result.typ() = n.typ
for i in 0..<n.len:
case n[i].kind
@@ -513,9 +512,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
var tmp: PSym = nil
let isExpr = not isEmptyType(n.typ)
if isExpr:
tmp = ctx.newTempVar(n.typ)
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
tmp = ctx.newTempVar(n.typ, result)
else:
result = newNodeI(nkStmtList, n.info)
@@ -566,7 +565,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
else:
internalError(ctx.g.config, "lowerStmtListExpr(nkIf): " & $branch.kind)
if isExpr: result.add(ctx.newEnvVarAccess(tmp))
if isExpr: result.add(ctx.newTempVarAccess(tmp))
of nkTryStmt, nkHiddenTryStmt:
var ns = false
@@ -579,8 +578,8 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if isExpr:
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
let tmp = ctx.newTempVar(n.typ)
result.typ() = n.typ
let tmp = ctx.newTempVar(n.typ, result)
n[0] = ctx.convertExprBodyToAsgn(n[0], tmp)
for i in 1..<n.len:
@@ -596,7 +595,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
else:
internalError(ctx.g.config, "lowerStmtListExpr(nkTryStmt): " & $branch.kind)
result.add(n)
result.add(ctx.newEnvVarAccess(tmp))
result.add(ctx.newTempVarAccess(tmp))
of nkCaseStmt:
var ns = false
@@ -609,9 +608,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
let isExpr = not isEmptyType(n.typ)
if isExpr:
let tmp = ctx.newTempVar(n.typ)
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
let tmp = ctx.newTempVar(n.typ, result)
if n[0].kind == nkStmtListExpr:
let (st, ex) = exprToStmtList(n[0])
@@ -628,7 +627,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
else:
internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind)
result.add(n)
result.add(ctx.newEnvVarAccess(tmp))
result.add(ctx.newTempVarAccess(tmp))
elif n[0].kind == nkStmtListExpr:
result = newNodeI(nkStmtList, n.info)
let (st, ex) = exprToStmtList(n[0])
@@ -647,7 +646,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if isExpr:
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
else:
result = newNodeI(nkStmtList, n.info)
@@ -658,10 +657,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
result.add(st)
cond = ex
let tmp = ctx.newTempVar(cond.typ)
result.add(ctx.newEnvVarAsgn(tmp, cond))
let tmp = ctx.newTempVar(cond.typ, result, cond)
# result.add(ctx.newTempVarAsgn(tmp, cond))
var check = ctx.newEnvVarAccess(tmp)
var check = ctx.newTempVarAccess(tmp)
if n[0].sym.magic == mOr:
check = ctx.g.newNotCall(check)
@@ -671,12 +670,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
let (st, ex) = exprToStmtList(cond)
ifBody.add(st)
cond = ex
ifBody.add(ctx.newEnvVarAsgn(tmp, cond))
ifBody.add(ctx.newTempVarAsgn(tmp, cond))
let ifBranch = newTree(nkElifBranch, check, ifBody)
let ifNode = newTree(nkIfStmt, ifBranch)
result.add(ifNode)
result.add(ctx.newEnvVarAccess(tmp))
result.add(ctx.newTempVarAccess(tmp))
else:
for i in 0..<n.len:
if n[i].kind == nkStmtListExpr:
@@ -685,9 +684,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
n[i] = ex
if n[i].kind in nkCallKinds: # XXX: This should better be some sort of side effect tracking
let tmp = ctx.newTempVar(n[i].typ)
result.add(ctx.newEnvVarAsgn(tmp, n[i]))
n[i] = ctx.newEnvVarAccess(tmp)
let tmp = ctx.newTempVar(n[i].typ, result, n[i])
# result.add(ctx.newTempVarAsgn(tmp, n[i]))
n[i] = ctx.newTempVarAccess(tmp)
result.add(n)
@@ -703,6 +702,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
let (st, ex) = exprToStmtList(c[^1])
result.add(st)
c[^1] = ex
for i in 0 .. c.len - 3:
if c[i].kind == nkSym:
let s = c[i].sym
if sfForceLift in s.flags:
ctx.captureVar(s)
result.add(varSect)
of nkDiscardStmt, nkReturnStmt, nkRaiseStmt:
@@ -727,7 +732,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if ns:
needsSplit = true
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
let (st, ex) = exprToStmtList(n[^1])
result.add(st)
n[^1] = ex
@@ -797,7 +802,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if ns:
needsSplit = true
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
let (st, ex) = exprToStmtList(n[0])
result.add(st)
n[0] = ex
@@ -809,10 +814,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
if ns:
needsSplit = true
result = newNodeI(nkStmtListExpr, n.info)
result.typ = n.typ
result.typ() = n.typ
let (st, ex) = exprToStmtList(n[1])
n.transitionSonsKind(nkBlockStmt)
n.typ = nil
n.typ() = nil
n[1] = st
result.add(n)
result.add(ex)
@@ -833,9 +838,9 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
# raise
let curExc = ctx.newCurExcAccess()
let nilnode = newNode(nkNilLit)
nilnode.typ = curExc.typ
nilnode.typ() = curExc.typ
let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
cmp.typ = ctx.g.getSysType(info, tyBool)
cmp.typ() = ctx.g.getSysType(info, tyBool)
let retStmt =
if ctx.nearestFinally == 0:
@@ -1180,11 +1185,11 @@ proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: P
proc createExceptionTable(ctx: var Ctx): PNode {.inline.} =
result = newNodeI(nkBracket, ctx.fn.info)
result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.idgen, ctx.fn)
result.typ() = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.idgen, ctx.fn)
for i in ctx.exceptionTable:
let elem = newIntNode(nkIntLit, i)
elem.typ = ctx.g.getSysType(ctx.fn.info, tyInt16)
elem.typ() = ctx.g.getSysType(ctx.fn.info, tyInt16)
result.add(elem)
proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
@@ -1207,7 +1212,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
let getNextState = newTree(nkBracketExpr,
ctx.createExceptionTable(),
ctx.newStateAccess())
getNextState.typ = intTyp
getNextState.typ() = intTyp
# :state = exceptionTable[:state]
result.add(ctx.newStateAssgn(getNextState))
@@ -1218,7 +1223,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
ctx.g.getSysMagic(info, "==", mEqI).newSymNode(),
ctx.newStateAccess(),
newIntTypeNode(0, intTyp))
cond.typ = boolTyp
cond.typ() = boolTyp
let raiseStmt = newTree(nkRaiseStmt, ctx.g.emptyNode)
let ifBranch = newTree(nkElifBranch, cond, raiseStmt)
@@ -1231,7 +1236,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
newIntTypeNode(0, intTyp),
ctx.newStateAccess())
cond.typ = boolTyp
cond.typ() = boolTyp
let asgn = newTree(nkAsgn, ctx.newUnrollFinallyAccess(info), cond)
result.add(asgn)
@@ -1242,12 +1247,12 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
ctx.newStateAccess(),
newIntTypeNode(0, intTyp))
cond.typ = boolTyp
cond.typ() = boolTyp
let negateState = newTree(nkCall,
ctx.g.getSysMagic(info, "-", mUnaryMinusI).newSymNode,
ctx.newStateAccess())
negateState.typ = intTyp
negateState.typ() = intTyp
let ifBranch = newTree(nkElifBranch, cond, ctx.newStateAssgn(negateState))
let ifStmt = newTree(nkIfStmt, ifBranch)
@@ -1279,13 +1284,6 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode =
result.info = n.info
let localVars = newNodeI(nkStmtList, n.info)
if not ctx.stateVarSym.isNil:
let varSect = newNodeI(nkVarSection, n.info)
addVar(varSect, newSymNode(ctx.stateVarSym))
localVars.add(varSect)
if not ctx.tempVars.isNil:
localVars.add(ctx.tempVars)
let blockStmt = newNodeI(nkBlockStmt, n.info)
blockStmt.add(newSymNode(ctx.stateLoopLabel))
@@ -1433,15 +1431,81 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
for i in 0 ..< n.len:
result[i] = preprocess(c, n[i])
proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) =
case n.kind
of nkSym:
let s = n.sym
if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn:
let vs = c.varStates.getOrDefault(s.itemId, localNotSeen)
if vs == localNotSeen: # First seing this variable
c.varStates[s.itemId] = stateIdx
elif vs == localRequiresLifting:
discard # Sym already marked
elif vs != stateIdx:
c.captureVar(s)
of nkReturnStmt:
if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
# we have a `result = result` expression produced by the closure
# transform, let's not touch the LHS in order to make the lifting pass
# correct when `result` is lifted
detectCapturedVars(c, n[0][1], stateIdx)
else:
detectCapturedVars(c, n[0], stateIdx)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt,
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
discard
of nkLambdaKinds, nkIteratorDef:
if n.typ != nil:
detectCapturedVars(c, n[namePos], stateIdx)
else:
for i in 0 ..< n.safeLen:
detectCapturedVars(c, n[i], stateIdx)
proc detectCapturedVars(c: var Ctx) =
for i, s in c.states:
detectCapturedVars(c, s.body, i)
proc liftLocals(c: var Ctx, n: PNode): PNode =
result = n
case n.kind
of nkSym:
let s = n.sym
if c.varStates.getOrDefault(s.itemId) == localRequiresLifting:
# lift
let e = getEnvParam(c.fn)
let field = getFieldFromObj(e.typ.elementType, s)
assert(field != nil)
result = rawIndirectAccess(newSymNode(e), field, n.info)
# elif c.varStates.getOrDefault(s.itemId, localNotSeen) != localNotSeen:
# echo "Not lifting ", s.name.s
of nkReturnStmt:
if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
# we have a `result = result` expression produced by the closure
# transform, let's not touch the LHS in order to make the lifting pass
# correct when `result` is lifted
n[0][1] = liftLocals(c, n[0][1])
else:
n[0] = liftLocals(c, n[0])
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt,
nkTypeOfExpr, nkMixinStmt, nkBindStmt,
nkLambdaKinds, nkIteratorDef:
discard
else:
for i in 0 ..< n.safeLen:
n[i] = liftLocals(c, n[i])
proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode =
var ctx = Ctx(g: g, fn: fn, idgen: idgen)
if getEnvParam(fn).isNil:
# Lambda lifting was not done yet. Use temporary :state sym, which will
# be handled specially by lambda lifting. Local temp vars (if needed)
# should follow the same logic.
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info)
ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
# The transformation should always happen after at least partial lambdalifting
# is performed, so that the closure iter environment is always created upfront.
doAssert(getEnvParam(fn) != nil, "Env param not created before iter transformation")
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info)
var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
var n = preprocess(pc, n.toStmtList)
@@ -1466,6 +1530,10 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
let caseDispatcher = newTreeI(nkCaseStmt, n.info,
ctx.newStateAccess())
# Lamdalifting will not touch our locals, it is our responsibility to lift those that
# need it.
detectCapturedVars(ctx)
for s in ctx.states:
let body = ctx.transformStateAssignments(s.body)
caseDispatcher.add newTreeI(nkOfBranch, body.info, g.newIntLit(body.info, s.label), body)
@@ -1473,6 +1541,7 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
caseDispatcher.add newTreeI(nkElse, n.info, newTreeI(nkReturnStmt, n.info, g.emptyNode))
result = wrapIntoStateLoop(ctx, caseDispatcher)
result = liftLocals(ctx, result)
when false:
echo "TRANSFORM TO STATES: "
@@ -1481,3 +1550,5 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
echo "exception table:"
for i, e in ctx.exceptionTable:
echo i, " -> ", e
echo "ENV: ", renderTree(getEnvParam(fn).typ.elementType.n)

View File

@@ -24,7 +24,7 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils]
import
msgs, options, nversion, condsyms, extccomp, platform,
wordrecg, nimblecmd, lineinfos, pathutils
@@ -203,13 +203,14 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
else: invalidCmdLineOption(conf, pass, orig, info)
let isSomeHint = state in {wHint, wHintAsError}
let isSomeWarning = state in {wWarning, wWarningAsError}
template findNote(noteMin, noteMax, name) =
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
let x = findStr(noteMin, noteMax, id, errUnknown)
if x != errUnknown: notes = {TNoteKind(x)}
else:
if isSomeHint:
message(conf, info, hintUnknownHint, id)
if isSomeHint or isSomeWarning:
message(conf, info, warnUnknownNotes, "unknown $#: $#" % [name, id])
else:
localError(conf, info, "unknown $#: $#" % [name, id])
case id.normalize
@@ -248,6 +249,7 @@ const
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
template warningOptionNoop(switch: string) =
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
@@ -303,6 +305,12 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
else:
result = false
localError(conf, info, errInvalidExceptionSystem % arg)
of "experimental":
try:
result = conf.features.contains parseEnum[Feature](arg)
except ValueError:
result = false
localError(conf, info, errInvalidFeatureButXFound % arg)
else:
result = false
invalidCmdLineOption(conf, passCmd1, switch, info)
@@ -451,7 +459,7 @@ template handleStdinOrCmdInput =
conf.outDir = getNimcacheDir(conf)
proc handleStdinInput*(conf: ConfigRef) =
conf.projectName = "stdinfile"
conf.projectName = conf.stdinFile.string
conf.projectIsStdin = true
handleStdinOrCmdInput()
@@ -462,7 +470,6 @@ proc handleCmdInput*(conf: ConfigRef) =
proc parseCommand*(command: string): Command =
case command.normalize
of "c", "cc", "compile", "compiletoc": cmdCompileToC
of "nir": cmdCompileToNir
of "cpp", "compiletocpp": cmdCompileToCpp
of "objc", "compiletooc": cmdCompileToOC
of "js", "compiletojs": cmdCompileToJS
@@ -500,7 +507,6 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
of cmdCompileToCpp: conf.backend = backendCpp
of cmdCompileToOC: conf.backend = backendObjc
of cmdCompileToJS: conf.backend = backendJs
of cmdCompileToNir: conf.backend = backendNir
else: discard
proc setCommandEarly*(conf: ConfigRef, command: string) =
@@ -914,6 +920,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
discard parseSaturatedNatural(arg, value)
if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
conf.maxLoopIterationsVM = value
of "maxcalldepthvm":
expectArg(conf, switch, arg, pass, info)
var value: int = 2_000
discard parseSaturatedNatural(arg, value)
if value <= 0: localError(conf, info, "maxCallDepthVM must be a positive integer greater than zero")
conf.maxCallDepthVM = value
of "errormax":
expectArg(conf, switch, arg, pass, info)
# Note: `nim check` (etc) can overwrite this.
@@ -923,6 +935,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
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)
of "verbosity":
expectArg(conf, switch, arg, pass, info)
let verbosity = parseInt(arg)

View File

@@ -11,7 +11,7 @@
## for details. Note this is a first implementation and only the "Concept matching"
## section has been implemented.
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
import std/intsets
@@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem):
import std/assertions
const
logBindings = false
logBindings = when defined(debugConcepts): true else: false
## Code dealing with Concept declarations
## --------------------------------------
@@ -70,88 +70,244 @@ proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
## ----------------
type
MatchFlags* = enum
mfDontBind # Do not bind generic parameters
mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand
MatchCon = object ## Context we pass around during concept matching.
inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
## that turned out to be wrong.
bindings: LayeredIdTable
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
depthCount = 0
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.
for i in 0..<m.inferred.len:
if m.inferred[i][0] == key: return m.inferred[i][1]
return nil
result = m.bindings.lookup(key)
if result == nil:
result = key
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
const
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred}
proc matchType(c: PContext; f, a: PType; 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 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
if m.depthCount > 0:
# concepts that are more then 2 levels deep are treated like
# tyAnything to stop dependencies from getting out of control
return true
var efPot = potentialImpl
if potentialImpl.isSelf:
if m.concpt.n == concpt.n:
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
inc m.depthCount
result = processConcept(c, concpt, invocation, oldBindings, m)
dec m.depthCount
m.potentialImplementation = oldPotentialImplementation
m.concpt = oldConcept
m.bindings = oldBindings
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 matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
## The heart of the concept matching process. 'f' is the formal parameter of some
## routine inside the concept that we're looking for. 'a' is the formal parameter
## of a routine that might match.
const
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
var
a = ao
f = fo
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
case f.kind
of tyAlias:
result = matchType(c, f.skipModifier, a, m)
of tyTypeDesc:
if isSelf(f):
#let oldLen = m.inferred.len
result = matchType(c, a, m.potentialImplementation, m)
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
#m.inferred.setLen oldLen
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
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)
else:
if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
if f.hasElementType:
if a.kind == tyTypeDesc:
if not(a.hasElementType) or a.elementType.kind == tyNone:
result = true
elif f.hasElementType:
result = matchType(c, f.elementType, a.elementType, m)
else:
result = true # both lack it
else:
result = false
of tyGenericInvocation:
result = false
if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
for i in FirstGenericParamAt ..< f.kidsLen:
if not matchType(c, f[i], a[i], m): return false
return true
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.hasElementType and f.elementType.kind != tyNone:
# also check the generic's constraints:
let oldLen = m.inferred.len
result = matchType(c, f.elementType, 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, last ak))
result = true
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
else:
result = false
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
of tyVar, tySink, tyLent, tyOwned:
# modifiers in the concept must be there in the actual implementation
# too but not vice versa.
@@ -159,70 +315,119 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
result = matchType(c, f.elementType, a.elementType, m)
elif m.magic == mArrPut:
result = matchType(c, f.elementType, a, m)
else:
result = false
of tyEnum, tyObject, tyDistinct:
result = sameType(f, a)
if a.kind in ignorableForArgType:
result = matchType(c, f, a.skipTypes(ignorableForArgType), m)
else:
if a.kind == tyGenericInst:
# tyOr does this to generic typeclasses
result = a.base.sym == f.sym
else:
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 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:
# ^ XXX Rewrite this logic, it's more complex than it needs to be.
result = false
let ak = a.skipTypes(ignorableForArgType - {f.kind})
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
for i in 0..<ak.kidsLen:
if not matchType(c, f[i], ak[i], m): return false
return true
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 f.kids:
for aa in a.kids:
let oldLenB = m.inferred.len
let r = matchType(c, ff, aa, m)
if r:
inc covered
break
m.inferred.setLen oldLenB
result = covered >= a.kidsLen
if not result:
m.inferred.setLen oldLen
(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:
result = false
for ff in f.kids:
result = matchType(c, ff, a, m)
if result: break # and remember the binding!
m.inferred.setLen oldLen
let ak = a.skipTypes(ignorableForArgType - {f.kind})
if ak.kind == f.kind and 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
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:
let oldLen = m.inferred.len
m.bindings = m.bindings.newTypeMapLayer()
result = not matchType(c, f.elementType, a, m)
m.inferred.setLen oldLen
of tyAnything:
m.bindings.setToPreviousLayer()
of tyAnd:
m.bindings = m.bindings.newTypeMapLayer()
result = true
of tyOrdinal:
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
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
of tyOr:
if a.kind == tyOr:
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)
if r:
inc covered
break
m.bindings.setToPreviousLayer()
result = covered >= a.kidsLen
else:
for ff in f.kids:
m.bindings = m.bindings.newTypeMapLayer()
result = matchType(c, ff, a, m)
if result: break # and remember the binding!
m.bindings.setToPreviousLayer()
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
@@ -232,30 +437,38 @@ proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
elif a == nil:
result = false
else:
result = matchType(c, f, a, m)
result = checkConstraint(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.
let oldLen = m.inferred.len
m.bindings = m.bindings.newTypeMapLayer()
let can = candidate.typ.n
let con = n[0].sym.typ.n
let con = defSignatureType(n).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 matchType(c, con[i].typ, can[i].typ, m):
m.inferred.setLen oldLen
if not checkConstraint(c, con[i].typ, can[i].typ, m):
m.bindings.setToPreviousLayer()
return false
if not matchReturnType(c, n[0].sym.typ.returnType, candidate.typ.returnType, m):
m.inferred.setLen oldLen
if not matchReturnType(c, n.defSignatureType.returnType, candidate.typ.returnType, m):
m.bindings.setToPreviousLayer()
return false
# all other parameters have to be optional parameters:
@@ -263,7 +476,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.inferred.setLen oldLen
m.bindings.setToPreviousLayer()
return false
return true
@@ -271,12 +484,14 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
## 'n' is the nkProcDef or similar from the concept that we try to match.
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
for candidate in candidates:
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
m.magic = candidate.magic
if matchSym(c, candidate, n, m): return true
result = false
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
for candidate in candidates:
m.magic = candidate.magic
if matchSym(c, candidate, n, m):
result = true
break
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
## Traverse the concept's AST ('n') and see if every declaration inside 'n'
@@ -309,7 +524,48 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
# error was reported earlier.
result = false
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool =
proc fixBindings(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:
when logBindings: echo "Invocation bind: ", thisSym, " ", found
bindings.put(thisSym, found)
# 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 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 and mfDontBind notin m.flags:
fixBindings(bindings, concpt, invocation, m)
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType, flags: set[MatchFlags] = {}): bool =
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
@@ -318,26 +574,16 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; i
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
## we do not add any bindings at all on an unsuccessful match!
var m = MatchCon(inferred: @[], potentialImplementation: arg)
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.kidsLen == arg.kidsLen-1:
# bind even more generic parameters
assert invocation.kind == tyGenericInvocation
for i in FirstGenericParamAt ..< invocation.kidsLen:
bindings.idTablePut(invocation[i], arg[i])
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags)
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)

View File

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

View File

@@ -439,8 +439,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 first branch.
gen(c, n[0][1])
# This is "when nimvm" node. Chose the second branch.
gen(c, n[1][0])
of nkCaseStmt: genCase(c, n)
of nkWhileStmt: genWhile(c, n)
of nkBlockExpr, nkBlockStmt: genBlock(c, n)

View File

@@ -115,7 +115,7 @@ proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, s
proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex =
let invalid = rstast.FileIndex(-1)
result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid)
result = d.nimToRstFid.getOrDefault(fileIndex, invalid)
if result == invalid:
let fname = toFullPath(d.conf, fileIndex)
result = addFilename(d.sharedState, fname)
@@ -830,8 +830,8 @@ proc getName(n: PNode): string =
of nkAccQuoted:
result = "`"
for i in 0..<n.len: result.add(getName(n[i]))
result = "`"
of nkOpenSymChoice, nkClosedSymChoice:
result.add('`')
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = getName(n[0])
else:
result = ""
@@ -849,7 +849,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
var r = ""
for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
result = getIdent(cache, r)
of nkOpenSymChoice, nkClosedSymChoice:
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = getNameIdent(cache, n[0])
else:
result = nil
@@ -863,7 +863,7 @@ proc getRstName(n: PNode): PRstNode =
of nkAccQuoted:
result = getRstName(n[0])
for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
of nkOpenSymChoice, nkClosedSymChoice:
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = getRstName(n[0])
else:
result = nil
@@ -1206,7 +1206,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
var param = %{"name": %($genericParam)}
if genericParam.sym.typ.len > 0:
param["types"] = newJArray()
param["types"].add %($genericParam.sym.typ.elementType)
param["types"] = %($genericParam.sym.typ.elementType)
result.json["signature"]["genericParams"].add param
if optGenIndex in d.conf.globalOptions:
genItem(d, n, nameNode, k, kForceExport)
@@ -1320,7 +1320,7 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id
if t.startsWith("ref "): t = substr(t, 4)
effects[i] = newIdentNode(getIdent(cache, t), n.info)
# set the type so that the following analysis doesn't screw up:
effects[i].typ = real[i].typ
effects[i].typ() = real[i].typ
result = newTreeI(nkExprColonExpr, n.info,
newIdentNode(getIdent(cache, $effectType), n.info), effects)
@@ -1406,11 +1406,14 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
for it in n: traceDeps(d, it)
of nkExportStmt:
for it in n:
# bug #23051; don't generate documentation for exported symbols again
if it.kind == nkSym and sfExported notin it.sym.flags:
if d.module != nil and d.module == it.sym.owner:
generateDoc(d, it.sym.ast, orig, config, kForceExport)
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
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])

View File

@@ -33,6 +33,10 @@ 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)

View File

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

View File

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

View File

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

View File

@@ -63,7 +63,7 @@ type
# Configuration settings for various compilers.
# When adding new compilers, the cmake sources could be a good reference:
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
# https://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
template compiler(name, settings: untyped): untyped =
proc name: TInfoCC {.compileTime.} = settings
@@ -162,7 +162,11 @@ compiler vcc:
linkerExe: "cl",
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
includeCmd: " /I",
linkDirCmd: " /LIBPATH:",
# HACK: we call `cl` so we have to pass `/link` for linker options,
# but users may still want to pass arguments to `cl` (see #14221)
# to deal with this, we add `/link` before each `/LIBPATH`,
# the linker ignores extra `/link`s since it's an unrecognized argument
linkDirCmd: " /link /LIBPATH:",
linkLibCmd: " $1.lib",
debug: " /RTC1 /Z7 ",
pic: "",
@@ -172,6 +176,22 @@ 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"
@@ -237,8 +257,8 @@ compiler tcc:
linkerExe: "tcc",
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
includeCmd: " -I",
linkDirCmd: "", # XXX: not supported yet
linkLibCmd: "", # XXX: not supported yet
linkDirCmd: " -L",
linkLibCmd: " -l$1",
debug: " -g ",
pic: "",
asmStmtFrmt: "asm($1);$n",
@@ -285,7 +305,9 @@ const
envcc(),
icl(),
icc(),
clangcl()]
clangcl(),
hipcc(),
nvcc()]
hExt* = ".h"
@@ -319,7 +341,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
var fullSuffix = suffix
case conf.backend
of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
of backendC, backendNir: discard
of backendC: discard
of backendInvalid:
# during parsing of cfg files; we don't know the backend yet, no point in
# guessing wrong thing
@@ -452,6 +474,11 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
result = conf.compileOptions
if (conf.cCompiler == ccGcc or conf.cCompiler == ccCLang) and
conf.selectedGC == gcRefc:
# bug #10625
addOpt(result, "-fno-omit-frame-pointer")
for option in conf.compileOptionsCmd:
if strutils.find(result, option, 0) < 0:
addOpt(result, option)
@@ -759,7 +786,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
# https://blog.molecular-matters.com/2017/05/09/deleting-pdb-files-locked-by-visual-studio/
# and a bit about the .pdb format in case that is ever needed:
# https://github.com/crosire/blink
# http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
# https://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
if conf.hcrOn and isVSCompatible(conf):
let t = now()
let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
@@ -982,10 +1009,11 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
# works out of the box with `hashMainCompilationParams`.
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
type BuildCache = object
cacheVersion: string
outputFile: string
outputLastModificationTime: string
compile: seq[(string, string)]
link: seq[string]
linkcmd: string
@@ -1029,6 +1057,8 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
bcache.depfiles.add (path, $secureHashFile(path))
bcache.nimexe = hashNimExe()
if fileExists(bcache.outputFile):
bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
@@ -1049,6 +1079,8 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
# xxx optimize by returning false if stdin input was the same
for (file, hash) in bcache.depfiles:
if $secureHashFile(file) != hash: return true
if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
return true
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
var bcache: BuildCache = default(BuildCache)
@@ -1065,7 +1097,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
"jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
[outputCurrent, output, jsonFile.string])
var cmds: TStringSeq = default(TStringSeq)
var prettyCmds: TStringSeq= default(TStringSeq)
var prettyCmds: TStringSeq = default(TStringSeq)
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
for (name, cmd) in bcache.compile:
cmds.add cmd

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
# (c) Copyright 2024 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View File

@@ -20,7 +20,7 @@ when defined(nimPreviewSlimSystem):
import std/assertions
import ".." / [ast, modulegraphs, msgs, options]
import ".." / nir / nirlineinfos
import iclineinfos
import packed_ast, bitabs, ic
type

View File

@@ -16,7 +16,7 @@ import std/[hashes, tables, strtabs]
import bitabs, rodfiles
import ".." / [ast, options]
import ".." / nir / nirlineinfos
import iclineinfos
when defined(nimPreviewSlimSystem):
import std/assertions

View File

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

View File

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

View File

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

View File

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

View File

@@ -145,11 +145,13 @@ 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)
rawAddField(result, createStateField(g, owner, idgen))
if owner.isIterator:
rawAddField(result, createStateField(g, owner, idgen))
proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
if resultPos < iter.ast.len:
@@ -172,26 +174,23 @@ proc addHiddenParam(routine: PSym, param: PSym) =
assert sfFromGeneric in param.flags
#echo "produced environment: ", param.id, " for ", routine.id
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
proc getEnvParam*(routine: PSym): PSym =
if routine.ast.isNil: return nil
let params = routine.ast[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
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
else:
result = nil
proc interestingVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
sfGlobal notin s.flags and
@@ -200,7 +199,7 @@ 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:
@@ -230,15 +229,6 @@ 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
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
template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
## lambda lifting can be harmful for JS-like code generators.
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
@@ -285,15 +275,6 @@ 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) =
@@ -303,8 +284,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
localError(g.config, n.info,
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
" Consider using a <ref $2> which can be captured.") %
[s.name.s, typeToString(s.typ), g.config$s.info])
" Consider using a <ref T> which can be captured.") %
[s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
[s.name.s, owner.name.s, $owner.typ.callConv])
@@ -341,9 +322,13 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
info: TLineInfo): PType =
result = c.ownerToType.getOrDefault(owner.id)
if result.isNil:
result = newType(tyRef, c.idgen, owner)
let obj = createEnvObj(c.graph, c.idgen, owner, info)
rawAddSon(result, obj)
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
c.ownerToType[owner.id] = result
proc asOwnedRef(c: var DetectionPass; t: PType): PType =
@@ -435,6 +420,12 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
#echo "adding closure to ", fn.name.s
proc iterEnvHasUpField(g: ModuleGraph, iter: PSym): bool =
let cp = getEnvParam(iter)
doAssert(cp != nil, "Env param not present in iter")
let upField = lookupInRecord(cp.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
upField != nil
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
case n.kind
of nkSym:
@@ -453,23 +444,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
let body = transformBody(c.graph, c.idgen, s, {useCache})
detectCapturedVars(body, s, c)
let ow = s.skipGenericOwner
let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
let innerClosure = innerProc and s.typ.callConv == ccClosure and (not s.isIterator or iterEnvHasUpField(c.graph, s))
let interested = interestingVar(s)
if ow == owner:
if owner.isIterator:
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.flags.incl sfNoInit
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 """
@@ -629,7 +609,7 @@ proc rawClosureCreation(owner: PSym;
let unowned = c.unownedEnvVars[owner.id]
assert unowned != nil
let env2 = copyTree(env)
env2.typ = unowned.typ
env2.typ() = unowned.typ
result.add newAsgnStmt(unowned, env2, env.info)
createTypeBoundOpsLL(d.graph, unowned.typ, env.info, d.idgen, owner)
@@ -669,16 +649,27 @@ proc finishClosureCreation(owner: PSym; d: var DetectionPass; c: LiftingPass;
res.add newAsgnStmt(unowned, nilLit, info)
createTypeBoundOpsLL(d.graph, unowned.typ, info, d.idgen, owner)
proc closureCreationForIter(iter: PNode;
proc getUpForIter(g: ModuleGraph; owner, iterOwner: PSym, expectedUpTyp: PType): PNode =
var p = getHiddenParam(g, owner)
var res = p.newSymNode
while res.typ.skipTypes({tyOwned, tyRef, tyPtr}) != expectedUpTyp:
let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
if upField == nil:
return nil
p = upField
res = rawIndirectAccess(res, upField, p.info)
res
proc closureCreationForIter(owner: PSym, iter: PNode;
d: var DetectionPass; c: var LiftingPass): PNode =
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
let owner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
let iterOwner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, iterOwner, iter.info)
incl(v.flags, sfShadowed)
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
var vnode: PNode
if owner.isIterator:
let it = getHiddenParam(d.graph, owner)
if iterOwner.isIterator:
let it = getHiddenParam(d.graph, iterOwner)
addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
vnode = indirectAccess(newSymNode(it), v, v.info)
else:
@@ -687,12 +678,14 @@ proc closureCreationForIter(iter: PNode;
addVar(vs, vnode)
result.add(vs)
result.add genCreateEnv(vnode)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, iterOwner)
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
if upField != nil:
let u = setupEnvVar(owner, d, c, iter.info)
if u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == upField.typ.skipTypes({tyOwned, tyRef, tyPtr}):
let expectedUpTyp = upField.typ.skipTypes({tyOwned, tyRef, tyPtr})
let u = if iterOwner == owner: setupEnvVar(iterOwner, d, c, iter.info)
else: getUpForIter(d.graph, owner, iterOwner, expectedUpTyp)
if u != nil and u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == expectedUpTyp:
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
u, iter.info))
else:
@@ -726,7 +719,7 @@ proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
let available = getHiddenParam(d.graph, owner)
result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
elif s.isIterator:
result = closureCreationForIter(n, d, c)
result = closureCreationForIter(owner, n, d, c)
elif s.skipGenericOwner == owner:
# direct dependency, so use the outer's env variable:
result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
@@ -773,8 +766,6 @@ 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,
@@ -795,7 +786,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
let oldInContainer = c.inContainer
c.inContainer = 0
let m = newSymNode(n[namePos].sym)
m.typ = n.typ
m.typ() = n.typ
result = liftCapturedVars(m, owner, d, c)
c.inContainer = oldInContainer
of nkHiddenStdConv:
@@ -892,6 +883,9 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
# 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)

91
compiler/layeredtable.nim Normal file
View File

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

@@ -40,7 +40,7 @@ template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
info: TLineInfo; idgen: IdGenerator): PSym
proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
idgen: IdGenerator)
@@ -49,7 +49,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
result = newNodeI(nkBracketExpr, a.info, 2)
result[0] = a
result[1] = i
result.typ = elemType
result.typ() = elemType
proc destructorOverridden(g: ModuleGraph; t: PType): bool =
let op = getAttachedOp(g, t, attachedDestructor)
@@ -68,7 +68,7 @@ proc dotField(x: PNode, f: PSym): PNode =
else:
result[0] = x
result[1] = newSymNode(f, x.info)
result.typ = f.typ
result.typ() = f.typ
proc newAsgnStmt(le, ri: PNode): PNode =
result = newNodeI(nkAsgn, le.info, 2)
@@ -88,10 +88,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 +105,7 @@ proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
result = newNodeI(nkWhileStmt, c.info, 2)
let cmp = genBuiltin(c, mLtI, "<", i)
cmp.add genLen(c.g, dest)
cmp.typ = getSysType(c.g, c.info, tyBool)
cmp.typ() = getSysType(c.g, c.info, tyBool)
result[0] = cmp
result[1] = newNodeI(nkStmtList, c.info)
@@ -127,10 +127,10 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
dotExpr.add newSymNode(field)
let offsetOf = genBuiltin(c, mOffsetOf, "offsetof", dotExpr)
offsetOf.typ = intType
offsetOf.typ() = intType
let minusExpr = genBuiltin(c, mSubI, "-", castExpr1)
minusExpr.typ = intType
minusExpr.typ() = intType
minusExpr.add offsetOf
let objPtr = makePtrType(objType.owner, objType, c.idgen)
@@ -148,7 +148,7 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
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
@@ -168,7 +168,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:
@@ -224,10 +224,16 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
if t.baseClass != nil:
let obj = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
obj.add newNodeI(nkEmpty, c.info)
obj.add x
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, obj, y)
let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
dest.add newNodeI(nkEmpty, c.info)
dest.add x
var src = y
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
src.add newNodeI(nkEmpty, c.info)
src.add y
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
@@ -259,7 +265,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
# because the wasMoved(dest) call would zero out src, if dest aliases src.
var cond = newTree(nkCall, newSymNode(c.g.getSysMagic(c.info, "==", mEqRef)),
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
cond.typ = getSysType(c.g, x.info, tyBool)
cond.typ() = getSysType(c.g, x.info, tyBool)
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
temp.typ = x.typ
@@ -271,7 +277,8 @@ 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
@@ -290,7 +297,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
result = newIntLit(g, info, ord value)
result.typ = getSysType(g, info, tyBool)
result.typ() = getSysType(g, info, tyBool)
proc getCycleParam(c: TLiftCtx): PNode =
assert c.kind in {attachedAsgn, attachedDup}
@@ -539,18 +546,18 @@ proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
# don't call genAddr(c, x) here:
result = genBuiltin(c, mNewSeq, "newSeq", x)
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
lenCall.typ = getSysType(c.g, x.info, tyInt)
lenCall.typ() = getSysType(c.g, x.info, tyInt)
result.add lenCall
proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
let lenCall = genBuiltin(c, mLengthStr, "len", y)
lenCall.typ = getSysType(c.g, x.info, tyInt)
lenCall.typ() = getSysType(c.g, x.info, tyInt)
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
result.add lenCall
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
lenCall.typ = getSysType(c.g, x.info, tyInt)
lenCall.typ() = getSysType(c.g, x.info, tyInt)
var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
op = instantiateGeneric(c, op, t, t)
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
@@ -573,7 +580,7 @@ proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
)
cond.typ = getSysType(c.g, c.info, tyBool)
cond.typ() = getSysType(c.g, c.info, tyBool)
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
@@ -606,7 +613,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)
@@ -644,7 +651,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)
@@ -666,7 +673,7 @@ 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
@@ -714,7 +721,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if isFinal(elemType):
addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
alignOf.typ = getSysType(c.g, c.info, tyInt)
alignOf.typ() = getSysType(c.g, c.info, tyInt)
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf)
else:
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
@@ -724,7 +731,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if isCyclic:
if isFinal(elemType):
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
typInfo.typ = getSysType(c.g, c.info, tyPointer)
typInfo.typ() = getSysType(c.g, c.info, tyPointer)
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
else:
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
@@ -732,7 +739,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x)
else:
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
cond.typ = getSysType(c.g, x.info, tyBool)
cond.typ() = getSysType(c.g, x.info, tyBool)
case c.kind
of attachedSink:
@@ -759,13 +766,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)
@@ -780,7 +787,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
## Closures are really like refs except they always use a virtual destructor
## and we need to do the refcounting only on the ref field which we call 'xenv':
let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
xenv.typ = getSysType(c.g, c.info, tyPointer)
xenv.typ() = getSysType(c.g, c.info, tyPointer)
let isCyclic = c.g.config.selectedGC == gcOrc
let tmp =
@@ -796,7 +803,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if isCyclic: "nimDecRefIsLastCyclicDyn"
else: "nimDecRefIsLast"
let cond = callCodegenProc(c.g, decRefProc, c.info, tmp)
cond.typ = getSysType(c.g, x.info, tyBool)
cond.typ() = getSysType(c.g, x.info, tyBool)
case c.kind
of attachedSink:
@@ -808,7 +815,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add newAsgnStmt(x, y)
of attachedAsgn:
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
yenv.typ = getSysType(c.g, c.info, tyPointer)
yenv.typ() = getSysType(c.g, c.info, tyPointer)
if isCyclic:
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
body.add newAsgnStmt(x, y)
@@ -820,7 +827,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)))
@@ -832,7 +839,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
@@ -860,7 +867,7 @@ 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)
@@ -872,7 +879,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if isFinal(elemType):
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
alignOf.typ = getSysType(c.g, c.info, tyInt)
alignOf.typ() = getSysType(c.g, c.info, tyInt)
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf)
else:
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
@@ -888,21 +895,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
@@ -911,13 +918,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:
@@ -928,11 +935,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)
@@ -946,7 +953,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
@@ -996,9 +1003,13 @@ 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)
forallElements(c, t, body, x, y)
if c.kind == attachedWasMoved:
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
else:
forallElements(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyString:
@@ -1015,7 +1026,7 @@ 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)
else:
@@ -1040,7 +1051,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
else:
discard "cannot copy openArray"
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
@@ -1057,9 +1068,7 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
assert typ.kind == tyDistinct
let baseType = typ.elementType
if getAttachedOp(g, baseType, kind) == nil:
# TODO: fixme `isDistinct` is a fix for #23552; remove it after
# `-d:nimPreviewNonVarDestructor` becomes the default
discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
discard produceSym(g, c, baseType, kind, info, idgen)
result = getAttachedOp(g, baseType, kind)
setAttachedOp(g, idgen.module, typ, kind, result)
@@ -1098,7 +1107,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache
incl result.flags, sfGeneratedOp
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym =
if kind == attachedDup:
return symDupPrototype(g, typ, owner, kind, info, idgen)
@@ -1109,7 +1118,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
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} and not isDistinct)):
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence})):
dest.typ = typ
else:
dest.typ = makeVarType(typ.owner, typ, idgen)
@@ -1146,18 +1155,18 @@ 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;
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
info: TLineInfo; idgen: IdGenerator): PSym =
if typ.kind == tyDistinct:
return produceSymDistinctType(g, c, typ, kind, info, idgen)
result = getAttachedOp(g, typ, kind)
if result == nil:
result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
result = symPrototype(g, typ, typ.owner, kind, info, idgen)
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
fn: result)
@@ -1191,7 +1200,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 lacksMTypeField(typ):
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)
@@ -1201,6 +1210,8 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
result.ast[pragmasPos].add newTree(nkExprColonExpr,
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))
if kind == attachedDestructor:
incl result.options, optQuirky
completePartialOp(g, idgen.module, typ, kind, result)
@@ -1270,6 +1281,10 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
## The later 'injectdestructors' pass depends on it.
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
incl orig.flags, tfCheckedForDestructor
# for user defined generic destructors:
let origRoot = genericRoot(orig)
if origRoot != nil:
incl origRoot.flags, tfGenericHasDestructor
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return

View File

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

View File

@@ -92,8 +92,9 @@ type
warnStmtListLambda = "StmtListLambda",
warnBareExcept = "BareExcept",
warnImplicitDefaultValue = "ImplicitDefaultValue",
warnGenericsIgnoredInjection = "GenericsIgnoredInjection",
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
warnStdPrefix = "StdPrefix"
warnUnknownNotes = "UnknownNotes"
warnUser = "User",
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
# hints
@@ -109,9 +110,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] = [
@@ -198,8 +199,9 @@ const
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
warnBareExcept: "$1",
warnImplicitDefaultValue: "$1",
warnGenericsIgnoredInjection: "$1",
warnIgnoredSymbolInjection: "$1",
warnStdPrefix: "$1 needs the 'std' prefix",
warnUnknownNotes: "$1",
warnUser: "$1",
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
hintSuccess: "operation successful: $#",
@@ -235,9 +237,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",
hintUnknownHint: "unknown hint: $1"
hintDeclaredLoc: "$1"
]
const
@@ -268,6 +270,7 @@ const
NotesVerbosity* = computeNotesVerbosity()
errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
errFloatToString* = "cannot convert '$1' to '$2'"
type
TFileInfo* = object

View File

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

View File

@@ -11,7 +11,7 @@
import
pathutils
import std/strutils
when defined(nimPreviewSlimSystem):
import std/syncio
@@ -68,6 +68,7 @@ when not declared(readLineFromStdin):
# fallback implementation:
proc readLineFromStdin(prompt: string, line: var string): bool =
stdout.write(prompt)
stdout.flushFile()
result = readLine(stdin, line)
if not result:
stdout.write("\n")
@@ -85,6 +86,47 @@ 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)
@@ -92,7 +134,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))
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) or
line.endsWithLineContinuationToken()
)
proc countTriples(s: string): int =
result = 0
@@ -108,7 +152,10 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
s.rd = 0
var line = newStringOfCap(120)
var triples = 0
while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
while true:
if not readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
# now readLineFromStdin meets EOF (ctrl-D/Z) or ctrl-C
quit()
s.s.add(line)
s.s.add("\n")
inc triples, countTriples(line)

View File

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

View File

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

View File

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

View File

@@ -22,8 +22,6 @@ import
modules,
modulegraphs, lineinfos, pathutils, vmprofiler
# ensure NIR compiles:
import nir / nir
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
@@ -48,9 +46,6 @@ proc writeDepsFile(g: ModuleGraph) =
f.writeLine(toFullPath(g.config, k))
f.close()
proc writeNinjaFile(g: ModuleGraph) =
discard "to implement"
proc writeCMakeDepsFile(conf: ConfigRef) =
## write a list of C files for build systems like CMake.
## only updated when the C file list changes.
@@ -161,26 +156,6 @@ proc commandCompileToC(graph: ModuleGraph) =
if optGenCDeps in graph.config.globalOptions:
writeCMakeDepsFile(conf)
proc commandCompileToNir(graph: ModuleGraph) =
let conf = graph.config
extccomp.initVars(conf)
if conf.symbolFiles == disabledSf:
if {optRun, optForceFullMake} * conf.globalOptions == {optRun}:
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
# nothing changed
graph.config.notes = graph.config.mainPackageNotes
return
if not extccomp.ccHasSaneOverflow(conf):
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
if conf.symbolFiles == disabledSf:
setPipeLinePass(graph, NirPass)
else:
setPipeLinePass(graph, SemPass)
compilePipelineProject(graph)
writeNinjaFile(graph)
proc commandJsonScript(graph: ModuleGraph) =
extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
@@ -197,16 +172,13 @@ proc commandCompileToJS(graph: ModuleGraph) =
if optGenScript in conf.globalOptions:
writeDepsFile(graph)
proc commandInteractive(graph: ModuleGraph; useNir: bool) =
proc commandInteractive(graph: ModuleGraph) =
graph.config.setErrorMaxHighMaybe
initDefines(graph.config.symbols)
if useNir:
defineSymbol(graph.config.symbols, "noSignalHandler")
else:
defineSymbol(graph.config.symbols, "nimscript")
defineSymbol(graph.config.symbols, "nimscript")
# note: seems redundant with -d:nimHasLibFFI
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
setPipeLinePass(graph, if useNir: NirReplPass else: InterpreterPass)
setPipeLinePass(graph, InterpreterPass)
compilePipelineSystemModule(graph)
if graph.config.commandArgs.len > 0:
discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
@@ -293,8 +265,6 @@ 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 backendNir:
if conf.exc == excNone: conf.exc = excGoto
of backendInvalid: raiseAssert "unreachable"
proc compileToBackend() =
@@ -305,7 +275,6 @@ proc mainCommand*(graph: ModuleGraph) =
of backendCpp: commandCompileToC(graph)
of backendObjc: commandCompileToC(graph)
of backendJs: commandCompileToJS(graph)
of backendNir: commandCompileToNir(graph)
of backendInvalid: raiseAssert "unreachable"
template docLikeCmd(body) =
@@ -444,7 +413,7 @@ proc mainCommand*(graph: ModuleGraph) =
wantMainModule(conf)
commandView(graph)
#msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir"))
of cmdInteractive: commandInteractive(graph)
of cmdNimscript:
if conf.projectIsCmd or conf.projectIsStdin: discard
elif not fileExists(conf.projectFull):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -116,9 +116,9 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
conf.backend = backendC
if conf.selectedGC == gcUnselected:
if conf.backend in {backendC, backendCpp, backendObjc, backendNir} or
(conf.cmd == cmdInteractive and isDefined(conf, "nir")) or
(conf.cmd in cmdDocLike and conf.backend != backendJs):
if conf.backend in {backendC, backendCpp, backendObjc} or
(conf.cmd in cmdDocLike and conf.backend != backendJs) or
conf.cmd == cmdGendepend:
initOrcDefines(conf)
mainCommand(graph)

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,983 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# We produce C code as a list of tokens.
import std / [assertions, syncio, tables, intsets, formatfloat]
from std / strutils import toOctal
import .. / ic / [bitabs, rodfiles]
import nirtypes, nirinsts, nirfiles
import ../../dist/checksums/src/checksums/md5
type
Token = LitId # indexing into the tokens BiTable[string]
PredefinedToken = enum
IgnoreMe = "<unused>"
EmptyToken = ""
CurlyLe = "{"
CurlyRi = "}"
ParLe = "("
ParRi = ")"
BracketLe = "["
BracketRi = "]"
NewLine = "\n"
Semicolon = ";"
Comma = ", "
Space = " "
Colon = ": "
Dot = "."
Arrow = "->"
Star = "*"
Amp = "&"
AsgnOpr = " = "
ScopeOpr = "::"
ConstKeyword = "const "
StaticKeyword = "static "
ExternKeyword = "extern "
WhileKeyword = "while "
IfKeyword = "if ("
ElseKeyword = "else "
SwitchKeyword = "switch "
CaseKeyword = "case "
DefaultKeyword = "default:"
BreakKeyword = "break"
NullPtr = "nullptr"
IfNot = "if (!("
ReturnKeyword = "return "
TypedefStruct = "typedef struct "
TypedefUnion = "typedef union "
IncludeKeyword = "#include "
proc fillTokenTable(tab: var BiTable[string]) =
for e in EmptyToken..high(PredefinedToken):
let id = tab.getOrIncl $e
assert id == LitId(e), $(id, " ", ord(e))
type
GeneratedCode* = object
m: NirModule
includes: seq[LitId]
includedHeaders: IntSet
data: seq[LitId]
protos: seq[LitId]
code: seq[LitId]
init: seq[LitId]
tokens: BiTable[string]
emittedStrings: IntSet
needsPrefix: IntSet
generatedTypes: IntSet
mangledModules: Table[LitId, LitId]
proc initGeneratedCode*(m: sink NirModule): GeneratedCode =
result = GeneratedCode(m: m, code: @[], tokens: initBiTable[string]())
fillTokenTable(result.tokens)
proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} =
g.code.add Token(t)
proc add*(g: var GeneratedCode; s: string) {.inline.} =
g.code.add g.tokens.getOrIncl(s)
proc mangleModuleName(c: var GeneratedCode; key: LitId): LitId =
result = c.mangledModules.getOrDefault(key, LitId(0))
if result == LitId(0):
let u {.cursor.} = c.m.lit.strings[key]
var last = u.len - len(".nim") - 1
var start = last
while start >= 0 and u[start] != '/': dec start
var sum = getMD5(u)
sum.setLen(8)
let dest = u.substr(start+1, last) & sum
result = c.tokens.getOrIncl(dest)
c.mangledModules[key] = result
type
CppFile = object
f: File
proc write(f: var CppFile; s: string) = write(f.f, s)
proc write(f: var CppFile; c: char) = write(f.f, c)
proc writeTokenSeq(f: var CppFile; s: seq[Token]; c: GeneratedCode) =
var indent = 0
for i in 0..<s.len:
let x = s[i]
case x
of Token(CurlyLe):
inc indent
write f, c.tokens[x]
write f, "\n"
for i in 1..indent*2: write f, ' '
of Token(CurlyRi):
dec indent
write f, c.tokens[x]
if i+1 < s.len and s[i+1] == Token(CurlyRi):
discard
else:
write f, "\n"
for i in 1..indent*2: write f, ' '
of Token(Semicolon):
write f, c.tokens[x]
if i+1 < s.len and s[i+1] == Token(CurlyRi):
discard "no newline before }"
else:
write f, "\n"
for i in 1..indent*2: write f, ' '
of Token(NewLine):
write f, c.tokens[x]
for i in 1..indent*2: write f, ' '
else:
write f, c.tokens[x]
# Type graph
type
TypeList = object
processed: IntSet
s: seq[(TypeId, PredefinedToken)]
proc add(dest: var TypeList; elem: TypeId; decl: PredefinedToken) =
if not containsOrIncl(dest.processed, int(elem)):
dest.s.add (elem, decl)
type
TypeOrder = object
forwardedDecls, ordered: TypeList
typeImpls: Table[string, TypeId]
lookedAt: IntSet
proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId)
proc recordDependency(types: TypeGraph; lit: Literals; c: var TypeOrder; parent, child: TypeId) =
var ch = child
var viaPointer = false
while true:
case types[ch].kind
of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
viaPointer = true
ch = elementType(types, ch)
of LastArrayTy:
ch = elementType(types, ch)
else:
break
case types[ch].kind
of ObjectTy, UnionTy:
let decl = if types[ch].kind == ObjectTy: TypedefStruct else: TypedefUnion
let obj = c.typeImpls.getOrDefault(lit.strings[types[ch].litId])
if viaPointer:
c.forwardedDecls.add obj, decl
else:
if not containsOrIncl(c.lookedAt, obj.int):
traverseObject(types, lit, c, obj)
c.ordered.add obj, decl
of ArrayTy:
if viaPointer:
c.forwardedDecls.add ch, TypedefStruct
else:
if not containsOrIncl(c.lookedAt, ch.int):
traverseObject(types, lit, c, ch)
c.ordered.add ch, TypedefStruct
else:
discard "uninteresting type as we only focus on the required struct declarations"
proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId) =
for x in sons(types, t):
case types[x].kind
of FieldDecl:
recordDependency types, lit, c, t, x.firstSon
of ObjectTy:
# inheritance
recordDependency types, lit, c, t, x
else: discard
proc traverseTypes(types: TypeGraph; lit: Literals; c: var TypeOrder) =
for t in allTypes(types):
if types[t].kind in {ObjectDecl, UnionDecl}:
assert types[t.firstSon].kind == NameVal
c.typeImpls[lit.strings[types[t.firstSon].litId]] = t
for t in allTypesIncludingInner(types):
case types[t].kind
of ObjectDecl, UnionDecl:
traverseObject types, lit, c, t
let decl = if types[t].kind == ObjectDecl: TypedefStruct else: TypedefUnion
c.ordered.add t, decl
of ArrayTy:
traverseObject types, lit, c, t
c.ordered.add t, TypedefStruct
else: discard
when false:
template emitType(s: string) = c.types.add c.tokens.getOrIncl(s)
template emitType(t: Token) = c.types.add t
template emitType(t: PredefinedToken) = c.types.add Token(t)
proc genType(g: var GeneratedCode; types: TypeGraph; lit: Literals; t: TypeId; name = "") =
template maybeAddName =
if name != "":
g.add Space
g.add name
template atom(s: string) =
g.add s
maybeAddName()
case types[t].kind
of VoidTy: atom "void"
of IntTy: atom "NI" & $types[t].integralBits
of UIntTy: atom "NU" & $types[t].integralBits
of FloatTy: atom "NF" & $types[t].integralBits
of BoolTy: atom "NB" & $types[t].integralBits
of CharTy: atom "NC" & $types[t].integralBits
of ObjectTy, UnionTy, NameVal, AnnotationVal:
atom lit.strings[types[t].litId]
of VarargsTy:
g.add "..."
of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
genType g, types, lit, elementType(types, t)
g.add Star
maybeAddName()
of ArrayTy:
genType g, types, lit, arrayName(types, t)
maybeAddName()
of LastArrayTy:
genType g, types, lit, elementType(types, t)
maybeAddName()
g.add BracketLe
g.add BracketRi
of ProcTy:
let (retType, callConv) = returnType(types, t)
genType g, types, lit, retType
g.add Space
g.add ParLe
genType g, types, lit, callConv
g.add Star # "(*fn)"
maybeAddName()
g.add ParRi
g.add ParLe
var i = 0
for ch in params(types, t):
if i > 0: g.add Comma
genType g, types, lit, ch
inc i
g.add ParRi
of ObjectDecl, UnionDecl:
atom lit.strings[types[t.firstSon].litId]
of IntVal, SizeVal, AlignVal, OffsetVal, FieldDecl:
#raiseAssert "did not expect: " & $types[t].kind
g.add "BUG "
atom $types[t].kind
proc generateTypes(g: var GeneratedCode; types: TypeGraph; lit: Literals; c: TypeOrder) =
for (t, declKeyword) in c.forwardedDecls.s:
let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
let s {.cursor.} = lit.strings[types[name].litId]
g.add declKeyword
g.add s
g.add Space
g.add s
g.add Semicolon
for (t, declKeyword) in c.ordered.s:
let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
let litId = types[name].litId
if not g.generatedTypes.containsOrIncl(litId.int):
let s {.cursor.} = lit.strings[litId]
g.add declKeyword
g.add CurlyLe
if types[t].kind == ArrayTy:
genType g, types, lit, elementType(types, t), "a"
g.add BracketLe
g.add $arrayLen(types, t)
g.add BracketRi
g.add Semicolon
else:
var i = 0
for x in sons(types, t):
case types[x].kind
of FieldDecl:
genType g, types, lit, x.firstSon, "F" & $i
g.add Semicolon
inc i
of ObjectTy:
genType g, types, lit, x, "P"
g.add Semicolon
else: discard
g.add CurlyRi
g.add s
g.add Semicolon
# Procs
proc toCChar*(c: char; result: var string) {.inline.} =
case c
of '\0'..'\x1F', '\x7F'..'\xFF':
result.add '\\'
result.add toOctal(c)
of '\'', '\"', '\\', '?':
result.add '\\'
result.add c
else:
result.add c
proc makeCString(s: string): string =
result = newStringOfCap(s.len + 10)
result.add('"')
for c in s: toCChar(c, result)
result.add('"')
template emitData(s: string) = c.data.add c.tokens.getOrIncl(s)
template emitData(t: Token) = c.data.add t
template emitData(t: PredefinedToken) = c.data.add Token(t)
proc genStrLit(c: var GeneratedCode; lit: Literals; litId: LitId): Token =
result = Token(c.tokens.getOrIncl "QStr" & $litId)
if not containsOrIncl(c.emittedStrings, int(litId)):
let s {.cursor.} = lit.strings[litId]
emitData "static const struct "
emitData CurlyLe
emitData "NI cap"
emitData Semicolon
emitData "NC8 data"
emitData BracketLe
emitData $s.len
emitData "+1"
emitData BracketRi
emitData Semicolon
emitData CurlyRi
emitData result
emitData AsgnOpr
emitData CurlyLe
emitData $s.len
emitData " | NIM_STRLIT_FLAG"
emitData Comma
emitData makeCString(s)
emitData CurlyRi
emitData Semicolon
proc genIntLit(c: var GeneratedCode; lit: Literals; litId: LitId) =
let i = lit.numbers[litId]
if i > low(int32) and i <= high(int32):
c.add $i
elif i == low(int32):
# Nim has the same bug for the same reasons :-)
c.add "(-2147483647 -1)"
elif i > low(int64):
c.add "IL64("
c.add $i
c.add ")"
else:
c.add "(IL64(-9223372036854775807) - IL64(1))"
proc gen(c: var GeneratedCode; t: Tree; n: NodePos)
proc genDisplayName(c: var GeneratedCode; symId: SymId) =
let displayName = c.m.symnames[symId]
if displayName != LitId(0):
c.add "/*"
c.add c.m.lit.strings[displayName]
c.add "*/"
proc genSymDef(c: var GeneratedCode; t: Tree; n: NodePos) =
if t[n].kind == SymDef:
let symId = t[n].symId
c.needsPrefix.incl symId.int
genDisplayName c, symId
gen c, t, n
proc genGlobal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
c.add annotation
let m: string
if t[name].kind == SymDef:
let symId = t[name].symId
m = c.tokens[mangleModuleName(c, c.m.namespace)] & "__" & $symId
genDisplayName c, symId
else:
assert t[name].kind == ModuleSymUse
let (x, y) = sons2(t, name)
m = c.tokens[mangleModuleName(c, t[x].litId)] & "__" & $t[y].immediateVal
genType c, c.m.types, c.m.lit, t[typ].typeId, m
proc genLocal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
assert t[name].kind == SymDef
c.add annotation
let symId = t[name].symId
genType c, c.m.types, c.m.lit, t[typ].typeId, "q" & $symId
genDisplayName c, symId
proc genProcDecl(c: var GeneratedCode; t: Tree; n: NodePos; isExtern: bool) =
let signatureBegin = c.code.len
let name = n.firstSon
var prc = n.firstSon
next t, prc
while true:
case t[prc].kind
of PragmaPair:
let (x, y) = sons2(t, prc)
let key = cast[PragmaKey](t[x].rawOperand)
case key
of HeaderImport:
let lit = t[y].litId
let headerAsStr {.cursor.} = c.m.lit.strings[lit]
let header = c.tokens.getOrIncl(headerAsStr)
# headerAsStr can be empty, this has the semantics of the `nodecl` pragma:
if headerAsStr.len > 0 and not c.includedHeaders.containsOrIncl(int header):
if headerAsStr[0] == '#':
discard "skip the #include"
else:
c.includes.add Token(IncludeKeyword)
c.includes.add header
c.includes.add Token NewLine
# do not generate code for importc'ed procs:
return
of DllImport:
let lit = t[y].litId
raiseAssert "cannot eval: " & c.m.lit.strings[lit]
else: discard
of PragmaId: discard
else: break
next t, prc
var resultDeclPos = NodePos(-1)
if t[prc].kind == SummonResult:
resultDeclPos = prc
gen c, t, prc.firstSon
next t, prc
else:
c.add "void"
c.add Space
genSymDef c, t, name
c.add ParLe
var params = 0
while t[prc].kind == SummonParam:
if params > 0: c.add Comma
let (typ, sym) = sons2(t, prc)
genLocal c, t, sym, typ, ""
next t, prc
inc params
if params == 0:
c.add "void"
c.add ParRi
for i in signatureBegin ..< c.code.len:
c.protos.add c.code[i]
c.protos.add Token Semicolon
if isExtern:
c.code.setLen signatureBegin
else:
c.add CurlyLe
if resultDeclPos.int >= 0:
gen c, t, resultDeclPos
for ch in sonsRest(t, n, prc):
gen c, t, ch
c.add CurlyRi
template triop(opr) =
let (typ, a, b) = sons3(t, n)
c.add ParLe
c.add ParLe
gen c, t, typ
c.add ParRi
gen c, t, a
c.add opr
gen c, t, b
c.add ParRi
template cmpop(opr) =
let (_, a, b) = sons3(t, n)
c.add ParLe
gen c, t, a
c.add opr
gen c, t, b
c.add ParRi
template binaryop(opr) =
let (typ, a) = sons2(t, n)
c.add ParLe
c.add ParLe
gen c, t, typ
c.add ParRi
c.add opr
gen c, t, a
c.add ParRi
template checkedBinaryop(opr) =
let (typ, labIdx, a, b) = sons4(t, n)
let bits = integralBits(c.m.types[t[typ].typeId])
let lab = t[labIdx].label
c.add (opr & $bits)
c.add ParLe
c.gen t, a
c.add Comma
c.gen t, b
c.add Comma
c.add "L" & $lab.int
c.add ParRi
proc genNumberConv(c: var GeneratedCode; t: Tree; n: NodePos) =
let (typ, arg) = sons2(t, n)
if t[arg].kind == IntVal:
let litId = t[arg].litId
c.add ParLe
c.add ParLe
gen c, t, typ
c.add ParRi
case c.m.types[t[typ].typeId].kind
of UIntTy:
let x = cast[uint64](c.m.lit.numbers[litId])
c.add $x
of FloatTy:
let x = cast[float64](c.m.lit.numbers[litId])
c.add $x
else:
gen c, t, arg
c.add ParRi
else:
binaryop ""
template moveToDataSection(body: untyped) =
let oldLen = c.code.len
body
for i in oldLen ..< c.code.len:
c.data.add c.code[i]
setLen c.code, oldLen
proc gen(c: var GeneratedCode; t: Tree; n: NodePos) =
case t[n].kind
of Nop:
discard "nothing to emit"
of ImmediateVal:
c.add $t[n].immediateVal
of IntVal:
genIntLit c, c.m.lit, t[n].litId
of StrVal:
c.code.add genStrLit(c, c.m.lit, t[n].litId)
of Typed:
genType c, c.m.types, c.m.lit, t[n].typeId
of SymDef, SymUse:
let s = t[n].symId
if c.needsPrefix.contains(s.int):
c.code.add mangleModuleName(c, c.m.namespace)
c.add "__"
c.add $s
else:
# XXX Use proper names here
c.add "q"
c.add $s
of ModuleSymUse:
let (x, y) = sons2(t, n)
let u = mangleModuleName(c, t[x].litId)
let s = t[y].immediateVal
c.code.add u
c.add "__"
c.add $s
of NilVal:
c.add NullPtr
of LoopLabel:
c.add WhileKeyword
c.add ParLe
c.add "1"
c.add ParRi
c.add CurlyLe
of GotoLoop:
c.add CurlyRi
of Label:
let lab = t[n].label
c.add "L"
c.add $lab.int
c.add Colon
c.add Semicolon
of Goto:
let lab = t[n].label
c.add "goto L"
c.add $lab.int
c.add Semicolon
of CheckedGoto:
discard "XXX todo"
of ArrayConstr:
c.add CurlyLe
c.add ".a = "
c.add CurlyLe
var i = 0
for ch in sonsFrom1(t, n):
if i > 0: c.add Comma
c.gen t, ch
inc i
c.add CurlyRi
c.add CurlyRi
of ObjConstr:
c.add CurlyLe
var i = 0
for ch in sonsFrom1(t, n):
if i mod 2 == 0:
if i > 0: c.add Comma
c.add ".F" & $t[ch].immediateVal
c.add AsgnOpr
else:
c.gen t, ch
inc i
c.add CurlyRi
of Ret:
c.add ReturnKeyword
c.gen t, n.firstSon
c.add Semicolon
of Select:
c.add SwitchKeyword
c.add ParLe
let (_, selector) = sons2(t, n)
c.gen t, selector
c.add ParRi
c.add CurlyLe
for ch in sonsFromN(t, n, 2):
c.gen t, ch
c.add CurlyRi
of SelectPair:
let (le, ri) = sons2(t, n)
c.gen t, le
c.gen t, ri
of SelectList:
for ch in sons(t, n):
c.gen t, ch
of SelectValue:
c.add CaseKeyword
c.gen t, n.firstSon
c.add Colon
of SelectRange:
let (le, ri) = sons2(t, n)
c.add CaseKeyword
c.gen t, le
c.add " ... "
c.gen t, ri
c.add Colon
of ForeignDecl:
c.data.add LitId(ExternKeyword)
c.gen t, n.firstSon
of SummonGlobal:
moveToDataSection:
let (typ, sym) = sons2(t, n)
c.genGlobal t, sym, typ, ""
c.add Semicolon
of SummonThreadLocal:
moveToDataSection:
let (typ, sym) = sons2(t, n)
c.genGlobal t, sym, typ, "__thread "
c.add Semicolon
of SummonConst:
moveToDataSection:
let (typ, sym, val) = sons3(t, n)
c.genGlobal t, sym, typ, "const "
c.add AsgnOpr
c.gen t, val
c.add Semicolon
of Summon, SummonResult:
let (typ, sym) = sons2(t, n)
c.genLocal t, sym, typ, ""
c.add Semicolon
of SummonParam:
raiseAssert "SummonParam should have been handled in genProc"
of Kill:
discard "we don't care about Kill instructions"
of AddrOf:
let (_, arg) = sons2(t, n)
c.add "&"
gen c, t, arg
of DerefArrayAt:
let (_, a, i) = sons3(t, n)
gen c, t, a
c.add BracketLe
gen c, t, i
c.add BracketRi
of ArrayAt:
let (_, a, i) = sons3(t, n)
gen c, t, a
c.add Dot
c.add "a"
c.add BracketLe
gen c, t, i
c.add BracketRi
of FieldAt:
let (_, a, b) = sons3(t, n)
gen c, t, a
let field = t[b].immediateVal
c.add Dot
c.add "F" & $field
of DerefFieldAt:
let (_, a, b) = sons3(t, n)
gen c, t, a
let field = t[b].immediateVal
c.add Arrow
c.add "F" & $field
of Load:
let (_, arg) = sons2(t, n)
c.add ParLe
c.add "*"
gen c, t, arg
c.add ParRi
of Store:
raiseAssert "Assumption was that Store is unused!"
of Asgn:
let (_, dest, src) = sons3(t, n)
gen c, t, dest
c.add AsgnOpr
gen c, t, src
c.add Semicolon
of CheckedRange:
c.add "nimCheckRange"
c.add ParLe
let (_, gotoInstr, x, a, b) = sons5(t, n)
gen c, t, x
c.add Comma
gen c, t, a
c.add Comma
gen c, t, b
c.add Comma
c.add "L" & $t[gotoInstr].label.int
c.add ParRi
of CheckedIndex:
c.add "nimCheckIndex"
c.add ParLe
let (gotoInstr, x, a) = sons3(t, n)
gen c, t, x
c.add Comma
gen c, t, a
c.add Comma
c.add "L" & $t[gotoInstr].label.int
c.add ParRi
of Call, IndirectCall:
let (typ, fn) = sons2(t, n)
gen c, t, fn
c.add ParLe
var i = 0
for ch in sonsFromN(t, n, 2):
if i > 0: c.add Comma
gen c, t, ch
inc i
c.add ParRi
if c.m.types[t[typ].typeId].kind == VoidTy:
c.add Semicolon
of CheckedCall, CheckedIndirectCall:
let (typ, gotoInstr, fn) = sons3(t, n)
gen c, t, fn
c.add ParLe
var i = 0
for ch in sonsFromN(t, n, 3):
if i > 0: c.add Comma
gen c, t, ch
inc i
c.add ParRi
if c.m.types[t[typ].typeId].kind == VoidTy:
c.add Semicolon
of CheckedAdd: checkedBinaryop "nimAddInt"
of CheckedSub: checkedBinaryop "nimSubInt"
of CheckedMul: checkedBinaryop "nimMulInt"
of CheckedDiv: checkedBinaryop "nimDivInt"
of CheckedMod: checkedBinaryop "nimModInt"
of Add: triop " + "
of Sub: triop " - "
of Mul: triop " * "
of Div: triop " / "
of Mod: triop " % "
of BitShl: triop " << "
of BitShr: triop " >> "
of BitAnd: triop " & "
of BitOr: triop " | "
of BitXor: triop " ^ "
of BitNot: binaryop " ~ "
of BoolNot: binaryop " !"
of Eq: cmpop " == "
of Le: cmpop " <= "
of Lt: cmpop " < "
of Cast: binaryop ""
of NumberConv: genNumberConv c, t, n
of CheckedObjConv: binaryop ""
of ObjConv: binaryop ""
of Emit: raiseAssert "cannot interpret: Emit"
of ProcDecl: genProcDecl c, t, n, false
of ForeignProcDecl: genProcDecl c, t, n, true
of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc:
c.add "cannot interpret: " & $t[n].kind
const
Prelude = """
/* GENERATED CODE. DO NOT EDIT. */
#ifdef __cplusplus
#define NB8 bool
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
// see #13798: to avoid conflicts for code emitting `#include <stdbool.h>`
#define NB8 _Bool
#else
typedef unsigned char NB8; // best effort
#endif
typedef unsigned char NC8;
typedef float NF32;
typedef double NF64;
#if defined(__BORLANDC__) || defined(_MSC_VER)
typedef signed char NI8;
typedef signed short int NI16;
typedef signed int NI32;
typedef __int64 NI64;
/* XXX: Float128? */
typedef unsigned char NU8;
typedef unsigned short int NU16;
typedef unsigned int NU32;
typedef unsigned __int64 NU64;
#elif defined(HAVE_STDINT_H)
#ifndef USE_NIM_NAMESPACE
# include <stdint.h>
#endif
typedef int8_t NI8;
typedef int16_t NI16;
typedef int32_t NI32;
typedef int64_t NI64;
typedef uint8_t NU8;
typedef uint16_t NU16;
typedef uint32_t NU32;
typedef uint64_t NU64;
#elif defined(HAVE_CSTDINT)
#ifndef USE_NIM_NAMESPACE
# include <cstdint>
#endif
typedef std::int8_t NI8;
typedef std::int16_t NI16;
typedef std::int32_t NI32;
typedef std::int64_t NI64;
typedef std::uint8_t NU8;
typedef std::uint16_t NU16;
typedef std::uint32_t NU32;
typedef std::uint64_t NU64;
#else
/* Unknown compiler/version, do our best */
#ifdef __INT8_TYPE__
typedef __INT8_TYPE__ NI8;
#else
typedef signed char NI8;
#endif
#ifdef __INT16_TYPE__
typedef __INT16_TYPE__ NI16;
#else
typedef signed short int NI16;
#endif
#ifdef __INT32_TYPE__
typedef __INT32_TYPE__ NI32;
#else
typedef signed int NI32;
#endif
#ifdef __INT64_TYPE__
typedef __INT64_TYPE__ NI64;
#else
typedef long long int NI64;
#endif
/* XXX: Float128? */
#ifdef __UINT8_TYPE__
typedef __UINT8_TYPE__ NU8;
#else
typedef unsigned char NU8;
#endif
#ifdef __UINT16_TYPE__
typedef __UINT16_TYPE__ NU16;
#else
typedef unsigned short int NU16;
#endif
#ifdef __UINT32_TYPE__
typedef __UINT32_TYPE__ NU32;
#else
typedef unsigned int NU32;
#endif
#ifdef __UINT64_TYPE__
typedef __UINT64_TYPE__ NU64;
#else
typedef unsigned long long int NU64;
#endif
#endif
#ifdef NIM_INTBITS
# if NIM_INTBITS == 64
typedef NI64 NI;
typedef NU64 NU;
# elif NIM_INTBITS == 32
typedef NI32 NI;
typedef NU32 NU;
# elif NIM_INTBITS == 16
typedef NI16 NI;
typedef NU16 NU;
# elif NIM_INTBITS == 8
typedef NI8 NI;
typedef NU8 NU;
# else
# error "invalid bit width for int"
# endif
#endif
#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
#define nimAddInt64(a, b, L) ({long long int res; if(__builtin_saddll_overflow(a, b, &res)) goto L; res})
#define nimSubInt64(a, b, L) ({long long int res; if(__builtin_ssubll_overflow(a, b, &res)) goto L; res})
#define nimMulInt64(a, b, L) ({long long int res; if(__builtin_smulll_overflow(a, b, &res)) goto L; res})
#define nimAddInt32(a, b, L) ({long int res; if(__builtin_sadd_overflow(a, b, &res)) goto L; res})
#define nimSubInt32(a, b, L) ({long int res; if(__builtin_ssub_overflow(a, b, &res)) goto L; res})
#define nimMulInt32(a, b, L) ({long int res; if(__builtin_smul_overflow(a, b, &res)) goto L; res})
#define nimCheckRange(x, a, b, L) ({if (x < a || x > b) goto L; x})
#define nimCheckIndex(x, a, L) ({if (x >= a) goto L; x})
"""
proc traverseCode(c: var GeneratedCode) =
const AllowedInToplevelC = {SummonConst, SummonGlobal, SummonThreadLocal,
ProcDecl, ForeignDecl, ForeignProcDecl}
var i = NodePos(0)
while i.int < c.m.code.len:
let oldLen = c.code.len
let moveToInitSection = c.m.code[NodePos(i)].kind notin AllowedInToplevelC
gen c, c.m.code, NodePos(i)
next c.m.code, i
if moveToInitSection:
for i in oldLen ..< c.code.len:
c.init.add c.code[i]
setLen c.code, oldLen
proc generateCode*(inp, outp: string) =
var c = initGeneratedCode(load(inp))
var co = TypeOrder()
traverseTypes(c.m.types, c.m.lit, co)
generateTypes(c, c.m.types, c.m.lit, co)
let typeDecls = move c.code
traverseCode c
var f = CppFile(f: open(outp, fmWrite))
f.write "#define NIM_INTBITS " & $c.m.intbits & "\n"
f.write Prelude
writeTokenSeq f, c.includes, c
writeTokenSeq f, typeDecls, c
writeTokenSeq f, c.data, c
writeTokenSeq f, c.protos, c
writeTokenSeq f, c.code, c
if c.init.len > 0:
f.write "void __attribute__((constructor)) init(void) {"
writeTokenSeq f, c.init, c
f.write "}\n\n"
f.f.close

View File

@@ -1,105 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nim Intermediate Representation, designed to capture all of Nim's semantics without losing too much
## precious information. Can easily be translated into C. And to JavaScript, hopefully.
from std/os import addFileExt, `/`, createDir
import std / assertions
import ".." / [ast, modulegraphs, renderer, transf, options, msgs, lineinfos]
import nirtypes, nirinsts, ast2ir, nirlineinfos, nirfiles, nirvm
import ".." / ic / [rodfiles, bitabs]
type
PCtx* = ref object of TPassContext
m: ModuleCon
c: ProcCon
oldErrorCount: int
bytecode: Bytecode
proc newCtx*(module: PSym; g: ModuleGraph; idgen: IdGenerator): PCtx =
var lit = Literals()
var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
var m = initModuleCon(g, g.config, idgen, module, nirm)
m.noModularity = true
PCtx(m: m, c: initProcCon(m, nil, g.config), idgen: idgen, bytecode: initBytecode(nirm))
proc refresh*(c: PCtx; module: PSym; idgen: IdGenerator) =
#c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module, c.m.nirm)
#c.m.noModularity = true
c.c = initProcCon(c.m, nil, c.m.graph.config)
c.idgen = idgen
proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) =
if graph.repl.isNil:
graph.repl = newCtx(module, graph, idgen)
#registerAdditionalOps(PCtx graph.repl)
else:
refresh(PCtx graph.repl, module, idgen)
proc setupNirReplGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
setupGlobalCtx(module, graph, idgen)
result = PCtx graph.repl
proc evalStmt(c: PCtx; n: PNode) =
let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
let pc = genStmt(c.c, n)
#var res = ""
#toString c.m.nirm.code, NodePos(pc), c.m.nirm.lit.strings, c.m.nirm.lit.numbers, c.m.symnames, res
#res.add "\n--------------------------\n"
#toString res, c.m.types.g
if pc.int < c.m.nirm.code.len:
c.bytecode.interactive = c.m.graph.interactive
execCode c.bytecode, c.m.nirm.code, pc
#echo res
proc runCode*(c: PPassContext; n: PNode): PNode =
let c = PCtx(c)
# don't eval errornous code:
if c.oldErrorCount == c.m.graph.config.errorCounter:
evalStmt(c, n)
result = newNodeI(nkEmpty, n.info)
else:
result = n
c.oldErrorCount = c.m.graph.config.errorCounter
type
NirPassContext* = ref object of TPassContext
m: ModuleCon
c: ProcCon
proc openNirBackend*(g: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
var lit = Literals()
var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
let m = initModuleCon(g, g.config, idgen, module, nirm)
NirPassContext(m: m, c: initProcCon(m, nil, g.config), idgen: idgen)
proc gen(c: NirPassContext; n: PNode) =
let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
let pc = genStmt(c.c, n)
proc nirBackend*(c: PPassContext; n: PNode): PNode =
gen(NirPassContext(c), n)
result = n
proc closeNirBackend*(c: PPassContext; finalNode: PNode) =
discard nirBackend(c, finalNode)
let c = NirPassContext(c)
let nimcache = getNimcacheDir(c.c.config).string
createDir nimcache
let outp = nimcache / c.m.module.name.s.addFileExt("nir")
#c.m.nirm.code = move c.c.code
try:
store c.m.nirm[], outp
echo "created: ", outp
except IOError:
rawMessage(c.c.config, errFatal, "serialization failed: " & outp)

View File

@@ -1,52 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nir Compiler.
import ".." / ic / [bitabs, rodfiles]
import nirinsts, nirtypes, nirlineinfos, nirfiles, cir
proc view(filename: string) =
let m = load(filename)
var res = ""
allTreesToString m.code, m.lit.strings, m.lit.numbers, m.symnames, res
res.add "\n# TYPES\n"
nirtypes.toString res, m.types
echo res
import std / [syncio, parseopt]
proc writeHelp =
echo """Usage: nirc view|c <file.nir>"""
quit 0
proc main =
var inp = ""
var cmd = ""
for kind, key, val in getopt():
case kind
of cmdArgument:
if cmd.len == 0: cmd = key
elif inp.len == 0: inp = key
else: quit "Error: too many arguments"
of cmdLongOption, cmdShortOption:
case key
of "help", "h": writeHelp()
of "version", "v": stdout.write "1.0\n"
of cmdEnd: discard
if inp.len == 0:
quit "Error: no input file specified"
case cmd
of "", "view":
view inp
of "c":
let outp = inp & ".c"
cir.generateCode inp, outp
main()

View File

@@ -1,83 +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 ".." / ic / [bitabs, rodfiles]
import nirinsts, nirtypes, nirlineinfos
type
NirModule* = object
code*: Tree
man*: LineInfoManager
types*: TypeGraph
lit*: Literals
namespace*: LitId
intbits*: uint32
symnames*: SymNames
proc load*(filename: string): NirModule =
let lit = Literals()
result = NirModule(lit: lit, types: initTypeGraph(lit))
var r = rodfiles.open(filename)
try:
r.loadHeader(nirCookie)
r.loadSection stringsSection
r.load result.lit.strings
r.loadSection numbersSection
r.load result.lit.numbers
r.loadSection bodiesSection
r.load result.code
r.loadSection typesSection
r.load result.types
r.loadSection sideChannelSection
r.load result.man
r.loadSection namespaceSection
r.loadPrim result.namespace
r.loadPrim result.intbits
r.loadSection symnamesSection
r.load result.symnames
finally:
r.close()
proc store*(m: NirModule; outp: string) =
var r = rodfiles.create(outp)
try:
r.storeHeader(nirCookie)
r.storeSection stringsSection
r.store m.lit.strings
r.storeSection numbersSection
r.store m.lit.numbers
r.storeSection bodiesSection
r.store m.code
r.storeSection typesSection
r.store m.types
r.storeSection sideChannelSection
r.store m.man
r.storeSection namespaceSection
r.storePrim m.namespace
r.storePrim m.intbits
r.storeSection symnamesSection
r.store m.symnames
finally:
r.close()
if r.err != ok:
raise newException(IOError, "could store into: " & outp)

View File

@@ -1,582 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## NIR instructions. Somewhat inspired by LLVM's instructions.
import std / [assertions, hashes]
import .. / ic / [bitabs, rodfiles]
import nirlineinfos, nirtypes
const
NirVersion = 1
nirCookie* = [byte(0), byte('N'), byte('I'), byte('R'),
byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(NirVersion)]
type
SymId* = distinct int
proc `$`*(s: SymId): string {.borrow.}
proc hash*(s: SymId): Hash {.borrow.}
proc `==`*(a, b: SymId): bool {.borrow.}
type
Opcode* = enum
Nop,
ImmediateVal,
IntVal,
StrVal,
SymDef,
SymUse,
Typed, # with type ID
PragmaId, # with Pragma ID, possible values: see PragmaKey enum
NilVal,
Label,
Goto,
CheckedGoto,
LoopLabel,
GotoLoop, # last atom
ModuleSymUse, # `"module".x`
ArrayConstr,
ObjConstr,
Ret,
Yld,
Select,
SelectPair, # ((values...), Label)
SelectList, # (values...)
SelectValue, # (value)
SelectRange, # (valueA..valueB)
ForeignDecl, # Can wrap SummonGlobal, SummonThreadLocal, SummonConst
SummonGlobal,
SummonThreadLocal,
Summon, # x = Summon Typed <Type ID>; x begins to live
SummonResult,
SummonParam,
SummonConst,
Kill, # `Kill x`: scope end for `x`
AddrOf,
ArrayAt, # a[i]
DerefArrayAt, # a[i] where `a` is a PtrArray; `a[][i]`
FieldAt, # obj.field
DerefFieldAt, # obj[].field
Load, # a[]
Store, # a[] = b
Asgn, # a = b
SetExc,
TestExc,
CheckedRange,
CheckedIndex,
Call,
IndirectCall,
CheckedCall, # call that can raise
CheckedIndirectCall, # call that can raise
CheckedAdd, # with overflow checking etc.
CheckedSub,
CheckedMul,
CheckedDiv,
CheckedMod,
Add,
Sub,
Mul,
Div,
Mod,
BitShl,
BitShr,
BitAnd,
BitOr,
BitXor,
BitNot,
BoolNot,
Eq,
Le,
Lt,
Cast,
NumberConv,
CheckedObjConv,
ObjConv,
TestOf,
Emit,
ProcDecl,
ForeignProcDecl,
PragmaPair
type
PragmaKey* = enum
FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall,
CoreName,
ExternName,
HeaderImport,
DllImport,
DllExport,
ObjExport
const
LastAtomicValue = GotoLoop
OpcodeBits = 8'u32
OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal}
ValueProducing* = {
ImmediateVal,
IntVal,
StrVal,
SymUse,
NilVal,
ModuleSymUse,
ArrayConstr,
ObjConstr,
CheckedAdd,
CheckedSub,
CheckedMul,
CheckedDiv,
CheckedMod,
Add,
Sub,
Mul,
Div,
Mod,
BitShl,
BitShr,
BitAnd,
BitOr,
BitXor,
BitNot,
BoolNot,
Eq,
Le,
Lt,
Cast,
NumberConv,
CheckedObjConv,
ObjConv,
AddrOf,
Load,
ArrayAt,
DerefArrayAt,
FieldAt,
DerefFieldAt,
TestOf
}
type
Instr* = object # 8 bytes
x: uint32
info*: PackedLineInfo
template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask)
template operand(n: Instr): uint32 = (n.x shr OpcodeBits)
template rawOperand*(n: Instr): uint32 = (n.x shr OpcodeBits)
template toX(k: Opcode; operand: uint32): uint32 =
uint32(k) or (operand shl OpcodeBits)
template toX(k: Opcode; operand: LitId): uint32 =
uint32(k) or (operand.uint32 shl OpcodeBits)
type
Tree* = object
nodes: seq[Instr]
Values* = object
numbers: BiTable[int64]
strings: BiTable[string]
type
PatchPos* = distinct int
NodePos* = distinct int
const
InvalidPatchPos* = PatchPos(-1)
proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos =
result = PatchPos tree.nodes.len
tree.nodes.add Instr(x: toX(kind, 1'u32), info: info)
proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
proc patch*(tree: var Tree; pos: PatchPos) =
let pos = pos.int
let k = tree.nodes[pos].kind
assert k > LastAtomicValue
let distance = int32(tree.nodes.len - pos)
assert distance > 0
tree.nodes[pos].x = toX(k, cast[uint32](distance))
template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) =
let pos = prepare(tree, info, kind)
body
patch(tree, pos)
template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
let pos = prepare(tree, info, kind)
tree.addTyped info, typ
body
patch(tree, pos)
proc len*(tree: Tree): int {.inline.} = tree.nodes.len
template rawSpan(n: Instr): int = int(operand(n))
proc nextChild(tree: Tree; pos: var int) {.inline.} =
if tree.nodes[pos].kind > LastAtomicValue:
assert tree.nodes[pos].operand > 0'u32
inc pos, tree.nodes[pos].rawSpan
else:
inc pos
proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos)
template firstSon*(n: NodePos): NodePos = NodePos(n.int+1)
template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2)
iterator sons*(tree: Tree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > LastAtomicValue
let last = pos + tree.nodes[pos].rawSpan
inc pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sonsFrom1*(tree: Tree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > LastAtomicValue
let last = pos + tree.nodes[pos].rawSpan
inc pos
nextChild tree, pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > LastAtomicValue
let last = pos + tree.nodes[pos].rawSpan
inc pos
for i in 1..toSkip:
nextChild tree, pos
while pos < last:
yield NodePos pos
nextChild tree, pos
template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int]
iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos =
var pos = n.int
assert tree[parent].kind > LastAtomicValue
let last = parent.int + tree[parent].rawSpan
while pos < last:
yield NodePos pos
nextChild tree, pos
proc span(tree: Tree; pos: int): int {.inline.} =
if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
proc copyTree*(dest: var Tree; src: Tree) =
let pos = 0
let L = span(src, pos)
let d = dest.nodes.len
dest.nodes.setLen(d + L)
assert L > 0
for i in 0..<L:
dest.nodes[d+i] = src.nodes[pos+i]
proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
result = (NodePos a, NodePos b)
proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
result = (NodePos a, NodePos b, NodePos c)
proc sons4*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
let d = c + span(tree, c)
result = (NodePos a, NodePos b, NodePos c, NodePos d)
proc sons5*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos, NodePos) {.inline.} =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
let d = c + span(tree, c)
let e = d + span(tree, d)
result = (NodePos a, NodePos b, NodePos c, NodePos d, NodePos e)
proc typeId*(ins: Instr): TypeId {.inline.} =
assert ins.kind == Typed
result = TypeId(ins.operand)
proc symId*(ins: Instr): SymId {.inline.} =
assert ins.kind in {SymUse, SymDef}
result = SymId(ins.operand)
proc immediateVal*(ins: Instr): int {.inline.} =
assert ins.kind == ImmediateVal
result = cast[int](ins.operand)
proc litId*(ins: Instr): LitId {.inline.} =
assert ins.kind in {StrVal, IntVal}
result = LitId(ins.operand)
type
LabelId* = distinct int
proc `==`*(a, b: LabelId): bool {.borrow.}
proc hash*(a: LabelId): Hash {.borrow.}
proc label*(ins: Instr): LabelId {.inline.} =
assert ins.kind in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
result = LabelId(ins.operand)
proc newLabel*(labelGen: var int): LabelId {.inline.} =
result = LabelId labelGen
inc labelGen
proc newLabels*(labelGen: var int; n: int): LabelId {.inline.} =
result = LabelId labelGen
inc labelGen, n
proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId =
assert k in {Label, LoopLabel}
result = LabelId labelGen
t.nodes.add Instr(x: toX(k, uint32(result)), info: info)
inc labelGen
proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) =
assert k in {Goto, GotoLoop, CheckedGoto}
t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} =
assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info)
proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
proc addNop*(t: var Tree; info: PackedLineInfo) {.inline.} =
t.nodes.add Instr(x: toX(Nop, 0'u32), info: info)
proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} =
assert typ.int >= 0
t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} =
assert typ.int >= 0
assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam, SummonResult}
let x = prepare(t, info, opc)
t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
patch t, x
proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) =
assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) =
t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info)
proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
buildTyped t, info, NumberConv, typ:
t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info)
proc boolVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; b: bool) =
buildTyped t, info, NumberConv, Bool8Id:
t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(ord b))), info: info)
proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info)
proc addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) =
t.nodes.add Instr(x: toX(StrVal, uint32(s)), info: info)
proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) =
buildTyped t, info, NumberConv, typ:
t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info)
proc store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes
proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes
proc escapeToNimLit*(s: string; result: var string) =
result.add '"'
for c in items s:
if c < ' ' or int(c) >= 128:
result.add '\\'
result.addInt int(c)
elif c == '\\':
result.add r"\\"
elif c == '\n':
result.add r"\n"
elif c == '\r':
result.add r"\r"
elif c == '\t':
result.add r"\t"
else:
result.add c
result.add '"'
type
SymNames* = object
s: seq[LitId]
proc `[]=`*(t: var SymNames; key: SymId; val: LitId) =
let k = int(key)
if k >= t.s.len: t.s.setLen k+1
t.s[k] = val
proc `[]`*(t: SymNames; key: SymId): LitId =
let k = int(key)
if k < t.s.len: result = t.s[k]
else: result = LitId(0)
template localName(s: SymId): string =
let name = names[s]
if name != LitId(0):
strings[name]
else:
$s.int
proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s)
proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s)
proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64];
names: SymNames;
r: var string; nesting = 0) =
if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}:
r.add ' '
case t[pos].kind
of Nop: r.add "Nop"
of ImmediateVal:
r.add $t[pos].operand
of IntVal:
r.add "IntVal "
r.add $integers[LitId t[pos].operand]
of StrVal:
escapeToNimLit(strings[LitId t[pos].operand], r)
of SymDef:
r.add "SymDef "
r.add localName(SymId t[pos].operand)
of SymUse:
r.add "SymUse "
r.add localName(SymId t[pos].operand)
of PragmaId:
r.add $cast[PragmaKey](t[pos].operand)
of Typed:
r.add "T<"
r.add $t[pos].operand
r.add ">"
of NilVal:
r.add "NilVal"
of Label:
# undo the nesting:
var spaces = r.len-1
while spaces >= 0 and r[spaces] == ' ': dec spaces
r.setLen spaces+1
r.add "\n L"
r.add $t[pos].operand
of Goto, CheckedGoto, LoopLabel, GotoLoop:
r.add $t[pos].kind
r.add " L"
r.add $t[pos].operand
else:
r.add $t[pos].kind
r.add "{\n"
for i in 0..<(nesting+1)*2: r.add ' '
for p in sons(t, pos):
toString t, p, strings, integers, names, r, nesting+1
r.add "\n"
for i in 0..<nesting*2: r.add ' '
r.add "}"
proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64];
names: SymNames;
r: var string) =
var i = 0
while i < t.len:
toString t, NodePos(i), strings, integers, names, r
nextChild t, i
type
Value* = distinct Tree
proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} =
assert k in ValueProducing - ValueProducingAtoms
result = prepare(Tree(dest), info, k)
proc patch*(dest: var Value; pos: PatchPos) {.inline.} =
patch(Tree(dest), pos)
proc localToValue*(info: PackedLineInfo; s: SymId): Value =
result = Value(Tree())
Tree(result).addSymUse info, s
proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0
proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0
proc extractTemp*(v: Value): SymId =
if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse:
result = SymId(Tree(v)[NodePos 0].operand)
else:
result = SymId(-1)
proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src)
proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) =
assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) =
let pos = prepare(Tree(tree), info, kind)
body
patch(tree, pos)
proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} =
addTyped(Tree(t), info, typ)
template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
let pos = prepare(tree, info, kind)
tree.addTyped info, typ
body
patch(tree, pos)
proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
addStrVal(Tree(t), strings, info, s)
proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) =
addNilVal Tree(t), info, typ
proc addIntVal*(t: var Value; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
addIntVal Tree(t), integers, info, typ, x

View File

@@ -1,104 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Management of slots. Similar to "register allocation"
## in lower level languages.
import std / [assertions, tables]
import nirtypes, nirinsts
type
SlotManagerFlag* = enum
ReuseTemps,
ReuseVars
SlotKind* = enum
Temp, Perm
SlotManager* = object # "register allocator"
live: Table[SymId, (SlotKind, TypeId)]
dead: Table[TypeId, seq[SymId]]
flags: set[SlotManagerFlag]
inScope: seq[SymId]
proc initSlotManager*(flags: set[SlotManagerFlag]): SlotManager {.inline.} =
SlotManager(flags: flags)
proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag; k: SlotKind;
symIdgen: var int32): SymId {.inline.} =
if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0:
result = m.dead[t].pop()
else:
inc symIdgen
result = SymId(symIdgen)
m.inScope.add result
m.live[result] = (k, t)
proc allocTemp*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} =
result = allocRaw(m, t, ReuseTemps, Temp, symIdgen)
proc allocVar*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} =
result = allocRaw(m, t, ReuseVars, Perm, symIdgen)
proc freeLoc*(m: var SlotManager; s: SymId) =
let t = m.live.getOrDefault(s)
assert t[1].int != 0
m.live.del s
m.dead.mgetOrPut(t[1], @[]).add s
proc freeTemp*(m: var SlotManager; s: SymId) =
let t = m.live.getOrDefault(s)
if t[1].int != 0 and t[0] == Temp:
m.live.del s
m.dead.mgetOrPut(t[1], @[]).add s
iterator stillAlive*(m: SlotManager): (SymId, TypeId) =
for k, v in pairs(m.live):
yield (k, v[1])
proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s][1]
proc openScope*(m: var SlotManager) =
m.inScope.add SymId(-1) # add marker
proc closeScope*(m: var SlotManager) =
var i = m.inScope.len - 1
while i >= 0:
if m.inScope[i] == SymId(-1):
m.inScope.setLen i
break
dec i
when isMainModule:
var symIdgen: int32
var m = initSlotManager({ReuseTemps})
var g = initTypeGraph(Literals())
let a = g.openType ArrayTy
g.addBuiltinType Int8Id
g.addArrayLen 5
let finalArrayType = finishType(g, a)
let obj = g.openType ObjectDecl
g.addName "MyType"
g.addField "p", finalArrayType, 0
let objB = finishType(g, obj)
let x = m.allocTemp(objB, symIdgen)
assert x.int == 0
let y = m.allocTemp(objB, symIdgen)
assert y.int == 1
let z = m.allocTemp(Int8Id, symIdgen)
assert z.int == 2
m.freeLoc y
let y2 = m.allocTemp(objB, symIdgen)
assert y2.int == 1

View File

@@ -1,475 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Type system for NIR. Close to C's type system but without its quirks.
import std / [assertions, hashes]
import .. / ic / [bitabs, rodfiles]
type
NirTypeKind* = enum
VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal,
IntVal, SizeVal, AlignVal, OffsetVal,
AnnotationVal,
ObjectTy,
UnionTy,
VarargsTy, # the `...` in a C prototype; also the last "atom"
APtrTy, # pointer to aliasable memory
UPtrTy, # pointer to unique/unaliasable memory
AArrayPtrTy, # pointer to array of aliasable memory
UArrayPtrTy, # pointer to array of unique/unaliasable memory
ArrayTy,
LastArrayTy, # array of unspecified size as a last field inside an object
ProcTy,
ObjectDecl,
UnionDecl,
FieldDecl
const
TypeKindBits = 8'u32
TypeKindMask = (1'u32 shl TypeKindBits) - 1'u32
type
TypeNode* = object # 4 bytes
x: uint32
template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask)
template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits)
proc integralBits*(n: TypeNode): int {.inline.} =
# Number of bits in the IntTy, etc. Only valid for integral types.
assert n.kind in {IntTy, UIntTy, FloatTy, BoolTy, CharTy}
result = int(n.operand)
template toX(k: NirTypeKind; operand: uint32): uint32 =
uint32(k) or (operand shl TypeKindBits)
template toX(k: NirTypeKind; operand: LitId): uint32 =
uint32(k) or (operand.uint32 shl TypeKindBits)
type
TypeId* = distinct int
proc `==`*(a, b: TypeId): bool {.borrow.}
proc hash*(a: TypeId): Hash {.borrow.}
type
Literals* = ref object
strings*: BiTable[string]
numbers*: BiTable[int64]
TypeGraph* = object
nodes: seq[TypeNode]
lit: Literals
const
VoidId* = TypeId 0
Bool8Id* = TypeId 1
Char8Id* = TypeId 2
Int8Id* = TypeId 3
Int16Id* = TypeId 4
Int32Id* = TypeId 5
Int64Id* = TypeId 6
UInt8Id* = TypeId 7
UInt16Id* = TypeId 8
UInt32Id* = TypeId 9
UInt64Id* = TypeId 10
Float32Id* = TypeId 11
Float64Id* = TypeId 12
VoidPtrId* = TypeId 13
LastBuiltinId* = 13
proc initTypeGraph*(lit: Literals): TypeGraph =
result = TypeGraph(nodes: @[
TypeNode(x: toX(VoidTy, 0'u32)),
TypeNode(x: toX(BoolTy, 8'u32)),
TypeNode(x: toX(CharTy, 8'u32)),
TypeNode(x: toX(IntTy, 8'u32)),
TypeNode(x: toX(IntTy, 16'u32)),
TypeNode(x: toX(IntTy, 32'u32)),
TypeNode(x: toX(IntTy, 64'u32)),
TypeNode(x: toX(UIntTy, 8'u32)),
TypeNode(x: toX(UIntTy, 16'u32)),
TypeNode(x: toX(UIntTy, 32'u32)),
TypeNode(x: toX(UIntTy, 64'u32)),
TypeNode(x: toX(FloatTy, 32'u32)),
TypeNode(x: toX(FloatTy, 64'u32)),
TypeNode(x: toX(APtrTy, 2'u32)),
TypeNode(x: toX(VoidTy, 0'u32))
], lit: lit)
assert result.nodes.len == LastBuiltinId+2
type
TypePatchPos* = distinct int
const
InvalidTypePatchPos* = TypePatchPos(-1)
LastAtomicValue = VarargsTy
proc isValid(p: TypePatchPos): bool {.inline.} = p.int != -1
proc prepare(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
result = TypePatchPos tree.nodes.len
tree.nodes.add TypeNode(x: toX(kind, 1'u32))
proc isAtom(tree: TypeGraph; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
proc isAtom(tree: TypeGraph; pos: TypeId): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
proc patch(tree: var TypeGraph; pos: TypePatchPos) =
let pos = pos.int
let k = tree.nodes[pos].kind
assert k > LastAtomicValue
let distance = int32(tree.nodes.len - pos)
assert distance > 0
tree.nodes[pos].x = toX(k, cast[uint32](distance))
proc len*(tree: TypeGraph): int {.inline.} = tree.nodes.len
template rawSpan(n: TypeNode): int = int(operand(n))
proc nextChild(tree: TypeGraph; pos: var int) {.inline.} =
if tree.nodes[pos].kind > LastAtomicValue:
assert tree.nodes[pos].operand > 0'u32
inc pos, tree.nodes[pos].rawSpan
else:
inc pos
iterator sons*(tree: TypeGraph; n: TypeId): TypeId =
var pos = n.int
assert tree.nodes[pos].kind > LastAtomicValue
let last = pos + tree.nodes[pos].rawSpan
inc pos
while pos < last:
yield TypeId pos
nextChild tree, pos
template `[]`*(t: TypeGraph; n: TypeId): TypeNode = t.nodes[n.int]
proc elementType*(tree: TypeGraph; n: TypeId): TypeId {.inline.} =
assert tree[n].kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ArrayTy, LastArrayTy}
result = TypeId(n.int+1)
proc litId*(n: TypeNode): LitId {.inline.} =
assert n.kind in {NameVal, IntVal, SizeVal, AlignVal, OffsetVal, AnnotationVal, ObjectTy, UnionTy}
result = LitId(n.operand)
proc kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind
proc span(tree: TypeGraph; pos: int): int {.inline.} =
if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
proc sons2(tree: TypeGraph; n: TypeId): (TypeId, TypeId) =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
result = (TypeId a, TypeId b)
proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
result = (TypeId a, TypeId b, TypeId c)
proc arrayName*(tree: TypeGraph; n: TypeId): TypeId {.inline.} =
assert tree[n].kind == ArrayTy
let (_, _, c) = sons3(tree, n)
result = c
proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt =
assert tree[n].kind == ArrayTy
let (_, b) = sons2(tree, n)
result = tree.lit.numbers[LitId tree[b].operand]
proc returnType*(tree: TypeGraph; n: TypeId): (TypeId, TypeId) =
# Returns the positions of the return type + calling convention.
var pos = n.int
assert tree.nodes[pos].kind == ProcTy
let a = n.int+1
let b = a + span(tree, a)
result = (TypeId b, TypeId a) # not a typo, order is reversed
iterator params*(tree: TypeGraph; n: TypeId): TypeId =
var pos = n.int
assert tree.nodes[pos].kind == ProcTy
let last = pos + tree.nodes[pos].rawSpan
inc pos
nextChild tree, pos
nextChild tree, pos
while pos < last:
yield TypeId pos
nextChild tree, pos
proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy,
ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl,
FieldDecl}
result = prepare(tree, kind)
template typeInvariant(p: TypePatchPos) =
when false:
if tree[TypeId(p)].kind == FieldDecl:
var k = 0
for ch in sons(tree, TypeId(p)):
inc k
assert k > 2, "damn! " & $k
proc sealType*(tree: var TypeGraph; p: TypePatchPos) =
patch tree, p
typeInvariant(p)
proc finishType*(tree: var TypeGraph; p: TypePatchPos): TypeId =
# Search for an existing instance of this type in
# order to reduce memory consumption:
patch tree, p
typeInvariant(p)
let s = span(tree, p.int)
var i = 0
while i < p.int:
if tree.nodes[i].x == tree.nodes[p.int].x:
var isMatch = true
for j in 1..<s:
if tree.nodes[j+i].x == tree.nodes[j+p.int].x:
discard "still a match"
else:
isMatch = false
break
if isMatch:
if p.int+s == tree.len:
setLen tree.nodes, p.int
return TypeId(i)
nextChild tree, i
result = TypeId(p)
proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId =
assert kind in {ObjectTy, UnionTy}
let content = TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
for i in 0..<tree.len:
if tree.nodes[i].x == content.x:
return TypeId(i)
result = TypeId tree.nodes.len
tree.nodes.add content
proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) =
assert kind in {ObjectTy, UnionTy}
tree.nodes.add TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
proc getTypeTag*(tree: TypeGraph; t: TypeId): string =
assert tree[t].kind in {ObjectTy, UnionTy}
result = tree.lit.strings[LitId tree[t].operand]
proc addVarargs*(tree: var TypeGraph) =
tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32))
proc getFloat128Type*(tree: var TypeGraph): TypeId =
result = TypeId tree.nodes.len
tree.nodes.add TypeNode(x: toX(FloatTy, 128'u32))
proc addBuiltinType*(g: var TypeGraph; id: TypeId) =
g.nodes.add g[id]
template firstSon*(n: TypeId): TypeId = TypeId(n.int+1)
proc addType*(g: var TypeGraph; t: TypeId) =
# We cannot simply copy `*Decl` nodes. We have to introduce `*Ty` nodes instead:
if g[t].kind in {ObjectDecl, UnionDecl}:
assert g[t.firstSon].kind == NameVal
let name = LitId g[t.firstSon].operand
if g[t].kind == ObjectDecl:
g.nodes.add TypeNode(x: toX(ObjectTy, name))
else:
g.nodes.add TypeNode(x: toX(UnionTy, name))
else:
let pos = t.int
let L = span(g, pos)
let d = g.nodes.len
g.nodes.setLen(d + L)
assert L > 0
for i in 0..<L:
g.nodes[d+i] = g.nodes[pos+i]
proc addArrayLen*(g: var TypeGraph; len: int64) =
g.nodes.add TypeNode(x: toX(IntVal, g.lit.numbers.getOrIncl(len)))
proc addSize*(g: var TypeGraph; s: int64) =
g.nodes.add TypeNode(x: toX(SizeVal, g.lit.numbers.getOrIncl(s)))
proc addOffset*(g: var TypeGraph; offset: int64) =
g.nodes.add TypeNode(x: toX(OffsetVal, g.lit.numbers.getOrIncl(offset)))
proc addAlign*(g: var TypeGraph; a: int64) =
g.nodes.add TypeNode(x: toX(AlignVal, g.lit.numbers.getOrIncl(a)))
proc addName*(g: var TypeGraph; name: string) =
g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
proc addAnnotation*(g: var TypeGraph; name: string) =
g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
proc addField*(g: var TypeGraph; name: string; typ: TypeId; offset: int64) =
let f = g.openType FieldDecl
g.addType typ
g.addOffset offset
g.addName name
sealType(g, f)
proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
let f = g.openType APtrTy
g.addType t
result = finishType(g, f)
proc arrayPtrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
let f = g.openType AArrayPtrTy
g.addType t
result = finishType(g, f)
proc store*(r: var RodFile; g: TypeGraph) =
storeSeq r, g.nodes
proc load*(r: var RodFile; g: var TypeGraph) =
loadSeq r, g.nodes
proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
case g[i].kind
of VoidTy: dest.add "void"
of IntTy:
dest.add "i"
dest.addInt g[i].operand
of UIntTy:
dest.add "u"
dest.addInt g[i].operand
of FloatTy:
dest.add "f"
dest.addInt g[i].operand
of BoolTy:
dest.add "b"
dest.addInt g[i].operand
of CharTy:
dest.add "c"
dest.addInt g[i].operand
of NameVal, AnnotationVal:
dest.add g.lit.strings[LitId g[i].operand]
of IntVal, SizeVal, AlignVal, OffsetVal:
dest.add $g[i].kind
dest.add ' '
dest.add $g.lit.numbers[LitId g[i].operand]
of VarargsTy:
dest.add "..."
of APtrTy:
dest.add "aptr["
toString(dest, g, g.elementType(i))
dest.add "]"
of UPtrTy:
dest.add "uptr["
toString(dest, g, g.elementType(i))
dest.add "]"
of AArrayPtrTy:
dest.add "aArrayPtr["
toString(dest, g, g.elementType(i))
dest.add "]"
of UArrayPtrTy:
dest.add "uArrayPtr["
toString(dest, g, g.elementType(i))
dest.add "]"
of ArrayTy:
dest.add "Array["
let (elems, len, name) = g.sons3(i)
toString(dest, g, elems)
dest.add ", "
toString(dest, g, len)
dest.add ", "
toString(dest, g, name)
dest.add "]"
of LastArrayTy:
# array of unspecified size as a last field inside an object
dest.add "LastArrayTy["
toString(dest, g, g.elementType(i))
dest.add "]"
of ObjectTy:
dest.add "object "
dest.add g.lit.strings[LitId g[i].operand]
of UnionTy:
dest.add "union "
dest.add g.lit.strings[LitId g[i].operand]
of ProcTy:
dest.add "proc["
for t in sons(g, i):
dest.add ' '
toString(dest, g, t)
dest.add "]"
of ObjectDecl:
dest.add "object["
for t in sons(g, i):
toString(dest, g, t)
dest.add '\n'
dest.add "]"
of UnionDecl:
dest.add "union["
for t in sons(g, i):
toString(dest, g, t)
dest.add '\n'
dest.add "]"
of FieldDecl:
dest.add "field["
for t in sons(g, i):
toString(dest, g, t)
dest.add ' '
dest.add "]"
when false:
let (typ, offset, name) = g.sons3(i)
toString(dest, g, typ)
dest.add ' '
toString(dest, g, offset)
dest.add ' '
toString(dest, g, name)
proc toString*(dest: var string; g: TypeGraph) =
var i = 0
while i < g.len:
dest.add "T<"
dest.addInt i
dest.add "> "
toString(dest, g, TypeId i)
dest.add '\n'
nextChild g, i
iterator allTypes*(g: TypeGraph; start = 0): TypeId =
var i = start
while i < g.len:
yield TypeId i
nextChild g, i
iterator allTypesIncludingInner*(g: TypeGraph; start = 0): TypeId =
var i = start
while i < g.len:
yield TypeId i
inc i
proc `$`(g: TypeGraph): string =
result = ""
toString(result, g)
when isMainModule:
var g = initTypeGraph(Literals())
let a = g.openType ArrayTy
g.addBuiltinType Int8Id
g.addArrayLen 5
g.addName "SomeArray"
let finalArrayType = finishType(g, a)
let obj = g.openType ObjectDecl
g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl("MyType")))
g.addField "p", finalArrayType, 0
sealType(g, obj)
echo g

File diff suppressed because it is too large Load Diff

View File

@@ -1,200 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## included from ast2ir.nim
#[
case s
of "abc", "abbd":
echo 1
of "hah":
echo 2
of "gah":
echo 3
# we produce code like this:
if s[0] <= 'a':
if s == "abc: goto L1
elif s == "abbd": goto L1
else:
if s[2] <= 'h':
if s == "hah": goto L2
elif s == "gah": goto L3
goto afterCase
L1:
echo 1
goto afterCase
L2:
echo 2
goto afterCase
L3:
echo 3
goto afterCase
afterCase: ...
]#
# We split the set of strings into 2 sets of roughly the same size.
# The condition used for splitting is a (position, char) tuple.
# Every string of length > position for which s[position] <= char is in one
# set else it is in the other set.
from std/sequtils import addUnique
type
Key = (LitId, LabelId)
proc splitValue(strings: BiTable[string]; a: openArray[Key]; position: int): (char, float) =
var cand: seq[char] = @[]
for t in items a:
let s = strings[t[0]]
if s.len > position: cand.addUnique s[position]
result = ('\0', -1.0)
for disc in items cand:
var hits = 0
for t in items a:
let s = strings[t[0]]
if s.len > position and s[position] <= disc:
inc hits
# the split is the better, the more `hits` is close to `a.len / 2`:
let grade = 100000.0 - abs(hits.float - a.len.float / 2.0)
if grade > result[1]:
result = (disc, grade)
proc tryAllPositions(strings: BiTable[string]; a: openArray[Key]): (char, int) =
var m = 0
for t in items a:
m = max(m, strings[t[0]].len)
result = ('\0', -1)
var best = -1.0
for i in 0 ..< m:
let current = splitValue(strings, a, i)
if current[1] > best:
best = current[1]
result = (current[0], i)
type
SearchKind = enum
LinearSearch, SplitSearch
SearchResult* = object
case kind: SearchKind
of LinearSearch:
a: seq[Key]
of SplitSearch:
span: int
best: (char, int)
proc emitLinearSearch(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
var d = SearchResult(kind: LinearSearch, a: @[])
for x in a: d.a.add x
dest.add d
proc split(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
if a.len <= 4:
emitLinearSearch strings, a, dest
else:
let best = tryAllPositions(strings, a)
var groupA: seq[Key] = @[]
var groupB: seq[Key] = @[]
for t in items a:
let s = strings[t[0]]
if s.len > best[1] and s[best[1]] <= best[0]:
groupA.add t
else:
groupB.add t
if groupA.len == 0 or groupB.len == 0:
emitLinearSearch strings, a, dest
else:
let toPatch = dest.len
dest.add SearchResult(kind: SplitSearch, span: 1, best: best)
split strings, groupA, dest
split strings, groupB, dest
let dist = dest.len - toPatch
assert dist > 0
dest[toPatch].span = dist
proc toProblemDescription(c: var ProcCon; n: PNode): (seq[Key], LabelId) =
result = (@[], newLabels(c.labelGen, n.len))
assert n.kind == nkCaseStmt
for i in 1..<n.len:
let it = n[i]
let thisBranch = LabelId(result[1].int + i - 1)
if it.kind == nkOfBranch:
for j in 0..<it.len-1:
assert it[j].kind in {nkStrLit..nkTripleStrLit}
result[0].add (c.lit.strings.getOrIncl(it[j].strVal), thisBranch)
proc decodeSolution(c: var ProcCon; dest: var Tree; s: seq[SearchResult]; i: int;
selector: Value; info: PackedLineInfo) =
case s[i].kind
of SplitSearch:
let thenA = i+1
let elseA = thenA + (if s[thenA].kind == LinearSearch: 1 else: s[thenA].span)
let best = s[i].best
let tmp = getTemp(c, Bool8Id, info)
buildTyped dest, info, Asgn, Bool8Id:
dest.copyTree tmp
buildTyped dest, info, Call, Bool8Id:
c.addUseCodegenProc dest, "nimStrAtLe", info
dest.copyTree selector
dest.addIntVal c.lit.numbers, info, c.m.nativeIntId, best[1]
dest.addIntVal c.lit.numbers, info, Char8Id, best[0].int
template then() =
c.decodeSolution dest, s, thenA, selector, info
template otherwise() =
c.decodeSolution dest, s, elseA, selector, info
buildIfThenElse tmp, then, otherwise
freeTemp c, tmp
of LinearSearch:
let tmp = getTemp(c, Bool8Id, info)
for x in s[i].a:
buildTyped dest, info, Asgn, Bool8Id:
dest.copyTree tmp
buildTyped dest, info, Call, Bool8Id:
c.addUseCodegenProc dest, "eqStrings", info
dest.copyTree selector
dest.addStrLit info, x[0]
buildIf tmp:
c.code.gotoLabel info, Goto, x[1]
freeTemp c, tmp
proc genStringCase(c: var ProcCon; n: PNode; d: var Value) =
let (problem, firstBranch) = toProblemDescription(c, n)
var solution: seq[SearchResult] = @[]
split c.lit.strings, problem, solution
# XXX Todo move complex case selector into a temporary.
let selector = c.genx(n[0])
let info = toLineInfo(c, n.info)
decodeSolution c, c.code, solution, 0, selector, info
let lend = newLabel(c.labelGen)
c.code.addLabel info, Goto, lend
for i in 1..<n.len:
let it = n[i]
let thisBranch = LabelId(firstBranch.int + i - 1)
c.code.addLabel info, Label, thisBranch
if it.kind == nkOfBranch:
gen(c, it.lastSon, d)
c.code.addLabel info, Goto, lend
else:
gen(c, it.lastSon, d)
c.code.addLabel info, Label, lend
freeTemp c, selector

View File

@@ -1,525 +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 std / [assertions, tables, sets]
import ".." / [ast, types, options, sighashes, modulegraphs]
import nirtypes
type
TypesCon* = object
processed: Table[ItemId, TypeId]
processedByName: Table[string, TypeId]
recursionCheck: HashSet[ItemId]
conf: ConfigRef
stringType: TypeId
proc initTypesCon*(conf: ConfigRef): TypesCon =
TypesCon(conf: conf, stringType: TypeId(-1))
proc mangle(c: var TypesCon; t: PType): string =
result = $sighashes.hashType(t, c.conf)
template cached(c: var TypesCon; t: PType; body: untyped) =
result = c.processed.getOrDefault(t.itemId)
if result.int == 0:
body
c.processed[t.itemId] = result
template cachedByName(c: var TypesCon; t: PType; body: untyped) =
let key = mangle(c, t)
result = c.processedByName.getOrDefault(key)
if result.int == 0:
body
c.processedByName[key] = result
proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId
proc collectFieldTypes(c: var TypesCon; g: var TypeGraph; n: PNode; dest: var Table[ItemId, TypeId]) =
case n.kind
of nkRecList:
for i in 0..<n.len:
collectFieldTypes(c, g, n[i], dest)
of nkRecCase:
assert(n[0].kind == nkSym)
collectFieldTypes(c, g, n[0], dest)
for i in 1..<n.len:
case n[i].kind
of nkOfBranch, nkElse:
collectFieldTypes c, g, lastSon(n[i]), dest
else: discard
of nkSym:
dest[n.sym.itemId] = typeToIr(c, g, n.sym.typ)
else:
assert false, "unknown node kind: " & $n.kind
proc objectToIr(c: var TypesCon; g: var TypeGraph; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) =
case n.kind
of nkRecList:
for i in 0..<n.len:
objectToIr(c, g, n[i], fieldTypes, unionId)
of nkRecCase:
assert(n[0].kind == nkSym)
objectToIr(c, g, n[0], fieldTypes, unionId)
let u = openType(g, UnionDecl)
g.addName "u_" & $unionId
inc unionId
for i in 1..<n.len:
case n[i].kind
of nkOfBranch, nkElse:
let subObj = openType(g, ObjectDecl)
g.addName "uo_" & $unionId & "_" & $i
objectToIr c, g, lastSon(n[i]), fieldTypes, unionId
sealType(g, subObj)
else: discard
sealType(g, u)
of nkSym:
g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset
else:
assert false, "unknown node kind: " & $n.kind
proc objectToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
if t.baseClass != nil:
# ensure we emitted the base type:
discard typeToIr(c, g, t.baseClass)
var unionId = 0
var fieldTypes = initTable[ItemId, TypeId]()
collectFieldTypes c, g, t.n, fieldTypes
let obj = openType(g, ObjectDecl)
g.addName mangle(c, t)
g.addSize c.conf.getSize(t)
g.addAlign c.conf.getAlign(t)
if t.baseClass != nil:
g.addNominalType(ObjectTy, mangle(c, t.baseClass))
else:
g.addBuiltinType VoidId # object does not inherit
if not lacksMTypeField(t):
let f2 = g.openType FieldDecl
let voidPtr = openType(g, APtrTy)
g.addBuiltinType(VoidId)
sealType(g, voidPtr)
g.addOffset 0 # type field is always at offset 0
g.addName "m_type"
sealType(g, f2) # FieldDecl
objectToIr c, g, t.n, fieldTypes, unionId
result = finishType(g, obj)
proc objectHeaderToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
result = g.nominalType(ObjectTy, mangle(c, t))
proc tupleToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
var fieldTypes = newSeq[TypeId](t.len)
for i in 0..<t.len:
fieldTypes[i] = typeToIr(c, g, t[i])
let obj = openType(g, ObjectDecl)
g.addName mangle(c, t)
g.addSize c.conf.getSize(t)
g.addAlign c.conf.getAlign(t)
var accum = OffsetAccum(maxAlign: 1)
for i in 0..<t.len:
let child = t[i]
g.addField "f_" & $i, fieldTypes[i], accum.offset
computeSizeAlign(c.conf, child)
accum.align(child.align)
accum.inc(int32(child.size))
result = finishType(g, obj)
proc procToIr(c: var TypesCon; g: var TypeGraph; t: PType; addEnv = false): TypeId =
var fieldTypes = newSeq[TypeId](0)
for i in 0..<t.len:
if t[i] == nil or not isCompileTimeOnly(t[i]):
fieldTypes.add typeToIr(c, g, t[i])
let obj = openType(g, ProcTy)
case t.callConv
of ccNimCall, ccFastCall, ccClosure: g.addAnnotation "__fastcall"
of ccStdCall: g.addAnnotation "__stdcall"
of ccCDecl: g.addAnnotation "__cdecl"
of ccSafeCall: g.addAnnotation "__safecall"
of ccSysCall: g.addAnnotation "__syscall"
of ccInline: g.addAnnotation "__inline"
of ccNoInline: g.addAnnotation "__noinline"
of ccThisCall: g.addAnnotation "__thiscall"
of ccNoConvention, ccMember: g.addAnnotation ""
for i in 0..<fieldTypes.len:
g.addType fieldTypes[i]
if addEnv:
let a = openType(g, APtrTy)
g.addBuiltinType(VoidId)
sealType(g, a)
if tfVarargs in t.flags:
g.addVarargs()
result = finishType(g, obj)
proc nativeInt(c: TypesCon): TypeId =
case c.conf.target.intSize
of 2: result = Int16Id
of 4: result = Int32Id
else: result = Int64Id
proc openArrayPayloadType*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
let e = elementType(t)
let elementType = typeToIr(c, g, e)
let arr = g.openType AArrayPtrTy
g.addType elementType
result = finishType(g, arr) # LastArrayTy
proc openArrayToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
# object (a: ArrayPtr[T], len: int)
let e = elementType(t)
let mangledBase = mangle(c, e)
let typeName = "NimOpenArray" & mangledBase
let elementType = typeToIr(c, g, e)
#assert elementType.int >= 0, typeToString(t)
let p = openType(g, ObjectDecl)
g.addName typeName
g.addSize c.conf.target.ptrSize*2
g.addAlign c.conf.target.ptrSize
let f = g.openType FieldDecl
let arr = g.openType AArrayPtrTy
g.addType elementType
sealType(g, arr) # LastArrayTy
g.addOffset 0
g.addName "data"
sealType(g, f) # FieldDecl
g.addField "len", c.nativeInt, c.conf.target.ptrSize
result = finishType(g, p) # ObjectDecl
proc strPayloadType(c: var TypesCon; g: var TypeGraph): (string, TypeId) =
result = ("NimStrPayload", TypeId(-1))
let p = openType(g, ObjectDecl)
g.addName result[0]
g.addSize c.conf.target.ptrSize*2
g.addAlign c.conf.target.ptrSize
g.addField "cap", c.nativeInt, 0
let f = g.openType FieldDecl
let arr = g.openType LastArrayTy
g.addBuiltinType Char8Id
result[1] = finishType(g, arr) # LastArrayTy
g.addOffset c.conf.target.ptrSize # comes after the len field
g.addName "data"
sealType(g, f) # FieldDecl
sealType(g, p)
proc strPayloadPtrType*(c: var TypesCon; g: var TypeGraph): (TypeId, TypeId) =
let (mangled, arrayType) = strPayloadType(c, g)
let ffp = g.openType APtrTy
g.addNominalType ObjectTy, mangled
result = (finishType(g, ffp), arrayType) # APtrTy
proc stringToIr(c: var TypesCon; g: var TypeGraph): TypeId =
#[
NimStrPayload = object
cap: int
data: UncheckedArray[char]
NimStringV2 = object
len: int
p: ptr NimStrPayload
]#
let payload = strPayloadType(c, g)
let str = openType(g, ObjectDecl)
g.addName "NimStringV2"
g.addSize c.conf.target.ptrSize*2
g.addAlign c.conf.target.ptrSize
g.addField "len", c.nativeInt, 0
let fp = g.openType FieldDecl
let ffp = g.openType APtrTy
g.addNominalType ObjectTy, "NimStrPayload"
sealType(g, ffp) # APtrTy
g.addOffset c.conf.target.ptrSize # comes after 'len' field
g.addName "p"
sealType(g, fp) # FieldDecl
result = finishType(g, str) # ObjectDecl
proc seqPayloadType(c: var TypesCon; g: var TypeGraph; t: PType): (string, TypeId) =
#[
NimSeqPayload[T] = object
cap: int
data: UncheckedArray[T]
]#
let e = elementType(t)
result = (mangle(c, e), TypeId(-1))
let payloadName = "NimSeqPayload" & result[0]
let elementType = typeToIr(c, g, e)
let p = openType(g, ObjectDecl)
g.addName payloadName
g.addSize c.conf.target.intSize
g.addAlign c.conf.target.intSize
g.addField "cap", c.nativeInt, 0
let f = g.openType FieldDecl
let arr = g.openType LastArrayTy
g.addType elementType
# DO NOT USE `finishType` here as it is an inner type. This is subtle and we
# probably need an even better API here.
sealType(g, arr)
result[1] = TypeId(arr)
g.addOffset c.conf.target.ptrSize
g.addName "data"
sealType(g, f) # FieldDecl
sealType(g, p)
proc seqPayloadPtrType*(c: var TypesCon; g: var TypeGraph; t: PType): (TypeId, TypeId) =
let (mangledBase, arrayType) = seqPayloadType(c, g, t)
let ffp = g.openType APtrTy
g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
result = (finishType(g, ffp), arrayType) # APtrTy
proc seqToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
#[
NimSeqV2*[T] = object
len: int
p: ptr NimSeqPayload[T]
]#
let (mangledBase, _) = seqPayloadType(c, g, t)
let sq = openType(g, ObjectDecl)
g.addName "NimSeqV2" & mangledBase
g.addSize c.conf.getSize(t)
g.addAlign c.conf.getAlign(t)
g.addField "len", c.nativeInt, 0
let fp = g.openType FieldDecl
let ffp = g.openType APtrTy
g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
sealType(g, ffp) # APtrTy
g.addOffset c.conf.target.ptrSize
g.addName "p"
sealType(g, fp) # FieldDecl
result = finishType(g, sq) # ObjectDecl
proc closureToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
# struct {fn(args, void* env), env}
# typedef struct {$n" &
# "N_NIMCALL_PTR($2, ClP_0) $3;$n" &
# "void* ClE_0;$n} $1;$n"
let mangledBase = mangle(c, t)
let typeName = "NimClosure" & mangledBase
let procType = procToIr(c, g, t, addEnv=true)
let p = openType(g, ObjectDecl)
g.addName typeName
g.addSize c.conf.getSize(t)
g.addAlign c.conf.getAlign(t)
let f = g.openType FieldDecl
g.addType procType
g.addOffset 0
g.addName "ClP_0"
sealType(g, f) # FieldDecl
let f2 = g.openType FieldDecl
let voidPtr = openType(g, APtrTy)
g.addBuiltinType(VoidId)
sealType(g, voidPtr)
g.addOffset c.conf.target.ptrSize
g.addName "ClE_0"
sealType(g, f2) # FieldDecl
result = finishType(g, p) # ObjectDecl
proc bitsetBasetype*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
let s = int(getSize(c.conf, t))
case s
of 1: result = UInt8Id
of 2: result = UInt16Id
of 4: result = UInt32Id
of 8: result = UInt64Id
else: result = UInt8Id
proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
if t == nil: return VoidId
case t.kind
of tyInt:
case int(getSize(c.conf, t))
of 2: result = Int16Id
of 4: result = Int32Id
else: result = Int64Id
of tyInt8: result = Int8Id
of tyInt16: result = Int16Id
of tyInt32: result = Int32Id
of tyInt64: result = Int64Id
of tyFloat:
case int(getSize(c.conf, t))
of 4: result = Float32Id
else: result = Float64Id
of tyFloat32: result = Float32Id
of tyFloat64: result = Float64Id
of tyFloat128: result = getFloat128Type(g)
of tyUInt:
case int(getSize(c.conf, t))
of 2: result = UInt16Id
of 4: result = UInt32Id
else: result = UInt64Id
of tyUInt8: result = UInt8Id
of tyUInt16: result = UInt16Id
of tyUInt32: result = UInt32Id
of tyUInt64: result = UInt64Id
of tyBool: result = Bool8Id
of tyChar: result = Char8Id
of tyVoid: result = VoidId
of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange:
result = typeToIr(c, g, t.skipModifier)
of tyEnum:
if firstOrd(c.conf, t) < 0:
result = Int32Id
else:
case int(getSize(c.conf, t))
of 1: result = UInt8Id
of 2: result = UInt16Id
of 4: result = Int32Id
of 8: result = Int64Id
else: result = Int32Id
of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
if t.len > 0:
result = typeToIr(c, g, t.skipModifier)
else:
result = TypeId(-1)
of tyFromExpr:
if t.n != nil and t.n.typ != nil:
result = typeToIr(c, g, t.n.typ)
else:
result = TypeId(-1)
of tyArray:
cached(c, t):
var n = toInt64(lengthOrd(c.conf, t))
if n <= 0: n = 1 # make an array of at least one element
let elemType = typeToIr(c, g, t.elementType)
let a = openType(g, ArrayTy)
g.addType(elemType)
g.addArrayLen n
g.addName mangle(c, t)
result = finishType(g, a)
of tyPtr, tyRef:
cached(c, t):
let e = t.elementType
if e.kind == tyUncheckedArray:
let elemType = typeToIr(c, g, e.elementType)
let a = openType(g, AArrayPtrTy)
g.addType(elemType)
result = finishType(g, a)
else:
let elemType = typeToIr(c, g, t.elementType)
let a = openType(g, APtrTy)
g.addType(elemType)
result = finishType(g, a)
of tyVar, tyLent:
cached(c, t):
let e = t.elementType
if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
# skip the modifier, `var openArray` is a (ptr, len) pair too:
result = typeToIr(c, g, e)
else:
let elemType = typeToIr(c, g, e)
let a = openType(g, APtrTy)
g.addType(elemType)
result = finishType(g, a)
of tySet:
let s = int(getSize(c.conf, t))
case s
of 1: result = UInt8Id
of 2: result = UInt16Id
of 4: result = UInt32Id
of 8: result = UInt64Id
else:
# array[U8, s]
cached(c, t):
let a = openType(g, ArrayTy)
g.addType(UInt8Id)
g.addArrayLen s
g.addName mangle(c, t)
result = finishType(g, a)
of tyPointer, tyNil:
# tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim
let a = openType(g, APtrTy)
g.addBuiltinType(VoidId)
result = finishType(g, a)
of tyObject:
# Objects are special as they can be recursive in Nim. This is easily solvable.
# We check if we are already "processing" t. If so, we produce `ObjectTy`
# instead of `ObjectDecl`.
cached(c, t):
if not c.recursionCheck.containsOrIncl(t.itemId):
result = objectToIr(c, g, t)
else:
result = objectHeaderToIr(c, g, t)
of tyTuple:
cachedByName(c, t):
result = tupleToIr(c, g, t)
of tyProc:
cached(c, t):
if t.callConv == ccClosure:
result = closureToIr(c, g, t)
else:
result = procToIr(c, g, t)
of tyVarargs, tyOpenArray:
cached(c, t):
result = openArrayToIr(c, g, t)
of tyString:
if c.stringType.int < 0:
result = stringToIr(c, g)
c.stringType = result
else:
result = c.stringType
of tySequence:
cachedByName(c, t):
result = seqToIr(c, g, t)
of tyCstring:
cached(c, t):
let a = openType(g, AArrayPtrTy)
g.addBuiltinType Char8Id
result = finishType(g, a)
of tyUncheckedArray:
# We already handled the `ptr UncheckedArray` in a special way.
cached(c, t):
let elemType = typeToIr(c, g, t.elementType)
let a = openType(g, LastArrayTy)
g.addType(elemType)
result = finishType(g, a)
of tyUntyped, tyTyped:
# this avoids a special case for system.echo which is not a generic but
# uses `varargs[typed]`:
result = VoidId
of tyNone, tyEmpty, tyTypeDesc,
tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
result = TypeId(-1)

View File

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

View File

@@ -28,11 +28,14 @@ type
hasReturn, hasBreak: bool
label: PSym # can be nil
parent: ptr BasicBlock
symToDel: seq[PNode]
Con = object
somethingTodo: bool
inFinally: int
proc invalidateWasMoved(c: var BasicBlock; x: PNode)
proc nestedBlock(parent: var BasicBlock; kind: TNodeKind): BasicBlock =
BasicBlock(wasMovedLocs: @[], kind: kind, hasReturn: false, hasBreak: false,
label: nil, parent: addr(parent))
@@ -62,6 +65,10 @@ proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} =
if this.hasReturn:
parent.wasMovedLocs.setLen 0
parent.hasReturn = true
elif this.symToDel.len > 0:
parent.symToDel = this.symToDel
for i in this.symToDel:
invalidateWasMoved(parent, i)
proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool =
result = false
@@ -149,6 +156,7 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
# any usage of the location before destruction implies we
# cannot elide the 'wasMoved(x)':
b.invalidateWasMoved n
b.symToDel.add n
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,

View File

@@ -25,7 +25,7 @@ const
useEffectSystem* = true
useWriteTracking* = false
hasFFI* = defined(nimHasLibFFI)
copyrightYear* = "2024"
copyrightYear* = "2025"
nimEnableCovariance* = defined(nimEnableCovariance)
@@ -139,7 +139,6 @@ type
backendCpp = "cpp"
backendJs = "js"
backendObjc = "objc"
backendNir = "nir"
# backendNimscript = "nimscript" # this could actually work
# backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
@@ -147,7 +146,6 @@ type
cmdNone # not yet processed command
cmdUnknown # command unmapped
cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS,
cmdCompileToNir,
cmdCrun # compile and run in nimache
cmdTcc # run the project via TCC backend
cmdCheck # semantic checking for whole project
@@ -176,12 +174,11 @@ type
const
cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS, cmdCrun, cmdCompileToNir}
cmdCompileToJS, cmdCrun}
cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
cmdCtags, cmdBuildindex}
type
NimVer* = tuple[major: int, minor: int, patch: int]
TStringSeq* = seq[string]
TGCMode* = enum # the selected GC
gcUnselected = "unselected"
@@ -228,8 +225,11 @@ type
strictDefs,
strictCaseObjects,
inferGenericTypes,
genericsOpenSym,
openSym, # remove nfDisabledOpenSym when this is default
# alternative to above:
genericsOpenSym
vtables
typeBoundOps
LegacyFeature* = enum
allowSemcheckedAstModification,
@@ -248,13 +248,15 @@ type
## Useful for libraries that rely on local passC
jsNoLambdaLifting
## Old transformation for closures in JS backend
noPanicOnExcept
## don't panic on bare except
SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
TSystemCC* = enum
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl
ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc
ExceptionSystem* = enum
excNone, # no exception system selected yet
@@ -370,7 +372,6 @@ type
arguments*: string ## the arguments to be passed to the program that
## should be run
ideCmd*: IdeCmd
oldNewlines*: bool
cCompiler*: TSystemCC # the used compiler
modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs
cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline
@@ -384,6 +385,7 @@ type
warnCounter*: int
errorMax*: int
maxLoopIterationsVM*: int ## VM: max iterations of all loops
maxCallDepthVM*: int ## VM: max call depth
isVmTrace*: bool
configVars*: StringTableRef
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
@@ -397,12 +399,12 @@ type
outDir*: AbsoluteDir
jsonBuildFile*: AbsoluteFile
prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
nimStdlibVersion*: NimVer
dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef
dllOverrides*, moduleOverrides*, cfileSpecificOptions*: StringTableRef
projectName*: string # holds a name like 'nim'
projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
projectFull*: AbsoluteFile # projectPath/projectName
projectIsStdin*: bool # whether we're compiling from stdin
stdinFile*: AbsoluteFile # Filename to use in messages for stdin
lastMsgWasDot*: set[StdOrrKind] # the last compiler message was a single '.'
projectMainIdx*: FileIndex # the canonical path id of the main module
projectMainIdx2*: FileIndex # consider merging with projectMainIdx
@@ -410,7 +412,6 @@ type
commandArgs*: seq[string] # any arguments after the main command
commandLine*: string
extraCmds*: seq[string] # for writeJsonBuildInstructions
keepComments*: bool # whether the parser needs to keep comments
implicitImports*: seq[string] # modules that are to be implicitly imported
implicitIncludes*: seq[string] # modules that are to be implicitly included
docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
@@ -451,16 +452,6 @@ type
clientProcessId*: int
proc parseNimVersion*(a: string): NimVer =
# could be moved somewhere reusable
result = default(NimVer)
if a.len > 0:
let b = a.split(".")
assert b.len == 3, a
template fn(i) = result[i] = b[i].parseInt # could be optimized if needed
fn(0)
fn(1)
fn(2)
proc assignIfDefault*[T](result: var T, val: T, def = default(T)) =
## if `result` was already assigned to a value (that wasn't `def`), this is a noop.
@@ -590,11 +581,11 @@ proc newConfigRef*(): ConfigRef =
projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
projectFull: AbsoluteFile"", # projectPath/projectName
projectIsStdin: false, # whether we're compiling from stdin
stdinFile: AbsoluteFile"stdinfile",
projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
command: "", # the main command (e.g. cc, check, scan, etc)
commandArgs: @[], # any arguments after the main command
commandLine: "",
keepComments: true, # whether the parser needs to keep comments
implicitImports: @[], # modules that are to be implicitly imported
implicitIncludes: @[], # modules that are to be implicitly included
docSeeSrcUrl: "",
@@ -612,6 +603,7 @@ proc newConfigRef*(): ConfigRef =
arguments: "",
suggestMaxResults: 10_000,
maxLoopIterationsVM: 10_000_000,
maxCallDepthVM: 2_000,
vmProfileData: newProfileData(),
spellSuggestMax: spellSuggestSecretSauce,
currentConfigDir: ""
@@ -635,12 +627,6 @@ proc newPartialConfigRef*(): ConfigRef =
proc cppDefine*(c: ConfigRef; define: string) =
c.cppDefines.incl define
proc getStdlibVersion*(conf: ConfigRef): NimVer =
if conf.nimStdlibVersion == (0,0,0):
let s = conf.symbols.getOrDefault("nimVersion", "")
conf.nimStdlibVersion = s.parseNimVersion
result = conf.nimStdlibVersion
proc isDefined*(conf: ConfigRef; symbol: string): bool =
if conf.symbols.hasKey(symbol):
result = true

View File

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

View File

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

View File

@@ -638,8 +638,9 @@ proc semiStmtList(p: var Parser, result: PNode) =
getTok(p)
if p.tok.tokType == tkParRi:
break
elif not (sameInd(p) or realInd(p)):
parMessage(p, errInvalidIndentation)
# ignore indent:
#elif not (sameOrNoInd(p) or realInd(p)):
# parMessage(p, errInvalidIndentation)
let a = complexOrSimpleStmt(p)
if a.kind == nkEmpty:
parMessage(p, errExprExpected, p.tok)
@@ -699,10 +700,12 @@ proc parsePar(p: var Parser): PNode =
asgn.add b
result.add(asgn)
if p.tok.tokType == tkSemiColon:
getTok(p)
semiStmtList(p, result)
elif p.tok.tokType == tkSemiColon:
# stmt context:
result.add(a)
getTok(p)
semiStmtList(p, result)
else:
a = colonOrEquals(p, a)
@@ -1401,7 +1404,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode =
result = primarySuffix(p, result, baseInd, mode)
proc binaryNot(p: var Parser; a: PNode): PNode =
if p.tok.tokType == tkNot:
if p.tok.tokType == tkNot and p.tok.indent < 0:
let notOpr = newIdentNodeP(p.tok.ident, p)
getTok(p)
optInd(p, notOpr)
@@ -2107,12 +2110,28 @@ proc parseObjectCase(p: var Parser): PNode =
#| objectBranches = objectBranch (IND{=} objectBranch)*
#| (IND{=} 'elif' expr colcom objectPart)*
#| (IND{=} 'else' colcom objectPart)?
#| objectCase = 'case' declColonEquals ':'? COMMENT?
#| objectCase = 'case' (declColonEquals / pragma)? ':'? COMMENT?
#| (IND{>} objectBranches DED
#| | IND{=} objectBranches)
result = newNodeP(nkRecCase, p)
getTokNoInd(p)
var a = parseIdentColonEquals(p, {withPragma})
getTok(p)
if p.tok.tokType != tkOf:
# of case will be handled later
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
var a: PNode
if p.tok.tokType in {tkSymbol, tkAccent}:
a = parseIdentColonEquals(p, {withPragma})
else:
a = newNodeP(nkIdentDefs, p)
if p.tok.tokType == tkCurlyDotLe:
var prag = newNodeP(nkPragmaExpr, p)
prag.add(p.emptyNode)
prag.add(parsePragma(p))
a.add(prag)
else:
a.add(p.emptyNode)
a.add(p.emptyNode)
a.add(p.emptyNode)
result.add(a)
if p.tok.tokType == tkColon: getTok(p)
flexComment(p, result)

View File

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

View File

@@ -77,7 +77,7 @@ proc inSymChoice(sc, x: PNode): bool =
result = false
for i in 0..<sc.len:
if sc[i].sym == x.sym: return true
elif sc.kind == nkOpenSymChoice:
elif sc.kind in {nkOpenSymChoice, nkOpenSym}:
# same name suffices for open sym choices!
result = sc[0].sym.name.id == x.sym.name.id
else:

View File

@@ -13,7 +13,6 @@ when not defined(leanCompiler):
import std/[syncio, objectdollar, assertions, tables, strutils, strtabs]
import renderer
import ic/replayer
import nir/nir
proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) =
graph.pipelinePass = pass
@@ -45,16 +44,11 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
result = nil
of EvalPass, InterpreterPass:
result = interpreterCode(bModule, semNode)
of NirReplPass:
result = runCode(bModule, semNode)
of NirPass:
result = nirBackend(bModule, semNode)
of NonePass:
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
) =
proc processImplicitImports*(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator) =
# XXX fixme this should actually be relative to the config file!
let relativeTo = toFullPath(graph.config, m.info)
for module in items(implicits):
@@ -69,7 +63,7 @@ proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind
if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
break
proc prePass(c: PContext; n: PNode) =
proc prePass*(c: PContext; n: PNode) =
for son in n:
if son.kind == nkPragma:
for s in son:
@@ -111,8 +105,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
case graph.pipelinePass
of CgenPass:
setupCgen(graph, module, idgen)
of NirPass:
openNirBackend(graph, module, idgen)
of JSgenPass:
when not defined(leanCompiler):
setupJSgen(graph, module, idgen)
@@ -120,8 +112,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
nil
of EvalPass, InterpreterPass:
setupEvalGen(graph, module, idgen)
of NirReplPass:
setupNirReplGen(graph, module, idgen)
of GenDependPass:
setupDependPass(graph, module, idgen)
of Docgen2Pass:
@@ -209,10 +199,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
discard finalJSCodeGen(graph, bModule, finalNode)
of EvalPass, InterpreterPass:
discard interpreterCode(bModule, finalNode)
of NirReplPass:
discard runCode(bModule, finalNode)
of NirPass:
closeNirBackend(bModule, finalNode)
of SemPass, GenDependPass:
discard
of Docgen2Pass, Docgen2TexPass:
@@ -248,7 +234,8 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
result = moduleFromRodFile(graph, fileIdx, cachedModules)
let path = toFullPath(graph.config, fileIdx)
let filename = AbsoluteFile path
if fileExists(filename): # it could be a stdinfile
# it could be a stdinfile/cmdfile
if fileExists(filename) and not graph.config.projectIsStdin:
graph.cachedFiles[path] = $secureHashFile(path)
if result == nil:
result = newModule(graph, fileIdx)

View File

@@ -81,7 +81,7 @@ const
wRequiresInit, wNoalias, wAlign, wNoInit} - {wExportNims, wNodecl} # why exclude these?
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
wNoInit, wCompileTime, wGlobal,
wNoInit, wCompileTime, wGlobal, wLiftLocals,
wGensym, wInject, wCodegenDecl,
wGuard, wGoto, wCursor, wNoalias, wAlign}
constPragmas* = declPragmas + {wHeader, wMagic,
@@ -107,7 +107,7 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
return it[1]
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
isStatement: bool = false)
isStatement: bool = false; comesFromPush = false)
proc recordPragma(c: PContext; n: PNode; args: varargs[string]) =
var recorded = newNodeI(nkReplayAction, n.info)
@@ -156,16 +156,16 @@ proc pragmaEnsures(c: PContext, n: PNode) =
proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
# special cases to improve performance:
if extname == "$1":
s.loc.r = rope(s.name.s)
s.loc.snippet = rope(s.name.s)
elif '$' notin extname:
s.loc.r = rope(extname)
s.loc.snippet = rope(extname)
else:
try:
s.loc.r = rope(extname % s.name.s)
s.loc.snippet = rope(extname % s.name.s)
except ValueError:
localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
when hasFFI:
s.cname = $s.loc.r
s.cname = $s.loc.snippet
proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
@@ -370,7 +370,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
proc processNote(c: PContext, n: PNode) =
template handleNote(enumVals, notes) =
let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
if x != errUnknown:
if x != errUnknown:
nk = TNoteKind(x)
let x = c.semConstBoolExpr(c, n[1])
n[1] = x
@@ -590,7 +590,7 @@ proc processCompile(c: PContext, n: PNode) =
var customArgs = ""
if n.kind in nkCallKinds:
s = getStrLit(c, n, 1)
if n.len <= 3:
if n.len == 3:
customArgs = getStrLit(c, n, 2)
else:
localError(c.config, n.info, "'.compile' pragma takes up 2 arguments")
@@ -637,7 +637,10 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
# XXX what to do here if 'amb' is true?
if e != nil:
incl(e.flags, sfUsed)
result.add newSymNode(e)
if isDefined(con.config, "nimPreviewAsmSemSymbol"):
result.add con.semExprWithType(con, newSymNode(e), {efTypeAllowed})
else:
result.add newSymNode(e)
else:
result.add newStrNode(nkStrLit, sub)
else:
@@ -722,12 +725,12 @@ proc processPragma(c: PContext, n: PNode, i: int) =
proc pragmaRaisesOrTags(c: PContext, n: PNode) =
proc processExc(c: PContext, x: PNode) =
if c.hasUnresolvedArgs(c, x):
x.typ = makeTypeFromExpr(c, x)
x.typ() = makeTypeFromExpr(c, x)
else:
var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
if t.kind notin {tyObject, tyOr}:
localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
x.typ = t
x.typ() = t
if n.kind in nkPragmaCallKinds and n.len == 2:
let it = n[1]
@@ -800,13 +803,14 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode =
var callNode: PNode
if n.kind in {nkIdent, nkSym}:
case n.kind
of nkIdentKinds:
# pragma -> pragma()
callNode = newTree(nkCall, n)
elif n.kind == nkExprColonExpr:
of nkExprColonExpr:
# pragma: arg -> pragma(arg)
callNode = newTree(nkCall, n[0], n[1])
elif n.kind in nkPragmaCallKinds:
of nkPragmaCallKinds - {nkExprColonExpr}:
callNode = n
else:
invalidPragma(c, n)
@@ -882,16 +886,16 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
# number of pragmas increase/decrease with user pragma expansion
inc c.instCounter
defer: dec c.instCounter
if c.instCounter > 100:
globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s)
if keyDeep:
localError(c.config, it.info, "user pragma cannot have arguments")
pragma(c, sym, userPragma.ast, validPragmas, isStatement)
pragma(c, sym, userPragma.ast, validPragmas, isStatement, comesFromPush)
n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
dec c.instCounter
else:
let k = whichKeyword(ident)
if k in validPragmas:
@@ -943,15 +947,19 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wSize:
if sym.typ == nil: invalidPragma(c, it)
var size = expectIntLit(c, it)
case size
of 1, 2, 4:
sym.typ.size = size
sym.typ.align = int16 size
of 8:
sym.typ.size = 8
sym.typ.align = floatInt64Align(c.config)
if sfImportc in sym.flags:
# no restrictions on size for imported types
setImportedTypeSize(c.config, sym.typ, size)
else:
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
case size
of 1, 2, 4:
sym.typ.size = size
sym.typ.align = int16 size
of 8:
sym.typ.size = 8
sym.typ.align = floatInt64Align(c.config)
else:
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
of wAlign:
let alignment = expectIntLit(c, it)
if isPowerOfTwo(alignment) and alignment > 0:
@@ -1014,7 +1022,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
incl(sym.loc.flags, lfHeader)
incl(sym.loc.flags, lfNoDecl)
# implies nodecl, because otherwise header would not make sense
if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
of wNoSideEffect:
noVal(c, it)
if sym != nil:
@@ -1308,13 +1316,18 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
noVal(c, it)
if sym == nil: invalidPragma(c, it)
else: sym.flags.incl sfUsed
of wLiftLocals: discard
of wLiftLocals:
sym.flags.incl(sfForceLift)
of wRequires, wInvariant, wAssume, wAssert:
pragmaProposition(c, it)
of wEnsures:
pragmaEnsures(c, it)
of wEnforceNoRaises, wQuirky:
of wEnforceNoRaises:
sym.flags.incl sfNeverRaises
of wQuirky:
sym.flags.incl sfNeverRaises
if sym.kind in {skProc, skMethod, skConverter, skFunc, skIterator}:
sym.options.incl optQuirky
of wSystemRaisesDefect:
sym.flags.incl sfSystemRaisesDefect
of wVirtual:
@@ -1342,6 +1355,16 @@ proc mergePragmas(n, pragmas: PNode) =
else:
for p in pragmas: n[pragmasPos].add p
proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) =
if n[pragmasPos].kind == nkEmpty:
n[pragmasPos] = newNodeI(nkPragma, n.info)
for p in pragmas:
let prag = whichPragma(p)
if prag in validPragmas:
let copy = copyTree(p)
overwriteLineInfo copy, n.info
n[pragmasPos].add copy
proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
validPragmas: TSpecialWords) =
if sym != nil and sym.kind != skModule:
@@ -1355,7 +1378,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
internalError(c.config, info, "implicitPragmas")
inc i
popInfoContext(c.config)
if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o)
if sym.kind in routineKinds and sym.ast != nil:
mergeValidPragmas(sym.ast, o, validPragmas)
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
localError(c.config, info, ".dynlib requires .exportc")
@@ -1364,7 +1388,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
sfImportc in sym.flags and lib != nil:
incl(sym.loc.flags, lfDynamicLib)
addToLib(lib, sym)
if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
if n == nil: return false
@@ -1385,11 +1409,12 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
inc i
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
isStatement: bool) =
isStatement: bool; comesFromPush = false) =
if n == nil: return
pragmaRec(c, sym, n, validPragmas, isStatement)
# XXX: in the case of a callable def, this should use its info
implicitPragmas(c, sym, n.info, validPragmas)
if not comesFromPush:
implicitPragmas(c, sym, n.info, validPragmas)
proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords,
isStatement: bool = false) =

View File

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

View File

@@ -410,7 +410,7 @@ proc atom(g: TSrcGen; n: PNode): string =
of nkEmpty: result = ""
of nkIdent: result = n.ident.s
of nkSym: result = n.sym.name.s
of nkClosedSymChoice, nkOpenSymChoice: result = n[0].sym.name.s
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym: result = n[0].sym.name.s
of nkStrLit: result = ""; result.addQuoted(n.strVal)
of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
@@ -442,6 +442,11 @@ proc atom(g: TSrcGen; n: PNode): string =
result = $n.floatVal & "\'f64"
else:
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
of nkFloat128Lit:
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
result = $n.floatVal & "\'f128"
else:
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128"
of nkNilLit: result = "nil"
of nkType:
if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
@@ -514,6 +519,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
of nkClosedSymChoice, nkOpenSymChoice:
if n.len > 0: result += lsub(g, n[0])
of nkOpenSym: result = lsub(g, n[0])
of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
of nkTupleClassTy: result = len("tuple")
of nkDotExpr: result = lsons(g, n) + 1
@@ -559,8 +565,16 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkIfExpr:
result = lsub(g, n[0][0]) + lsub(g, n[0][1]) + lsons(g, n, 1) +
len("if_:_")
of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
of nkElseExpr: result = lsub(g, n[0]) + len("_else:_") # type descriptions
of nkElifExpr, nkElifBranch:
if isEmptyType(n[1].typ):
result = lsons(g, n) + len("elif_:_")
else:
result = lsons(g, n) + len("_elif_:_")
of nkElseExpr, nkElse:
if isEmptyType(n[0].typ):
result = lsub(g, n[0]) + len("else:_")
else:
result = lsub(g, n[0]) + len("_else:_") # type descriptions
of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n[0]) else: 0)+len("typeof()")
of nkRefTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ref")
of nkPtrTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ptr")
@@ -603,8 +617,6 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkCommentStmt: result = n.comment.len
of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
of nkImportAs: result = lsub(g, n[0]) + len("_as_") + lsub(g, n[1])
of nkElifBranch: result = lsons(g, n) + len("elif_:_")
of nkElse: result = lsub(g, n[0]) + len("else:_")
of nkFinally: result = lsub(g, n[0]) + len("finally:_")
of nkGenericParams: result = lcomma(g, n) + 2
of nkFormalParams:
@@ -996,7 +1008,7 @@ type
proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
if renderIds notin g.flags:
case n.kind
of nkClosedSymChoice, nkOpenSymChoice:
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym:
if n.len > 0: result = bracketKind(g, n[0])
else: result = bkNone
of nkSym:
@@ -1013,7 +1025,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
proc skipHiddenNodes(n: PNode): PNode =
result = n
while result != nil:
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1:
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1:
result = result[1]
elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
result.len > 0:
@@ -1275,6 +1287,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
else:
gsub(g, n, 0)
of nkOpenSym: gsub(g, n, 0)
of nkPar, nkClosure:
put(g, tkParLe, "(")
gcomma(g, n, c)
@@ -1304,10 +1317,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
put(g, tkCustomLit, n[0].strVal)
gsub(g, n, 1)
else:
gsub(g, n, 0)
for i in 0..<n.len-1:
gsub(g, n, i)
put(g, tkDot, ".")
assert n.len == 2, $n.len
accentedName(g, n[1])
if n.len > 1:
accentedName(g, n[^1])
of nkBind:
putWithSpace(g, tkBind, "bind")
gsub(g, n, 0)
@@ -1407,10 +1421,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
of nkPrefix:
gsub(g, n, 0)
if n.len > 1:
let opr = if n[0].kind == nkIdent: n[0].ident
elif n[0].kind == nkSym: n[0].sym.name
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
else: nil
let opr = getPIdent(n[0])
let nNext = skipHiddenNodes(n[1])
if nNext.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
put(g, tkSpaces, Space)
@@ -1461,15 +1472,30 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
putWithSpace(g, tkColon, ":")
if n.len > 0: gsub(g, n[0], 1)
gsons(g, n, emptyContext, 1)
of nkElifExpr:
putWithSpace(g, tkElif, " elif")
gcond(g, n[0])
putWithSpace(g, tkColon, ":")
gsub(g, n, 1)
of nkElseExpr:
put(g, tkElse, " else")
putWithSpace(g, tkColon, ":")
gsub(g, n, 0)
of nkElifExpr, nkElifBranch:
if isEmptyType(n[1].typ):
optNL(g)
putWithSpace(g, tkElif, "elif")
gsub(g, n, 0)
putWithSpace(g, tkColon, ":")
gcoms(g)
gstmts(g, n[1], c)
else:
putWithSpace(g, tkElif, " elif")
gcond(g, n[0])
putWithSpace(g, tkColon, ":")
gsub(g, n, 1)
of nkElseExpr, nkElse:
if isEmptyType(n[0].typ):
optNL(g)
put(g, tkElse, "else")
putWithSpace(g, tkColon, ":")
gcoms(g)
gstmts(g, n[0], c)
else:
put(g, tkElse, " else")
putWithSpace(g, tkColon, ":")
gsub(g, n, 0)
of nkTypeOfExpr:
put(g, tkType, "typeof")
put(g, tkParLe, "(")
@@ -1731,19 +1757,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
of nkMixinStmt:
putWithSpace(g, tkMixin, "mixin")
gcomma(g, n, c)
of nkElifBranch:
optNL(g)
putWithSpace(g, tkElif, "elif")
gsub(g, n, 0)
putWithSpace(g, tkColon, ":")
gcoms(g)
gstmts(g, n[1], c)
of nkElse:
optNL(g)
put(g, tkElse, "else")
putWithSpace(g, tkColon, ":")
gcoms(g)
gstmts(g, n[0], c)
of nkFinally, nkDefer:
optNL(g)
if n.kind == nkFinally:

View File

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

View File

@@ -18,7 +18,7 @@ import
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
lowerings, plugins/active, lineinfos, int128,
isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs,
extccomp
extccomp, layeredtable
import vtables
import std/[strtabs, math, tables, intsets, strutils, packedsets]
@@ -89,6 +89,11 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
changeType(c, x, formal, check=true)
result = arg
result = skipHiddenSubConv(result, c.graph, c.idgen)
# mark inserted converter as used:
var a = result
if a.kind == nkHiddenDeref: a = a[0]
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
markUsed(c, a.info, a[0].sym)
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
@@ -97,13 +102,13 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
renderTree(arg, {renderNoComments}))
# error correction:
result = copyTree(arg)
result.typ = formal
result.typ() = formal
elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum:
# Pick the right 'sym' from the sym choice by looking at 'formal' type:
result = nil
for ch in arg:
if sameType(ch.typ, formal):
return getConstExpr(c.module, ch, c.idgen, c.graph)
return ch
typeMismatch(c.config, info, formal, arg.typ, arg)
else:
result = indexTypesMatch(c, formal, arg.typ, arg)
@@ -111,7 +116,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
typeMismatch(c.config, info, formal, arg.typ, arg)
# error correction:
result = copyTree(arg)
result.typ = formal
result.typ() = formal
else:
result = fitNodePostMatch(c, formal, result)
@@ -251,7 +256,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
# when there is a nested proc inside a template, semtmpl
# will assign a wrong owner during the first pass over the
# template; we must fix it here: see #909
result.owner = getCurrOwner(c)
setOwner(result, getCurrOwner(c))
else:
result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
if find(result.name.s, '`') >= 0:
@@ -465,7 +470,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
renderTree(result, {renderNoComments}))
result = newSymNode(errorSym(c, result))
else:
result.typ = makeTypeDesc(c, typ)
result.typ() = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info)
else:
if s.ast[genericParamsPos] != nil and retType.isMetaType:
@@ -473,7 +478,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
# e.g. template foo(T: typedesc): seq[T]
# We will instantiate the return type here, because
# we now know the supplied arguments
var paramTypes = initTypeMapping()
var paramTypes = initLayeredTypeMap()
for param, value in genericParamsInMacroCall(s, call):
var givenType = value.typ
# the sym nodes used for the supplied generic arguments for
@@ -481,7 +486,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
# in this case, get the type directly from the sym
if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
givenType = value.sym.typ
idTablePut(paramTypes, param.typ, givenType)
put(paramTypes, param.typ, givenType)
retType = generateTypeInstance(c, paramTypes,
macroResult.info, retType)
@@ -489,15 +494,29 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
if retType.kind == tyVoid:
result = semStmt(c, result, flags)
else:
result = semExpr(c, result, flags, expectedType)
result = semExpr(c, result, flags)
result = fitNode(c, retType, result, result.info)
#globalError(s.info, errInvalidParamKindX, typeToString(s.typ.returnType))
dec(c.config.evalTemplateCounter)
discard c.friendModules.pop()
proc getLineInfo(n: PNode): TLineInfo =
case n.kind
of nkPostfix:
if len(n) > 1:
result = getLineInfo(n[1])
else:
result = n.info
of nkAccQuoted, nkPragmaExpr:
if len(n) > 0:
result = getLineInfo(n[0])
else:
result = n.info
else:
result = n.info
const
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
errFloatToString = "cannot convert '$1' to '$2'"
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
@@ -522,7 +541,9 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
if efNoSemCheck notin flags:
result = semAfterMacroCall(c, n, result, sym, flags, expectedType)
if c.config.macrosToExpand.hasKey(sym.name.s):
message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
message(c.config, nOrig.info, hintExpandMacro, renderTree(result, {
renderNonExportedFields, renderDocComments, renderNoComments
}))
result = wrapInComesFrom(nOrig.info, sym, result)
popInfoContext(c.config)
@@ -608,7 +629,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, ch
newNodeIT(nkType, recNode.info, asgnType)
)
asgnExpr.flags.incl nfSkipFieldChecking
asgnExpr.typ = recNode.typ
asgnExpr.typ() = recNode.typ
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
else:
raiseAssert "unreachable"
@@ -630,7 +651,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
if checkDefault: # don't add defaults when checking whether a case branch has default fields
return
defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
defaultValue.typ = discriminator.typ
defaultValue.typ() = discriminator.typ
selectedBranch = recNode.pickCaseBranchIndex defaultValue
defaultValue.flags.incl nfSkipFieldChecking
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
@@ -643,7 +664,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
elif recType.kind in {tyObject, tyArray, tyTuple}:
let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
if asgnExpr != nil:
asgnExpr.typ = recNode.typ
asgnExpr.typ() = recNode.typ
asgnExpr.flags.incl nfSkipFieldChecking
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
else:
@@ -651,41 +672,48 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
if aTypSkip.kind == tyObject:
case aTypSkip.kind
of tyObject:
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
if child.len > 0:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
asgnExpr.typ = aTyp
asgnExpr.typ() = aTyp
asgnExpr.sons.add child
result = semExpr(c, asgnExpr)
else:
result = nil
elif aTypSkip.kind == tyArray:
of tyArray:
let child = defaultNodeField(c, a, aTypSkip[1], checkDefault)
if child != nil:
let node = newNode(nkIntLit)
node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
semExprWithType(c, child),
let typeNode = newNode(nkType)
typeNode.typ() = makeTypeDesc(c, aTypSkip[1])
result = semExpr(c, newTree(nkCall, newTree(nkBracketExpr, newSymNode(getSysSym(c.graph, a.info, "arrayWithDefault"), a.info), typeNode),
node
))
result.typ = aTyp
result.typ() = aTyp
else:
result = nil
elif aTypSkip.kind == tyTuple:
of tyTuple:
var hasDefault = false
if aTypSkip.n != nil:
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
if hasDefault and children.len > 0:
result = newNodeI(nkTupleConstr, a.info)
result.typ = aTyp
result.typ() = aTyp
result.sons.add children
result = semExpr(c, result)
else:
result = nil
else:
result = nil
of tyRange:
if c.graph.config.isDefined("nimPreviewRangeDefault"):
result = firstRange(c.config, aTypSkip)
else:
result = nil
else:
result = nil
@@ -722,9 +750,12 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
result.semOverloadedCall = semOverloadedCall
result.semInferredLambda = semInferredLambda
result.semGenerateInstance = generateInstance
result.instantiateOnlyProcType = instantiateOnlyProcType
result.fitDefaultNode = fitDefaultNode
result.semTypeNode = semTypeNode
result.instTypeBoundOp = sigmatch.instTypeBoundOp
result.hasUnresolvedArgs = hasUnresolvedArgs
result.semAsgnOpr = semAsgnOpr
result.templInstCounter = new int
pushProcCon(result, module)
@@ -842,9 +873,9 @@ proc semWithPContext*(c: PContext, n: PNode): PNode =
proc reportUnusedModules(c: PContext) =
if c.config.cmd == cmdM: return
for i in 0..high(c.unusedImports):
if sfUsed notin c.unusedImports[i][0].flags:
message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
for (s, info) in c.unusedImports:
if sfUsed notin s.flags:
message(c.config, info, warnUnusedImportX, s.name.s)
proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
if c.config.cmd == cmdIdeTools and not c.suggestionsMade:

View File

@@ -56,7 +56,7 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
proc name[T: static proc()]() = T()
name[proc() = echo"hello"]()
]#
for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}):
for paramSym in searchScopesAll(c, symx.name, {skConst}):
let paramTyp = paramSym.typ
if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter:
result.add((paramTyp.n.sym, o.lastOverloadScope))
@@ -69,6 +69,39 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
result[0].scope, diagnostics)
best.state = csNoMatch
proc isAttachableRoutineTo(prc: PSym, arg: PType): bool =
result = false
if arg.owner != prc.owner: return false
for i in 1 ..< prc.typ.len:
if prc.typ.n[i].kind == nkSym and prc.typ.n[i].sym.ast != nil:
# has default value, parameter is not considered in type attachment
continue
let t = nominalRoot(prc.typ[i])
if t != nil and t.itemId == arg.itemId:
# parameter `i` is a nominal type in this module
# attachable if the nominal root `t` has the same id as `arg`
return true
proc addTypeBoundSymbols(graph: ModuleGraph, arg: PType, name: PIdent,
filter: TSymKinds, marker: var IntSet,
syms: var seq[tuple[s: PSym, scope: int]]) =
# add type bound ops for `name` based on the argument type `arg`
if arg != nil:
# argument must be typed first, meaning arguments always
# matching `untyped` are ignored
let t = nominalRoot(arg)
if t != nil and t.owner.kind == skModule:
# search module for routines attachable to `t`
let module = t.owner
var iter = default(ModuleIter)
var s = initModuleIter(iter, graph, module, name)
while s != nil:
if s.kind in filter and s.isAttachableRoutineTo(t) and
not containsOrIncl(marker, s.id):
# least priority scope, less than explicit imports:
syms.add((s, -2))
s = nextModuleIter(iter, graph)
proc pickBestCandidate(c: PContext, headSymbol: PNode,
n, orig: PNode,
initialBinding: PNode,
@@ -88,17 +121,29 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
best, alt, o, diagnosticsFlag)
if len(syms) == 0:
return
let allowTypeBoundOps = typeBoundOps in c.features and
# qualified or bound symbols cannot refer to type bound ops
headSymbol.kind in {nkIdent, nkAccQuoted, nkOpenSymChoice, nkOpenSym}
var symMarker = initIntSet()
for s in syms:
symMarker.incl(s.s.id)
# current overload being considered
var sym = syms[0].s
let name = sym.name
var scope = syms[0].scope
if allowTypeBoundOps:
for a in 1 ..< n.len:
# for every already typed argument, add type bound ops
let arg = n[a]
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
# starts at 1 because 0 is already done with setup, only needs checking
var nextSymIndex = 1
var z: TCandidate # current candidate
while true:
determineType(c, sym)
z = initCandidate(c, sym, initialBinding, scope, diagnosticsFlag)
# this is kinda backwards as without a check here the described
# problems in recalc would not happen, but instead it 100%
# does check forever in some cases
@@ -106,6 +151,14 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# may introduce new symbols with caveats described in recalc branch
matches(c, n, orig, z)
if allowTypeBoundOps:
# this match may have given some arguments new types,
# in which case add their type bound ops as well
# type bound ops of arguments always matching `untyped` are not considered
for x in z.newlyTypedOperands:
let arg = n[x]
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind == skIterator:
@@ -130,13 +183,20 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# 1) new symbols are discovered but the loop ends before we recalc
# 2) new symbols are discovered and resemmed forever
# not 100% sure if these are possible though as they would rely
# on somehow introducing a new overload during overload resolution
# on somehow introducing a new overload during overload resolution
# Symbol table has been modified. Restart and pre-calculate all syms
# before any further candidate init and compare. SLOW, but rare case.
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
best, alt, o, diagnosticsFlag)
symMarker = initIntSet()
for s in syms:
symMarker.incl(s.s.id)
if allowTypeBoundOps:
for a in 1 ..< n.len:
# for every already typed argument, add type bound ops
let arg = n[a]
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
# reset counter because syms may be in a new order
symCount = c.currentScope.symbols.counter
nextSymIndex = 0
@@ -184,14 +244,6 @@ proc effectProblem(f, a: PType; result: var string; c: PContext) =
if not c.graph.compatibleProps(c.graph, f, a):
result.add "\n The `.requires` or `.ensures` properties are incompatible."
proc renderNotLValue(n: PNode): string =
result = $n
let n = if n.kind == nkHiddenDeref: n[0] else: n
if n.kind == nkHiddenCallConv and n.len > 1:
result = $n[0] & "(" & result & ")"
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
(TPreferedDesc, string) =
var prefer = preferName
@@ -246,7 +298,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
candidates.add(getProcHeader(c.config, err.sym, prefer))
candidates.addDeclaredLocMaybe(c.config, err.sym)
candidates.add("\n")
let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam}
let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches
var argList = n
if isGenericMismatch and n[0].kind == nkBracketExpr:
argList = n[0]
let nArg =
if err.firstMismatch.arg < argList.len:
argList[err.firstMismatch.arg]
else:
nil
let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
if n.len > 1:
if verboseTypeMismatch notin c.config.legacyFeatures:
@@ -269,6 +330,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
of kMissingParam:
candidates.add(" missing parameter: " & nameParam)
candidates.add "\n"
of kExtraGenericParam:
candidates.add(" extra generic param given")
candidates.add "\n"
of kMissingGenericParam:
candidates.add(" missing generic parameter: " & nameParam)
candidates.add "\n"
of kVarNeeded:
doAssert nArg != nil
doAssert err.firstMismatch.formal != nil
@@ -278,6 +345,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
candidates.add "\n"
of kTypeMismatch:
doAssert nArg != nil
if nArg.kind in nkSymChoices:
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
let wanted = err.firstMismatch.formal.typ
doAssert err.firstMismatch.formal != nil
doAssert wanted != nil
@@ -292,9 +361,39 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
effectProblem(wanted, got, candidates, c)
candidates.add "\n"
of kGenericParamTypeMismatch:
let pos = err.firstMismatch.arg
doAssert n[0].kind == nkBracketExpr and pos < n[0].len
let arg = n[0][pos]
doAssert arg != nil
var wanted = err.firstMismatch.formal.typ
if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints:
wanted = wanted.genericConstraint
let got = arg.typ.skipTypes({tyTypeDesc})
doAssert err.firstMismatch.formal != nil
doAssert wanted != nil
doAssert got != nil
candidates.add " generic parameter mismatch, expected "
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
candidates.add " but got '"
candidates.add renderTree(arg)
candidates.add "' of type: "
candidates.addTypeDeclVerboseMaybe(c.config, got)
if nArg.kind in nkSymChoices:
candidates.add "\n"
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
if got != nil and got.kind == tyProc and wanted.kind == tyProc:
# These are proc mismatches so,
# add the extra explict detail of the mismatch
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
if got != nil:
effectProblem(wanted, got, candidates, c)
candidates.add "\n"
of kUnknown: discard "do not break 'nim check'"
else:
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
if err.firstMismatch.kind in genericParamMismatches:
candidates.add(" in generic parameters")
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
case err.firstMismatch.kind
of kUnknownNamedParam:
@@ -306,9 +405,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param")
of kExtraArg: candidates.add("\n extra argument given")
of kMissingParam: candidates.add("\n missing parameter: " & nameParam)
of kTypeMismatch, kVarNeeded:
of kExtraGenericParam:
candidates.add("\n extra generic param given")
of kMissingGenericParam:
candidates.add("\n missing generic parameter: " & nameParam)
of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded:
doAssert nArg != nil
let wanted = err.firstMismatch.formal.typ
var wanted = err.firstMismatch.formal.typ
if isGenericMismatch and wanted.kind == tyGenericParam and
wanted.genericParamHasConstraints:
wanted = wanted.genericConstraint
doAssert err.firstMismatch.formal != nil
candidates.add("\n required type for " & nameParam & ": ")
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
@@ -319,8 +425,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
else:
candidates.add renderTree(nArg)
candidates.add "' is of type: "
let got = nArg.typ
var got = nArg.typ
if isGenericMismatch: got = got.skipTypes({tyTypeDesc})
candidates.addTypeDeclVerboseMaybe(c.config, got)
if nArg.kind in nkSymChoices:
candidates.add "\n"
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
doAssert wanted != nil
if got != nil:
if got.kind == tyProc and wanted.kind == tyProc:
@@ -331,8 +441,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
of kUnknown: discard "do not break 'nim check'"
candidates.add "\n"
if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and
n.kind == nkCommand:
if err.firstMismatch.arg == 1 and nArg != nil and
nArg.kind == nkTupleConstr and n.kind == nkCommand:
maybeWrongSpace = true
for diag in err.diagnostics:
candidates.add(diag & "\n")
@@ -409,23 +519,6 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
result.add("\n" & errExpectedPosition & "\n" & candidates)
localError(c.config, n.info, result)
proc bracketNotFoundError(c: PContext; n: PNode) =
var errors: CandidateErrors = @[]
var o: TOverloadIter = default(TOverloadIter)
let headSymbol = n[0]
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind in routineKinds:
errors.add(CandidateError(sym: symx,
firstMismatch: MismatchInfo(),
diagnostics: @[],
enabled: false))
symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0:
localError(c.config, n.info, "could not resolve: " & $n)
else:
notFoundError(c, n, errors)
proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
result = ""
if c.compilesContextId > 0:
@@ -518,7 +611,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
if overloadsState == csEmpty and result.state == csEmpty:
if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
result.state = csNoMatch
if efNoDiagnostics in flags:
if c.inGenericContext > 0 and nfExprCall in n.flags:
# untyped expression calls end up here, see #24099
return
# xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
@@ -554,14 +648,52 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
getProcHeader(c.config, alt.calleeSym),
args])
proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
var errors: CandidateErrors = @[]
let headSymbol = n[0]
block:
# we build a closed symchoice of all `[]` overloads for their errors,
# except add a custom error for the magics which always match
var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c))
var o: TOverloadIter = default(TOverloadIter)
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind in routineKinds:
if symx.magic in {mArrGet, mArrPut}:
errors.add(CandidateError(sym: symx,
firstMismatch: MismatchInfo(),
diagnostics: @[],
enabled: false))
else:
choice.add newSymNode(symx, headSymbol.info)
symx = nextOverloadIter(o, c, headSymbol)
n[0] = choice
# copied from semOverloadedCallAnalyzeEffects, might be overkill:
const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}
let filter =
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
baseFilter + {skIterator}
else: baseFilter
# this will add the errors:
var r = resolveOverloads(c, n, n, filter, flags, errors, true)
if errors.len == 0:
localError(c.config, n.info, "could not resolve: " & $n)
else:
notFoundError(c, n, errors)
proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
let a = if a.kind == nkHiddenDeref: a[0] else: a
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
let s = a[0].sym
if s.isGenericRoutineStrict:
let finalCallee = generateInstance(c, s, x.bindings, a.info)
var src = s.typ.firstParamType
var convMatch = newCandidate(c, src)
let srca = typeRel(convMatch, src, a[1].typ)
if srca notin {isEqual, isGeneric, isSubtype}:
internalError(c.config, a.info, "generic converter failed rematch")
let finalCallee = generateInstance(c, s, convMatch.bindings, a.info)
a[0].sym = finalCallee
a[0].typ = finalCallee.typ
a[0].typ() = finalCallee.typ
#a.typ = finalCallee.typ.returnType
proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
@@ -570,6 +702,15 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
for i in 1..<n.len:
instGenericConvertersArg(c, n[i], x)
proc markConvertersUsed*(c: PContext, n: PNode) =
assert n.kind in nkCallKinds
for i in 1..<n.len:
var a = n[i]
if a == nil: continue
if a.kind == nkHiddenDeref: a = a[0]
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
markUsed(c, a.info, a[0].sym)
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
var m = newCandidate(c, f)
result = paramTypesMatch(m, f, a, arg, nil)
@@ -587,15 +728,15 @@ proc inferWithMetatype(c: PContext, formal: PType,
# This almost exactly replicates the steps taken by the compiler during
# param matching. It performs an embarrassing amount of back-and-forth
# type jugling, but it's the price to pay for consistency and correctness
result.typ = generateTypeInstance(c, m.bindings, arg.info,
result.typ() = generateTypeInstance(c, m.bindings, arg.info,
formal.skipTypes({tyCompositeTypeClass}))
else:
typeMismatch(c.config, arg.info, formal, arg.typ, arg)
# error correction:
result = copyTree(arg)
result.typ = formal
result.typ() = formal
proc updateDefaultParams(call: PNode) =
proc updateDefaultParams(c: PContext, call: PNode) =
# In generic procs, the default parameter may be unique for each
# instantiation (see tlateboundgenericparams).
# After a call is resolved, we need to re-assign any default value
@@ -605,8 +746,18 @@ proc updateDefaultParams(call: PNode) =
let calleeParams = call[0].sym.typ.n
for i in 1..<call.len:
if nfDefaultParam in call[i].flags:
let def = calleeParams[i].sym.ast
let formal = calleeParams[i].sym
let def = formal.ast
if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
# mirrored with sigmatch:
if def.kind == nkEmpty:
# The default param value is set to empty in `instantiateProcType`
# when the type of the default expression doesn't match the type
# of the instantiated proc param:
pushInfoContext(c.config, call.info, call[0].sym.detailedInfo)
typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast)
popInfoContext(c.config)
def.typ() = errorType(c)
call[i] = def
proc getCallLineInfo(n: PNode): TLineInfo =
@@ -663,7 +814,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
if t[i] == nil or u[i] == nil: return
stackPut(t[i], u[i])
of tyGenericParam:
let prebound = x.bindings.idTableGet(t)
let prebound = x.bindings.lookup(t)
if prebound != nil:
continue # Skip param, already bound
@@ -675,7 +826,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
discard
# update bindings
for i in 0 ..< flatUnbound.len():
x.bindings.idTablePut(flatUnbound[i], flatBound[i])
x.bindings.put(flatUnbound[i], flatBound[i])
proc semResolvedCall(c: PContext, x: var TCandidate,
n: PNode, flags: TExprFlags;
@@ -686,12 +837,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
markUsed(c, info, finalCallee)
onUse(info, finalCallee)
assert finalCallee.ast != nil
if x.hasFauxMatch:
if x.matchedErrorType:
result = x.call
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
result.typ = newTypeS(x.fauxMatch, c)
if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor
if containsGenericType(result.typ):
result.typ() = newTypeS(tyError, c)
incl result.typ.flags, tfCheckedForDestructor
return
let gp = finalCallee.ast[genericParamsPos]
if gp.isGenericParams:
@@ -717,16 +868,18 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
# this node will be used in template substitution,
# pretend this is an untyped node and let regular sem handle the type
# to prevent problems where a generic parameter is treated as a value
tn.typ = nil
tn.typ() = nil
x.call.add tn
else:
internalAssert c.config, false
result = x.call
instGenericConvertersSons(c, result, x)
markConvertersUsed(c, result)
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
result.typ = finalCallee.typ.returnType
updateDefaultParams(result)
if finalCallee.magic notin {mArrGet, mArrPut}:
result.typ() = finalCallee.typ.returnType
updateDefaultParams(c, result)
proc canDeref(n: PNode): bool {.inline.} =
result = n.len >= 2 and (let t = n[1].typ;
@@ -734,7 +887,7 @@ proc canDeref(n: PNode): bool {.inline.} =
proc tryDeref(n: PNode): PNode =
result = newNodeI(nkHiddenDeref, n.info)
result.typ = n.typ.skipTypes(abstractInst)[0]
result.typ() = n.typ.skipTypes(abstractInst)[0]
result.add n
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
@@ -751,9 +904,9 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
candidates)
result = semResolvedCall(c, r, n, flags, expectedType)
else:
if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil:
if c.inGenericContext > 0 and c.matchedConcept == nil:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, result.copyTree)
result.typ() = makeTypeFromExpr(c, result.copyTree)
elif efExplain notin flags:
# repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again)
@@ -768,22 +921,22 @@ proc explicitGenericInstError(c: PContext; n: PNode): PNode =
localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n))
result = n
proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
proc explicitGenericSym(c: PContext, n: PNode, s: PSym, errors: var CandidateErrors, doError: bool): PNode =
if s.kind in {skTemplate, skMacro}:
internalError c.config, n.info, "cannot get explicitly instantiated symbol of " &
(if s.kind == skTemplate: "template" else: "macro")
# binding has to stay 'nil' for this to work!
var m = newCandidate(c, s, nil)
for i in 1..<n.len:
let formal = s.ast[genericParamsPos][i-1].typ
var arg = n[i].typ
# try transforming the argument into a static one before feeding it into
# typeRel
if formal.kind == tyStatic and arg.kind != tyStatic:
let evaluated = c.semTryConstExpr(c, n[i], n[i].typ)
if evaluated != nil:
arg = newTypeS(tyStatic, c, son = evaluated.typ)
arg.n = evaluated
let tm = typeRel(m, formal, arg)
if tm in {isNone, isConvertible}: return nil
matchGenericParams(m, n, s)
if m.state != csMatch:
# state is csMatch only if *all* generic params were matched,
# including implicit parameters
if doError:
errors.add(CandidateError(
sym: s,
firstMismatch: m.firstMismatch,
diagnostics: m.diagnostics))
return nil
var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved
let info = getCallLineInfo(n)
@@ -802,46 +955,57 @@ proc setGenericParams(c: PContext, n, expectedParams: PNode) =
nil
e = semExprWithType(c, n[i], expectedType = constraint)
if e.typ == nil:
n[i].typ = errorType(c)
n[i].typ() = errorType(c)
else:
n[i].typ = e.typ.skipTypes({tyTypeDesc})
n[i].typ() = e.typ.skipTypes({tyTypeDesc})
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool): PNode =
assert n.kind == nkBracketExpr
setGenericParams(c, n, s.ast[genericParamsPos])
var s = s
var a = n[0]
var errors: CandidateErrors = @[]
if a.kind == nkSym:
# common case; check the only candidate has the right
# number of generic type parameters:
if s.ast[genericParamsPos].safeLen != n.len-1:
let expected = s.ast[genericParamsPos].safeLen
localError(c.config, getCallLineInfo(n), errGenerated, "cannot instantiate: '" & renderTree(n) &
"'; got " & $(n.len-1) & " typeof(s) but expected " & $expected)
return n
result = explicitGenericSym(c, n, s)
if result == nil: result = explicitGenericInstError(c, n)
result = explicitGenericSym(c, n, s, errors, doError)
if result == nil:
if c.inGenericContext > 0:
# same as in semOverloadedCall, make expression untyped,
# may have failed match due to unresolved types
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
elif doError:
notFoundError(c, n, errors)
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
# choose the generic proc with the proper number of type parameters.
# XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
# It's good enough for now.
result = newNodeI(a.kind, getCallLineInfo(n))
for i in 0..<a.len:
var candidate = a[i].sym
if candidate.kind in {skProc, skMethod, skConverter,
skFunc, skIterator}:
# it suffices that the candidate has the proper number of generic
# type parameters:
if candidate.ast[genericParamsPos].safeLen == n.len-1:
let x = explicitGenericSym(c, n, candidate)
if x != nil: result.add(x)
let x = explicitGenericSym(c, n, candidate, errors, doError)
if x != nil: result.add(x)
elif c.inGenericContext > 0:
# same as in semOverloadedCall, make expression untyped,
# may have failed match due to unresolved types
# any failing match stops building the symchoice for correctness,
# can also make it untyped from the start
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
# get rid of nkClosedSymChoice if not ambiguous:
if result.len == 1 and a.kind == nkClosedSymChoice:
result = result[0]
elif result.len == 0: result = explicitGenericInstError(c, n)
# candidateCount != 1: return explicitGenericInstError(c, n)
if result.len == 0:
result = nil
if doError:
notFoundError(c, n, errors)
else:
result = explicitGenericInstError(c, n)
# probably unreachable: we are trying to instantiate `a` which is not
# a sym/symchoice
if doError:
result = explicitGenericInstError(c, n)
else:
result = nil
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] =
# Searches for the fn in the symbol table. If the parameter lists are suitable

View File

@@ -9,14 +9,15 @@
## This module contains the data structures for the semantic checking phase.
import std/[tables, intsets, sets]
import std/[tables, intsets, sets, strutils]
when defined(nimPreviewSlimSystem):
import std/assertions
import
options, ast, astalgo, msgs, idents, renderer,
magicsys, vmdef, modulegraphs, lineinfos, pathutils
options, ast, msgs, idents, renderer,
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable,
types, lowerings, trees, parampatterns
import ic / ic
@@ -73,9 +74,9 @@ type
efNoUndeclared, efIsDotCall, efCannotBeDotCall,
# Use this if undeclared identifiers should not raise an error during
# overload resolution.
efNoDiagnostics,
efTypeAllowed # typeAllowed will be called after
efWantNoDefaults
efIgnoreDefaults # var statements without initialization
efAllowSymChoice # symchoice node should not be resolved
TExprFlags* = set[TExprFlag]
@@ -136,9 +137,13 @@ type
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.}
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
semInferredLambda*: proc(c: PContext, pt: LayeredIdTable, n: PNode): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: LayeredIdTable,
info: TLineInfo): PSym
instantiateOnlyProcType*: proc (c: PContext, pt: LayeredIdTable,
prc: PSym, info: TLineInfo): PType
# used by sigmatch for explicit generic instantiations
fitDefaultNode*: proc (c: PContext, n: var PNode, expectedType: PType)
includedFiles*: IntSet # used to detect recursive include files
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
userPragmas*: TStrTable
@@ -168,6 +173,10 @@ type
inUncheckedAssignSection*: int
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
inTypeofContext*: int
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
TBorrowState* = enum
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
@@ -191,29 +200,29 @@ proc getIntLitType*(c: PContext; literal: PNode): PType =
proc setIntLitType*(c: PContext; result: PNode) =
let i = result.intVal
case c.config.target.intSize
of 8: result.typ = getIntLitType(c, result)
of 8: result.typ() = getIntLitType(c, result)
of 4:
if i >= low(int32) and i <= high(int32):
result.typ = getIntLitType(c, result)
result.typ() = getIntLitType(c, result)
else:
result.typ = getSysType(c.graph, result.info, tyInt64)
result.typ() = getSysType(c.graph, result.info, tyInt64)
of 2:
if i >= low(int16) and i <= high(int16):
result.typ = getIntLitType(c, result)
result.typ() = getIntLitType(c, result)
elif i >= low(int32) and i <= high(int32):
result.typ = getSysType(c.graph, result.info, tyInt32)
result.typ() = getSysType(c.graph, result.info, tyInt32)
else:
result.typ = getSysType(c.graph, result.info, tyInt64)
result.typ() = getSysType(c.graph, result.info, tyInt64)
of 1:
# 8 bit CPUs are insane ...
if i >= low(int8) and i <= high(int8):
result.typ = getIntLitType(c, result)
result.typ() = getIntLitType(c, result)
elif i >= low(int16) and i <= high(int16):
result.typ = getSysType(c.graph, result.info, tyInt16)
result.typ() = getSysType(c.graph, result.info, tyInt16)
elif i >= low(int32) and i <= high(int32):
result.typ = getSysType(c.graph, result.info, tyInt32)
result.typ() = getSysType(c.graph, result.info, tyInt32)
else:
result.typ = getSysType(c.graph, result.info, tyInt64)
result.typ() = getSysType(c.graph, result.info, tyInt64)
else:
internalError(c.config, result.info, "invalid int size")
@@ -445,7 +454,7 @@ when false:
proc makeStaticExpr*(c: PContext, n: PNode): PNode =
result = newNodeI(nkStaticExpr, n.info)
result.sons = @[n]
result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ
result.typ() = if n.typ != nil and n.typ.kind == tyStatic: n.typ
else: newTypeS(tyStatic, c, n.typ)
proc makeAndType*(c: PContext, t1, t2: PType): PType =
@@ -504,7 +513,7 @@ proc errorType*(c: PContext): PType =
proc errorNode*(c: PContext, n: PNode): PNode =
result = newNodeI(nkEmpty, n.info)
result.typ = errorType(c)
result.typ() = errorType(c)
# These mimic localError
template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, msg: TMsgKind, arg: string): PNode =
@@ -525,10 +534,11 @@ template localErrorNode*(c: PContext, n: PNode, arg: string): PNode =
liMessage(c.config, n2.info, errGenerated, arg, doNothing, instLoc())
errorNode(c, n2)
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
dest.kind = kind
dest.owner = getCurrOwner(c)
dest.size = - 1
when false:
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
dest.kind = kind
dest.owner = getCurrOwner(c)
dest.size = - 1
proc makeRangeType*(c: PContext; first, last: BiggestInt;
info: TLineInfo; intType: PType = nil): PType =
@@ -559,7 +569,7 @@ proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym =
proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode =
result = newSymNode(symFromType(c, t, info), info)
result.typ = makeTypeDesc(c, t)
result.typ() = makeTypeDesc(c, t)
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
@@ -629,3 +639,166 @@ proc rememberExpansion*(c: PContext; info: TLineInfo; expandedSym: PSym) =
## delegated to the "rod" file mechanism.
if c.config.symbolFiles != disabledSf:
storeExpansion(c.encoder, c.packedRepr, info, expandedSym)
const
errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
errXStackEscape = "address of '$1' may not escape its stack frame"
proc renderNotLValue*(n: PNode): string =
result = $n
let n = if n.kind == nkHiddenDeref: n[0] else: n
if n.kind == nkHiddenCallConv and n.len > 1:
result = $n[0] & "(" & result & ")"
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
proc isAssignable(c: PContext, n: PNode): TAssignableResult =
result = parampatterns.isAssignable(c.p.owner, n)
proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or
sfCompileToCpp in c.module.flags):
checkSonsLen(n, 1, c.config)
result = n[0]
else:
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
result.add n
let aa = isAssignable(c, n)
let sym = getRoot(n)
if aa notin {arLValue, arLocalLValue}:
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
elif strictDefs in c.features and aa == arAddressableConst and
sym != nil and sym.kind == skLet and isOutParam:
discard "allow let varaibles to be passed to out parameters"
else:
localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
result = n
case n.kind
of nkSym:
# n.sym.typ can be nil in 'check' mode ...
if n.sym.typ != nil and
skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n.sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
of nkDotExpr:
checkSonsLen(n, 2, c.config)
if n[1].kind != nkSym:
internalError(c.config, n.info, "analyseIfAddressTaken")
return
if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n[1].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
of nkBracketExpr:
checkMinSonsLen(n, 1, c.config)
if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n, isOutParam)
else:
result = newHiddenAddrTaken(c, n, isOutParam)
proc analyseIfAddressTakenInCall*(c: PContext, n: PNode, isConverter = false) =
checkMinSonsLen(n, 1, c.config)
if n[0].typ == nil:
# n[0] might be erroring node in nimsuggest
return
const
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, mWasMoved}
template checkIfConverterCalled(c: PContext, n: PNode) =
## Checks if there is a converter call which wouldn't be checked otherwise
# Call can sometimes be wrapped in a deref
let node = if n.kind == nkHiddenDeref: n[0] else: n
if node.kind == nkHiddenCallConv:
analyseIfAddressTakenInCall(c, node, true)
# get the real type of the callee
# it may be a proc var with a generic alias type, so we skip over them
var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams:
# BUGFIX: check for L-Value still needs to be done for the arguments!
# note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
for i in 1..<n.len:
if i < t.len and t[i] != nil and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
let it = n[i]
let aa = isAssignable(c, it)
if aa notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr:
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
discard "allow access within a cast(unsafeAssign) section"
else:
localError(c.config, it.info, errVarForOutParamNeededX % $it)
# Make sure to still check arguments for converters
c.checkIfConverterCalled(n[i])
# bug #5113: disallow newSeq(result) where result is a 'var T':
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
var arg = n[1] #.skipAddr
if arg.kind == nkHiddenDeref: arg = arg[0]
if arg.kind == nkSym and arg.sym.kind == skResult and
arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments}))
return
for i in 1..<n.len:
let n = if n.kind == nkHiddenDeref: n[0] else: n
c.checkIfConverterCalled(n[i])
if i < t.len and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
# Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet.
# So we need to make sure we are checking them still when in a converter call
if n[i].kind != nkHiddenAddr or isConverter:
n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc})))
proc replaceHookMagic*(c: PContext, n: PNode, kind: TTypeAttachedOp): PNode =
## Replaces builtin generic hooks with lifted hooks.
case kind
of attachedDestructor:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedDestructor)
if op != nil:
result[0] = newSymNode(op)
if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
if n[1].kind == nkSym and n[1].sym.kind == skParam and
n[1].typ.kind == tyVar:
result[1] = genDeref(n[1])
else:
result[1] = skipAddr(n[1])
of attachedTrace:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedTrace)
if op != nil:
result[0] = newSymNode(op)
of attachedDup:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedDup)
if op != nil:
result[0] = newSymNode(op)
if op.typ.len == 3:
let boolLit = newIntLit(c.graph, n.info, 1)
boolLit.typ() = getSysType(c.graph, n.info, tyBool)
result.add boolLit
of attachedWasMoved:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedWasMoved)
if op != nil:
result[0] = newSymNode(op)
analyseIfAddressTakenInCall(c, result, false)
of attachedSink:
result = c.semAsgnOpr(c, n, nkSinkAsgn)
of attachedAsgn:
result = c.semAsgnOpr(c, n, nkAsgn)
of attachedDeepCopy:
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, kind)
if op != nil:
result[0] = newSymNode(op)

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,15 @@ type
replaceByFieldName: bool
c: PContext
proc wrapNewScope(c: PContext, n: PNode): PNode {.inline.} =
# use `if true` to not interfere with `break`
# just opening scope via `openScope(c)` isn't enough,
# a scope has to be opened in the codegen as well for reused
# template instantiations
let trueLit = newIntLit(c.graph, n.info, 1)
trueLit.typ() = getSysType(c.graph, n.info, tyBool)
result = newTreeI(nkIfStmt, n.info, newTreeI(nkElifBranch, n.info, trueLit, n))
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
if c.field != nil and isEmptyType(c.field.typ):
result = newNode(nkEmpty)
@@ -27,7 +36,8 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
of nkIdent, nkSym:
result = n
let ident = considerQuotedIdent(c.c, n)
if c.replaceByFieldName:
if c.replaceByFieldName and
ident.id != ord(wUnderscore):
if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
let fieldName = if c.tupleType.isNil: c.field.name.s
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
@@ -36,7 +46,8 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
return
# other fields:
for i in ord(c.replaceByFieldName)..<forLoop.len-2:
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id and
ident.id != ord(wUnderscore):
var call = forLoop[^2]
var tupl = call[i+1-ord(c.replaceByFieldName)]
if c.field.isNil:
@@ -72,7 +83,9 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
)
openScope(c.c)
inc c.c.inUnrolledContext
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
var body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
# new scope for each field that codegen should know about:
body = wrapNewScope(c.c, body)
father.add(semStmt(c.c, body, {}))
dec c.c.inUnrolledContext
closeScope(c.c)
@@ -148,6 +161,8 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
replaceByFieldName: m == mFieldPairs
)
var body = instFieldLoopBody(fc, loopBody, n)
# new scope for each field that codegen should know about:
body = wrapNewScope(c, body)
inc c.inUnrolledContext
stmts.add(semStmt(c, body, {}))
dec c.inUnrolledContext

View File

@@ -38,7 +38,7 @@ proc newIntNodeT*(intVal: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph):
# original type was 'int', not a distinct int etc.
if n.typ.kind == tyInt:
# access cache for the int lit type
result.typ = getIntLitTypeG(g, result, idgen)
result.typ() = getIntLitTypeG(g, result, idgen)
result.info = n.info
proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
@@ -46,12 +46,12 @@ proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
result = newFloatNode(nkFloat32Lit, floatVal)
else:
result = newFloatNode(nkFloatLit, floatVal)
result.typ = n.typ
result.typ() = n.typ
result.info = n.info
proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
result = newStrNode(nkStrLit, strVal)
result.typ = n.typ
result.typ() = n.typ
result.info = n.info
proc getConstExpr*(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
@@ -302,6 +302,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
of mMinusSet:
result = nimsets.diffSets(g.config, a, b)
result.info = n.info
of mXorSet:
result = nimsets.symdiffSets(g.config, a, b)
result.info = n.info
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
of mInSet: result = newIntNodeT(toInt128(ord(inSet(a, b))), n, idgen, g)
of mRepr:
@@ -316,7 +319,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g)
of mArrToSeq:
result = copyTree(a)
result.typ = n.typ
result.typ() = n.typ
of mCompileOption:
result = newIntNodeT(toInt128(ord(commands.testCompileOption(g.config, a.getStr, n.info))), n, idgen, g)
of mCompileOptionArg:
@@ -411,7 +414,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
result = newIntNodeT(toInt128(a.getOrdValue != 0), n, idgen, g)
of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`?
result = a
result.typ = n.typ
result.typ() = n.typ
else:
raiseAssert $srcTyp.kind
of tyInt..tyInt64, tyUInt..tyUInt64:
@@ -420,14 +423,17 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g)
of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
var val = a.getOrdValue
if check: rangeCheck(n, val, g)
result = newIntNodeT(val, n, idgen, g)
if dstTyp.kind in {tyUInt..tyUInt64}:
result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g)
result.transitionIntKind(nkUIntLit)
else:
if check: rangeCheck(n, val, g)
result = newIntNodeT(val, n, idgen, g)
else:
result = a
result.typ = n.typ
if check and result.kind in {nkCharLit..nkUInt64Lit}:
result.typ() = n.typ
if check and result.kind in {nkCharLit..nkUInt64Lit} and
dstTyp.kind notin {tyUInt..tyUInt64}:
rangeCheck(n, getInt(result), g)
of tyFloat..tyFloat64:
case srcTyp.kind
@@ -435,12 +441,12 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
result = newFloatNodeT(toFloat64(getOrdValue(a)), n, g)
else:
result = a
result.typ = n.typ
result.typ() = n.typ
of tyOpenArray, tyVarargs, tyProc, tyPointer:
result = nil
else:
result = a
result.typ = n.typ
result.typ() = n.typ
proc getArrayConstr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
if n.kind == nkBracket:
@@ -465,19 +471,20 @@ proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNo
if result.kind == nkExprColonExpr: result = result[1]
else:
result = nil
localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
#localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
of nkBracket:
idx -= toInt64(firstOrd(g.config, x.typ))
if idx >= 0 and idx < x.len: result = x[int(idx)]
else:
result = nil
localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
#localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
of nkStrLit..nkTripleStrLit:
result = newNodeIT(nkCharLit, x.info, n.typ)
if idx >= 0 and idx < x.strVal.len:
result.intVal = ord(x.strVal[int(idx)])
else:
localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
result = nil
#localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
else: result = nil
proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
@@ -511,10 +518,10 @@ proc foldConStrStr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode =
result = newSymNode(s, info)
if s.typ.kind != tyTypeDesc:
result.typ = newType(tyTypeDesc, idgen, s.owner)
result.typ() = newType(tyTypeDesc, idgen, s.owner)
result.typ.addSonSkipIntLit(s.typ, idgen)
else:
result.typ = s.typ
result.typ() = s.typ
proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
result = nil
@@ -583,7 +590,7 @@ proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
raise newException(ValueError, "invalid enum value: " & str)
else:
localError(g.config, s.info, "unsupported type $1 for define '$2'" %
[name, typeToString(rawTyp)])
[typeToString(rawTyp), name])
except ValueError as e:
localError(g.config, s.info,
"could not process define '$1' of type $2; $3" %
@@ -633,7 +640,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
if s.typ.kind == tyStatic:
if s.typ.n != nil and tfUnresolved notin s.typ.flags:
result = s.typ.n
result.typ = s.typ.base
result.typ() = s.typ.base
elif s.typ.isIntLit:
result = s.typ.n
else:
@@ -701,10 +708,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
except DivByZeroDefect:
localError(g.config, n.info, "division by zero")
of nkAddr:
var a = getConstExpr(m, n[0], idgen, g)
if a != nil:
result = n
n[0] = a
result = nil # don't fold paths containing nkAddr
of nkBracket, nkCurly:
result = copyNode(n)
for son in n.items:
@@ -749,7 +753,9 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
if a == nil: return
if leValueConv(n[1], a) and leValueConv(a, n[2]):
result = a # a <= x and x <= b
result.typ = n.typ
result.typ() = n.typ
elif n.typ.kind in {tyUInt..tyUInt64}:
discard "don't check uints"
else:
localError(g.config, n.info,
"conversion from $1 to $2 is invalid" %
@@ -758,7 +764,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
var a = getConstExpr(m, n[0], idgen, g)
if a == nil: return
result = a
result.typ = n.typ
result.typ() = n.typ
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var a = getConstExpr(m, n[1], idgen, g)
if a == nil: return
@@ -775,7 +781,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
not (n.typ.kind == tyProc and a.typ.kind == tyProc):
# we allow compile-time 'cast' for pointer types:
result = a
result.typ = n.typ
result.typ() = n.typ
of nkBracketExpr: result = foldArrayAccess(m, n, idgen, g)
of nkDotExpr: result = foldFieldAccess(m, n, idgen, g)
of nkCheckedFieldExpr:

View File

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

View File

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

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