Compare commits

..

115 Commits

Author SHA1 Message Date
metagn
fb3ed1c94d Merge branch 'devel' into fix_15097 2024-11-04 18:32:50 +03:00
metagn
f98964d99f cbuilder: add for range statements (#24391)
Finishes `genEnumInfo` as followup to #24351. As #24381 mentions this
covers every use of `for` loops in the codegen.

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2024-11-03 19:57:31 +03:00
metagn
f5d80ede80 cbuilder: make Builder an object (#24401)
Doing this early is useful so we can move the indentation logic into
`Builder` itself rather than mix it with the block logic in `ccgstmts`
(the `if` statements in #24381 have not been indented properly either).
However it also means `Builder` is now used for code that still
generates raw C code, so the diff won't be as clean when these get
updated.
2024-11-03 14:59:50 +01:00
metagn
5f056f87b2 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.
2024-11-03 14:56:20 +01:00
metagn
44026f49bd fixes, test 2024-11-03 11:00:21 +03:00
Phil Krylov
46bb47a444 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 );"
```
2024-11-02 07:58:19 +01:00
ringabout
08b82c90f5 improve httpclient docuementation (#24398)
ref https://github.com/nim-lang/Nim/issues/24394
2024-11-01 17:01:45 +01:00
metagn
2cde18ca7d Update compiler/semstmts.nim 2024-11-01 15:18:31 +03:00
Andrey R (cooldome)
1e11ff6b84 fix #15097 2024-11-01 15:18:31 +03:00
ringabout
8b88b5fdd8 fixes #24395; remove ndi (#24396)
fixes  #24395
2024-11-01 09:48:49 +01:00
ringabout
7b47987341 azure-pipelines update to ubuntu 24.04 gcc 14 (#24386) 2024-11-01 09:35:02 +01:00
metagn
c8072b1eb2 cbuilder: ccgexprs sweep part 2 (#24392)
follows up #24381

This is about another 30% of ccgexprs (remaining is up to around line
3000), stopped right before the point where #24391 is required.

The `Genode::log` and `Genode::Cstring` calls in `genEcho` are left as
is but could be mangled in the future.
2024-11-01 09:32:11 +01:00
metagn
0e3330ee96 use cbuilder for default proc generation (#24390)
C++ member procs are not implemented, and `codegenDecl` in general is
also not adapted, although it had to be included in the generation of
proc params for simplicity. Guessing `codegenDecl` and C++ stuff are
supposed to map to pragmas on NIFC, or are just not supported.
2024-10-31 23:02:14 +01:00
ringabout
d55cd40642 trigger package CI for version-2-2 (#24393) 2024-10-31 23:14:03 +08:00
metagn
658c9da33e cbuilder: ccgexprs sweep part 1, basic if stmts (#24381)
Most of what ccgexprs uses is now ported to cbuilder, so this PR makes
around ~25% of ccgexprs use it, along with adding `if` stmts (no
`while`/`switch` and `for` which is only used as `for (tmp = a; tmp < b;
tmp++)`). The `if` builder does not add indents for blocks since we
can't make `Builder` an object yet rather than an alias to `string`,
this will likely be one of the last refactors.

Somewhat unrelated but `ccgtypes` is not ready yet because proc
signatures are not implemented.
2024-10-31 07:50:05 +01:00
ringabout
5e56f0a356 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)`
2024-10-30 22:58:39 +01:00
ringabout
74df699ff1 fixes #24371; incorrect importc wrapper incompatible with gcc 14 on Windows (#24388)
fixes #24371
2024-10-30 20:17:36 +01:00
ringabout
1576563775 disable Test on aarch64 (#24389)
ref https://github.com/nim-lang/Nim/issues/24287
2024-10-30 20:17:19 +01:00
metagn
4091576ab7 implement generic default values for object fields (#24384)
fixes #21941, fixes #23594
2024-10-30 08:58:04 +01:00
ringabout
d61897459d fixes #24379; better error messages for ill-formed type symbols from macros (#24380)
fixes #24379
2024-10-29 15:32:30 +01:00
ringabout
815bbf0e73 fixes #23545; C compiler error when default initializing an object field function (#24375)
fixes #23545
2024-10-29 08:08:35 +01:00
ringabout
3fc87259bd improve passes.nim (#24376) 2024-10-29 08:01:44 +01:00
metagn
15271dba2f cbuilder: add basic number operations (#24373)
The types of the typed operations are not necessarily correct, the mixed
types of the operands in some cases are left the same to keep the C
integer promotion working as expected. To fix any wrong types the
integer promotion can be done explicitly instead, but this is a behavior
change to codegen.
2024-10-28 13:32:35 +01:00
metagn
2e9422df57 cbuilder: add decl visibilities, use it for HCR & typeinfo (#24368)
follows up #24362
2024-10-28 05:27:11 +01:00
metagn
24aa92c14f cbuilder: add calls, sizeof/alignof/offsetof, use in ccgtypes (#24362)
follows #24360, #24351
2024-10-26 20:55:50 +02:00
ringabout
031ad957ba 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.
2024-10-26 20:49:07 +02:00
metagn
506c8a5ce8 cbuilder: abstract over int and float generation (#24360)
Different from `intLiteral`, we add procs that just try to generate
integer values, assuming they're not an edge case like `<= low(int32)`
or `> high(int32)`.
2024-10-26 17:49:30 +02:00
metagn
40fc2d0e76 include static types in type bound ops (#24366)
refs https://github.com/nim-lang/Nim/pull/24315#discussion_r1816332587
2024-10-26 17:49:02 +02:00
metagn
efd603eb28 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.
2024-10-26 17:48:39 +02:00
Jake Leahy
79ce3fe6b7 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
2024-10-26 10:24:15 +02:00
ringabout
aa90d00caf 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)`.
2024-10-25 22:36:19 +02:00
ringabout
2af602a5c8 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.})
```
2024-10-25 22:35:26 +02:00
metagn
d303c289fa 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.
2024-10-25 22:13:22 +02:00
metagn
9d08f6cee3 use cbuilder for enum field name array (#24358)
follows up #24351
2024-10-25 21:16:58 +02:00
ringabout
294b1566e7 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.
2024-10-25 23:03:17 +08:00
metagn
820e2bee9c cbuilder: add assignments, fields, subscripts, deref (#24351)
This PR is somewhat large, worst case it can be split into one with just
assignments and one with just fields/derefs etc.

Assignments with calls as values have not been touched so they can be
done when calls are implemented. Similarly codegen with complex logic
i.e. `genEnumInfo`, `genTypeInfoV2`, `unaryExpr` is not completely
ported yet so they can be done in standalone PRs.
2024-10-25 14:16:39 +02:00
metagn
2864830941 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>
2024-10-25 11:26:42 +02:00
metagn
dd3a4b2aba 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.
2024-10-25 07:35:11 +02:00
ringabout
b534f34e95 adds noise to important_packages (#24352)
ref https://github.com/jangko/nim-noise
2024-10-24 10:23:51 +08:00
ringabout
3aaaed1acf std/nre now uses destructors instead of finializer (#24353)
Similar to changes in
bafb4f119c
2024-10-23 20:52:30 +02:00
ringabout
40b37c96d2 fixes #24347; Failed to bootstrap devel branch compiler on i386 (#24348)
fixes #24347

ref
https://github.com/nim-lang/nightlies/actions/runs/11422422702/job/31780399101#step:12:97
2024-10-23 16:22:27 +08:00
ringabout
baf3695c76 closes #19984; adds a test case (#24349)
closes #19984
2024-10-23 16:05:52 +08:00
metagn
2c56872b5c use cbuilder for int, set, const literals (#24336)
Most of this code was already structured in the `var Builder` manner.
`intLiteral` etc could be used for all int literals in the future.
2024-10-22 20:00:55 +02:00
metagn
ca5df9ab25 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`.
2024-10-22 19:56:37 +02:00
bptato
67442471ae 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.)
2024-10-20 18:15:39 +02:00
metagn
e69eb99a15 use cbuilder for typedefs, add array typedef (#24330)
The only remaining explicit use of `typedef` in the codegen (from my
search) is in `addForwardStructFormat` which from what I understand
won't do anything in NIFC.
2024-10-19 20:54:17 +02:00
metagn
041098e882 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.
2024-10-19 16:40:28 +02:00
Jake Leahy
93c24fe1c5 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
2024-10-19 16:39:15 +02:00
metagn
ae9287c4f3 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.
2024-10-19 10:07:00 +02:00
metagn
0a058a6b8f 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.
2024-10-18 19:06:42 +02:00
ringabout
0806fb0b6f build documentation for repr_v2 (#24325) 2024-10-18 16:52:33 +02:00
ringabout
68b2e9eb6a make PNode.typ a private field (#24326) 2024-10-18 16:52:07 +02:00
metagn
fce86e5937 cbuilder: add array vars, use for openarray init (#24324)
The remaining followup from #24259. A body for building the type doesn't
seem necessary here since the types with array fields are generally
atomic/already built from `getTypeDescAux`.
2024-10-18 10:37:57 +02:00
Yuriy Glukhov
5fa96ef270 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>
2024-10-18 10:36:41 +02:00
Tomohiro
b8f6088ac0 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>
2024-10-18 07:39:20 +02:00
metagn
52cf7dfde0 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.
2024-10-18 07:37:05 +02:00
ringabout
0347536ff2 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`.
2024-10-18 10:56:37 +08:00
ringabout
d0b6b9346e adds a getter/setter for owner (#24318) 2024-10-17 15:16:57 +02:00
ringabout
8be82c36c9 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
2024-10-16 20:49:31 +02:00
metagn
4a056b1849 cbuilder: implement designated initializers, finish default value braces (#24312)
follows up #24259

This is the remaining missing use of `StructInitializer` in
`getDefaultValue` after #24259 and #24302. The only remaining direct C
code in getDefaultValue is [this
line](922f7dfd71/compiler/ccgexprs.nim (L3525))
which creates a global array variable, which isn't implemented yet. Next
steps would be all remaining variable and `typedef` declarations, then
hopefully we can move on to general statements and expressions.
2024-10-16 20:48:53 +02:00
ringabout
a3aea224c9 make owner a private field of PType (#24314)
follow up https://github.com/nim-lang/Nim/pull/24311
2024-10-15 17:32:51 +02:00
ringabout
53460f312c make owner a private field of PSym (#24311) 2024-10-15 15:45:06 +08:00
ringabout
922f7dfd71 closes #19585; adds a test case for #21648 (#24310)
closes #19585
follow up #21648
2024-10-15 09:19:46 +08:00
ringabout
3e8f44b232 fixes ci_generate produces unnecessary spaces on Windows (#24309)
follow up https://github.com/nim-lang/Nim/pull/17899
2024-10-14 17:43:41 +02:00
ringabout
8b39b2df7d 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
2024-10-14 17:43:12 +02:00
metagn
6df050d6d2 only generate first field for default value of union (#24303)
fixes #20653
2024-10-14 17:07:57 +02:00
metagn
34c87de984 use cbuilder for ccgliterals (#24302)
follows up #24259 

This was the only use of the `STRING_LITERAL` macro in `nimbase.h`, so
this macro is now removed. We don't have to remove it though, maybe
people use it.
2024-10-14 08:46:50 +02:00
ringabout
d4b9c147ab define -d:nimHasDefaultFloatRoundtrip and enable datamancer (#24300)
ref https://github.com/SciNim/Datamancer/pull/73
ref https://github.com/SciNim/Datamancer/issues/72
2024-10-14 10:26:44 +08:00
metagn
07628b0dec use cbuilder for most braced initializers (#24259)
`StructInitializer` is now used for most braced initializers in the C
generation, mostly in `genBracedInit`, `getNullValueAux`,
`getDefaultValue`. The exceptions are:

* the default case branch initializer for objects uses C99 designated
initializers with field names, which are not implemented for
`StructInitializer` yet (`siNamedStruct`)
* the uses in `ccgliterals` are untouched so all of ccgliterals can be
done separately and in 1 go

There is one case where `genBracedInit` does not use cbuilder, which is
the global literal variable for openarrays. The reason for this is
simply that variables with C array type are not implemented, which I
thought would be best to leave out of this PR.

For the simplicity of the implementation, code in `getNullValueAuxT`
that reset the initializer back to its initial state if the `Sup` field
did not have any fields itself, is now disabled. This was so the
compiler does not generate `{}` for the Sup field, i.e. `{{}}`, but
every call to `getNullValueAuxT` still generates `{}` if the struct
doesn't have any fields, so I don't know if it really breaks anything.
The case where the Sup field doesn't have any fields but the struct does
also would have generated `{{}, field}`.

Worst case, we can implement either the "resetting" or just disable the
generation of the `Sup` field if there are no fields total. But a better
fix might be to always generate `{0}` if the struct has no fields, in
line with the `char dummy` field that gets added for all objects with no
fields. This doesn't seem necessary for now but might be for the NIFC
output, in which case we can probably keep the logic contained inside
cbuilder (if no fields generated for `siOrderedStruct`/`siNamedStruct`,
we add a `0` for the `dummy` field). This would stipulate that all uses
of struct initializers are exhaustive for every field in structs.
2024-10-13 19:56:17 +02:00
ringabout
80e6b35721 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.
2024-10-13 19:54:30 +02:00
ringabout
71515bf278 Revert "update to setup-nim-action@v2" (#24299)
Reverts nim-lang/Nim#24297

We need to set up a choosenim action
2024-10-13 19:38:23 +08:00
ringabout
a0d78d259b update to setup-nim-action@v2 (#24297) 2024-10-13 19:07:49 +08:00
Aryo
1dbf614858 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.
2024-10-13 07:00:23 +02:00
metagn
2f7586c066 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.
2024-10-13 06:59:20 +02:00
metagn
720d0aee5c 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.
2024-10-12 22:48:44 +02:00
metagn
def1fea43a 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.
2024-10-12 22:39:59 +02:00
Andreas Rumpf
25c068c070 modulegraphs: added a flag useful for gear2 (#24293) 2024-10-12 21:46:56 +02:00
metagn
1bebc236bd 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.
2024-10-12 21:20:21 +02:00
metagn
449106a5a4 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.
2024-10-12 21:17:30 +02:00
metagn
bb0006598d add tables.getOrDefault param name change to changelog (#24271)
refs
https://github.com/nim-lang/Nim/issues/23587#issuecomment-2404406187
2024-10-12 21:16:19 +02:00
Miran
f5cb39289b make package testing faster (#24284)
There's no need to run benchmarks for cow- and sso-strings: they take 15
minutes each to run.
2024-10-11 15:20:25 +02:00
Juan M Gómez
af23bc2941 Bumps nimble to v0.16.2 (#24283) 2024-10-11 13:33:43 +02:00
metagn
aaf6c408c6 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
2024-10-11 12:00:05 +02:00
metagn
706985997e 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.
2024-10-11 11:17:04 +02:00
metagn
9c85f4fd07 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.
2024-10-11 10:36:40 +02:00
Miran
274762638f 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.
2024-10-11 08:46:27 +02:00
dlesnoff
e9a4d096ab 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>
2024-10-10 20:30:40 +03:00
metagn
2f904535d0 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.
2024-10-10 15:35:51 +02:00
metagn
96d6eee9bc fix workaround for protobuf not installing combparser fork in CI (#24267)
fixes CI after #24265, the CI passed in the original PR somehow
2024-10-09 22:13:40 +03:00
metagn
67ea754b7f 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.
2024-10-09 18:20:43 +02:00
ringabout
95a7695810 documentation and comments use HTTPS when possible (#24264) 2024-10-08 21:50:35 +02:00
ringabout
f73e03b132 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
2024-10-08 22:40:18 +08:00
metagn
d72b848d17 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.
2024-10-07 23:18:45 +02:00
Tomohiro
d6633ae1da Change how to multiply 1.5 to ints to reduce overflow (#24257) 2024-10-07 23:18:11 +02:00
metagn
4515b2dae2 use cbuilder for most remaining structs, add typedef (#24253)
The only remaining use of `struct` after this is in
`genConstSeq`/`genConstSeqV2` which use `genBracedInit`, I figured these
should be done in the PR that adapts `genBracedInit` in general to
cbuilder.
2024-10-07 22:10:05 +02:00
ringabout
30e552e3d3 improves the 2.2.0 changelog (#24256) 2024-10-07 22:08:58 +02:00
metagn
c73eedfe6e 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.
2024-10-07 11:40:44 +02:00
metagn
911cef1621 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.
2024-10-07 11:39:26 +02:00
metagn
ea9811a4d2 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
2024-10-06 19:36:46 +02:00
Andreas Rumpf
7f2e6a1359 exports more helpers that are needed by nif-gear2 (#24247) 2024-10-06 19:35:20 +02:00
metagn
a2ee709199 use cbuilder for string literals, split into modules, document (#24237)
`cbuilder` is now split into `cbuilderbase`, `cbuilderexprs`,
`cbuilderdecls`, with all the struct builder code up to this point going
in `cbuilderdecls`.

Variable builders are added, with local, global and constant variables
implemented, but not threadvars.

A builder for struct (braced) initializers is added. The field names
have to be passed to build each field (so they can be used in `oconstr`
in nifc), but they're not used in the output code if a flag
`orderCompliant` is enabled, which means the initializer list is
generated in order of the built fields. The version which uses the names
on C is not implemented (C99 designated initializers), so this flag has
to be enabled for now.

The struct builders now generate the struct as an inline expression if a
name isn't provided rather than a statement. This means we can now use
`addSimpleStruct` etc for the type of fields, but we can't replace
`addFieldWithStructType` because of `#pragma pack(pop)`.

Doc comments are added to every usable proc but may still not be
sufficient.
2024-10-06 13:51:41 +02:00
ringabout
a65501325c enable nimExperimentalLinenoiseExtra (#24227)
follow up https://github.com/nim-lang/Nim/pull/16977

it was added in 1.6.0
2024-10-06 13:33:40 +02:00
metagn
cad8726907 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.
2024-10-06 12:55:34 +02:00
ringabout
aa605da92a -d:nimPreviewFloatRoundtrip becomes the default (#24217) 2024-10-06 08:35:03 +02:00
metagn
09043f409f delay markUsed for converters until call is resolved (#24243)
fixes #24241
2024-10-06 08:10:37 +02:00
metagn
9e30b39412 make new concepts match themselves (#24244)
fixes #22839
2024-10-06 08:09:52 +02:00
metagn
4a63186cda 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.
```
2024-10-06 06:33:44 +02:00
tersec
782b75cc08 update minimum recommended gcc version and fix manual typos (#24240)
ref https://github.com/nim-lang/Nim/issues/24235
2024-10-06 11:04:37 +08:00
Alex
f420a5a273 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>
2024-10-06 11:03:14 +08:00
metagn
7dfadb8b4e 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.
2024-10-03 20:39:55 +02:00
metagn
4eed341ba5 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.
2024-10-03 20:38:42 +02:00
metagn
d98ef312f0 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.
2024-10-03 19:35:53 +02:00
metagn
89978b48ba use cbuilder for seq type generation (#24202)
`addSimpleStruct` is just so the compiler doesn't use so much extra
computation on analyzing the `typ` parameter for `addStruct`, which
doesn't change anything for `seq` types. We could probably still get
away with using `addStruct` instead, or making `addStruct` accept `nil`
as the `typ` argument but this would be even more computation.

There were a lot of hidden issues with `addStruct` being a template &
template argument substitution, so most of the behavior is moved into
`startStruct`/`finishStruct` procs.

This is turning out to be a lot of code for just a couple of changed
lines, we might have to split `cbuilder` into multiple modules.
2024-10-03 19:35:21 +02:00
Miran
d6a71a1067 bump NimVersion to 2.2.1 (#24215) 2024-10-02 22:02:17 +02:00
ringabout
f7cb0322c2 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.
2024-10-02 18:25:59 +02:00
553 changed files with 6610 additions and 14341 deletions

View File

@@ -15,16 +15,9 @@ jobs:
name: ${{ matrix.platform }}-bisects
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Install OpenSSL (Windows)
if: |
runner.os == 'Windows'
run: choco install openssl.light --version=1.1.1.0 # OpenSSL 3.x removed SSL_library_init
shell: 'powershell'
# v2 wont work here, because uses "hardcoded" nim versions, action "dynamically" finds version with bug.
- uses: jiro4989/setup-nim-action@v1
- uses: jiro4989/setup-nim-action@v1
with:
nim-version: 'devel'

View File

@@ -41,9 +41,9 @@ jobs:
target: [linux, windows, osx]
include:
- target: linux
os: ubuntu-22.04
os: ubuntu-20.04
- target: windows
os: windows-latest
os: windows-2019
- target: osx
os: macos-13

View File

@@ -18,13 +18,9 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-14]
os: [ubuntu-20.04, macos-13]
cpu: [amd64]
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
include:
- os: ubuntu-latest
cpu: amd64
- os: macos-14
cpu: arm64
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
runs-on: ${{ matrix.os }}
timeout-minutes: 60 # refs bug #18178
@@ -45,11 +41,11 @@ jobs:
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
run: |
sudo apt-get update -qq
sudo apt-fast update -qq
DEBIAN_FRONTEND='noninteractive' \
sudo apt-get install --no-install-recommends -yq \
sudo apt-fast install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
valgrind libc6-dbg libblas-dev liblapack-dev libpcre3 xorg-dev
valgrind libc6-dbg libblas-dev xorg-dev
- name: 'Install dependencies (macOS)'
if: runner.os == 'macOS'
run: brew install boehmgc make sfml gtk+3

View File

@@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04]
os: [ubuntu-20.04]
cpu: [amd64]
name: '${{ matrix.os }}'
runs-on: ${{ matrix.os }}
@@ -21,10 +21,10 @@ jobs:
with:
fetch-depth: 2
- name: 'Install node.js'
- name: 'Install node.js 20.x'
uses: actions/setup-node@v4
with:
node-version: ''
node-version: '20.x'
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
@@ -34,6 +34,17 @@ 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

@@ -36,16 +36,16 @@ jobs:
CPU: amd64
NIM_COMPILE_TO_CPP: true
Windows_amd64_batch0_3:
vmImage: 'windows-2025'
vmImage: 'windows-2019'
CPU: amd64
# see also: `NIM_TEST_PACKAGES`
NIM_TESTAMENT_BATCH: "0_3"
Windows_amd64_batch1_3:
vmImage: 'windows-2025'
vmImage: 'windows-2019'
CPU: amd64
NIM_TESTAMENT_BATCH: "1_3"
Windows_amd64_batch2_3:
vmImage: 'windows-2025'
vmImage: 'windows-2019'
CPU: amd64
NIM_TESTAMENT_BATCH: "2_3"

View File

@@ -12,53 +12,15 @@ rounding guarantees (via the
avoid conflicts with `system.default`, so named argument usage for this
parameter like `getOrDefault(..., default = ...)` will have to be changed.
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
- Unknown warnings and hints now gives warnings `warnUnknownNotes` instead of
errors.
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
- The bare `except:` now panics on `Defect`. Use `except Exception:` or `except Defect:` to catch `Defect`. `--legacy:noPanicOnExcept` is provided for a transition period.
- With `-d:nimPreviewCStringComparisons`, comparsions (`<`, `>`, `<=`, `>=`) between cstrings switch from reference semantics to value semantics like `==` and `!=`.
- `std/parsesql` has been moved to a nimble package, use `nimble` or `atlas` to install it.
- With `-d:nimPreviewDuplicateModuleError`, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Use `import foo as foo1` (or other aliases) to avoid collisions.
## Standard library additions and changes
[//]: # "Additions:"
- `setutils.symmetricDifference` along with its operator version
`` setutils.`-+-` `` and in-place version `setutils.toggle` have been added
to more efficiently calculate the symmetric difference of bitsets.
- `strutils.multiReplace` overload for character set replacements in a single pass.
Useful for string sanitation. Follows existing multiReplace semantics.
- `std/files` adds:
- Exports `CopyFlag` enum and `FilePermission` type for fine-grained control of file operations
- New file operation procs with `Path` support:
- `getFilePermissions`, `setFilePermissions` for managing permissions
- `tryRemoveFile` for file deletion
- `copyFile` with configurable buffer size and symlink handling
- `copyFileWithPermissions` to preserve file attributes
- `copyFileToDir` for copying files into directories
- `std/dirs` adds:
- New directory operation procs with `Path` support:
- `copyDir` with special file handling options
- `copyDirWithPermissions` to recursively preserve attributes
- `system.setLenUninit` now supports refc, JS and VM backends.
[//]: # "Changes:"
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
## Language changes
@@ -102,4 +64,4 @@ errors.
## Tool changes
- 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 corresponding to the number of occurrences
now produces a `CountTable` with values correspoding to the number of occurrences
of the key in the input. It used to produce a table with all values set to `1`.
Counting occurrences in a sequence used to be:
@@ -339,7 +339,7 @@ for i in a..b:
- Fixed "ReraiseError when using try/except within finally block"
([#5871](https://github.com/nim-lang/Nim/issues/5871))
- Fixed "Range type inference leads to counter-intuitive behaviour"
- Fixed "Range type inference leads to counter-intuitive behvaiour"
([#5854](https://github.com/nim-lang/Nim/issues/5854))
- Fixed "JSON % operator can fail in extern procs with dynamic types"
([#6385](https://github.com/nim-lang/Nim/issues/6385))
@@ -403,7 +403,7 @@ for i in a..b:
([#6589](https://github.com/nim-lang/Nim/issues/6589))
- Fixed "Generated c code calls function twice"
([#6292](https://github.com/nim-lang/Nim/issues/6292))
- Fixed "Range type inference leads to counter-intuitive behaviour"
- Fixed "Range type inference leads to counter-intuitive behvaiour"
([#5854](https://github.com/nim-lang/Nim/issues/5854))
- Fixed "New backward indexing is too limited"
([#6631](https://github.com/nim-lang/Nim/issues/6631))

View File

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

View File

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

View File

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

View File

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

View File

@@ -279,7 +279,7 @@ const
GcTypeKinds* = {tyRef, tySequence, tyString}
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
tyUserTypeClass, tyUserTypeClassInst, tyConcept,
tyUserTypeClass, tyUserTypeClassInst,
tyAnd, tyOr, tyNot, tyAnything}
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
@@ -447,8 +447,6 @@ const
tfReturnsNew* = tfInheritable
tfNonConstExpr* = tfExplicitCallConv
## tyFromExpr where the expression shouldn't be evaluated as a static value
tfGenericHasDestructor* = tfExplicitCallConv
## tyGenericBody where an instance has a generated destructor
skError* = skUnknown
var
@@ -684,7 +682,6 @@ type
symbols*: TStrTable
parent*: PScope
allowPrivateAccess*: seq[PSym] # # enable access to private fields
optionStackLen*: int
PScope* = ref TScope
@@ -796,15 +793,6 @@ type
TPairSeq* = seq[TPair]
TIdPair*[T] = object
key*: ItemId
val*: T
TIdPairSeq*[T] = seq[TIdPair[T]]
TIdTable*[T] = object
counter*: int
data*: TIdPairSeq[T]
TNodePair* = object
h*: Hash # because it is expensive to compute!
key*: PNode
@@ -815,7 +803,6 @@ type
# nodes are compared by structure!
counter*: int
data*: TNodePairSeq
ignoreTypes*: bool
TObjectSeq* = seq[RootRef]
TObjectSet* = object
@@ -944,17 +931,16 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
case a.kind
of nkSym: a.sym.name
of nkIdent: a.ident
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: a.sons[0].sym.name
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
of nkOpenSym: getPIdent(a.sons[0])
else: nil
const
moduleShift = when defined(cpu32): 20 else: 24
template toId*(a: ItemId): int =
template id*(a: PType | PSym): int =
let x = a
(x.module.int shl moduleShift) + x.item.int
template id*(a: PType | PSym): int = toId(a.itemId)
(x.itemId.module.int shl moduleShift) + x.itemId.item.int
type
IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
@@ -1281,11 +1267,6 @@ proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
setLen(dest.data, src.data.len)
for i in 0..high(src.data): dest.data[i] = src.data[i]
proc copyIdTable*[T](dest: var TIdTable[T], src: TIdTable[T]) =
dest.counter = src.counter
newSeq(dest.data, src.data.len)
for i in 0..high(src.data): dest.data[i] = src.data[i]
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
dest.counter = src.counter
setLen(dest.data, src.data.len)
@@ -1624,22 +1605,12 @@ proc initStrTable*(): TStrTable =
result = TStrTable(counter: 0)
newSeq(result.data, StartSize)
proc initIdTable*[T](): TIdTable[T] =
result = TIdTable[T](counter: 0)
newSeq(result.data, StartSize)
proc resetIdTable*[T](x: var TIdTable[T]) =
x.counter = 0
# clear and set to old initial size:
setLen(x.data, 0)
setLen(x.data, StartSize)
proc initObjectSet*(): TObjectSet =
result = TObjectSet(counter: 0)
newSeq(result.data, StartSize)
proc initNodeTable*(ignoreTypes=false): TNodeTable =
result = TNodeTable(counter: 0, ignoreTypes: ignoreTypes)
proc initNodeTable*(): TNodeTable =
result = TNodeTable(counter: 0)
newSeq(result.data, StartSize)
proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
@@ -1931,7 +1902,7 @@ proc skipGenericOwner*(s: PSym): PSym =
proc originatingModule*(s: PSym): PSym =
result = s
while result.kind != skModule: result = result.owner
while result != nil and result.kind != skModule: result = result.owner
proc isRoutine*(s: PSym): bool {.inline.} =
result = s.kind in skProcKinds
@@ -2116,16 +2087,14 @@ proc canRaise*(fn: PNode): bool =
result = false
elif fn.kind == nkSym and fn.sym.magic == mEcho:
result = true
elif fn.typ != nil and fn.typ.kind == tyProc and fn.typ.n != nil:
else:
# TODO check for n having sons? or just return false for now if not
if fn.typ.n[0].kind == nkSym:
if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym:
result = false
else:
result = ((fn.typ.n[0].len < effectListLen) or
result = fn.typ != nil and fn.typ.n != nil and ((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
@@ -2162,8 +2131,14 @@ proc isTrue*(n: PNode): bool =
n.kind == nkIntLit and n.intVal != 0
type
TypeMapping* = TIdTable[PType]
SymMapping* = TIdTable[PSym]
TypeMapping* = Table[ItemId, PType]
SymMapping* = Table[ItemId, PSym]
template initSymMapping*(): SymMapping = initIdTable[PSym]()
template initTypeMapping*(): TypeMapping = initIdTable[PType]()
template idTableGet*(tab: typed; key: PSym | PType): untyped = tab.getOrDefault(key.itemId)
template idTablePut*(tab: typed; key, val: PSym | PType) = tab[key.itemId] = val
template initSymMapping*(): Table[ItemId, PSym] = initTable[ItemId, PSym]()
template initTypeMapping*(): Table[ItemId, PType] = initTable[ItemId, PType]()
template resetIdTable*(tab: Table[ItemId, PSym]) = tab.clear()
template resetIdTable*(tab: Table[ItemId, PType]) = tab.clear()

View File

@@ -713,70 +713,6 @@ iterator items*(tab: TStrTable): PSym =
yield s
s = nextIter(it, tab)
proc isNil(x: ItemId): bool {.inline.} =
x.module == 0 and x.item == 0
proc hasEmptySlot[T](data: TIdPairSeq[T]): bool =
for h in 0..high(data):
if isNil(data[h].key):
return true
result = false
proc idTableRawGet[T](t: TIdTable[T], key: int): int =
var h: Hash
h = key and high(t.data) # start with real hash value
while not isNil(t.data[h].key):
if toId(t.data[h].key) == key:
return h
h = nextTry(h, high(t.data))
result = - 1
proc getOrDefault*[T](t: TIdTable[T], key: ItemId): T =
var index = idTableRawGet(t, toId(key))
if index >= 0: result = t.data[index].val
else: result = default(T)
template idTableGet*[T](t: TIdTable[T], key: PType | PSym): T =
getOrDefault(t, key.itemId)
proc idTableRawInsert[T](data: var TIdPairSeq[T], key: ItemId, val: T) =
var h: Hash
let keyId = toId(key)
h = keyId and high(data)
while not isNil(data[h].key):
assert(toId(data[h].key) != keyId)
h = nextTry(h, high(data))
assert(isNil(data[h].key))
data[h].key = key
data[h].val = val
proc `[]=`*[T](t: var TIdTable[T], key: ItemId, val: T) =
var
index: int
n: TIdPairSeq[T]
index = idTableRawGet(t, toId(key))
if index >= 0:
assert(not isNil(t.data[index].key))
t.data[index].val = val
else:
if mustRehash(t.data.len, t.counter):
newSeq(n, t.data.len * GrowthFactor)
for i in 0..high(t.data):
if not isNil(t.data[i].key):
idTableRawInsert(n, t.data[i].key, t.data[i].val)
assert(hasEmptySlot(n))
swap(t.data, n)
idTableRawInsert(t.data, key, val)
inc(t.counter)
template idTablePut*[T](t: var TIdTable[T], key: PType | PSym, val: T) =
t[key.itemId] = val
iterator idTablePairs*[T](t: TIdTable[T]): tuple[key: ItemId, val: T] =
for i in 0..high(t.data):
if not isNil(t.data[i].key):
yield (t.data[i].key, t.data[i].val)
proc initIITable(x: var TIITable) =
x.counter = 0
newSeq(x.data, StartSize)

View File

@@ -1,121 +0,0 @@
type
Snippet = string
Builder = string
template newBuilder(s: string): Builder =
s
proc addField(obj: var Builder; name, typ: Snippet) =
obj.add('\t')
obj.add(typ)
obj.add(" ")
obj.add(name)
obj.add(";\n")
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool; initializer: Snippet) =
obj.add('\t')
if field.alignment > 0:
obj.add("NIM_ALIGN(")
obj.addInt(field.alignment)
obj.add(") ")
obj.add(typ)
if sfNoalias in field.flags:
obj.add(" NIM_NOALIAS")
obj.add(" ")
obj.add(name)
if isFlexArray:
obj.add("[SEQ_DECL_SIZE]")
if field.bitsize != 0:
obj.add(":")
obj.addInt(field.bitsize)
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc structOrUnion(t: PType): Snippet =
let t = t.skipTypes({tyAlias, tySink})
if tfUnion in t.flags: "union"
else: "struct"
proc ptrType(t: Snippet): Snippet =
t & "*"
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: string; body: typed) =
if tfPacked in typ.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add(structOrUnion(typ))
obj.add(" __attribute__((__packed__))")
else:
obj.add("#pragma pack(push, 1)\n")
obj.add(structOrUnion(typ))
else:
obj.add(structOrUnion(typ))
obj.add(" ")
obj.add(name)
type BaseClassKind = enum
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
var baseKind = bcNone
if typ.kind == tyObject:
if typ.baseClass == nil:
if lacksMTypeField(typ):
baseKind = bcNone
elif optTinyRtti in m.config.globalOptions:
baseKind = bcNoneTinyRtti
else:
baseKind = bcNoneRtti
elif m.compileToCpp:
baseKind = bcCppInherit
else:
baseKind = bcSupField
if baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
let currLen = obj.len
case baseKind
of bcNone:
# rest of the options add a field or don't need it due to inheritance,
# we need to add the dummy field for uncheckedarray ahead of time
# so that it remains trailing
if typ.itemId notin m.g.graph.memberProcsPerType and
typ.n != nil and typ.n.len == 1 and typ.n[0].kind == nkSym and
typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
# only consists of flexible array field, add *initial* dummy field
obj.addField(name = "dummy", typ = "char")
of bcCppInherit: discard
of bcNoneRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
of bcNoneTinyRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
of bcSupField:
obj.addField(name = "Sup", typ = baseType)
body
if baseKind == bcNone and currLen == obj.len and typ.itemId notin m.g.graph.memberProcsPerType:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = "char")
obj.add("};\n")
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
result.add("#pragma pack(pop)\n")
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
## adds a field with a `struct { ... }` type, building it according to `body`
obj.add('\t')
if tfPacked in parentTyp.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add("struct __attribute__((__packed__)) {\n")
else:
obj.add("#pragma pack(push, 1)\nstruct {")
else:
obj.add("struct {\n")
body
obj.add("} ")
obj.add(fieldName)
obj.add(";\n")
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
result.add("#pragma pack(pop)\n")
template addAnonUnion(obj: var Builder; body: typed) =
obj.add "union{\n"
body
obj.add("};\n")

88
compiler/cbuilderbase.nim Normal file
View File

@@ -0,0 +1,88 @@
import ropes, int128
type
Snippet* = string
Builder* = object
buf*: string
template newBuilder*(s: string): Builder =
Builder(buf: s)
proc extract*(builder: Builder): Snippet =
builder.buf
proc add*(builder: var Builder, s: string) =
builder.buf.add(s)
proc add*(builder: var Builder, s: char) =
builder.buf.add(s)
proc addIntValue*(builder: var Builder, val: int) =
builder.buf.addInt(val)
proc addIntValue*(builder: var Builder, val: int64) =
builder.buf.addInt(val)
proc addIntValue*(builder: var Builder, val: uint64) =
builder.buf.addInt(val)
proc addIntValue*(builder: var Builder, val: Int128) =
builder.buf.addInt128(val)
template cIntValue*(val: int): Snippet = $val
template cIntValue*(val: int64): Snippet = $val
template cIntValue*(val: uint64): Snippet = $val
template cIntValue*(val: Int128): Snippet = $val
import std/formatfloat
proc addFloatValue*(builder: var Builder, val: float) =
builder.buf.addFloat(val)
template cFloatValue*(val: float): Snippet = $val
proc addInt64Literal*(result: var Builder; i: BiggestInt) =
if i > low(int64):
result.add "IL64($1)" % [rope(i)]
else:
result.add "(IL64(-9223372036854775807) - IL64(1))"
proc addUint64Literal*(result: var Builder; i: uint64) =
result.add rope($i & "ULL")
proc addIntLiteral*(result: var Builder; i: BiggestInt) =
if i > low(int32) and i <= high(int32):
result.addIntValue(i)
elif i == low(int32):
# Nim has the same bug for the same reasons :-)
result.add "(-2147483647 -1)"
elif i > low(int64):
result.add "IL64($1)" % [rope(i)]
else:
result.add "(IL64(-9223372036854775807) - IL64(1))"
proc addIntLiteral*(result: var Builder; i: Int128) =
addIntLiteral(result, toInt64(i))
proc cInt64Literal*(i: BiggestInt): Snippet =
if i > low(int64):
result = "IL64($1)" % [rope(i)]
else:
result = "(IL64(-9223372036854775807) - IL64(1))"
proc cUint64Literal*(i: uint64): Snippet =
result = $i & "ULL"
proc cIntLiteral*(i: BiggestInt): Snippet =
if i > low(int32) and i <= high(int32):
result = rope(i)
elif i == low(int32):
# Nim has the same bug for the same reasons :-)
result = "(-2147483647 -1)"
elif i > low(int64):
result = "IL64($1)" % [rope(i)]
else:
result = "(IL64(-9223372036854775807) - IL64(1))"
proc cIntLiteral*(i: Int128): Snippet =
result = cIntLiteral(toInt64(i))

527
compiler/cbuilderdecls.nim Normal file
View File

@@ -0,0 +1,527 @@
type VarKind = enum
Local
Global
Threadvar
Const
AlwaysConst ## const even on C++
proc addVarHeader(builder: var Builder, kind: VarKind) =
## adds modifiers for given var kind:
## Local has no modifier
## Global has `static` modifier
## Const has `static NIM_CONST` modifier
## AlwaysConst has `static const` modifier (NIM_CONST is no-op on C++)
## Threadvar is unimplemented
case kind
of Local: discard
of Global:
builder.add("static ")
of Const:
builder.add("static NIM_CONST ")
of AlwaysConst:
builder.add("static const ")
of Threadvar:
doAssert false, "unimplemented"
proc addVar(builder: var Builder, kind: VarKind = Local, name: string, typ: Snippet, initializer: Snippet = "") =
## adds a variable declaration to the builder
builder.addVarHeader(kind)
builder.add(typ)
builder.add(" ")
builder.add(name)
if initializer.len != 0:
builder.add(" = ")
builder.add(initializer)
builder.add(";\n")
template addVarWithType(builder: var Builder, kind: VarKind = Local, name: string, body: typed) =
## adds a variable declaration to the builder, with the `body` building the type
builder.addVarHeader(kind)
body
builder.add(" ")
builder.add(name)
builder.add(";\n")
template addVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string,
typ: Snippet, initializerBody: typed) =
## adds a variable declaration to the builder, with
## `initializerBody` building the initializer. initializer must be provided
builder.addVarHeader(kind)
builder.add(typ)
builder.add(" ")
builder.add(name)
builder.add(" = ")
initializerBody
builder.add(";\n")
template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
typeBody, initializerBody: typed) =
## adds a variable declaration to the builder, with `typeBody` building the type, and
## `initializerBody` building the initializer. initializer must be provided
builder.addVarHeader(kind)
typeBody
builder.add(" ")
builder.add(name)
builder.add(" = ")
initializerBody
builder.add(";\n")
proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
## adds an array variable declaration to the builder
builder.addVarHeader(kind)
builder.add(elementType)
builder.add(" ")
builder.add(name)
builder.add("[")
builder.addIntValue(len)
builder.add("]")
if initializer.len != 0:
builder.add(" = ")
builder.add(initializer)
builder.add(";\n")
template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
## adds an array variable declaration to the builder with the initializer built according to `body`
builder.addVarHeader(kind)
builder.add(elementType)
builder.add(" ")
builder.add(name)
builder.add("[")
builder.addIntValue(len)
builder.add("] = ")
body
builder.add(";\n")
template addTypedef(builder: var Builder, name: string, typeBody: typed) =
## adds a typedef declaration to the builder with name `name` and type as
## built in `typeBody`
builder.add("typedef ")
typeBody
builder.add(" ")
builder.add(name)
builder.add(";\n")
proc addProcTypedef(builder: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
builder.add("typedef ")
builder.add(CallingConvToStr[callConv])
builder.add("_PTR(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
builder.add(";\n")
template addArrayTypedef(builder: var Builder, name: string, len: BiggestInt, typeBody: typed) =
## adds an array typedef declaration to the builder with name `name`,
## length `len`, and element type as built in `typeBody`
builder.add("typedef ")
typeBody
builder.add(" ")
builder.add(name)
builder.add("[")
builder.addIntValue(len)
builder.add("];\n")
type
StructInitializerKind = enum
siOrderedStruct ## struct constructor, but without named fields on C
siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer
siArray ## array constructor
siWrapper ## wrapper for a single field, generates it verbatim
StructInitializer = object
## context for building struct initializers, i.e. `{ field1, field2 }`
kind: StructInitializerKind
## if true, fields will not be named, instead values are placed in order
needsComma: bool
proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer =
## starts building a struct initializer, i.e. braced initializer list
result = StructInitializer(kind: kind, needsComma: false)
if kind != siWrapper:
builder.add("{")
template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) =
## adds a field to a struct initializer, with the value built in `valueBody`
if constr.needsComma:
assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields"
builder.add(", ")
else:
constr.needsComma = true
case constr.kind
of siArray, siWrapper:
# no name, can just add value
valueBody
of siOrderedStruct:
# no name, can just add value on C
assert name.len != 0, "name has to be given for struct initializer field"
valueBody
of siNamedStruct:
assert name.len != 0, "name has to be given for struct initializer field"
builder.add(".")
builder.add(name)
builder.add(" = ")
valueBody
proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
## finishes building a struct initializer
if constr.kind != siWrapper:
builder.add("}")
template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) =
## builds a struct initializer, i.e. `{ field1, field2 }`
## a `var StructInitializer` must be declared and passed as a parameter so
## that it can be used with `addField`
constr = builder.initStructInitializer(kind)
body
builder.finishStructInitializer(constr)
proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
## adds a field inside a struct/union type
obj.add('\t')
obj.add(typ)
obj.add(" ")
obj.add(name)
if isFlexArray:
obj.add("[SEQ_DECL_SIZE]")
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initializer: Snippet = "") =
## adds an array field inside a struct/union type
obj.add('\t')
obj.add(elementType)
obj.add(" ")
obj.add(name)
obj.add("[")
obj.addIntValue(len)
obj.add("]")
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
## adds an field inside a struct/union type, based on an `skField` symbol
obj.add('\t')
if field.alignment > 0:
obj.add("NIM_ALIGN(")
obj.addIntValue(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.addIntValue(field.bitsize)
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc addProcField(obj: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
obj.add(CallingConvToStr[callConv])
obj.add("_PTR(")
obj.add(rettype)
obj.add(", ")
obj.add(name)
obj.add(")")
obj.add(params)
obj.add(";\n")
type
BaseClassKind = enum
## denotes how and whether or not the base class/RTTI should be stored
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
StructBuilderInfo = object
## context for building `struct` types
baseKind: BaseClassKind
named: bool
preFieldsLen: int
proc structOrUnion(t: PType): Snippet =
let t = t.skipTypes({tyAlias, tySink})
if tfUnion in t.flags: "union"
else: "struct"
proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
obj.add("struct")
if result.named:
obj.add(" ")
obj.add(name)
if baseType.len != 0:
if m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
if result.baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
result.preFieldsLen = obj.buf.len
if result.baseKind == bcSupField:
obj.addField(name = "Sup", typ = baseType)
proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
if info.baseKind == bcNone and info.preFieldsLen == obj.buf.len:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = "char")
if info.named:
obj.add("};\n")
else:
obj.add("}")
template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
## builds a struct type not based on a Nim type with fields according to `body`,
## `name` can be empty to build as a type expression and not a statement
let info = startSimpleStruct(obj, m, name, baseType)
body
finishSimpleStruct(obj, m, info)
proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
if tfPacked in t.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add(structOrUnion(t))
obj.add(" __attribute__((__packed__))")
else:
obj.add("#pragma pack(push, 1)\n")
obj.add(structOrUnion(t))
else:
obj.add(structOrUnion(t))
if result.named:
obj.add(" ")
obj.add(name)
if t.kind == tyObject:
if t.baseClass == nil:
if lacksMTypeField(t):
result.baseKind = bcNone
elif optTinyRtti in m.config.globalOptions:
result.baseKind = bcNoneTinyRtti
else:
result.baseKind = bcNoneRtti
elif m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
elif baseType.len != 0:
if m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
if result.baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
result.preFieldsLen = obj.buf.len
case result.baseKind
of bcNone:
# rest of the options add a field or don't need it due to inheritance,
# we need to add the dummy field for uncheckedarray ahead of time
# so that it remains trailing
if t.itemId notin m.g.graph.memberProcsPerType and
t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
# only consists of flexible array field, add *initial* dummy field
obj.addField(name = "dummy", typ = "char")
of bcCppInherit: discard
of bcNoneRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
of bcNoneTinyRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
of bcSupField:
obj.addField(name = "Sup", typ = baseType)
proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
if info.baseKind == bcNone and info.preFieldsLen == obj.buf.len and
t.itemId notin m.g.graph.memberProcsPerType:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = "char")
if info.named:
obj.add("};\n")
else:
obj.add("}")
if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
obj.add("#pragma pack(pop)\n")
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
## builds a struct type directly based on `typ` with fields according to `body`,
## `name` can be empty to build as a type expression and not a statement
let info = startStruct(obj, m, typ, name, baseType)
body
finishStruct(obj, m, typ, info)
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
## adds a field with a `struct { ... }` type, building the fields according to `body`
obj.add('\t')
if tfPacked in parentTyp.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add("struct __attribute__((__packed__)) {\n")
else:
obj.add("#pragma pack(push, 1)\nstruct {")
else:
obj.add("struct {\n")
body
obj.add("} ")
obj.add(fieldName)
obj.add(";\n")
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
result.add("#pragma pack(pop)\n")
template addAnonUnion(obj: var Builder; body: typed) =
## adds an anonymous union i.e. `union { ... };` with fields according to `body`
obj.add "union{\n"
body
obj.add("};\n")
type DeclVisibility = enum
None
Extern
ExternC
ImportLib
ExportLib
ExportLibVar
Private
StaticProc
template addDeclWithVisibility(builder: var Builder, visibility: DeclVisibility, declBody: typed) =
## adds a declaration as in `declBody` with the given visibility
case visibility
of None: discard
of Extern:
builder.add("extern ")
of ExternC:
builder.add("extern \"C\" ")
of ImportLib:
builder.add("N_LIB_IMPORT ")
of ExportLib:
builder.add("N_LIB_EXPORT ")
of ExportLibVar:
builder.add("N_LIB_EXPORT_VAR ")
of Private:
builder.add("N_LIB_PRIVATE ")
of StaticProc:
builder.add("static ")
declBody
type ProcParamBuilder = object
needsComma: bool
proc initProcParamBuilder(builder: var Builder): ProcParamBuilder =
result = ProcParamBuilder(needsComma: false)
builder.add("(")
proc finishProcParamBuilder(builder: var Builder, params: ProcParamBuilder) =
if params.needsComma:
builder.add(")")
else:
builder.add("void)")
template cgDeclFrmt*(s: PSym): string =
s.constraint.strVal
proc addParam(builder: var Builder, params: var ProcParamBuilder, name: string, typ: Snippet) =
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
builder.add(typ)
builder.add(" ")
builder.add(name)
proc addParam(builder: var Builder, params: var ProcParamBuilder, param: PSym, typ: Snippet) =
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
var modifiedTyp = typ
if sfNoalias in param.flags:
modifiedTyp.add(" NIM_NOALIAS")
if sfCodegenDecl notin param.flags:
builder.add(modifiedTyp)
builder.add(" ")
builder.add(param.loc.snippet)
else:
builder.add runtimeFormat(param.cgDeclFrmt, [modifiedTyp, param.loc.snippet])
proc addUnnamedParam(builder: var Builder, params: var ProcParamBuilder, typ: Snippet) =
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
builder.add(typ)
proc addVarargsParam(builder: var Builder, params: var ProcParamBuilder) =
# does not exist in NIFC, needs to be proc pragma
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
builder.add("...")
template addProcParams(builder: var Builder, params: out ProcParamBuilder, body: typed) =
params = initProcParamBuilder(builder)
body
finishProcParamBuilder(builder, params)
proc addProcHeader(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet, addAttributes: bool) =
# on nifc should build something like (proc name params type pragmas
# with no body given
let noreturn = isNoReturn(m, prc)
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(naked) ")
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(noreturn) ")
builder.add(CallingConvToStr[prc.typ.callConv])
builder.add("(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
if addAttributes:
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((naked))")
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((noreturn))")
proc finishProcHeaderAsProto(builder: var Builder) =
builder.add(";\n")
template finishProcHeaderWithBody(builder: var Builder, body: typed) =
builder.add(" {\n")
body
builder.add("}\n\n")
proc addProcVar(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet,
isStatic = false, ignoreAttributes = false) =
# on nifc, builds full variable
if isStatic:
builder.add("static ")
let noreturn = isNoReturn(m, prc)
if not ignoreAttributes:
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(naked) ")
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(noreturn) ")
builder.add(CallingConvToStr[prc.typ.callConv])
builder.add("_PTR(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
if not ignoreAttributes:
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((naked))")
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((noreturn))")
# ensure we are just adding a variable:
builder.add(";\n")

232
compiler/cbuilderexprs.nim Normal file
View File

@@ -0,0 +1,232 @@
# XXX make complex ones like bitOr use builder instead
# XXX add stuff like NI, NIM_NIL as constants
proc constType(t: Snippet): Snippet =
# needs manipulation of `t` in nifc
"NIM_CONST " & t
proc constPtrType(t: Snippet): Snippet =
t & "* NIM_CONST"
proc ptrConstType(t: Snippet): Snippet =
"NIM_CONST " & t & "*"
proc ptrType(t: Snippet): Snippet =
t & "*"
const
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
"N_STDCALL", "N_CDECL", "N_SAFECALL",
"N_SYSCALL", # this is probably not correct for all platforms,
# but one can #define it to what one wants
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
"N_NOCONV" #ccMember is N_NOCONV
]
proc procPtrTypeUnnamed(rettype, params: Snippet): Snippet =
rettype & "(*)" & params
proc procPtrTypeUnnamedNimCall(rettype, params: Snippet): Snippet =
rettype & "(N_RAW_NIMCALL*)" & params
proc cCast(typ, value: Snippet): Snippet =
"((" & typ & ") " & value & ")"
proc wrapPar(value: Snippet): Snippet =
# used for expression group, no-op on sexp
"(" & value & ")"
proc removeSinglePar(value: Snippet): Snippet =
# removes a single paren layer expected to exist, to silence Wparentheses-equality
assert value[0] == '(' and value[^1] == ')'
value[1..^2]
template addCast(builder: var Builder, typ: Snippet, valueBody: typed) =
## adds a cast to `typ` with value built by `valueBody`
builder.add "(("
builder.add typ
builder.add ") "
valueBody
builder.add ")"
proc cAddr(value: Snippet): Snippet =
"&" & value
proc cDeref(value: Snippet): Snippet =
"(*" & value & ")"
proc subscript(a, b: Snippet): Snippet =
a & "[" & b & "]"
proc dotField(a, b: Snippet): Snippet =
a & "." & b
proc derefField(a, b: Snippet): Snippet =
a & "->" & b
proc bitOr(a, b: Snippet): Snippet =
"(" & a & " | " & b & ")"
type CallBuilder = object
needsComma: bool
proc initCallBuilder(builder: var Builder, callee: Snippet): CallBuilder =
result = CallBuilder(needsComma: false)
builder.add(callee)
builder.add("(")
template addArgument(builder: var Builder, call: var CallBuilder, valueBody: typed) =
if call.needsComma:
builder.add(", ")
else:
call.needsComma = true
valueBody
proc finishCallBuilder(builder: var Builder, call: CallBuilder) =
builder.add(")")
template addCall(builder: var Builder, call: out CallBuilder, callee: Snippet, body: typed) =
call = initCallBuilder(builder, callee)
body
finishCallBuilder(builder, call)
proc addCall(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
builder.add(callee)
builder.add("(")
if args.len != 0:
builder.add(args[0])
for i in 1 ..< args.len:
builder.add(", ")
builder.add(args[i])
builder.add(")")
proc cCall(callee: Snippet, args: varargs[Snippet]): Snippet =
result = callee
result.add("(")
if args.len != 0:
result.add(args[0])
for i in 1 ..< args.len:
result.add(", ")
result.add(args[i])
result.add(")")
proc addSizeof(builder: var Builder, val: Snippet) =
builder.add("sizeof(")
builder.add(val)
builder.add(")")
proc addAlignof(builder: var Builder, val: Snippet) =
builder.add("NIM_ALIGNOF(")
builder.add(val)
builder.add(")")
proc addOffsetof(builder: var Builder, val, member: Snippet) =
builder.add("offsetof(")
builder.add(val)
builder.add(", ")
builder.add(member)
builder.add(")")
template cSizeof(val: Snippet): Snippet =
"sizeof(" & val & ")"
template cAlignof(val: Snippet): Snippet =
"NIM_ALIGNOF(" & val & ")"
template cOffsetof(val, member: Snippet): Snippet =
"offsetof(" & val & ", " & member & ")"
type TypedBinaryOp = enum
Add, Sub, Mul, Div, Mod
Shr, Shl, BitAnd, BitOr, BitXor
const typedBinaryOperators: array[TypedBinaryOp, string] = [
Add: "+",
Sub: "-",
Mul: "*",
Div: "/",
Mod: "%",
Shr: ">>",
Shl: "<<",
BitAnd: "&",
BitOr: "|",
BitXor: "^"
]
type TypedUnaryOp = enum
Neg, BitNot
const typedUnaryOperators: array[TypedUnaryOp, string] = [
Neg: "-",
BitNot: "~",
]
type UntypedBinaryOp = enum
LessEqual, LessThan, GreaterEqual, GreaterThan, Equal, NotEqual
And, Or
const untypedBinaryOperators: array[UntypedBinaryOp, string] = [
LessEqual: "<=",
LessThan: "<",
GreaterEqual: ">=",
GreaterThan: ">",
Equal: "==",
NotEqual: "!=",
And: "&&",
Or: "||"
]
type UntypedUnaryOp = enum
Not
const untypedUnaryOperators: array[UntypedUnaryOp, string] = [
Not: "!"
]
proc addOp(builder: var Builder, binOp: TypedBinaryOp, t: Snippet, a, b: Snippet) =
builder.add('(')
builder.add(a)
builder.add(' ')
builder.add(typedBinaryOperators[binOp])
builder.add(' ')
builder.add(b)
builder.add(')')
proc addOp(builder: var Builder, unOp: TypedUnaryOp, t: Snippet, a: Snippet) =
builder.add('(')
builder.add(typedUnaryOperators[unOp])
builder.add('(')
builder.add(a)
builder.add("))")
proc addOp(builder: var Builder, binOp: UntypedBinaryOp, a, b: Snippet) =
builder.add('(')
builder.add(a)
builder.add(' ')
builder.add(untypedBinaryOperators[binOp])
builder.add(' ')
builder.add(b)
builder.add(')')
proc addOp(builder: var Builder, unOp: UntypedUnaryOp, a: Snippet) =
builder.add('(')
builder.add(untypedUnaryOperators[unOp])
builder.add('(')
builder.add(a)
builder.add("))")
template cOp(binOp: TypedBinaryOp, t: Snippet, a, b: Snippet): Snippet =
'(' & a & ' ' & typedBinaryOperators[binOp] & ' ' & b & ')'
template cOp(binOp: TypedUnaryOp, t: Snippet, a: Snippet): Snippet =
'(' & typedUnaryOperators[binOp] & '(' & a & "))"
template cOp(binOp: UntypedBinaryOp, a, b: Snippet): Snippet =
'(' & a & ' ' & untypedBinaryOperators[binOp] & ' ' & b & ')'
template cOp(binOp: UntypedUnaryOp, a: Snippet): Snippet =
'(' & untypedUnaryOperators[binOp] & '(' & a & "))"
template cIfExpr(cond, a, b: Snippet): Snippet =
# XXX used for `min` and `max`, maybe add nifc primitives for these
"(" & cond & " ? " & a & " : " & b & ")"

150
compiler/cbuilderstmts.nim Normal file
View File

@@ -0,0 +1,150 @@
template addAssignmentWithValue(builder: var Builder, lhs: Snippet, valueBody: typed) =
builder.add(lhs)
builder.add(" = ")
valueBody
builder.add(";\n")
template addFieldAssignmentWithValue(builder: var Builder, lhs: Snippet, name: string, valueBody: typed) =
builder.add(lhs)
builder.add("." & name & " = ")
valueBody
builder.add(";\n")
template addAssignment(builder: var Builder, lhs, rhs: Snippet) =
builder.addAssignmentWithValue(lhs):
builder.add(rhs)
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
builder.addFieldAssignmentWithValue(lhs, name):
builder.add(rhs)
template addMutualFieldAssignment(builder: var Builder, lhs, rhs: Snippet, name: string) =
builder.addFieldAssignmentWithValue(lhs, name):
builder.add(rhs)
builder.add("." & name)
template addAssignment(builder: var Builder, lhs: Snippet, rhs: int | int64 | uint64 | Int128) =
builder.addAssignmentWithValue(lhs):
builder.addIntValue(rhs)
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: int | int64 | uint64 | Int128) =
builder.addFieldAssignmentWithValue(lhs, name):
builder.addIntValue(rhs)
template addDerefFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
builder.add(lhs)
builder.add("->" & name & " = ")
builder.add(rhs)
builder.add(";\n")
template addSubscriptAssignment(builder: var Builder, lhs: Snippet, index: Snippet, rhs: Snippet) =
builder.add(lhs)
builder.add("[" & index & "] = ")
builder.add(rhs)
builder.add(";\n")
template addStmt(builder: var Builder, stmtBody: typed) =
## makes an expression built by `stmtBody` into a statement
stmtBody
builder.add(";\n")
proc addCallStmt(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
builder.addStmt():
builder.addCall(callee, args)
# XXX blocks need indent tracker in `Builder` object
template addSingleIfStmt(builder: var Builder, cond: Snippet, body: typed) =
builder.add("if (")
builder.add(cond)
builder.add(") {\n")
body
builder.add("}\n")
template addSingleIfStmtWithCond(builder: var Builder, condBody: typed, body: typed) =
builder.add("if (")
condBody
builder.add(") {\n")
body
builder.add("}\n")
type IfStmt = object
needsElse: bool
template addIfStmt(builder: var Builder, stmt: out IfStmt, body: typed) =
stmt = IfStmt(needsElse: false)
body
builder.add("\n")
template addElifBranch(builder: var Builder, stmt: var IfStmt, cond: Snippet, body: typed) =
if stmt.needsElse:
builder.add(" else ")
else:
stmt.needsElse = true
builder.add("if (")
builder.add(cond)
builder.add(") {\n")
body
builder.add("}")
template addElseBranch(builder: var Builder, stmt: var IfStmt, body: typed) =
assert stmt.needsElse
builder.add(" else {\n")
body
builder.add("}")
proc addForRangeHeader(builder: var Builder, i, start, bound: Snippet, inclusive: bool = false) =
builder.add("for (")
builder.add(i)
builder.add(" = ")
builder.add(start)
builder.add("; ")
builder.add(i)
if inclusive:
builder.add(" <= ")
else:
builder.add(" < ")
builder.add(bound)
builder.add("; ")
builder.add(i)
builder.add("++) {\n")
template addForRangeExclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
addForRangeHeader(builder, i, start, bound, false)
body
builder.add("}\n")
template addForRangeInclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
addForRangeHeader(builder, i, start, bound, true)
body
builder.add("}\n")
template addScope(builder: var Builder, body: typed) =
builder.add("{")
body
builder.add("\t}")
proc addLabel(builder: var Builder, name: TLabel) =
builder.add(name)
builder.add(": ;\n")
proc addReturn(builder: var Builder) =
builder.add("return;\n")
proc addReturn(builder: var Builder, value: string) =
builder.add("return ")
builder.add(value)
builder.add(";\n")
template addGoto(builder: var Builder, label: TLabel) =
builder.add("goto ")
builder.add(label)
builder.add(";\n")
template addIncr(builder: var Builder, val: Snippet) =
builder.add(val)
builder.add("++;\n")
template addDecr(builder: var Builder, val: Snippet) =
builder.add(val)
builder.add("--;\n")

View File

@@ -135,7 +135,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
d.snippet = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
if d.k == locNone and p.splitDecls == 0:
d = getTempCpp(p, typ.returnType, pl)
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
@@ -209,8 +209,7 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
lengthExpr)
else:
var lit = newRopeAppender()
intLiteral(first, lit)
let lit = cIntLiteral(first)
result = ("($4*)($1)+(($2)-($3))" %
[rdLoc(a), rdLoc(b), lit, dest],
lengthExpr)
@@ -307,7 +306,7 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
else:
result = a
proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
result = getTemp(p, a.lode.typ, needsInit=false)
genAssignment(p, result, a, {})
@@ -326,7 +325,7 @@ 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, expressionsNeedsTmp(p, a), result)
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
else:
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
elif p.module.compileToCpp and param.typ.kind in {tyVar} and

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -66,13 +66,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
var x = typ.baseClass
if x != nil: x = x.skipTypes(skipPtrs)
specializeResetT(p, accessor.parentObj(p.module), x)
if typ.n != nil:
if typ.sym != nil and sfImportc in typ.sym.flags:
# imported C struct, nimZeroMem
lineCg(p, cpsStmts, "#nimZeroMem((void**)&$1, sizeof($2));$n",
[accessor, getTypeDesc(p.module, typ)])
else:
specializeResetN(p, accessor, typ.n, typ)
if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
of tyTuple:
let typ = getUniqueType(typ)
for i, a in typ.ikids:

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
containsManagedMemory(v.loc.t):
containsGarbageCollectedRef(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)
@@ -146,16 +146,16 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
a.flags.incl(lfEnforceDeref)
expr(p, ri, a)
proc assignLabel(b: var TBlock; result: var Rope) {.inline.} =
proc assignLabel(b: var TBlock; result: var Builder) {.inline.} =
b.label = "LA" & b.id.rope
result.add b.label
proc blockBody(b: var TBlock; result: var Rope) =
result.add b.sections[cpsLocals]
proc blockBody(b: var TBlock; result: var Builder) =
result.add extract(b.sections[cpsLocals])
if b.frameLen > 0:
result.addf("FR_.len+=$1;$n", [b.frameLen.rope])
result.add(b.sections[cpsInit])
result.add(b.sections[cpsStmts])
result.add(extract(b.sections[cpsInit]))
result.add(extract(b.sections[cpsStmts]))
proc endBlock(p: BProc, blockEnd: Rope) =
let topBlock = p.blocks.len-1
@@ -279,9 +279,9 @@ proc genGotoVar(p: BProc; value: PNode) =
else:
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope)
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Builder)
proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Builder) =
if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:
discard "nothing to do"
elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and
@@ -330,8 +330,9 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
value.kind in nkCallKinds and value[0].kind == nkSym and
v.typ.kind != tyPtr and sfConstructor in value[0].sym.flags
var targetProc = p
var valueAsRope = ""
potentialValueInit(p, v, value, valueAsRope)
var valueBuilder = newBuilder("")
potentialValueInit(p, v, value, valueBuilder)
let valueAsRope = extract(valueBuilder)
if sfGlobal in v.flags:
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
value.kind == nkEmpty and
@@ -594,8 +595,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
return
let val = getOrdValue(it[j])
var lit = newRopeAppender()
intLiteral(toInt64(val)+id+1, lit)
let lit = cIntLiteral(toInt64(val)+id+1)
lineF(p, cpsStmts, "TMP$#_:$n", [lit])
genStmts(p, it.lastSon)
@@ -775,7 +775,7 @@ proc finallyActions(p: BProc) =
if finallyBlock != nil:
genSimpleBlock(p, finallyBlock[0])
proc raiseInstr(p: BProc; result: var Rope) =
proc raiseInstr(p: BProc; result: var Builder) =
if p.config.exc == excGoto:
let L = p.nestedTryStmts.len
if L == 0:
@@ -925,8 +925,7 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
[rdLoc(a), bitMask])
for j in 0..high(branches):
if branches[j] != "":
var lit = newRopeAppender()
intLiteral(j, lit)
let lit = cIntLiteral(j)
lineF(p, cpsStmts, "case $1: $n$2break;$n",
[lit, branches[j]])
lineF(p, cpsStmts, "}$n", []) # else statement:
@@ -964,22 +963,22 @@ proc genCaseRange(p: BProc, branch: PNode) =
for j in 0..<branch.len-1:
if branch[j].kind == nkRange:
if hasSwitchRange in CC[p.config.cCompiler].props:
var litA = newRopeAppender()
var litB = newRopeAppender()
var litA = newBuilder("")
var litB = newBuilder("")
genLiteral(p, branch[j][0], litA)
genLiteral(p, branch[j][1], litB)
lineF(p, cpsStmts, "case $1 ... $2:$n", [litA, litB])
lineF(p, cpsStmts, "case $1 ... $2:$n", [extract(litA), extract(litB)])
else:
var v = copyNode(branch[j][0])
while v.intVal <= branch[j][1].intVal:
var litA = newRopeAppender()
var litA = newBuilder("")
genLiteral(p, v, litA)
lineF(p, cpsStmts, "case $1:$n", [litA])
lineF(p, cpsStmts, "case $1:$n", [extract(litA)])
inc(v.intVal)
else:
var litA = newRopeAppender()
var litA = newBuilder("")
genLiteral(p, branch[j], litA)
lineF(p, cpsStmts, "case $1:$n", [litA])
lineF(p, cpsStmts, "case $1:$n", [extract(litA)])
proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
# analyse 'case' statement:
@@ -1641,8 +1640,7 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
if not containsOrIncl(p.module.declaredThings, field.id):
appcg(p.module, cfsVars, "extern $1",
[discriminatorTableDecl(p.module, t, field)])
var lit = newRopeAppender()
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1, lit)
let lit = cIntLiteral(toInt64(lengthOrd(p.config, field.typ))+1)
lineCg(p, cpsStmts,
"#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n",
[rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),

View File

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

View File

@@ -76,12 +76,11 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
let arraySize = lengthOrd(c.p.config, typ.indexType)
var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
var oldCode = p.s(cpsStmts)
freeze oldCode
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.snippet, arraySize])
let oldLen = p.s(cpsStmts).len
let oldLen = p.s(cpsStmts).buf.len
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
if p.s(cpsStmts).len == oldLen:
if p.s(cpsStmts).buf.len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
else:
@@ -119,14 +118,13 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
assert typ.kind == tySequence
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
var oldCode = p.s(cpsStmts)
freeze oldCode
var a = TLoc(snippet: accessor)
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.snippet, lenExpr(c.p, a)])
let oldLen = p.s(cpsStmts).len
let oldLen = p.s(cpsStmts).buf.len
genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType)
if p.s(cpsStmts).len == oldLen:
if p.s(cpsStmts).buf.len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
else:
@@ -160,7 +158,7 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
genTraverseProc(c, "(*a)".rope, typ.elementType)
let generatedProc = "$1 {$n$2$3$4}\n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
[header, extract(p.s(cpsLocals)), extract(p.s(cpsInit)), extract(p.s(cpsStmts))]
m.s[cfsProcHeaders].addf("$1;\n", [header])
m.s[cfsProcs].add(generatedProc)
@@ -189,7 +187,7 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
genTraverseProc(c, sLoc, s.loc.t)
let generatedProc = "$1 {$n$2$3$4}$n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
[header, extract(p.s(cpsLocals)), extract(p.s(cpsInit)), extract(p.s(cpsStmts))]
m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].add(generatedProc)

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,7 @@
import
ast, types, msgs, wordrecg,
platform, trees, options, cgendata, mangleutils, renderer
platform, trees, options, cgendata, mangleutils
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; extra: string = ""): string =
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
#Module::Type
var name = s.name.s & extra
var name = s.name.s
if makeUnique:
name = makeUnique(m, s, name)
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
proc encodeType*(m: BModule; t: PType): string =
result = ""
var kindName = ($t.kind)[2..^1]
kindName[0] = toLower($kindName[0])[0]
@@ -138,10 +138,10 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
result = encodeName(t[0].sym.name.s)
result.add "I"
for i in 1..<t.len - 1:
result.add encodeType(m, t[i], staticLists)
result.add encodeType(m, t[i])
result.add "E"
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
tyPtr, tyRef, tyVar, tyLent, tySink, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
result =
case t.kind:
of tySequence: encodeName("seq")
@@ -150,13 +150,8 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
for i in 0..<t.len:
let s = t[i]
if s.isNil: continue
result.add encodeType(m, s, staticLists)
result.add encodeType(m, s)
result.add "E"
of tyStatic:
if t.n != nil:
staticLists.add "_s" & renderTree(t.n)
else:
raiseAssert "unreachable"
of tyRange:
var val = "range_"
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
@@ -169,7 +164,7 @@ proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
result = encodeName(kindName)
of tyAlias, tyInferred, tyOwned:
result = encodeType(m, t.elementType, staticLists)
result = encodeType(m, t.elementType)
else:
assert false, "encodeType " & $t.kind

View File

@@ -16,7 +16,7 @@ import
rodutils, renderer, cgendata, aliases,
lowerings, lineinfos, pathutils, transf,
injectdestructors, astmsgs, modulepaths, pushpoppragmas,
mangleutils
mangleutils, cbuilderbase
from expanddefaults import caseObjDefaultBranch
@@ -138,6 +138,9 @@ proc cgFormatValue(result: var string; value: BiggestInt) =
proc cgFormatValue(result: var string; value: Int128) =
result.addInt128 value
template addf(result: var Builder, args: varargs[untyped]) =
result.buf.addf(args)
# TODO: please document
macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope =
args.expectKind nnkBracket
@@ -237,7 +240,15 @@ proc addIndent(p: BProc; result: var Rope) =
result[i] = '\t'
inc i
template appcg(m: BModule, c: var Rope, frmt: FormatStr,
proc addIndent(p: BProc; result: var Builder) =
var i = result.buf.len
let newLen = i + p.blocks.len
result.buf.setLen newLen
while i < newLen:
result.buf[i] = '\t'
inc i
template appcg(m: BModule, c: var (Rope | Builder), frmt: FormatStr,
args: untyped) =
c.add(ropecg(m, frmt, args))
@@ -275,7 +286,7 @@ proc safeLineNm(info: TLineInfo): int =
proc genPostprocessDir(field1, field2, field3: string): string =
result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd
proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) =
proc genCLineDir(r: var Builder, fileIdx: FileIndex, line: int; conf: ConfigRef) =
assert line >= 0
if optLineDir in conf.options and line > 0:
if fileIdx == InvalidFileIdx:
@@ -283,7 +294,7 @@ proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) =
else:
r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
proc genCLineDir(r: var Builder, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
assert line >= 0
if optLineDir in p.config.options and line > 0:
if fileIdx == InvalidFileIdx:
@@ -291,7 +302,7 @@ proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLi
else:
r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
proc genCLineDir(r: var Builder, info: TLineInfo; conf: ConfigRef) =
if optLineDir in conf.options:
genCLineDir(r, info.fileIndex, info.safeLineNm, conf)
@@ -304,7 +315,7 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool =
else:
result = false
proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) =
proc genCLineDir(r: var Builder, p: BProc, info: TLineInfo; conf: ConfigRef) =
if optLineDir in conf.options:
let lastFileIndex = p.lastLineInfo.fileIndex
if freshLineInfo(p, info):
@@ -328,7 +339,7 @@ proc genLineDir(p: BProc, t: PNode) =
proc accessThreadLocalVar(p: BProc, s: PSym)
proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
proc genProc(m: BModule, prc: PSym)
proc raiseInstr(p: BProc; result: var Rope)
proc raiseInstr(p: BProc; result: var Builder)
template compileToCpp(m: BModule): untyped =
m.config.backend == backendCpp or sfCompileToCpp in m.module.flags
@@ -337,31 +348,45 @@ proc getTempName(m: BModule): Rope =
result = m.tmpBase & rope(m.labels)
inc m.labels
proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
sfNoReturn in s.flags and m.config.exc != excGoto
include cbuilderexprs
include cbuilderdecls
include cbuilderstmts
proc rdLoc(a: TLoc): Rope =
# 'read' location (deref if indirect)
if lfIndirect in a.flags:
result = "(*" & a.snippet & ")"
result = cDeref(a.snippet)
else:
result = a.snippet
proc addRdLoc(a: TLoc; result: var Rope) =
if lfIndirect in a.flags:
result.add "(*" & a.snippet & ")"
result.add cDeref(a.snippet)
else:
result.add a.snippet
proc lenField(p: BProc): Rope {.inline.} =
result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
proc lenField(p: BProc, val: Rope): Rope {.inline.} =
if p.module.compileToCpp:
result = derefField(val, "len")
else:
result = dotField(derefField(val, "Sup"), "len")
proc lenExpr(p: BProc; a: TLoc): Rope =
if optSeqDestructors in p.config.globalOptions:
result = rdLoc(a) & ".len"
result = dotField(rdLoc(a), "len")
else:
result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
let ra = rdLoc(a)
result = cIfExpr(ra, lenField(p, ra), cIntValue(0))
proc dataFieldAccessor(p: BProc, sym: Rope): Rope =
if optSeqDestructors in p.config.globalOptions:
result = "(" & sym & ").p"
result = dotField(wrapPar(sym), "p")
else:
result = sym
@@ -371,9 +396,11 @@ proc dataField(p: BProc): Rope =
else:
result = rope"->data"
proc dataField(p: BProc, val: Rope): Rope {.inline.} =
result = derefField(dataFieldAccessor(p, val), "data")
proc genProcPrototype(m: BModule, sym: PSym)
include cbuilder
include ccgliterals
include ccgtypes
@@ -386,20 +413,20 @@ template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) =
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
result.add "(&" & a.snippet & ")"
result.add wrapPar(cAddr(a.snippet))
else:
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.snippet & ")"
result = wrapPar(cAddr(a.snippet))
else:
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.snippet & ")"
result = wrapPar(cAddr(a.snippet))
else:
result = a.snippet
@@ -407,7 +434,7 @@ proc rdCharLoc(a: TLoc): Rope =
# read a location that may need a char-cast:
result = rdLoc(a)
if skipTypes(a.t, abstractRange).kind == tyChar:
result = "((NU8)($1))" % [result]
result = cCast("NU8", result)
type
TAssignmentFlag = enum
@@ -596,28 +623,29 @@ proc getIntTemp(p: BProc): TLoc =
linefmt(p, cpsLocals, "NI $1;$n", [result.snippet])
proc localVarDecl(p: BProc; n: PNode): Rope =
result = ""
var res = newBuilder("")
let s = n.sym
if s.loc.k == locNone:
fillLocalName(p, s)
fillLoc(s.loc, locLocalVar, n, OnStack)
if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
result.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
res.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
genCLineDir(result, p, n.info, p.config)
genCLineDir(res, p, n.info, p.config)
result.add getTypeDesc(p.module, s.typ, dkVar)
res.add getTypeDesc(p.module, s.typ, dkVar)
if sfCodegenDecl notin s.flags:
if sfRegister in s.flags: result.add(" register")
if sfRegister in s.flags: res.add(" register")
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
# decl.add(" GC_GUARD")
if sfVolatile in s.flags: result.add(" volatile")
if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
result.add(" ")
result.add(s.loc.snippet)
if sfVolatile in s.flags: res.add(" volatile")
if sfNoalias in s.flags: res.add(" NIM_NOALIAS")
res.add(" ")
res.add(s.loc.snippet)
result = extract(res)
else:
result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet])
result = runtimeFormat(s.cgDeclFrmt, [extract(res), s.loc.snippet])
proc assignLocalVar(p: BProc, n: PNode) =
#assert(s.loc.k == locNone) # not yet assigned
@@ -745,7 +773,7 @@ proc getLabel(p: BProc): TLabel =
result = "LA" & rope(p.labels) & "_"
proc fixLabel(p: BProc, labl: TLabel) =
p.s(cpsStmts).add("$1: ;$n" % [labl])
p.s(cpsStmts).addLabel(labl)
proc genVarPrototype(m: BModule, n: PNode)
proc requestConstImpl(p: BProc, sym: PSym)
@@ -753,8 +781,7 @@ proc genStmts(p: BProc, t: PNode)
proc expr(p: BProc, n: PNode, d: var TLoc)
proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
proc intLiteral(i: BiggestInt; result: var Rope)
proc genLiteral(p: BProc, n: PNode; result: var Rope)
proc genLiteral(p: BProc, n: PNode; result: var Builder)
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int)
proc raiseExit(p: BProc)
proc raiseExitCleanup(p: BProc, destroy: string)
@@ -791,7 +818,7 @@ $1define nimlf_(n, file) \
FR_.line = n; FR_.filename = file;
"""
if p.module.s[cfsFrameDefines].len == 0:
if p.module.s[cfsFrameDefines].buf.len == 0:
appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"])
cgsym(p.module, "nimFrame")
@@ -832,7 +859,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
var s: TStringSeq = @[]
libCandidates(lib.path.strVal, s)
rawMessage(m.config, hintDependency, lib.path.strVal)
var loadlib: Rope = ""
var loadlib = newBuilder("")
for i in 0..high(s):
inc(m.labels)
if i > 0: loadlib.add("||")
@@ -843,7 +870,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
loadlib.addf "))$n", []
appcg(m, m.s[cfsDynLibInit],
"if (!($1)) #nimLoadLibraryError(",
[loadlib])
[extract(loadlib)])
genStringLiteral(m, lib.path, m.s[cfsDynLibInit])
m.s[cfsDynLibInit].addf ");$n", []
@@ -857,9 +884,9 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
[getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)])
expr(p, lib.path, dest)
m.s[cfsVars].add(p.s(cpsLocals))
m.s[cfsDynLibInit].add(p.s(cpsInit))
m.s[cfsDynLibInit].add(p.s(cpsStmts))
m.s[cfsVars].add(extract(p.s(cpsLocals)))
m.s[cfsDynLibInit].add(extract(p.s(cpsInit)))
m.s[cfsDynLibInit].add(extract(p.s(cpsStmts)))
appcg(m, m.s[cfsDynLibInit],
"if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n",
[tmp, rdLoc(dest)])
@@ -979,12 +1006,12 @@ proc generateHeaders(m: BModule) =
#undef unix
""")
proc openNamespaceNim(namespace: string; result: var Rope) =
proc openNamespaceNim(namespace: string; result: var Builder) =
result.add("namespace ")
result.add(namespace)
result.add(" {\L")
proc closeNamespaceNim(result: var Rope) =
proc closeNamespaceNim(result: var Builder) =
result.add("}\L")
proc closureSetup(p: BProc, prc: PSym) =
@@ -1150,14 +1177,7 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
else:
allPathsInBranch(n[i].lastSon)
of nkCallKinds:
if canRaiseDisp(p, n[0]) or
(n[0].kind == nkSym and sfNoReturn in n[0].sym.flags):
# requires initializations when encountering unreachable code
result = InitRequired
elif n[0].kind == nkSym and
n[0].sym.magic in {mUnaryMinusI..mAbsI, mAddI..mPred} and
optOverflowCheck in p.config.options:
# arithmetic operations may raise exceptions
if canRaiseDisp(p, n[0]):
result = InitRequired
else:
for i in 0..<n.safeLen:
@@ -1175,30 +1195,32 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
proc getProcTypeCast(m: BModule, prc: PSym): Rope =
result = getTypeDesc(m, prc.loc.t)
if prc.typ.callConv == ccClosure:
var rettype, params: Rope = ""
var rettype: Snippet = ""
var desc = newBuilder("")
var check = initIntSet()
genProcParams(m, prc.typ, rettype, params, check)
result = "$1(*)$2" % [rettype, params]
genProcParams(m, prc.typ, rettype, desc, check)
let params = extract(desc)
result = procPtrTypeUnnamed(rettype = rettype, params = params)
proc genProcBody(p: BProc; procBody: PNode) =
genStmts(p, procBody) # modifies p.locals, p.init, etc.
if {nimErrorFlagAccessed, nimErrorFlagDeclared, nimErrorFlagDisabled} * p.flags == {nimErrorFlagAccessed}:
p.flags.incl nimErrorFlagDeclared
p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", []))
p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", []))
proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
sfNoReturn in s.flags and m.config.exc != excGoto
p.blocks[0].sections[cpsLocals].addVar(kind = Local,
name = "nimErr_", typ = ptrType("NIM_BOOL"))
p.blocks[0].sections[cpsInit].addAssignmentWithValue("nimErr_"):
p.blocks[0].sections[cpsInit].addCall(cgsymValue(p.module, "nimErrorFlag"))
proc genProcAux*(m: BModule, prc: PSym) =
var p = newProc(prc, m)
var header = newRopeAppender()
var header = newBuilder("")
let isCppMember = m.config.backend == backendCpp and sfCppMember * prc.flags != {}
var visibility: DeclVisibility = None
if isCppMember:
genMemberProcHeader(m, prc, header)
else:
genProcHeader(m, prc, header)
var returnStmt: Rope = ""
genProcHeader(m, prc, header, visibility, asPtr = false, addAttributes = false)
var returnStmt: Snippet = ""
assert(prc.ast != nil)
var procBody = transformBody(m.g.graph, m.idgen, prc, {})
@@ -1218,7 +1240,8 @@ proc genProcAux*(m: BModule, prc: PSym) =
if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
var decl = localVarDecl(p, resNode)
var a: TLoc = initLocExprSingleUse(p, val)
linefmt(p, cpsStmts, "$1 = $2;$n", [decl, rdLoc(a)])
let ra = rdLoc(a)
p.s(cpsStmts).addAssignment(decl, ra)
else:
# declare the result symbol:
assignLocalVar(p, resNode)
@@ -1230,7 +1253,10 @@ proc genProcAux*(m: BModule, prc: PSym) =
discard "result init optimized out"
else:
initLocalVar(p, res, immediateAsgn=false)
returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
var returnBuilder = newBuilder("\t")
let rres = rdLoc(res.loc)
returnBuilder.addReturn(rres)
returnStmt = extract(returnBuilder)
elif sfConstructor in prc.flags:
resNode.sym.loc.flags.incl lfIndirect
fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
@@ -1261,45 +1287,56 @@ proc genProcAux*(m: BModule, prc: PSym) =
prc.info = tmpInfo
var generatedProc: Rope = ""
var generatedProc = newBuilder("")
generatedProc.genCLineDir prc.info, m.config
if isNoReturn(p.module, prc):
if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
header = "__declspec(noreturn) " & header
if sfPure in prc.flags:
if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
header = "__declspec(naked) " & header
generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N",
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
else:
if m.hcrOn and isReloadable(m, prc):
# Add forward declaration for "_actual"-suffixed functions defined in the same module (or inline).
# This fixes the use of methods and also the case when 2 functions within the same module
# call each other using directly the "_actual" versions (an optimization) - see issue #11608
m.s[cfsProcHeaders].addf("$1;\n", [header])
generatedProc.add ropecg(p.module, "$1 {$n", [header])
if optStackTrace in prc.options:
generatedProc.add(p.s(cpsLocals))
var procname = makeCString(prc.name.s)
generatedProc.add(initFrame(p, procname, quotedFilename(p.config, prc.info)))
generatedProc.addDeclWithVisibility(visibility):
if sfPure in prc.flags:
generatedProc.add(extract(header))
generatedProc.finishProcHeaderWithBody():
generatedProc.add(extract(p.s(cpsLocals)))
generatedProc.add(extract(p.s(cpsInit)))
generatedProc.add(extract(p.s(cpsStmts)))
else:
generatedProc.add(p.s(cpsLocals))
if optProfiler in prc.options:
# invoke at proc entry for recursion:
appcg(p, cpsInit, "\t#nimProfile();$n", [])
# this pair of {} is required for C++ (C++ is weird with its
# control flow integrity checks):
if beforeRetNeeded in p.flags: generatedProc.add("{")
generatedProc.add(p.s(cpsInit))
generatedProc.add(p.s(cpsStmts))
if beforeRetNeeded in p.flags: generatedProc.add("\t}BeforeRet_: ;\n")
if optStackTrace in prc.options: generatedProc.add(deinitFrame(p))
generatedProc.add(returnStmt)
generatedProc.add("}\n")
m.s[cfsProcs].add(generatedProc)
if m.hcrOn and isReloadable(m, prc):
m.s[cfsProcHeaders].addDeclWithVisibility(visibility):
# Add forward declaration for "_actual"-suffixed functions defined in the same module (or inline).
# This fixes the use of methods and also the case when 2 functions within the same module
# call each other using directly the "_actual" versions (an optimization) - see issue #11608
m.s[cfsProcHeaders].add(extract(header))
m.s[cfsProcHeaders].finishProcHeaderAsProto()
generatedProc.add(extract(header))
generatedProc.finishProcHeaderWithBody():
if optStackTrace in prc.options:
generatedProc.add(extract(p.s(cpsLocals)))
var procname = makeCString(prc.name.s)
generatedProc.add(initFrame(p, procname, quotedFilename(p.config, prc.info)))
else:
generatedProc.add(extract(p.s(cpsLocals)))
if optProfiler in prc.options:
# invoke at proc entry for recursion:
p.s(cpsInit).add('\t')
p.s(cpsInit).addCallStmt(cgsymValue(m, "nimProfile"))
if beforeRetNeeded in p.flags:
# this pair of {} is required for C++ (C++ is weird with its
# control flow integrity checks):
generatedProc.addScope():
generatedProc.add(extract(p.s(cpsInit)))
generatedProc.add(extract(p.s(cpsStmts)))
generatedProc.addLabel("BeforeRet_")
else:
generatedProc.add(extract(p.s(cpsInit)))
generatedProc.add(extract(p.s(cpsStmts)))
if optStackTrace in prc.options: generatedProc.add(deinitFrame(p))
generatedProc.add(returnStmt)
m.s[cfsProcs].add(extract(generatedProc))
if isReloadable(m, prc):
m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n",
[prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
m.s[cfsDynLibInit].add('\t')
m.s[cfsDynLibInit].addAssignmentWithValue(prc.loc.snippet):
m.s[cfsDynLibInit].addCast(getProcTypeCast(m, prc)):
m.s[cfsDynLibInit].addCall("hcrRegisterProc",
getModuleDllPath(m, prc),
'"' & prc.loc.snippet & '"',
cCast("void*", prc.loc.snippet & "_actual"))
proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
result = (sfCompileToCpp in m.module.flags and
@@ -1316,26 +1353,39 @@ proc genProcPrototype(m: BModule, sym: PSym) =
if lfDynamicLib in sym.loc.flags:
if sym.itemId.module != m.module.position and
not containsOrIncl(m.declaredThings, sym.id):
m.s[cfsVars].add(ropecg(m, "$1 $2 $3;$n",
[(if isReloadable(m, sym): "static" else: "extern"),
getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)]))
let vis = if isReloadable(m, sym): StaticProc else: Extern
let name = mangleDynLibProc(sym)
let t = getTypeDesc(m, sym.loc.t)
m.s[cfsVars].addDeclWithVisibility(vis):
m.s[cfsVars].addVar(kind = Local,
name = name,
typ = t)
if isReloadable(m, sym):
m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
[mangleDynLibProc(sym), getTypeDesc(m, sym.loc.t), getModuleDllPath(m, sym)])
m.s[cfsDynLibInit].add('\t')
m.s[cfsDynLibInit].addAssignmentWithValue(name):
m.s[cfsDynLibInit].addCast(t):
m.s[cfsDynLibInit].addCall("hcrGetProc",
getModuleDllPath(m, sym),
'"' & name & '"')
elif not containsOrIncl(m.declaredProtos, sym.id):
let asPtr = isReloadable(m, sym)
var header = newRopeAppender()
genProcHeader(m, sym, header, asPtr)
if not asPtr:
if isNoReturn(m, sym) and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
header = "__declspec(noreturn) " & header
if sym.typ.callConv != ccInline and requiresExternC(m, sym):
header = "extern \"C\" " & header
if sfPure in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
header.add(" __attribute__((naked))")
if isNoReturn(m, sym) and hasAttribute in CC[m.config.cCompiler].props:
header.add(" __attribute__((noreturn))")
m.s[cfsProcHeaders].add(ropecg(m, "$1;$N", [header]))
var header = newBuilder("")
var visibility: DeclVisibility = None
genProcHeader(m, sym, header, visibility, asPtr = asPtr, addAttributes = true)
if asPtr:
m.s[cfsProcHeaders].addDeclWithVisibility(visibility):
# genProcHeader would give variable declaration, add it directly
m.s[cfsProcHeaders].add(extract(header))
else:
let extraVis =
if sym.typ.callConv != ccInline and requiresExternC(m, sym):
ExternC
else:
None
m.s[cfsProcHeaders].addDeclWithVisibility(extraVis):
m.s[cfsProcHeaders].addDeclWithVisibility(visibility):
m.s[cfsProcHeaders].add(extract(header))
m.s[cfsProcHeaders].finishProcHeaderAsProto()
# TODO: figure out how to rename this - it DOES generate a forward declaration
proc genProcNoForward(m: BModule, prc: PSym) =
@@ -1461,7 +1511,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
"\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.} =
proc addNimDefines(result: var Builder; conf: ConfigRef) {.inline.} =
result.addf("#define NIM_INTBITS $1\L", [
platform.CPU[conf.target.targetCPU].intSize.rope])
if conf.cppCustomNamespace.len > 0:
@@ -1485,9 +1535,10 @@ proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
rope(getCompileCFileCmd(conf, cfile))]
proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
result = getCopyright(conf, cfile)
if conf.hcrOn: result.add("#define NIM_HOT_CODE_RELOADING\L")
addNimDefines(result, conf)
var res = newBuilder(getCopyright(conf, cfile))
if conf.hcrOn: res.add("#define NIM_HOT_CODE_RELOADING\L")
addNimDefines(res, conf)
result = extract(res)
proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope =
## Returns a mangled module name.
@@ -1530,8 +1581,9 @@ proc genMainProc(m: BModule) =
assert prc != nil
let n = newStrNode(nkStrLit, prc.annex.path.strVal)
n.info = prc.annex.path.info
var strLit = newRopeAppender()
genStringLiteral(m, n, strLit)
var strLitBuilder = newBuilder("")
genStringLiteral(m, n, strLitBuilder)
let strLit = extract(strLitBuilder)
appcg(m, result, "\tif (!($1 = #nimLoadLibrary($2)))$N" &
"\t\t#nimLoadLibraryError($2);$N",
[handle, strLit])
@@ -1743,10 +1795,10 @@ proc registerInitProcs*(g: BModuleList; m: PSym; flags: set[ModuleBackendFlag])
proc whichInitProcs*(m: BModule): set[ModuleBackendFlag] =
# called from IC.
result = {}
if m.hcrOn or m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0:
if m.hcrOn or m.preInitProc.s(cpsInit).buf.len > 0 or m.preInitProc.s(cpsStmts).buf.len > 0:
result.incl HasModuleInitProc
for i in cfsTypeInit1..cfsDynLibInit:
if m.s[i].len != 0:
if m.s[i].buf.len != 0:
result.incl HasDatInitProc
break
@@ -1799,7 +1851,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
m.s[cfsInitProc].add(hcrModuleMeta)
return
if m.s[cfsDatInitProc].len > 0:
if m.s[cfsDatInitProc].buf.len > 0:
g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
g.mainDatInit.addf("\t$1();$N", [datInit])
@@ -1811,7 +1863,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []))
if m.s[cfsInitProc].len > 0:
if m.s[cfsInitProc].buf.len > 0:
g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
let initCall = "\t$1();$N" % [init]
if sfMainModule in m.module.flags:
@@ -1828,22 +1880,22 @@ proc genDatInitCode(m: BModule) =
var moduleDatInitRequired = m.hcrOn
var prc = "$1 N_NIMCALL(void, $2)(void) {$N" %
[rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), getDatInitName(m)]
var prc = newBuilder("$1 N_NIMCALL(void, $2)(void) {$N" %
[rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), getDatInitName(m)])
# we don't want to break into such init code - could happen if a line
# directive from a function written by the user spills after itself
genCLineDir(prc, InvalidFileIdx, 999999, m.config)
for i in cfsTypeInit1..cfsDynLibInit:
if m.s[i].len != 0:
if m.s[i].buf.len != 0:
moduleDatInitRequired = true
prc.add(m.s[i])
prc.add(extract(m.s[i]))
prc.addf("}$N$N", [])
if moduleDatInitRequired:
m.s[cfsDatInitProc].add(prc)
m.s[cfsDatInitProc].add(extract(prc))
#rememberFlag(m.g.graph, m.module, HasDatInitProc)
# Very similar to the contents of symInDynamicLib - basically only the
@@ -1870,8 +1922,8 @@ proc genInitCode(m: BModule) =
## into other modules, only simple rope manipulations are allowed
var moduleInitRequired = m.hcrOn
let initname = getInitName(m)
var prc = "$1 N_NIMCALL(void, $2)(void) {$N" %
[rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname]
var prc = newBuilder("$1 N_NIMCALL(void, $2)(void) {$N" %
[rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname])
# we don't want to break into such init code - could happen if a line
# directive from a function written by the user spills after itself
genCLineDir(prc, InvalidFileIdx, 999999, m.config)
@@ -1894,13 +1946,13 @@ proc genInitCode(m: BModule) =
[getModuleDllPath(m, m.module)])
template writeSection(thing: untyped, section: TCProcSection, addHcrGuards = false) =
if m.thing.s(section).len > 0:
if m.thing.s(section).buf.len > 0:
moduleInitRequired = true
if addHcrGuards: prc.add("\tif (nim_hcr_do_init_) {\n\n")
prc.add(m.thing.s(section))
prc.add(extract(m.thing.s(section)))
if addHcrGuards: prc.add("\n\t} // nim_hcr_do_init_\n")
if m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0:
if m.preInitProc.s(cpsInit).buf.len > 0 or m.preInitProc.s(cpsStmts).buf.len > 0:
# Give this small function its own scope
prc.addf("{$N", [])
# Keep a bogus frame in case the code needs one
@@ -1920,7 +1972,7 @@ proc genInitCode(m: BModule) =
prc.addf("{$N", [])
writeSection(initProc, cpsLocals)
if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0:
if m.initProc.s(cpsInit).buf.len > 0 or m.initProc.s(cpsStmts).buf.len > 0:
moduleInitRequired = true
if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
# BUT: the generated init code might depend on a current frame, so
@@ -1974,14 +2026,14 @@ proc genInitCode(m: BModule) =
prc.add(ex)
if moduleInitRequired or sfMainModule in m.module.flags:
m.s[cfsInitProc].add(prc)
m.s[cfsInitProc].add(extract(prc))
#rememberFlag(m.g.graph, m.module, HasModuleInitProc)
genDatInitCode(m)
if m.hcrOn:
m.s[cfsInitProc].addf("N_LIB_EXPORT N_NIMCALL(void, HcrCreateTypeInfos)(void) {$N", [])
m.s[cfsInitProc].add(m.hcrCreateTypeInfosProc)
m.s[cfsInitProc].add(extract(m.hcrCreateTypeInfosProc))
m.s[cfsInitProc].addf("}$N$N", [])
registerModuleToMain(m.g, m)
@@ -2023,31 +2075,32 @@ proc postprocessCode(conf: ConfigRef, r: var Rope) =
proc genModule(m: BModule, cfile: Cfile): Rope =
var moduleIsEmpty = true
result = getFileHeader(m.config, cfile)
var res = newBuilder(getFileHeader(m.config, cfile))
generateThreadLocalStorage(m)
generateHeaders(m)
result.add(m.s[cfsHeaders])
res.add(extract(m.s[cfsHeaders]))
if m.config.cppCustomNamespace.len > 0:
openNamespaceNim(m.config.cppCustomNamespace, result)
if m.s[cfsFrameDefines].len > 0:
result.add(m.s[cfsFrameDefines])
openNamespaceNim(m.config.cppCustomNamespace, res)
if m.s[cfsFrameDefines].buf.len > 0:
res.add(extract(m.s[cfsFrameDefines]))
for i in cfsForwardTypes..cfsProcs:
if m.s[i].len > 0:
if m.s[i].buf.len > 0:
moduleIsEmpty = false
result.add(m.s[i])
res.add(extract(m.s[i]))
if m.s[cfsInitProc].len > 0:
if m.s[cfsInitProc].buf.len > 0:
moduleIsEmpty = false
result.add(m.s[cfsInitProc])
if m.s[cfsDatInitProc].len > 0 or m.hcrOn:
res.add(extract(m.s[cfsInitProc]))
if m.s[cfsDatInitProc].buf.len > 0 or m.hcrOn:
moduleIsEmpty = false
result.add(m.s[cfsDatInitProc])
res.add(extract(m.s[cfsDatInitProc]))
if m.config.cppCustomNamespace.len > 0:
closeNamespaceNim(result)
closeNamespaceNim(res)
result = extract(res)
if optLineDir in m.config.options:
var srcFileDefs = ""
for fi in 0..m.config.m.fileInfos.high:
@@ -2078,11 +2131,12 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
result.typeInfoMarker = initTable[SigHash, Rope]()
result.sigConflicts = initCountTable[SigHash]()
result.initProc = newProc(nil, result)
for i in low(result.s)..high(result.s): result.s[i] = newRopeAppender()
for i in low(result.s)..high(result.s): result.s[i] = newBuilder("")
result.initProc.options = initProcOptions(result)
result.preInitProc = newProc(nil, result)
result.preInitProc.flags.incl nimErrorFlagDisabled
result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated
result.hcrCreateTypeInfosProc = newBuilder("")
result.dataCache = initNodeTable()
result.typeStack = @[]
result.typeNodesName = getTempName(result)
@@ -2121,7 +2175,7 @@ proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassCont
incl g.generatedHeader.flags, isHeaderFile
proc writeHeader(m: BModule) =
var result = headerTop()
var result = newBuilder(headerTop())
var guard = "__$1__" % [m.filename.splitFile.name.rope]
result.addf("#ifndef $1$n#define $1$n", [guard])
addNimDefines(result, m.config)
@@ -2129,17 +2183,17 @@ proc writeHeader(m: BModule) =
generateThreadLocalStorage(m)
for i in cfsHeaders..cfsProcs:
result.add(m.s[i])
result.add(extract(m.s[i]))
if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders:
openNamespaceNim(m.config.cppCustomNamespace, result)
result.add(m.s[cfsInitProc])
result.add(extract(m.s[cfsInitProc]))
if optGenDynLib in m.config.globalOptions:
result.add("N_LIB_IMPORT ")
result.addf("N_CDECL(void, $1NimMain)(void);$n", [rope m.config.nimMainPrefix])
if m.config.cppCustomNamespace.len > 0: closeNamespaceNim(result)
result.addf("#endif /* $1 */$n", [guard])
if not writeRope(result, m.filename):
if not writeRope(extract(result), m.filename):
rawMessage(m.config, errCannotOpenFile, m.filename.string)
proc getCFile(m: BModule): AbsoluteFile =
@@ -2175,20 +2229,6 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
genStmts(p, n)
proc handleProcGlobals(m: BModule) =
var procGlobals: seq[PNode] = move m.g.graph.procGlobals
for i in 0..<procGlobals.len:
var stmts = ""
# fixes recursive calls #24997
swap stmts, m.preInitProc.s(cpsStmts)
genStmts(m.preInitProc, procGlobals[i])
swap stmts, m.preInitProc.s(cpsStmts)
handleProcGlobals(m)
m.preInitProc.s(cpsStmts).add stmts
proc genTopLevelStmt*(m: BModule; n: PNode) =
## Also called from `ic/cbackend.nim`.
if pipelineutils.skipCodegen(m.config, n): return
@@ -2204,8 +2244,6 @@ proc genTopLevelStmt*(m: BModule; n: PNode) =
else:
genProcBody(m.initProc, transformedN)
handleProcGlobals(m)
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
if optForceFullMake notin m.config.globalOptions:
if not moduleHasChanged(m.g.graph, m.module):
@@ -2269,13 +2307,12 @@ proc updateCachedModule(m: BModule) =
addFileToCompile(m.config, cf)
proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym =
let prefixedName = m.config.nimMainPrefix & "NimDestroyGlobals"
let procname = getIdent(graph.cache, prefixedName)
let procname = getIdent(graph.cache, "NimDestroyGlobals")
result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info)
result.typ = newProcType(m.module.info, m.idgen, m.module.owner)
result.typ.callConv = ccCDecl
incl result.flags, sfExportc
result.loc.snippet = prefixedName
result.loc.snippet = "NimDestroyGlobals"
if isDynlib:
incl(result.loc.flags, lfExportLib)

View File

@@ -11,7 +11,7 @@
import
ast, ropes, options,
lineinfos, pathutils, modulegraphs
lineinfos, pathutils, modulegraphs, cbuilderbase
import std/[intsets, tables, sets]
@@ -43,12 +43,12 @@ type
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
ctCString
TCFileSections* = array[TCFileSection, Rope] # represents a generated C file
TCFileSections* = array[TCFileSection, Builder] # represents a generated C file
TCProcSection* = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc
cpsInit, # section for init of variables for C proc
cpsStmts # section of local statements for C proc
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
TCProcSections* = array[TCProcSection, Builder] # represents a generated C proc
BModule* = ref TCGen
BProc* = ref TCProc
TBlock* = object
@@ -161,7 +161,7 @@ type
typeInfoMarkerV2*: TypeCache
initProc*: BProc # code for init procedure
preInitProc*: BProc # code executed before the init proc
hcrCreateTypeInfosProc*: Rope # type info globals are in here when HCR=on
hcrCreateTypeInfosProc*: Builder # type info globals are in here when HCR=on
inHcrInitGuard*: bool # We are currently within a HCR reloading guard.
typeStack*: TTypeSeq # used for type generation
dataCache*: TNodeTable
@@ -181,18 +181,18 @@ proc includeHeader*(this: BModule; header: string) =
if not this.headerFiles.contains header:
this.headerFiles.add header
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
proc s*(p: BProc, s: TCProcSection): var Builder {.inline.} =
# section in the current block
result = p.blocks[^1].sections[s]
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
proc procSec*(p: BProc, s: TCProcSection): var Builder {.inline.} =
# top level proc sections
result = p.blocks[0].sections[s]
proc initBlock*(): TBlock =
result = TBlock()
for i in low(result.sections)..high(result.sections):
result.sections[i] = newRopeAppender()
result.sections[i] = newBuilder("")
proc newProc*(prc: PSym, module: BModule): BProc =
result = BProc(

File diff suppressed because it is too large Load Diff

View File

@@ -203,14 +203,13 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
else: invalidCmdLineOption(conf, pass, orig, info)
let isSomeHint = state in {wHint, wHintAsError}
let isSomeWarning = state in {wWarning, wWarningAsError}
template findNote(noteMin, noteMax, name) =
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
let x = findStr(noteMin, noteMax, id, errUnknown)
if x != errUnknown: notes = {TNoteKind(x)}
else:
if isSomeHint or isSomeWarning:
message(conf, info, warnUnknownNotes, "unknown $#: $#" % [name, id])
if isSomeHint:
message(conf, info, hintUnknownHint, id)
else:
localError(conf, info, "unknown $#: $#" % [name, id])
case id.normalize
@@ -459,7 +458,7 @@ template handleStdinOrCmdInput =
conf.outDir = getNimcacheDir(conf)
proc handleStdinInput*(conf: ConfigRef) =
conf.projectName = conf.stdinFile.string
conf.projectName = "stdinfile"
conf.projectIsStdin = true
handleStdinOrCmdInput()
@@ -920,12 +919,6 @@ 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.
@@ -935,10 +928,6 @@ 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, layeredtable
import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
import std/intsets
@@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem):
import std/assertions
const
logBindings = when defined(debugConcepts): true else: false
logBindings = false
## Code dealing with Concept declarations
## --------------------------------------
@@ -70,247 +70,88 @@ 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.
bindings: LayeredIdTable
inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
## that turned out to be wrong.
marker: IntSet ## Some protection against wild runaway recursions.
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
## cannot be fixed that easily.
## Thus we special case it here.
concpt: PType ## current concept being evaluated
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.
result = m.bindings.lookup(key)
if result == nil:
result = key
for i in 0..<m.inferred.len:
if m.inferred[i][0] == key: return m.inferred[i][1]
return nil
const
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred}
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
proc unrollGenericParam(param: PType): PType =
result = param.skipTypes(ignorableForArgType)
while result.kind in {tyGenericParam, tyTypeDesc} and result.hasElementType and result.elementType.kind != tyNone:
result = result.elementType
proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable .} =
if v.kind == tyTypeDesc:
return false
var value = unrollGenericParam(v)
if value.kind == tyGenericParam:
value = existingBinding(m, value)
if value.kind == tyGenericParam:
if value.hasElementType:
value = value.elementType
else:
return true
if value.kind == tyStatic:
return false
if m.magic in {mArrPut, mArrGet} and value.kind in arrPutGetMagicApplies:
value = value.last
let old = existingBinding(m, key)
if old != key:
# check previously bound value
if not matchType(c, old, value, m):
return false
elif key.hasElementType and 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 =
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
## The heart of the concept matching process. 'f' is the formal parameter of some
## routine inside the concept that we're looking for. 'a' is the formal parameter
## of a routine that might match.
var
a = ao
f = fo
if a.isSelf:
if m.magic in {mArrPut, mArrGet}:
return false
a = m.potentialImplementation
if a.kind in bindableTypes:
a = existingBinding(m, ao)
if a == ao and a.kind == tyGenericParam and a.hasElementType and a.elementType.kind != tyNone:
a = a.elementType
if f.isConcept:
if a.acceptsAllTypes:
return false
if a.skipTypes(ignorableForArgType).isConcept:
# if f is a subset of a then any match to a will also match f. Not the other way around
return conceptsMatch(c, a.reduceToBase, f.reduceToBase, m) >= mkSubset
else:
return matchConceptToImpl(c, f, a, m)
result = false
const
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
case f.kind
of tyAlias:
result = matchType(c, f.skipModifier, a, m)
of tyTypeDesc:
if isSelf(f):
let ua = a.skipTypes(asymmetricConceptParamMods)
if m.magic in {mArrPut, mArrGet}:
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
bindParam(c, m, a, last m.potentialImplementation)
result = true
#elif ua.isConcept:
# result = matchType(c, m.concpt, ua, m)
else:
result = matchType(c, a.skipTypes(ignorableForArgType), m.potentialImplementation, m)
#let oldLen = m.inferred.len
result = matchType(c, a, m.potentialImplementation, m)
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
#m.inferred.setLen oldLen
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
else:
if a.kind == tyTypeDesc:
if not(a.hasElementType) or a.elementType.kind == tyNone:
result = true
elif f.hasElementType:
if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
if 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.
@@ -318,126 +159,70 @@ proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
result = matchType(c, f.elementType, a.elementType, m)
elif m.magic == mArrPut:
result = matchType(c, f.elementType, a, m)
of tyEnum, tyObject, tyDistinct:
if a.kind in ignorableForArgType:
result = matchType(c, f, a.skipTypes(ignorableForArgType), m)
else:
if a.kind == tyGenericInst:
# tyOr does this to generic typeclasses
result = a.base.sym == f.sym
else:
result = sameType(f, a)
result = false
of tyEnum, tyObject, tyDistinct:
result = sameType(f, a)
of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
result = a.skipTypes(ignorableForArgType).kind == f.kind
of tyBool, tyChar, tyInt..tyUInt64:
let ak = a.skipTypes(ignorableForArgType)
result = ak.kind == f.kind or ak.kind == tyOrdinal or
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr:
if f.kind == tyArray and f.kidsLen == 3 and a.kind == tyArray:
# XXX: this is a work-around!
# system.nim creates these for the magic array typeclass
result = true
else:
let ak = a.skipTypes(ignorableForArgType - {f.kind})
if ak.kind == f.kind:
if f.base.kind == tyNone:
result = true
elif f.kidsLen == ak.kidsLen:
result = matchKids(c, f, ak, m)
of tyGenericInvocation, tyGenericInst:
(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 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:
m.bindings = m.bindings.newTypeMapLayer()
result = not matchType(c, f.elementType, a, m)
m.bindings.setToPreviousLayer()
of tyAnd:
m.bindings = m.bindings.newTypeMapLayer()
result = true
for ff in traverseTyOr(f):
let r = matchType(c, ff, a, m)
if not r:
m.bindings.setToPreviousLayer()
result = false
break
of tyGenericBody:
var ak = a
if a.kind == tyGenericBody:
ak = last(a)
result = matchType(c, last(f), ak, m)
of tyCompositeTypeClass:
if a.kind == tyCompositeTypeClass:
result = matchKids(c, f, a, m)
else:
result = matchType(c, last(f), a, m)
of tyBuiltInTypeClass:
let target = f.genericHead.kind
result = a.skipTypes(ignorableForArgType).reduceToBase.kind == target
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 traverseTyOr(f):
for aa in traverseTyOr(a):
m.bindings = m.bindings.newTypeMapLayer()
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.bindings.setToPreviousLayer()
m.inferred.setLen oldLenB
result = covered >= a.kidsLen
if not result:
m.inferred.setLen oldLen
else:
result = false
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()
of tySet:
result = false
if a.kind == tySet:
m.inferred.setLen oldLen
of tyNot:
if a.kind == tyNot:
result = matchType(c, f.elementType, a.elementType, m)
else:
let oldLen = m.inferred.len
result = not matchType(c, f.elementType, a, m)
m.inferred.setLen oldLen
of tyAnything:
result = true
of tyOrdinal:
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
else:
result = false
if result and ao.kind == tyGenericParam:
let bf = if f.isSelf: m.potentialImplementation else: f
if bindParam(c, m, ao, bf):
when logBindings: echo " ^ reverse binding"
proc checkConstraint(c: PContext; f, a: PType; m: var MatchCon): bool =
result = matchType(c, f, a, m) or matchType(c, a, f, m)
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
## Like 'matchType' but with extra logic dealing with proc return types
@@ -447,38 +232,30 @@ proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
elif a == nil:
result = false
else:
result = checkConstraint(c, f, a, m)
result = matchType(c, f, a, m)
proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
## or similar.
# watch out: only add bindings after a completely successful match.
m.bindings = m.bindings.newTypeMapLayer()
let oldLen = m.inferred.len
let can = candidate.typ.n
let con = defSignatureType(n).n
let con = n[0].sym.typ.n
if can.len < con.len:
# too few arguments, cannot be a match:
return false
if can.len > con.len:
# too many arguments (not optional)
for i in con.len ..< can.len:
if can[i].sym.ast == nil:
return false
when defined(debugConcepts):
echo "considering: ", renderTree(candidate.procDefSignature), " ", candidate.magic
let common = min(can.len, con.len)
for i in 1 ..< common:
if not checkConstraint(c, con[i].typ, can[i].typ, m):
m.bindings.setToPreviousLayer()
if not matchType(c, con[i].typ, can[i].typ, m):
m.inferred.setLen oldLen
return false
if not matchReturnType(c, n.defSignatureType.returnType, candidate.typ.returnType, m):
m.bindings.setToPreviousLayer()
if not matchReturnType(c, n[0].sym.typ.returnType, candidate.typ.returnType, m):
m.inferred.setLen oldLen
return false
# all other parameters have to be optional parameters:
@@ -486,7 +263,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
assert can[i].kind == nkSym
if can[i].sym.ast == nil:
# has too many arguments one of which is not optional:
m.bindings.setToPreviousLayer()
m.inferred.setLen oldLen
return false
return true
@@ -494,14 +271,12 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
## 'n' is the nkProcDef or similar from the concept that we try to match.
result = false
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
for candidate in candidates:
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
m.magic = candidate.magic
if matchSym(c, candidate, n, m):
result = true
break
if matchSym(c, candidate, n, m): return true
result = false
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
## Traverse the concept's AST ('n') and see if every declaration inside 'n'
@@ -534,48 +309,7 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
# error was reported earlier.
result = false
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 =
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType): bool =
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
@@ -584,16 +318,26 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
## we do not add any bindings at all on an unsuccessful match!
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags)
if arg.isConcept:
result = conceptsMatch(c, concpt.reduceToBase, arg.reduceToBase, m) >= mkSubset
elif arg.acceptsAllTypes:
# XXX: I think this is wrong, or at least partially wrong. Can still test ambiguous types
result = false
elif mfCheckGeneric in m.flags:
# prioritize concepts the least. Specifically if the arg is not a catch all as per above
result = true
else:
result = processConcept(c, concpt, invocation, bindings, m)
var m = MatchCon(inferred: @[], potentialImplementation: arg)
result = conceptMatchNode(c, concpt.n.lastSon, m)
if result:
for (a, b) in m.inferred:
if b.kind == tyGenericParam:
var dest = b
while true:
dest = existingBinding(m, dest)
if dest == nil or dest.kind != tyGenericParam: break
if dest != nil:
bindings.put(a, dest)
when logBindings: echo "A bind ", a, " ", dest
else:
bindings.put(a, b)
when logBindings: echo "B bind ", a, " ", b
# we have a match, so bind 'arg' itself to 'concpt':
bindings.put(concpt, arg)
# invocation != nil means we have a non-atomic concept:
if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1:
# bind even more generic parameters
assert invocation.kind == tyGenericInvocation
for i in FirstGenericParamAt ..< invocation.kidsLen:
bindings.put(invocation[i], arg[i])

View File

@@ -171,5 +171,3 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasJsNoLambdaLifting")
defineSymbol("nimHasDefaultFloatRoundtrip")
defineSymbol("nimHasXorSet")
defineSymbol("nimHasPreviewDuplicateModuleError")

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 second branch.
gen(c, n[1][0])
# This is "when nimvm" node. Chose the first branch.
gen(c, n[0][1])
of nkCaseStmt: genCase(c, n)
of nkWhileStmt: genWhile(c, n)
of nkBlockExpr, nkBlockStmt: genBlock(c, n)

View File

@@ -1406,14 +1406,11 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
for it in n: traceDeps(d, it)
of nkExportStmt:
for it in n:
if it.kind == nkSym:
if d.module != nil and d.module == it.sym.owner: # in current module
# bug #23051; don't generate documentation for exported symbols again
if sfExported notin it.sym.flags:
generateDoc(d, it.sym.ast, orig, config, kForceExport)
# else it's to be handled in `of XxxSection` branch
# 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)
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])
@@ -1892,9 +1889,6 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
else:
#echo getOutFile(gProjectFull, JsonExt)
let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
conf.outFile = filename.relativeTo(conf.outDir)
let dir = filename.splitFile.dir
createDir(dir)
try:
writeFile(filename, content)
except IOError:
@@ -1915,10 +1909,8 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
if optStdout in d.conf.globalOptions:
write(stdout, content)
else:
#echo getOutFile(gProjectFull, TagsExt)
let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
conf.outFile = filename.relativeTo(conf.outDir)
let dir = filename.splitFile.dir
createDir(dir)
try:
writeFile(filename, content)
except IOError:

View File

@@ -33,10 +33,6 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
#newIntTypeNode(nkIntLit, field.position, t)
# safety branch for invalid data:
caseStmt.add newTree(nkElse,
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res),
newStrNode("", info))))
body.add(caseStmt)

View File

@@ -257,8 +257,8 @@ compiler tcc:
linkerExe: "tcc",
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
includeCmd: " -I",
linkDirCmd: " -L",
linkLibCmd: " -l$1",
linkDirCmd: "", # XXX: not supported yet
linkLibCmd: "", # XXX: not supported yet
debug: " -g ",
pic: "",
asmStmtFrmt: "asm($1);$n",
@@ -474,11 +474,6 @@ 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)
@@ -810,59 +805,6 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body
(ose.msg & " " & $ose.errorCode))
raise
proc createMacAppBundle(conf: ConfigRef; exefile: AbsoluteFile) =
let (dir, name, _) = splitFile(exefile.string)
let appBundleName = name & ".app"
let appBundlePath = dir / appBundleName
let contentsPath = appBundlePath / "Contents"
let macosPath = contentsPath / "MacOS"
createDir(macosPath)
let bundleExePath = macosPath / name
copyFileWithPermissions(exefile.string, bundleExePath)
let infoPlistPath = contentsPath / "Info.plist"
proc xmlEscape(s: string): string =
result = newStringOfCap(s.len)
for c in items(s):
case c:
of '<': result.add("&lt;")
of '>': result.add("&gt;")
of '&': result.add("&amp;")
of '"': result.add("&quot;")
of '\'': result.add("&apos;")
else:
if ord(c) < 32:
result.add("&#" & $ord(c) & ';')
else:
result.add(c)
let escapedName = xmlEscape(name)
let infoPlistContent = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>$1</string>
<key>CFBundleIdentifier</key>
<string>com.nim.$1</string>
<key>CFBundleName</key>
<string>$1</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSUIElement</key>
<string>1</string>
</dict>
</plist>""" % [escapedName]
writeFile(infoPlistPath, infoPlistContent)
removeFile(exefile.string)
rawMessage(conf, hintUserRaw, "Created Mac app bundle: " & appBundlePath)
proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] =
result = @[]
when defined(macosx):
@@ -1047,10 +989,6 @@ proc callCCompiler*(conf: ConfigRef) =
preventLinkCmdMaxCmdLen(conf, linkCmd)
for cmd in extraCmds:
execExternalProgram(conf, cmd, hintExecuting)
# create Mac app bundle for GUI apps on macOS
when defined(macosx):
if conf.globalOptions * {optGenGuiApp, optGenDynLib, optGenStaticLib} == {optGenGuiApp}:
createMacAppBundle(conf, mainOutput)
else:
linkCmd = ""
if optGenScript in conf.globalOptions:

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

View File

@@ -100,7 +100,6 @@ when false:
proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
let root = parampatterns.exprRoot(n, allowCalls=false)
if root == nil: return false
elif sfSingleUsedTemp in root.flags: return true
var s = addr(scope)
while s != nil:
@@ -163,16 +162,13 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
else:
result = false
template hasDestructorOrAsgn(c: var Con, typ: PType): bool =
# bug #23354; an object type could have a non-trivial assignements when it is passed to a sink parameter
hasDestructor(c, typ) or (c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
typ.kind == tyObject and not isTrivial(getAttachedOp(c.graph, typ, attachedAsgn)))
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
if not hasDestructorOrAsgn(c, n.typ): return true
# bug #23354; an object type could have a non-trival assignements when it is passed to a sink parameter
if not hasDestructor(c, n.typ) and (n.typ.kind != tyObject or isTrival(getAttachedOp(c.graph, n.typ, attachedAsgn))): return true
let m = skipConvDfa(n)
result = isLastReadImpl(n, c, s)
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
isLastReadImpl(n, c, s)
proc isFirstWrite(n: PNode; c: var Con): bool =
let m = skipConvDfa(n)
@@ -189,11 +185,10 @@ proc isCursor(n: PNode): bool =
else:
false
template isFullyUnpackedTuple(n: PNode): bool =
template isUnpackedTuple(n: PNode): bool =
## we move out all elements of unpacked tuples,
## hence unpacked tuples themselves don't need to be destroyed
## except it's already a cursor
## restricted to `skTemp`, tuple temps where not every field is unpacked should not use `skTemp`
(n.kind == nkSym and n.sym.kind == skTemp and
n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
@@ -202,8 +197,7 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFr
if inferredFromCopy:
m.add ", which is inferred from unavailable '=copy'"
if (opname == "=" or opname == "=copy" or opname == "=dup") and
ri != nil:
if (opname == "=" or opname == "=copy" or opname == "=dup") and ri != nil:
m.add "; requires a copy because it's not the last read of '"
m.add renderTree(ri)
m.add '\''
@@ -253,12 +247,7 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode
dbg:
if kind == attachedDestructor:
echo "destructor is ", op.id, " ", op.ast
if sfError in op.flags:
if ri != nil:
checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
else:
# uses the lineinfos of `dest` is `ri` is not available
checkForErrorPragma(c, t, dest, AttachedOpToStr[kind])
if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
c.genOp(op, dest)
proc genDestroy(c: var Con; dest: PNode): PNode =
@@ -286,7 +275,7 @@ proc deepAliases(dest, ri: PNode): bool =
return aliases(dest, ri) != no
proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
if (c.inLoopCond == 0 and (isFullyUnpackedTuple(dest) or IsDecl in flags or
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
isNoInit(dest) or IsReturn in flags:
# optimize sink call into a bitwise memcopy
@@ -411,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 & ")")
@@ -460,7 +449,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
let tmp = c.getTemp(s, nTyp, n.info)
if hasDestructorOrAsgn(c, nTyp):
if hasDestructor(c, nTyp):
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil and tfHasOwned notin typ.flags:
@@ -570,7 +559,7 @@ proc cycleCheck(n: PNode; c: var Con) =
proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
# move the variable declaration to the top of the frame:
s.vars.add v.sym
if isFullyUnpackedTuple(v):
if isUnpackedTuple(v):
if c.inLoop > 0:
# unpacked tuple needs reset at every loop iteration
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
@@ -813,7 +802,15 @@ 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:
result = p(n, c, s, consumed)
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)
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
@@ -889,6 +886,12 @@ 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
@@ -906,19 +909,13 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
isDangerous = true
result = shallowCopy(n)
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)
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:
@@ -946,9 +943,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
of nkVarSection, nkLetSection:
# transform; var x = y to var x; x op y where op is a move or copy
result = newNodeI(nkStmtList, n.info)
let isInProc = c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}
for it in n:
var ri = it[^1]
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
@@ -964,15 +958,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
s.locals.add v.sym
pVarTopLevel(v, c, s, result)
if ri.kind != nkEmpty:
let isGlobalPragma = v.kind == nkSym and
{sfPure, sfGlobal} <= v.sym.flags and
isInProc
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
if isGlobalPragma:
c.graph.procGlobals.add value
else:
result.add value
result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
elif ri.kind == nkEmpty and c.inLoop > 0:
let skipInit = v.kind == nkDotExpr and # Closure var
sfNoInit in v[1].sym.flags
@@ -1144,25 +1130,6 @@ proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags:
var snk = c.genSink(s, dest, newAccess, flags)
result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
proc ownsData(c: var Con; s: var Scope; orig: PNode; flags: set[MoveOrCopyFlag]): PNode =
var n = orig
while true:
case n.kind
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
n = n[0]
else:
break
if n.kind in nkCallKinds and n.typ != nil and hasDestructor(c, n.typ):
result = newNodeIT(nkStmtListExpr, orig.info, orig.typ)
let tmp = c.getTemp(s, n.typ, n.info)
tmp.sym.flags.incl sfSingleUsedTemp
result.add newTree(nkFastAsgn, tmp, copyTree(n))
s.final.add c.genDestroy(tmp)
n[] = tmp[]
result.add copyTree(orig)
else:
result = nil
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
var ri = ri
var isEnsureMove = 0
@@ -1189,7 +1156,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
of nkCallKinds:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
of nkBracketExpr:
if isFullyUnpackedTuple(ri[0]):
if isUnpackedTuple(ri[0]):
# unpacking of tuple: take over the elements
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
@@ -1238,22 +1205,15 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, flags, isFromSink = false)
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
if IsExplicitSink in flags:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
else:
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock:
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
# We know the result will be a stmt so we use that fact to optimize
handleNestedTempl(ri, process, willProduceStmt = true)
of nkRaiseStmt:
result = pRaiseStmt(ri, c, s)
else:
let isOwnsData = ownsData(c, s, ri2, flags)
if isOwnsData != nil:
result = moveOrCopy(dest, isOwnsData, c, s, flags)
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
canBeMoved(c, dest.typ):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(s, dest, ri, flags)

View File

@@ -6,7 +6,7 @@ Name: "Nim"
Version: "$version"
Platforms: """
windows: i386;amd64
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;s390x;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
macosx: i386;amd64;powerpc64;arm64
solaris: i386;amd64;sparc;sparc64
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el

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 existing inheritance and closure restrictions.
# worse than the already exisiting inheritance and closure restrictions.
case n.kind
of nkCharLit..nkNilLit:
result = true

View File

@@ -1586,11 +1586,7 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
of nkObjDownConv:
gen(p, n[0], r)
of nkHiddenDeref, nkDerefExpr:
if n.kind in {nkAddr, nkHiddenAddr}:
# addr ( deref ( x )) --> x
gen(p, n[0][0], r)
else:
gen(p, n[0], r)
gen(p, n[0], r)
of nkHiddenAddr:
gen(p, n[0], r)
of nkConv:
@@ -2879,8 +2875,6 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
elif dest.kind in tyFloat..tyFloat64:
if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
r.res = "Number($1)" % [r.res]
elif dest.kind == tyChar and (fromInt or fromUint):
r.res = "($1 & 255)" % [r.res]
elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
r.address = r.res
r.res = "null"

View File

@@ -122,7 +122,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
[name, genTypeInfo(p, typ.baseClass)])
proc genTypeInfo(p: PProc, typ: PType): Rope =
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned} + tyUserTypeClasses)
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind

View File

@@ -150,7 +150,6 @@ template isIterator*(owner: PSym): bool =
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
result = createObj(g, idgen, owner, info, final=false)
result.flags.incl tfFinal
if owner.isIterator:
rawAddField(result, createStateField(g, owner, idgen))
@@ -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:
@@ -975,20 +974,6 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
for i in 0..<op.len-1:
result.add op[i]
elif op.kind != nkSym: # might have side effects
# bug #25046
# create a temp for the closure
# var :closureTemp
# :closureTemp = ...
let tempSym = newSym(skLet, getIdent(g.cache, ":closureTemp"), idgen, owner, body.info)
tempSym.typ = call[0].typ
let temp = newSymNode(tempSym)
var v = newNodeI(nkVarSection, body.info)
addVar(v, temp)
result.add(v)
result.add newAsgnStmt(temp, call[0], body.info)
call[0] = temp
var loopBody = newNodeI(nkStmtList, body.info, 3)
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
whileLoop[0] = newIntTypeNode(1, getSysType(g, body.info, tyBool))

View File

@@ -1,5 +1,5 @@
import std/[tables]
import ast, astalgo
import std/tables
import ast
type
LayeredIdTableObj* {.acyclic.} = object
@@ -28,15 +28,14 @@ proc shallowCopy*(pt: LayeredIdTable): LayeredIdTable {.inline.} =
## copies only the type bindings of the current layer, but not any parent layers,
## useful for write-only bindings
result = LayeredIdTable(topLayer: pt.topLayer, nextLayer: pt.nextLayer, previousLen: pt.previousLen)
#copyIdTable(result.topLayer, pt.topLayer)
proc currentLen*(pt: LayeredIdTable): int =
## the sum of the cached total binding count of the parents and
## the current binding count, just used to track if bindings were added
pt.previousLen + pt.topLayer.counter
pt.previousLen + pt.topLayer.len
proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable =
result = LayeredIdTable(topLayer: initTypeMapping(), previousLen: pt.currentLen)
result = LayeredIdTable(topLayer: initTable[ItemId, PType](), previousLen: pt.currentLen)
when useRef:
result.nextLayer = pt
else:
@@ -54,15 +53,6 @@ proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
let tmp = pt.nextLayer[]
pt = tmp
iterator pairs*(pt: LayeredIdTable): (ItemId, PType) =
var tm = pt
while true:
for (k, v) in idTablePairs(tm.topLayer):
yield (k, v)
if tm.nextLayer == nil:
break
tm.setToPreviousLayer
proc lookup(typeMap: ref LayeredIdTableObj, key: ItemId): PType =
result = nil
var tm = typeMap

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): PSym
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
idgen: IdGenerator)
@@ -91,7 +91,7 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
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:
@@ -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:
@@ -218,38 +218,23 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
c.filterDiscriminator = oldfilterDiscriminator
of nkRecList:
# destroys in reverse order #24719
if c.kind == attachedDestructor:
for i in countdown(n.len-1, 0):
fillBodyObj(c, n[i], body, x, y, enforceDefaultOp, enforceWasMoved)
else:
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
else:
illFormedAstLocal(n, c.g.config)
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
template fillBase =
if t.baseClass != nil:
let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
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
if t.baseClass != nil:
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)
template fillFields =
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
if c.kind == attachedDestructor:
# destroys in reverse order #24719
fillFields()
fillBase()
else:
fillBase()
fillFields()
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) =
var hasCase = isCaseObj(t.n)
@@ -292,8 +277,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
#body.add newAsgnStmt(blob, x)
var wasMovedCall = newNodeI(nkCall, c.info)
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "`=wasMoved`", mWasMoved)))
wasMovedCall.add x # mWasMoved does not take the address
body.add wasMovedCall
@@ -628,7 +612,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)
@@ -666,7 +650,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)
@@ -688,7 +672,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
@@ -787,7 +771,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# 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)
@@ -854,7 +838,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
@@ -882,7 +866,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)
@@ -910,7 +894,7 @@ 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:
@@ -950,7 +934,7 @@ 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)
@@ -968,7 +952,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
@@ -1018,13 +1002,9 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# 'selectedGC' here to determine if we have the new runtime.
discard considerUserDefinedOp(c, t, body, x, y)
elif tfHasAsgn in t.flags:
# seqs with elements using custom hooks in refc
if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
body.add newSeqCall(c, x, y)
if c.kind == attachedWasMoved:
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
else:
forallElements(c, t, body, x, y)
forallElements(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyString:
@@ -1041,11 +1021,9 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of {attachedAsgn, attachedSink, attachedDup}:
body.add newAsgnStmt(x, y)
of attachedWasMoved:
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
else:
fillBodyObjT(c, t, body, x, y)
elif tfUnion in t.flags: # bug #25236
defaultOp(c, t, body, x, y)
else:
if c.kind == attachedDup:
var op2 = getAttachedOp(c.g, t, attachedAsgn)
@@ -1085,7 +1063,9 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
assert typ.kind == tyDistinct
let baseType = typ.elementType
if getAttachedOp(g, baseType, kind) == nil:
discard produceSym(g, c, baseType, kind, info, idgen)
# 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)
result = getAttachedOp(g, baseType, kind)
setAttachedOp(g, idgen.module, typ, kind, result)
@@ -1124,7 +1104,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): PSym =
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
if kind == attachedDup:
return symDupPrototype(g, typ, owner, kind, info, idgen)
@@ -1135,7 +1115,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})):
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)):
dest.typ = typ
else:
dest.typ = makeVarType(typ.owner, typ, idgen)
@@ -1177,13 +1157,13 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add newAsgnStmt(xx, yy)
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator): PSym =
info: TLineInfo; idgen: IdGenerator; isDistinct = false): 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)
result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
fn: result)
@@ -1217,7 +1197,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
fillStrOp(a, typ, result.ast[bodyPos], d, src)
else:
fillBody(a, typ, result.ast[bodyPos], d, src)
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(typ):
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not lacksMTypeField(typ):
# bug #19205: Do not forget to also copy the hidden type field:
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
@@ -1227,8 +1207,6 @@ 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)
@@ -1288,7 +1266,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
else:
localError(g.config, info, "unresolved generic parameter")
proc isTrivial*(s: PSym): bool {.inline.} =
proc isTrival*(s: PSym): bool {.inline.} =
s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
@@ -1298,10 +1276,6 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
## The later 'injectdestructors' pass depends on it.
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
incl orig.flags, tfCheckedForDestructor
# for user defined generic destructors:
let origRoot = genericRoot(orig)
if origRoot != nil:
incl origRoot.flags, tfGenericHasDestructor
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
@@ -1343,8 +1317,8 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
if canon != orig:
setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
if not isTrivial(getAttachedOp(g, orig, attachedDestructor)):
#or not isTrivial(orig.assignment) or
# not isTrivial(orig.sink):
if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
#or not isTrival(orig.assignment) or
# not isTrival(orig.sink):
orig.flags.incl tfHasAsgn
# ^ XXX Breaks IC!

View File

@@ -94,7 +94,6 @@ type
warnImplicitDefaultValue = "ImplicitDefaultValue",
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
warnStdPrefix = "StdPrefix"
warnUnknownNotes = "UnknownNotes"
warnUser = "User",
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
# hints
@@ -110,9 +109,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] = [
@@ -201,7 +200,6 @@ const
warnImplicitDefaultValue: "$1",
warnIgnoredSymbolInjection: "$1",
warnStdPrefix: "$1 needs the 'std' prefix",
warnUnknownNotes: "$1",
warnUser: "$1",
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
hintSuccess: "operation successful: $#",
@@ -237,9 +235,9 @@ const
hintUser: "$1",
hintUserRaw: "$1",
hintExtendedContext: "$1",
hintUnknownRaises: "$1 is a forward declaration without explicit .raises, assuming it can raise anything",
hintMsgOrigin: "$1",
hintDeclaredLoc: "$1"
hintDeclaredLoc: "$1",
hintUnknownHint: "unknown hint: $1"
]
const

View File

@@ -11,7 +11,7 @@
import
pathutils
import std/strutils
when defined(nimPreviewSlimSystem):
import std/syncio
@@ -86,47 +86,6 @@ const
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
'|', '%', '&', '$', '@', '~', ','}
AdditionalLineContinuationOprs = {'#', ':', '='}
LineContinuationTokens = [
"let", "var", "const", "type", # section
"object", "tuple",
# from ./layouter.oprSet
"div", "mod", "shl", "shr", "in", "notin", "is",
"isnot", "not", "of", "as", "from", "..", "and", "or", "xor",
] # must be all `nimIdentNormalized`-ed
proc eqIdent(a, bNormalized: string): bool =
a.nimIdentNormalize == bNormalized
proc endsWithIdent(s, subs: string): bool =
let le = subs.len
if le > s.len: return false
s[^le .. ^1].eqIdent subs
proc continuesWithIdent(s, subs: string, start: int): bool =
s.substr(start, start+subs.high).eqIdent subs
proc endsWithIdent(s, subs: string, endIdx: var int): bool =
endIdx.dec subs.len
result = s.continuesWithIdent(subs, endIdx+1)
proc containsObjectOf(x: string): bool =
const sep = ' '
var idx = x.rfind(sep)
if idx == -1: return
template eatWord(word) =
while x[idx] == sep: idx.dec
result = x.endsWithIdent(word, idx)
if not result: return
eatWord "of"
eatWord "object"
result = true
proc endsWithLineContinuationToken(x: string): bool =
result = false
for tok in LineContinuationTokens:
if x.endsWithIdent(tok):
return true
result = x.containsObjectOf
proc endsWithOpr*(x: string): bool =
result = x.endsWith(LineContinuationOprs)
@@ -134,9 +93,7 @@ proc endsWithOpr*(x: string): bool =
proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
result = inTripleString or line.len > 0 and (
line[0] == ' ' or
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) or
line.endsWithLineContinuationToken()
)
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
proc countTriples(s: string): int =
result = 0
@@ -152,10 +109,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
s.rd = 0
var line = newStringOfCap(120)
var triples = 0
while true:
if not readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
# now readLineFromStdin meets EOF (ctrl-D/Z) or ctrl-C
quit()
while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
s.s.add(line)
s.s.add("\n")
inc triples, countTriples(line)

View File

@@ -58,11 +58,13 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
else: handleError(n, origin)
result = getIdent(c.cache, id)
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
if n[0].kind == nkSym:
result = n[0].sym.name
else:
handleError(n, origin)
of nkOpenSym:
result = considerQuotedIdent(c, n[0], origin)
else:
handleError(n, origin)
@@ -75,13 +77,10 @@ proc addUniqueSym*(scope: PScope, s: PSym): PSym =
proc openScope*(c: PContext): PScope {.discardable.} =
result = PScope(parent: c.currentScope,
symbols: initStrTable(),
depthLevel: c.scopeDepth + 1,
optionStackLen: c.optionStack.len)
depthLevel: c.scopeDepth + 1)
c.currentScope = result
proc rawCloseScope*(c: PContext) =
if c.currentScope.optionStackLen >= 1:
c.optionStack.setLen(c.currentScope.optionStackLen)
c.currentScope = c.currentScope.parent
proc closeScope*(c: PContext) =
@@ -222,14 +221,7 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
if i == limit: return
inc i
proc searchImportsAll*(c: PContext, s: PIdent, filter: TSymKinds, holding: var seq[PSym]) =
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s, c.graph):
if s.kind in filter:
holding.add s
proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = @[]
for scope in allScopes(c.currentScope):
var ti: TIdentIter = default(TIdentIter)
@@ -239,12 +231,14 @@ proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result.add candidate
candidate = nextIdentIter(ti, scope.symbols)
proc searchScopesAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = searchScopes(c,s,filter)
if result.len == 0:
searchImportsAll(c, s, filter, result)
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s, c.graph):
if s.kind in filter:
result.add s
proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
result = @[]
block outer:
for scope in allScopes(c.currentScope):
@@ -258,7 +252,11 @@ proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PS
candidate = nextIdentIter(ti, scope.symbols)
if result.len == 0:
searchImportsAll(c, s, filter, result)
var marked = initIntSet()
for im in c.imports.mitems:
for s in symbols(im, marked, s, c.graph):
if s.kind in filter:
result.add s
proc cmpScopes*(ctx: PContext, s: PSym): int =
# Do not return a negative number
@@ -388,7 +386,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
if sym.name.id == ord(wUnderscore): return
let conflict = scope.addUniqueSym(sym)
if conflict != nil:
if sym.kind == skModule and conflict.kind == skModule and not c.config.isDefined("nimPreviewDuplicateModuleError"):
if sym.kind == skModule and conflict.kind == skModule:
# e.g.: import foo; import foo
# xxx we could refine this by issuing a different hint for the case
# where a duplicate import happens inside an include.
@@ -646,7 +644,7 @@ const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
includePureEnum = false): seq[PSym] =
result = selectFromScopesElseAll(c, ident, filter)
result = searchInScopesFilterBy(c, ident, filter)
if skEnumField in filter and (result.len == 0 or includePureEnum):
result.add allPureEnumFields(c, ident)
@@ -727,7 +725,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if n.kind == nkOpenSym:
# maybe the logic in semexprs should be mirrored here instead
# for now it only seems this is called for `pickSym` in `getTypeIdent`
# for now it only seems this is called for `pickSym` in `getTypeIdent`
return initOverloadIter(o, c, n[0])
o.importIdx = -1
o.marked = initIntSet()

View File

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

View File

@@ -136,8 +136,6 @@ type
cachedFiles*: StringTableRef
procGlobals*: seq[PNode]
TPassContext* = object of RootObj # the pass's context
idgen*: IdGenerator
PPassContext* = ref TPassContext
@@ -456,10 +454,10 @@ template getPContext(): untyped =
else: c.c
when defined(nimsuggest):
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
template onUse*(info: TLineInfo; s: PSym) = discard
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
else:
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
template onUse*(info: TLineInfo; s: PSym) = discard
template onDef*(info: TLineInfo; s: PSym) = discard
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard

View File

@@ -79,10 +79,6 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
else:
result = fileInfoIdx(conf, fullPath)
type
SelectedBase = enum
FromProject, FromSearchPath, FromNimblePath
proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
## Mangle a relative module path to avoid path and symbol collisions.
##
@@ -91,27 +87,9 @@ proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
##
## Example:
## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar`
var best = relativeTo(path, conf.projectPath).string
var selectedBase = FromProject
for x in conf.searchPaths:
let other = relativeTo(path, x).string
if other.len < best.len:
best = other
selectedBase = FromSearchPath
for x in conf.nimblePaths:
let other = relativeTo(path, x).string
if other.len < best.len:
best = other
selectedBase = FromNimblePath
let prefix =
case selectedBase
of FromProject: "@m"
of FromSearchPath: "@p"
of FromNimblePath: "@n"
prefix & best.multiReplace(
"@m" & relativeTo(path, conf.projectPath).string.multiReplace(
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
proc demangleModuleName*(path: string): string =
## Demangle a relative module path.
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@p": "", "@n": "", "@c": ":"})
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})

View File

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

View File

@@ -207,6 +207,7 @@ proc parseAssignment(L: var Lexer, tok: var Token;
checkSymbol(L, tok)
val.add($tok)
confTok(L, tok, config, condStack)
config.currentConfigDir = parentDir(filename.string)
if percent:
processSwitch(s, strtabs.`%`(val, config.configVars,
{useEnvironment, useEmpty}), passPP, info, config)
@@ -248,8 +249,6 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
setDefaultLibpath(conf)
template readConfigFile(path) =
let configPath = path
conf.currentConfigDir = configPath.splitFile.dir.string
setConfigVar(conf, "selfDir", conf.currentConfigDir)
if readConfigFile(configPath, cache, conf):
conf.configFiles.add(configPath)

View File

@@ -28,14 +28,11 @@ 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))
@@ -65,10 +62,6 @@ 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
@@ -156,7 +149,6 @@ 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* = "2025"
copyrightYear* = "2024"
nimEnableCovariance* = defined(nimEnableCovariance)
@@ -248,8 +248,6 @@ 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
@@ -385,7 +383,6 @@ 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
@@ -404,7 +401,6 @@ type
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
@@ -581,7 +577,6 @@ 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
@@ -603,7 +598,6 @@ proc newConfigRef*(): ConfigRef =
arguments: "",
suggestMaxResults: 10_000,
maxLoopIterationsVM: 10_000_000,
maxCallDepthVM: 2_000,
vmProfileData: newProfileData(),
spellSuggestMax: spellSuggestSecretSauce,
currentConfigDir: ""

View File

@@ -13,7 +13,7 @@
import ast, types, msgs, idents, renderer, wordrecg, trees,
options
import std/[strutils, assertions]
import std/strutils
# we precompile the pattern here for efficiency into some internal
# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
@@ -216,11 +216,6 @@ proc exprRoot*(n: PNode; allowCalls = true): PSym =
else:
break
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult
proc isLentableBranch(owner: PSym, n: PNode): bool =
result = isAssignable(owner, n) in {arLentValue, arAddressableConst, arLentValue}
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
## 'owner' can be nil!
result = arNone
@@ -313,35 +308,6 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
# nkVarTy denotes an lvalue, but the example above is the only
# possible code which will get us here
result = arLValue
of nkIfExpr, nkIfStmt:
# allow 'if' expressions to be lent if all branches are lentable
for branch in n:
if branch.len == 2:
if not isLentableBranch(owner, branch[1]):
return
elif branch.len == 1:
if not isLentableBranch(owner, branch[0]):
return
else:
raiseAssert "Malformed `if` statement in isAssignable"
result = arLentValue
of nkCaseStmt:
# allow 'case' expressions to be lent if all branches are lentable
for i in 1 ..< n.len:
let branch = n[i]
case branch.kind
of nkOfBranch:
if not isLentableBranch(owner, branch[^1]):
return
of nkElifBranch:
if not isLentableBranch(owner, branch[1]):
return
of nkElse:
if not isLentableBranch(owner, branch[0]):
return
else:
raiseAssert "Malformed `case` statement in isAssignable"
result = arLentValue
else:
discard

View File

@@ -638,9 +638,8 @@ proc semiStmtList(p: var Parser, result: PNode) =
getTok(p)
if p.tok.tokType == tkParRi:
break
# ignore indent:
#elif not (sameOrNoInd(p) or realInd(p)):
# parMessage(p, errInvalidIndentation)
elif not (sameInd(p) or realInd(p)):
parMessage(p, errInvalidIndentation)
let a = complexOrSimpleStmt(p)
if a.kind == nkEmpty:
parMessage(p, errExprExpected, p.tok)
@@ -700,12 +699,10 @@ 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)
@@ -1156,10 +1153,7 @@ proc parseParamList(p: var Parser, retColon = true): PNode =
parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'")
break
else:
if p.tok.tokType in tokKeywordLow..tokKeywordHigh:
parMessage(p, errGenerated, "'" & $p.tok.ident.s & "' is a keyword and cannot be used as a parameter name")
else:
parMessage(p, "expected closing ')'")
parMessage(p, "expected closing ')'")
break
result.add(a)
if p.tok.tokType notin {tkComma, tkSemiColon}: break
@@ -2113,28 +2107,12 @@ proc parseObjectCase(p: var Parser): PNode =
#| objectBranches = objectBranch (IND{=} objectBranch)*
#| (IND{=} 'elif' expr colcom objectPart)*
#| (IND{=} 'else' colcom objectPart)?
#| objectCase = 'case' (declColonEquals / pragma)? ':'? COMMENT?
#| objectCase = 'case' declColonEquals ':'? COMMENT?
#| (IND{>} objectBranches DED
#| | IND{=} objectBranches)
result = newNodeP(nkRecCase, p)
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)
getTokNoInd(p)
var a = parseIdentColonEquals(p, {withPragma})
result.add(a)
if p.tok.tokType == tkColon: getTok(p)
flexComment(p, result)

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 in {nkOpenSymChoice, nkOpenSym}:
elif sc.kind == nkOpenSymChoice:
# same name suffices for open sym choices!
result = sc[0].sym.name.id == x.sym.name.id
else:

View File

@@ -234,8 +234,7 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
result = moduleFromRodFile(graph, fileIdx, cachedModules)
let path = toFullPath(graph.config, fileIdx)
let filename = AbsoluteFile path
# it could be a stdinfile/cmdfile
if fileExists(filename) and not graph.config.projectIsStdin:
if fileExists(filename): # it could be a stdinfile
graph.cachedFiles[path] = $secureHashFile(path)
if result == nil:
result = newModule(graph, fileIdx)

View File

@@ -210,8 +210,8 @@ type
cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips,
cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430,
cpuSparc64, cpuS390x, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64,
cpuEsp, cpuWasm32, cpuE2k, cpuLoongArch64
cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32,
cpuE2k, cpuLoongArch64
type
TInfoCPU* = tuple[name: string, intSize: int, endian: Endianness,
@@ -241,7 +241,6 @@ const
(name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
(name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
(name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
(name: "s390x", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
(name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
(name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "riscv32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),

View File

@@ -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; comesFromPush = false)
isStatement: bool = false)
proc recordPragma(c: PContext; n: PNode; args: varargs[string]) =
var recorded = newNodeI(nkReplayAction, n.info)
@@ -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,10 +637,7 @@ 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)
if isDefined(con.config, "nimPreviewAsmSemSymbol"):
result.add con.semExprWithType(con, newSymNode(e), {efTypeAllowed})
else:
result.add newSymNode(e)
result.add newSymNode(e)
else:
result.add newStrNode(nkStrLit, sub)
else:
@@ -893,7 +890,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
if keyDeep:
localError(c.config, it.info, "user pragma cannot have arguments")
pragma(c, sym, userPragma.ast, validPragmas, isStatement, comesFromPush)
pragma(c, sym, userPragma.ast, validPragmas, isStatement)
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
else:
@@ -947,19 +944,15 @@ 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)
if sfImportc in sym.flags:
# no restrictions on size for imported types
setImportedTypeSize(c.config, sym.typ, size)
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:
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")
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:
@@ -1322,12 +1315,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
pragmaProposition(c, it)
of wEnsures:
pragmaEnsures(c, it)
of wEnforceNoRaises:
of wEnforceNoRaises, wQuirky:
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:
@@ -1409,12 +1398,11 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
inc i
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
isStatement: bool; comesFromPush = false) =
isStatement: bool) =
if n == nil: return
pragmaRec(c, sym, n, validPragmas, isStatement)
# XXX: in the case of a callable def, this should use its info
if not comesFromPush:
implicitPragmas(c, sym, n.info, validPragmas)
implicitPragmas(c, sym, n.info, validPragmas)
proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords,
isStatement: bool = false) =

View File

@@ -30,7 +30,7 @@ type
TRenderFlags* = set[TRenderFlag]
TRenderTok* = object
kind*: TokType
length*: int32
length*: int16
sym*: PSym
Section = enum
@@ -154,7 +154,7 @@ proc initSrcGen(renderFlags: TRenderFlags; config: ConfigRef): TSrcGen =
)
proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
g.tokens.add TRenderTok(kind: kind, length: int32(s.len), sym: sym)
g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym)
g.buf.add(s)
if kind != tkSpaces:
inc g.col, s.len
@@ -327,10 +327,6 @@ proc pushCom(g: var TSrcGen, n: PNode) =
setLen(g.comStack, g.comStack.len + 1)
g.comStack[^1] = n
proc popCom(g: var TSrcGen): PNode =
result = g.comStack[^1]
setLen(g.comStack, g.comStack.len - 1)
proc popAllComs(g: var TSrcGen) =
setLen(g.comStack, 0)
@@ -414,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, nkOpenSym: result = n[0].sym.name.s
of nkClosedSymChoice, nkOpenSymChoice: 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 & "\"\"\""
@@ -569,16 +565,8 @@ 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, 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 nkElifExpr: result = lsons(g, n) + len("_elif_:_")
of nkElseExpr: 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")
@@ -621,6 +609,8 @@ 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:
@@ -1012,7 +1002,7 @@ type
proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
if renderIds notin g.flags:
case n.kind
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym:
of nkClosedSymChoice, nkOpenSymChoice:
if n.len > 0: result = bracketKind(g, n[0])
else: result = bkNone
of nkSym:
@@ -1357,10 +1347,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
if not n[0].isExported() and renderNonExportedFields notin g.flags:
# Skip if this is a property in a type and its not exported
# (While also not allowing rendering of non exported fields)
if shouldRenderComment(g, n):
# `shouldRenderComment` indicts that we have comments to render
# but it's a non-exported field, so we just pop without rendering any comment
discard popCom(g)
return
# render postfix for object fields:
exclFlags = g.flags * {renderNoPostfix}
@@ -1429,7 +1415,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
of nkPrefix:
gsub(g, n, 0)
if n.len > 1:
let opr = getPIdent(n[0])
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 nNext = skipHiddenNodes(n[1])
if nNext.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
put(g, tkSpaces, Space)
@@ -1480,30 +1469,15 @@ 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, 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 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 nkTypeOfExpr:
put(g, tkType, "typeof")
put(g, tkParLe, "(")
@@ -1765,6 +1739,19 @@ 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, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
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)

View File

@@ -321,7 +321,7 @@ proc hasCycle(n: PNode): bool =
break
excl n.flags, nfNone
proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode; producedClosure: var bool): PNode =
proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
# recompute the types as 'eval' isn't guaranteed to construct types nor
# that the types are sound:
when true:
@@ -333,7 +333,7 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode; producedClosure: v
if hasCycle(result):
result = localErrorNode(c, eOrig, "the resulting AST is cyclic and cannot be processed further")
else:
semmacrosanity.annotateType(result, expectedType, c.config, producedClosure)
semmacrosanity.annotateType(result, expectedType, c.config)
else:
result = semExprWithType(c, evaluated)
#result = fitNode(c, e.typ, result) inlined with special case:
@@ -346,19 +346,6 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode; producedClosure: v
isArrayConstr(arg):
arg.typ = eOrig.typ
proc resetEvalPosition(n: PNode) =
# resets the eval position of variables because `tryConstExpr` may be
# called multiple times on the same node
case n.kind
of {nkNone..nkNilLit}-{nkSym}:
discard
of nkSym:
if n.sym.kind in {skVar, skLet} and sfGlobal notin n.sym.flags:
n.sym.position = 0
else:
for i in 0..<n.safeLen:
resetEvalPosition(n[i])
proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
var e = semExprWithType(c, n, expectedType = expectedType)
if e == nil: return
@@ -383,10 +370,7 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
if result == nil or result.kind == nkEmpty:
result = nil
else:
var producedClosure = false
result = fixupTypeAfterEval(c, result, e, producedClosure)
if producedClosure:
result = nil
result = fixupTypeAfterEval(c, result, e)
except ERecoverableError:
result = nil
@@ -395,8 +379,6 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
# Restore the error hook
c.graph.config.structuredErrorHook = tempHook
resetEvalPosition(n)
c.config.errorCounter = oldErrorCount
c.config.errorMax = oldErrorMax
c.config.m.errorOutputs = oldErrorOutputs
@@ -425,10 +407,7 @@ proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
# error correction:
result = e
else:
var producedClosure = false
result = fixupTypeAfterEval(c, result, e, producedClosure)
if producedClosure:
result = nil
result = fixupTypeAfterEval(c, result, e)
proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
if efNeedStatic in flags:
@@ -521,21 +500,6 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
dec(c.config.evalTemplateCounter)
discard c.friendModules.pop()
proc getLineInfo(n: PNode): TLineInfo =
case n.kind
of nkPostfix:
if len(n) > 1:
result = getLineInfo(n[1])
else:
result = n.info
of nkAccQuoted, nkPragmaExpr:
if len(n) > 0:
result = getLineInfo(n[0])
else:
result = n.info
else:
result = n.info
const
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
@@ -562,9 +526,7 @@ 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, {
renderNonExportedFields, renderDocComments, renderNoComments
}))
message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
result = wrapInComesFrom(nOrig.info, sym, result)
popInfoContext(c.config)
@@ -709,9 +671,8 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
if child != nil:
let node = newNode(nkIntLit)
node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
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),
result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
semExprWithType(c, child),
node
))
result.typ() = aTyp
@@ -776,7 +737,6 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
result.semTypeNode = semTypeNode
result.instTypeBoundOp = sigmatch.instTypeBoundOp
result.hasUnresolvedArgs = hasUnresolvedArgs
result.semAsgnOpr = semAsgnOpr
result.templInstCounter = new int
pushProcCon(result, module)
@@ -894,9 +854,9 @@ proc semWithPContext*(c: PContext, n: PNode): PNode =
proc reportUnusedModules(c: PContext) =
if c.config.cmd == cmdM: return
for (s, info) in c.unusedImports:
if sfUsed notin s.flags:
message(c.config, info, warnUnusedImportX, s.name.s)
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)
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 searchScopesAll(c, symx.name, {skConst}):
for paramSym in searchInScopesAllCandidatesFilterBy(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))
@@ -144,6 +144,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
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
@@ -183,7 +184,7 @@ 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.
@@ -244,6 +245,14 @@ 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
@@ -581,7 +590,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
let overloadsState = result.state
if overloadsState != csMatch:
if nfDotField in n.flags:
internalAssert c.config, f.kind in nkIdentKinds and n.len >= 2
internalAssert c.config, f.kind == nkIdent and n.len >= 2
# leave the op head symbol empty,
# we are going to try multiple variants
@@ -686,14 +695,7 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
let s = a[0].sym
if s.isGenericRoutineStrict:
var src = s.typ.firstParamType
var convMatch = newCandidate(c, src)
var arg = a[1]
if arg.kind in {nkHiddenAddr, nkHiddenSubConv}: arg = arg[^1]
let srca = typeRel(convMatch, src, arg.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)
let finalCallee = generateInstance(c, s, x.bindings, a.info)
a[0].sym = finalCallee
a[0].typ() = finalCallee.typ
#a.typ = finalCallee.typ.returnType
@@ -836,12 +838,9 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
assert x.state == csMatch
var finalCallee = x.calleeSym
let info = getCallLineInfo(n)
markUsed(c, info, finalCallee, isGenericInstance = false)
onUse(info, finalCallee, isGenericInstance = false)
markUsed(c, info, finalCallee)
onUse(info, finalCallee)
assert finalCallee.ast != nil
if x.matchedErrorType:
markUsed(c, info, finalCallee, isGenericInstance = true)
onUse(info, finalCallee, isGenericInstance = true)
if x.matchedErrorType:
result = x.call
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
@@ -877,8 +876,6 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
x.call.add tn
else:
internalAssert c.config, false
markUsed(c, info, finalCallee, isGenericInstance = true)
onUse(info, finalCallee, isGenericInstance = true)
result = x.call
instGenericConvertersSons(c, result, x)
@@ -914,15 +911,15 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
if c.inGenericContext > 0 and c.matchedConcept == nil:
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
elif efNoUndeclared in flags:
result = nil
elif efExplain notin flags:
# repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again)
result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
else:
elif efNoUndeclared notin flags:
result = nil
notFoundError(c, n, errors)
else:
result = nil
proc explicitGenericInstError(c: PContext; n: PNode): PNode =
localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n))
@@ -947,10 +944,8 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym, errors: var CandidateErr
var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved
let info = getCallLineInfo(n)
markUsed(c, info, s, isGenericInstance = false)
onUse(info, s, isGenericInstance = false)
markUsed(c, info, newInst, isGenericInstance = true)
onUse(info, newInst, isGenericInstance = true)
markUsed(c, info, s)
onUse(info, s)
result = newSymNode(newInst, info)
proc setGenericParams(c: PContext, n, expectedParams: PNode) =
@@ -978,14 +973,8 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
# common case; check the only candidate has the right
# number of generic type parameters:
result = explicitGenericSym(c, n, s, errors, doError)
if 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)
if doError and result == nil:
notFoundError(c, n, errors)
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
# choose the generic proc with the proper number of type parameters.
result = newNodeI(a.kind, getCallLineInfo(n))
@@ -995,14 +984,6 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
skFunc, skIterator}:
let x = explicitGenericSym(c, n, candidate, errors, doError)
if x != nil: result.add(x)
elif c.inGenericContext > 0:
# same as in semOverloadedCall, make expression untyped,
# may have failed match due to unresolved types
# any failing match stops building the symchoice for correctness,
# can also make it untyped from the start
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
# get rid of nkClosedSymChoice if not ambiguous:
if result.len == 0:
result = nil

View File

@@ -9,15 +9,14 @@
## This module contains the data structures for the semantic checking phase.
import std/[tables, intsets, sets, strutils]
import std/[tables, intsets, sets]
when defined(nimPreviewSlimSystem):
import std/assertions
import
options, ast, msgs, idents, renderer,
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable,
types, lowerings, trees, parampatterns, astalgo
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable
import ic / ic
@@ -42,7 +41,7 @@ type
breakInLoop*: bool # whether we are in a loop without block
next*: PProcCon # used for stacking procedure contexts
mappingExists*: bool
mapping*: SymMapping
mapping*: Table[ItemId, PSym]
caseContext*: seq[tuple[n: PNode, idx: int]]
localBindStmts*: seq[PNode]
@@ -174,9 +173,6 @@ type
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
@@ -258,7 +254,7 @@ proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
proc put*(p: PProcCon; key, val: PSym) =
if not p.mappingExists:
p.mapping = initSymMapping()
p.mapping = initTable[ItemId, PSym]()
p.mappingExists = true
#echo "put into table ", key.info
p.mapping[key.itemId] = val
@@ -290,24 +286,22 @@ proc considerGenSyms*(c: PContext; n: PNode) =
considerGenSyms(c, n[i])
proc newOptionEntry*(conf: ConfigRef): POptionEntry =
result = POptionEntry(
options: conf.options,
defaultCC: ccNimCall,
dynlib: nil,
notes: conf.notes,
warningAsErrors: conf.warningAsErrors
)
new(result)
result.options = conf.options
result.defaultCC = ccNimCall
result.dynlib = nil
result.notes = conf.notes
result.warningAsErrors = conf.warningAsErrors
proc pushOptionEntry*(c: PContext): POptionEntry =
let prev = c.optionStack[^1]
result = POptionEntry(
options: c.config.options,
defaultCC: prev.defaultCC,
dynlib: prev.dynlib,
notes: c.config.notes,
warningAsErrors: c.config.warningAsErrors,
features: c.features
)
new(result)
var prev = c.optionStack[^1]
result.options = c.config.options
result.defaultCC = prev.defaultCC
result.dynlib = prev.dynlib
result.notes = c.config.notes
result.warningAsErrors = c.config.warningAsErrors
result.features = c.features
c.optionStack.add(result)
proc popOptionEntry*(c: PContext) =
@@ -318,23 +312,22 @@ proc popOptionEntry*(c: PContext) =
c.optionStack.setLen(c.optionStack.len - 1)
proc newContext*(graph: ModuleGraph; module: PSym): PContext =
result = PContext(
optionStack: @[newOptionEntry(graph.config)],
libs: @[],
module: module,
friendModules: @[module],
converters: @[],
patterns: @[],
includedFiles: initIntSet(),
pureEnumFields: initStrTable(),
userPragmas: initStrTable(),
generics: @[],
unknownIdents: initIntSet(),
cache: graph.cache,
graph: graph,
signatures: initStrTable(),
features: graph.config.features
)
new(result)
result.optionStack = @[newOptionEntry(graph.config)]
result.libs = @[]
result.module = module
result.friendModules = @[module]
result.converters = @[]
result.patterns = @[]
result.includedFiles = initIntSet()
result.pureEnumFields = initStrTable()
result.userPragmas = initStrTable()
result.generics = @[]
result.unknownIdents = initIntSet()
result.cache = graph.cache
result.graph = graph
result.signatures = initStrTable()
result.features = graph.config.features
if graph.config.symbolFiles != disabledSf:
let id = module.position
if graph.config.cmd != cmdM:
@@ -398,7 +391,8 @@ proc reexportSym*(c: PContext; s: PSym) =
addReexport(c.encoder, c.packedRepr, s)
proc newLib*(kind: TLibKind): PLib =
result = PLib(kind: kind) #result.syms = initObjectSet()
new(result)
result.kind = kind #result.syms = initObjectSet()
proc addToLib*(lib: PLib, sym: PSym) =
#if sym.annex != nil and not isGenericRoutine(sym):
@@ -641,166 +635,3 @@ 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)

View File

@@ -16,6 +16,7 @@ when defined(nimCompilerStacktraceHints):
const
errExprXHasNoType = "expression '$1' has no type (or is ambiguous)"
errXExpectsTypeOrValue = "'$1' expects a type or value"
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"
errExprHasNoAddress = "expression has no address"
errCannotInterpretNodeX = "cannot evaluate '$1'"
@@ -113,8 +114,6 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = symChoice(c, n, s, scClosed)
if result.kind == nkSym:
markUsed(c, n.info, s)
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode
@@ -726,7 +725,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
# nkBracket nodes can also be produced by the VM as seq constant nodes
# in which case, we cannot produce a new array type for the node,
# as this might lose type info even when the node has array type
let constructType = n.typ.isNil or n.typ.kind == tyFromExpr
let constructType = n.typ.isNil
var expectedElementType, expectedIndexType: PType = nil
var expectedBase: PType = nil
if constructType:
@@ -775,11 +774,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
var typ: PType
var isGeneric = false
if yy.typ != nil and yy.typ.kind == tyFromExpr:
isGeneric = true
typ = nil # will not be used
elif constructType:
if constructType:
typ = yy.typ
if expectedElementType == nil:
expectedElementType = typ
@@ -804,21 +799,11 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
result.add xx
if xx.typ != nil and xx.typ.kind == tyFromExpr:
isGeneric = true
elif constructType:
if constructType:
typ = commonType(c, typ, xx.typ)
#n[i] = semExprWithType(c, x, {})
#result.add fitNode(c, typ, n[i])
inc(lastIndex)
if isGeneric:
for i in 0..<result.len:
if isIntLit(result[i].typ):
# generic instantiation strips int lit type which makes conversions fail
result[i].typ() = nil
result.typ() = nil # current result.typ is invalid, index type is nil
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
if constructType:
addSonSkipIntLit(result.typ, typ, c.idgen)
for i in 0..<result.len:
@@ -880,6 +865,105 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
if hasUnresolvedArgs(c, n[i]): return true
return false
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})))
include semmagic
proc evalAtCompileTime(c: PContext, n: PNode): PNode =
@@ -951,15 +1035,11 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
result = evalStaticExpr(c.module, c.idgen, c.graph, call, c.p.owner)
if result.isNil:
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
else:
var producedClosure = false
result = fixupTypeAfterEval(c, result, n, producedClosure)
else: result = fixupTypeAfterEval(c, result, n)
else:
result = evalConstExpr(c.module, c.idgen, c.graph, call)
if result.isNil: result = n
else:
var producedClosure = false
result = fixupTypeAfterEval(c, result, n, producedClosure)
else: result = fixupTypeAfterEval(c, result, n)
else:
result = n
#if result != n:
@@ -977,8 +1057,7 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
result = c.graph.emptyNode
else:
var producedClosure = false
result = fixupTypeAfterEval(c, result, a, producedClosure)
result = fixupTypeAfterEval(c, result, a)
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags; expectedType: PType = nil): PNode =
@@ -1297,9 +1376,7 @@ proc readTypeParameter(c: PContext, typ: PType,
# This seems semantically correct and then we'll be able
# to return the section symbol directly here
let foundType = makeTypeDesc(c, def[2].typ)
let s = copySym(def[0].sym, c.idgen)
s.typ = foundType
return newSymNode(s, info)
return newSymNode(copySym(def[0].sym, c.idgen).linkTo(foundType), info)
of nkConstSection:
for def in statement:
@@ -1324,9 +1401,7 @@ proc readTypeParameter(c: PContext, typ: PType,
return c.graph.emptyNode
else:
let foundTyp = makeTypeDesc(c, rawTyp)
let s = copySym(tParam.sym, c.idgen)
s.typ = foundTyp
return newSymNode(s, info)
return newSymNode(copySym(tParam.sym, c.idgen).linkTo(foundTyp), info)
return nil
@@ -1605,46 +1680,29 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
result = tryReadingGenericParam(c, n, i, t)
flags.incl efCannotBeDotCall
proc hiddenDerefDepth(n: PNode): int =
result = 0
var n = n
while n.kind == nkHiddenDeref:
inc result
n = n[0]
proc dotTransformation(c: PContext, n: PNode, initialDerefs: int): PNode =
var root = n[0]
let currentDerefs = hiddenDerefDepth(root)
if currentDerefs > initialDerefs:
# hidden derefs were inserted by `builtinFieldAccess` for fields of
# `ref object` etc.
# undo the derefs for overload resolution
for _ in initialDerefs ..< currentDerefs:
root = root[0]
root = copyTree(root)
proc dotTransformation(c: PContext, n: PNode): PNode =
if isSymChoice(n[1]) or
# generics usually leave field names as symchoices, but not types
(n[1].kind == nkSym and n[1].sym.kind == skType):
result = newNodeI(nkDotCall, n.info)
result.add n[1]
result.add root
result.add copyTree(n[0])
else:
var i = considerQuotedIdent(c, n[1], n)
result = newNodeI(nkDotCall, n.info)
result.flags.incl nfDotField
result.add newIdentNode(i, n[1].info)
result.add root
result.add copyTree(n[0])
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this is difficult, because the '.' is used in many different contexts
# in Nim. We first allow types in the semantic checking.
var f = flags - {efIsDotCall}
let initialDerefDepth = hiddenDerefDepth(n[0])
result = builtinFieldAccess(c, n, f)
if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and
efIsDotCall in flags and callOperator notin c.features and
efCannotBeDotCall notin f):
result = dotTransformation(c, n, initialDerefDepth)
result = dotTransformation(c, n)
proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
result = newNodeI(nkCall, n.info)
@@ -1758,9 +1816,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags, afterOverloading = f
# type parameters: partial generic specialization
n[0] = semSymGenericInstantiation(c, n[0], s)
result = maybeInstantiateGeneric(c, n, s, doError = afterOverloading)
if result != nil and
# leave untyped generic expression alone:
(result.typ == nil or result.typ.kind != tyFromExpr):
if result != nil:
# check newly created sym/symchoice
result = semExpr(c, result, flags)
of skMacro, skTemplate:
@@ -1798,7 +1854,7 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
result = newTreeI(nkCall, n.info, setterId, a[0], n[1])
result.flags.incl nfDotSetter
let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1])
result = semOverloadedCallAnalyseEffects(c, result, orig, {efNoUndeclared})
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
if result != nil:
result = afterCallActions(c, result, nOrig, {})
@@ -1932,20 +1988,8 @@ proc makeTupleAssignments(c: PContext; n: PNode): PNode =
for i in 0..<lhs.len:
if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
# tuple unpacking `skTemp` does not generate a destructor and
# expects all fields to be unpacked, so instead of skipping,
# generate `let _ = temp[i]` which should generate a destructor
let utemp = newSym(skLet, lhs[i].ident, c.idgen, getCurrOwner(c), lhs[i].info)
utemp.typ = value.typ[i]
temp.flags.incl(sfGenSym)
var uv = newNodeI(nkLetSection, lhs[i].info)
let utempNode = newSymNode(utemp)
var uvpart = newNodeI(nkIdentDefs, v.info, 3)
uvpart[0] = utempNode
uvpart[1] = c.graph.emptyNode
uvpart[2] = newTupleAccessRaw(tempNode, i)
uv.add uvpart
result.add(uv)
# skip _ assignments if we are using a temp as they are already evaluated
discard
else:
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
@@ -1958,48 +2002,34 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
# --> `f=` (r, x)
let nOrig = n.copyTree
var flags = {efLValue}
let initialDerefDepth = hiddenDerefDepth(a[0])
a = builtinFieldAccess(c, a, flags)
if a == nil:
a = propertyWriteAccess(c, n, nOrig, n[0])
if a != nil: return a
# we try without the '='; proc that return 'var' or macros are still
# possible:
a = dotTransformation(c, n[0], initialDerefDepth)
a = dotTransformation(c, n[0])
if a.kind == nkDotCall:
a.transitionSonsKind(nkCall)
a = semExprWithType(c, a, {efLValue})
of nkBracketExpr:
# a[i] = x
# --> `[]=`(a, i, x)
# try builtin subscript for LHS first:
a = semSubscript(c, a, {efLValue})
if a == nil:
result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]="))
result.add(n[1])
if mode == noOverloadedSubscript:
# `[]=` overloads failed and builtin subscript failed, try `[]` overloads for LHS
# will error if not found:
a = semExprWithType(c, n[0], {efLValue})
bracketNotFoundError(c, result, {})
return errorNode(c, n)
else:
# magic overload of `[]=` will always match so cannot check for mismatch here,
# will go to above `if` branch instead
result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]="))
result.add(n[1])
result = semExprNoType(c, result)
return result
of nkCurlyExpr:
# a{i} = x --> `{}=`(a, i, x)
# no builtin behavior/magic overloads for curly subscript,
# try `{}=` overloads first then try `{}` overloads for LHS:
let nOrig = n.copyTree
result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "{}="))
result.add(n[1])
result = semOverloadedCallAnalyseEffects(c, result, result.copyTree, {efNoUndeclared})
if result != nil:
result = afterCallActions(c, result, nOrig, {})
return
else:
# will error if `{}` overloads not found:
a = semExprWithType(c, a, {efLValue})
return semExprNoType(c, result)
of nkPar, nkTupleConstr:
if a.len >= 2 or a.kind == nkTupleConstr:
# unfortunately we need to rewrite ``(x, y) = foo()`` already here so
@@ -2217,8 +2247,10 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
result = someSym(c.graph, m, ident)
of nkSym:
result = n.sym
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
result = n[0].sym
of nkOpenSym:
result = lookUpForDeclared(c, n[0], onlyCurrentScope)
else:
localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
result = nil
@@ -2756,21 +2788,16 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
else:
# only semantic checking for all elements, later type checking:
var typ: PType = nil
var isGeneric = false
for i in 0..<n.len:
let doSetType = typ == nil
if isRange(n[i]):
checkSonsLen(n[i], 3, c.config)
n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType)
n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType)
if (n[i][1].typ != nil and n[i][1].typ.kind == tyFromExpr) or
(n[i][2].typ != nil and n[i][2].typ.kind == tyFromExpr):
isGeneric = true
else:
if doSetType:
typ = skipTypes(n[i][1].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
n[i].typ() = n[i][2].typ # range node needs type too
if doSetType:
typ = skipTypes(n[i][1].typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
n[i].typ() = n[i][2].typ # range node needs type too
elif n[i].kind == nkRange:
# already semchecked
if doSetType:
@@ -2778,11 +2805,9 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
else:
n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
if n[i].typ != nil and n[i].typ.kind == tyFromExpr:
isGeneric = true
elif doSetType:
if doSetType:
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
if doSetType and not isGeneric:
if doSetType:
if not isOrdinalType(typ, allowEnumWithHoles=true):
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
@@ -2797,14 +2822,6 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
if expectedElementType == nil:
expectedElementType = typ
if isGeneric:
for i in 0..<n.len:
if isIntLit(n[i].typ):
# generic instantiation strips int lit type which makes conversions fail
n[i].typ() = nil
result.add n[i]
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
addSonSkipIntLit(result.typ, typ, c.idgen)
for i in 0..<n.len:
var m: PNode
@@ -2877,7 +2894,6 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
var typ = newTypeS(tyTuple, c)
typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs
var ids = initIntSet()
var isGeneric = false
for i in 0..<n.len:
if n[i].kind != nkExprColonExpr:
illFormedAst(n[i], c.config)
@@ -2887,17 +2903,12 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
# can check if field name matches expected type here
let expectedElemType = if expected != nil: expected[i] else: nil
n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType)
if n[i][1].typ != nil and n[i][1].typ.kind == tyFromExpr:
isGeneric = true
elif expectedElemType != nil and
if expectedElemType != nil and
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
# hasEmpty/nil check is to not break existing code like
# `const foo = [(1, {}), (2, {false})]`,
# `const foo = if true: (0, nil) else: (1, new(int))`
let conversion = indexTypesMatch(c, expectedElemType, n[i][1].typ, n[i][1])
# ignore matching error, full tuple will be matched later which may call converter, see #24609
if conversion != nil:
n[i][1] = conversion
n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info)
if n[i][1].typ.kind == tyTypeDesc:
localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.")
@@ -2910,21 +2921,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
typ.n.add newSymNode(f)
n[i][0] = newSymNode(f)
result.add n[i]
if isGeneric:
for i in 0..<result.len:
if isIntLit(result[i][1].typ):
# generic instantiation strips int lit type which makes conversions fail
result[i][1].typ() = nil
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
let oldType = n.typ
result.typ() = typ
if oldType != nil and not hasEmpty(oldType): # see hasEmpty comment above
# convert back to old type
let conversion = indexTypesMatch(c, oldType, typ, result)
# ignore matching error, the goal is just to keep the original type info
if conversion != nil:
result.typ() = oldType
proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = n # we don't modify n, but compute the type:
@@ -2935,37 +2932,17 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT
if not (expected.kind == tyTuple and expected.len == n.len):
expected = nil
var typ = newTypeS(tyTuple, c) # leave typ.n nil!
var isGeneric = false
for i in 0..<n.len:
let expectedElemType = if expected != nil: expected[i] else: nil
n[i] = semExprWithType(c, n[i], {}, expectedElemType)
if n[i].typ != nil and n[i].typ.kind == tyFromExpr:
isGeneric = true
elif expectedElemType != nil and
if expectedElemType != nil and
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
# hasEmpty/nil check is to not break existing code like
# `const foo = [(1, {}), (2, {false})]`,
# `const foo = if true: (0, nil) else: (1, new(int))`
let conversion = indexTypesMatch(c, expectedElemType, n[i].typ, n[i])
# ignore matching error, full tuple will be matched later which may call converter, see #24609
if conversion != nil:
n[i] = conversion
n[i] = fitNode(c, expectedElemType, n[i], n[i].info)
addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
if isGeneric:
for i in 0..<result.len:
if isIntLit(result[i].typ):
# generic instantiation strips int lit type which makes conversions fail
result[i].typ() = nil
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
let oldType = n.typ
result.typ() = typ
if oldType != nil and not hasEmpty(oldType): # see hasEmpty comment above
# convert back to old type
let conversion = indexTypesMatch(c, oldType, typ, result)
# ignore matching error, the goal is just to keep the original type info
if conversion != nil:
result.typ() = oldType
include semobjconstr
@@ -3054,15 +3031,9 @@ proc semExport(c: PContext, n: PNode): PNode =
s = nextOverloadIter(o, c, a)
proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
result = semTuplePositionsConstr(c, n, flags, expectedType)
if result.typ.kind == tyFromExpr:
# tyFromExpr is already ambivalent between types and values
return
var tupexp = result
while tupexp.kind == nkHiddenSubConv: tupexp = tupexp[1]
var tupexp = semTuplePositionsConstr(c, n, flags, expectedType)
var isTupleType: bool = false
if tupexp.len > 0: # don't interpret () as type
internalAssert c.config, tupexp.kind == nkTupleConstr
isTupleType = tupexp[0].typ.kind == tyTypeDesc
# check if either everything or nothing is tyTypeDesc
for i in 1..<tupexp.len:
@@ -3072,13 +3043,15 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
result = n
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
result.typ() = makeTypeDesc(c, typ)
else:
result = tupexp
proc isExplicitGenericCall(c: PContext, n: PNode): bool =
## checks if a call node `n` is a routine call with explicit generic params
##
##
## the callee node needs to be either an nkBracketExpr or a call to a
## symchoice of `[]` in which case it will be transformed into nkBracketExpr
##
##
## the LHS of the bracket expr has to either be a symchoice or resolve to
## a routine symbol
template checkCallee(n: PNode) =
@@ -3324,7 +3297,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
#performProcvarCheck(c, n, s)
result = symChoice(c, n, s, scClosed)
if result.kind == nkSym:
markUsed(c, n.info, s)
markIndirect(c, result.sym)
# if isGenericRoutine(result.sym):
# localError(c.config, n.info, errInstantiateXExplicitly, s.name.s)
@@ -3574,7 +3546,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
of nkMacroDef: result = semMacroDef(c, n)
of nkTemplateDef: result = semTemplateDef(c, n)
of nkImportStmt:
trySuggestModuleNames(c, n)
# this particular way allows 'import' in a 'compiles' context so that
# template canImport(x): bool =
# compiles:

View File

@@ -36,8 +36,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
of nkIdent, nkSym:
result = n
let ident = considerQuotedIdent(c.c, n)
if c.replaceByFieldName and
ident.id != ord(wUnderscore):
if c.replaceByFieldName:
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
@@ -46,8 +45,7 @@ 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 and
ident.id != ord(wUnderscore):
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
var call = forLoop[^2]
var tupl = call[i+1-ord(c.replaceByFieldName)]
if c.field.isNil:

View File

@@ -471,20 +471,19 @@ 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:
result = nil
#localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
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 =
@@ -590,7 +589,7 @@ proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
raise newException(ValueError, "invalid enum value: " & str)
else:
localError(g.config, s.info, "unsupported type $1 for define '$2'" %
[typeToString(rawTyp), name])
[name, typeToString(rawTyp)])
except ValueError as e:
localError(g.config, s.info,
"could not process define '$1' of type $2; $3" %

View File

@@ -218,7 +218,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
let ident = considerQuotedIdent(c, n)
# could be type conversion if like a.T and not a.T()
let symKinds = if inCall: routineKinds else: routineKinds+{skType}
var candidates = selectFromScopesElseAll(c, ident, symKinds)
var candidates = searchInScopesFilterBy(c, ident, symKinds)
if candidates.len > 0:
let s = candidates[0] # XXX take into account the other candidates!
isMacro = s.kind in {skTemplate, skMacro}
@@ -274,8 +274,7 @@ proc semGenericStmt(c: PContext, n: PNode,
result = lookup(c, n, flags, ctx)
if result != nil and result.kind == nkSym:
assert result.sym != nil
incl result.sym.flags, sfUsed
markOwnerModuleAsUsed(c, result.sym)
markUsed(c, n.info, result.sym)
of nkDotExpr:
#let luf = if withinMixin notin flags: {checkUndeclared} else: {}
#var s = qualifiedLookUp(c, n, luf)

View File

@@ -308,8 +308,6 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
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)
@@ -320,8 +318,6 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
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)

View File

@@ -10,28 +10,9 @@
## Implements type sanity checking for ASTs resulting from macros. Lots of
## room for improvement here.
import ast, msgs, types, options, trees, nimsets
import ast, msgs, types, options
type
FieldTracker = object
index: int
remaining: int
constr: PNode
delete: bool # to delete fields from inactive case branches
FieldInfo = ref object
sym: PSym
delete: bool
proc caseBranchMatchesExpr(branch, matched: PNode): bool =
# copied from sem
result = false
for i in 0 ..< branch.len-1:
if branch[i].kind == nkRange:
if overlap(branch[i], matched): return true
elif exprStructuralEquivalent(branch[i], matched):
return true
proc ithField(n: PNode, field: var FieldTracker): FieldInfo =
proc ithField(n: PNode, field: var int): PSym =
result = nil
case n.kind
of nkRecList:
@@ -42,42 +23,18 @@ proc ithField(n: PNode, field: var FieldTracker): FieldInfo =
if n[0].kind != nkSym: return
result = ithField(n[0], field)
if result != nil: return
# value of the discriminator field, from (index - remaining - 1 + 1):
# - 1 because the `ithField` call above decreased it by 1,
# + 1 because the constructor node has an initial type child
let val = field.constr[field.index - field.remaining][1]
var branchFound = false
for i in 1..<n.len:
let previousDelete = field.delete
case n[i].kind
of nkOfBranch:
if branchFound or previousDelete or
not caseBranchMatchesExpr(n[i], val):
# if this is not the active case branch,
# mark all fields inside as deleted
field.delete = true
else:
branchFound = true
of nkOfBranch, nkElse:
result = ithField(lastSon(n[i]), field)
if result != nil: return
field.delete = previousDelete
of nkElse:
if branchFound:
# if this is not the active case branch,
# mark all fields inside as deleted
field.delete = true
result = ithField(lastSon(n[i]), field)
if result != nil: return
field.delete = previousDelete
else: discard
of nkSym:
if field.remaining == 0:
result = FieldInfo(sym: n.sym, delete: field.delete)
else:
dec(field.remaining)
if field == 0: result = n.sym
else: dec(field)
else: discard
proc ithField(t: PType, field: var FieldTracker): FieldInfo =
proc ithField(t: PType, field: var int): PSym =
var base = t.baseClass
while base != nil:
let b = skipTypes(base, skipPtrs)
@@ -86,7 +43,7 @@ proc ithField(t: PType, field: var FieldTracker): FieldInfo =
base = b.baseClass
result = ithField(t.n, field)
proc annotateType*(n: PNode, t: PType; conf: ConfigRef; producedClosure: var bool) =
proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
let x = t.skipTypes(abstractInst+{tyRange})
# Note: x can be unequal to t and we need to be careful to use 't'
# to not to skip tyGenericInst
@@ -96,26 +53,21 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef; producedClosure: var boo
n.typ() = t
n[0].typ() = t
for i in 1..<n.len:
var tracker = FieldTracker(index: i-1, remaining: i-1, constr: n, delete: false)
let field = x.ithField(tracker)
var j = i-1
let field = x.ithField(j)
if field.isNil:
globalError conf, n.info, "invalid field at index " & $i
else:
internalAssert(conf, n[i].kind == nkExprColonExpr)
annotateType(n[i][1], field.sym.typ, conf, producedClosure)
if field.delete:
# only codegen fields from active case branches
incl(n[i].flags, nfPreventCg)
annotateType(n[i][1], field.typ, conf)
of nkPar, nkTupleConstr:
if x.kind == tyTuple:
n.typ() = t
for i in 0..<n.len:
if i >= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i
else: annotateType(n[i], x[i], conf, producedClosure)
else: annotateType(n[i], x[i], conf)
elif x.kind == tyProc and x.callConv == ccClosure:
n.typ() = t
if n.len > 1 and n[1].kind notin {nkEmpty, nkNilLit}:
producedClosure = true
elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples
if n.kind == nkTupleConstr:
let
@@ -127,11 +79,11 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef; producedClosure: var boo
of nkStrKinds:
for i in left..right:
bracketExpr.add newIntNode(nkCharLit, BiggestInt n[0].strVal[i])
annotateType(bracketExpr[^1], x.elementType, conf, producedClosure)
annotateType(bracketExpr[^1], x.elementType, conf)
of nkBracket:
for i in left..right:
bracketExpr.add n[0][i]
annotateType(bracketExpr[^1], x.elementType, conf, producedClosure)
annotateType(bracketExpr[^1], x.elementType, conf)
else:
globalError(conf, n.info, "Incorrectly generated tuple constr")
n[] = bracketExpr[]
@@ -142,18 +94,13 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef; producedClosure: var boo
of nkBracket:
if x.kind in {tyArray, tySequence, tyOpenArray}:
n.typ() = t
for m in n: annotateType(m, x.elemType, conf, producedClosure)
for m in n: annotateType(m, x.elemType, conf)
else:
globalError(conf, n.info, "[] must have some form of array type")
of nkCurly:
if x.kind in {tySet}:
n.typ() = t
for m in n:
if m.kind == nkRange:
annotateType(m[0], x.elemType, conf, producedClosure)
annotateType(m[1], x.elemType, conf, producedClosure)
else:
annotateType(m, x.elemType, conf, producedClosure)
for m in n: annotateType(m, x.elemType, conf)
else:
globalError(conf, n.info, "{} must have the set type")
of nkFloatLit..nkFloat128Lit:

View File

@@ -38,7 +38,7 @@ proc semAddr(c: PContext; n: PNode): PNode =
if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}:
localError(c.config, n.info, errExprHasNoAddress)
result.add x
result.typ() = makePtrType(c, x.typ.skipTypes({tySink}))
result.typ() = makePtrType(c, x.typ)
proc semTypeOf(c: PContext; n: PNode): PNode =
var m = BiggestInt 1 # typeOfIter
@@ -55,15 +55,7 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
result.add typExpr
if typExpr.typ.kind == tyFromExpr:
typExpr.typ.flags.incl tfNonConstExpr
var t = typExpr.typ
if t.kind == tyStatic:
let base = t.skipTypes({tyStatic})
if c.inGenericContext > 0 and base.containsGenericType:
t = makeTypeFromExpr(c, copyTree(typExpr))
t.flags.incl tfNonConstExpr
else:
t = base
result.typ() = makeTypeDesc(c, t)
result.typ() = makeTypeDesc(c, typExpr.typ)
type
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
@@ -612,10 +604,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mArrPut:
result = semArrPut(c, n, flags)
of mAsgn:
case n[0].sym.name.s
of "=", "=copy":
if n[0].sym.name.s == "=":
result = semAsgnOpr(c, n, nkAsgn)
of "=sink":
elif n[0].sym.name.s == "=sink":
result = semAsgnOpr(c, n, nkSinkAsgn)
else:
result = semShallowCopy(c, n, flags)
@@ -654,13 +645,42 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mNewFinalize:
result = semNewFinalize(c, n)
of mDestroy:
result = replaceHookMagic(c, n, 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 mTrace:
result = replaceHookMagic(c, n, 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 mDup:
result = replaceHookMagic(c, n, 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 mWasMoved:
result = replaceHookMagic(c, n, attachedWasMoved)
result = n
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedWasMoved)
if op != nil:
result[0] = newSymNode(op)
let addrExp = newNodeIT(nkHiddenAddr, result[1].info, makePtrType(c, t))
addrExp.add result[1]
result[1] = addrExp
of mUnown:
result = semUnown(c, n)
of mExists, mForall:

View File

@@ -68,10 +68,6 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
let assignment = initExpr[i]
if assignment.kind != nkExprColonExpr:
invalidObjConstr(c, assignment)
elif nfPreventCg in assignment.flags:
# this is an object constructor node generated by the VM and
# this field is in an inactive case branch, just ignore it
discard
elif fieldId == considerQuotedIdent(c, assignment[0]).id:
return assignment
@@ -83,7 +79,7 @@ proc semConstrField(c: PContext, flags: TExprFlags,
if nfSkipFieldChecking in assignment[1].flags:
discard
elif not fieldVisible(c, field):
localError(c.config, assignment[0].info,
localError(c.config, initExpr.info,
"the field '$1' is not accessible." % [field.name.s])
return
@@ -240,7 +236,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
let prevFields = fieldsPresentInBranch(selectedBranch)
let currentFields = fieldsPresentInBranch(i)
localError(c.config, constrCtx.initExpr.info,
("The fields $1 and $2 cannot be initialized together, " &
("The fields '$1' and '$2' cannot be initialized together, " &
"because they are from conflicting branches in the case object.") %
[prevFields, currentFields])
result.status = initConflict
@@ -469,14 +465,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
if t == nil:
return localErrorNode(c, result, "object constructor needs an object type")
when false:
# attempted type inference for generic object types,
# doesn't work since n[0] isn't set and seems underspecified
if t.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
expectedType != nil and expectedType.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
t = expectedType
if t.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
expectedType != nil and expectedType.skipTypes({tyGenericInst,
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
t = expectedType
t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
if t.kind == tyRef:
@@ -519,17 +512,13 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
invalidObjConstr(c, field)
hasError = true
continue
elif nfPreventCg in field.flags:
# this is an object constructor node generated by the VM and
# this field is in an inactive case branch, just ignore it
continue
let id = considerQuotedIdent(c, field[0])
# This node was not processed. There are two possible reasons:
# 1) It was shadowed by a field with the same name on the left
for j in 1..<i:
let prevId = considerQuotedIdent(c, result[j][0])
if prevId.id == id.id:
localError(c.config, field[0].info, errFieldInitTwice % id.s)
localError(c.config, field.info, errFieldInitTwice % id.s)
hasError = true
break
# 2) No such field exists in the constructed type

View File

@@ -11,7 +11,7 @@ import
ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, options, guards, lineinfos, semfold, semdata,
modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
semstrictfuncs, suggestsymdb, pushpoppragmas, lowerings
semstrictfuncs, suggestsymdb, pushpoppragmas
import std/[tables, intsets, strutils, sequtils]
@@ -84,7 +84,6 @@ type
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
isInnerProc: bool
inEnforcedNoSideEffects: bool
unknownRaises: seq[(PSym, TLineInfo)]
currOptions: TOptions
optionsStack: seq[(TOptions, TNoteKinds)]
config: ConfigRef
@@ -125,11 +124,10 @@ proc collectObjectTree(graph: ModuleGraph, n: PNode) =
else:
graph.objectTree[root].add (depthLevel, typ)
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit = false) =
if typ == nil or (sfGeneratedOp in tracked.owner.flags and not explicit):
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
if typ == nil or sfGeneratedOp in tracked.owner.flags:
# don't create type bound ops for anything in a function with a `nodestroy` pragma
# bug #21987
# unless this is an explicit call, bug #24626
return
when false:
let realType = typ.skipTypes(abstractInst)
@@ -214,7 +212,6 @@ proc varDecl(a: PEffects; n: PNode) {.inline.} =
proc skipHiddenDeref(n: PNode): PNode {.inline.} =
result = if n.kind == nkHiddenDeref: n[0] else: n
proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
let n = skipHiddenDeref(n)
if n.kind != nkSym: return
@@ -378,9 +375,7 @@ proc useVar(a: PEffects, n: PNode) =
# If the variable is explicitly marked as .noinit. do not emit any error
a.init.add s.id
elif s.id notin a.init:
if s.kind == skResult and tfRequiresInit in s.typ.flags:
localError(a.config, n.info, "'result' requires explicit initialization")
elif s.typ.requiresInit:
if s.typ.requiresInit:
message(a.config, n.info, warnProveInit, s.name.s)
elif a.leftPartOfAsgn <= 0:
if strictDefs in a.c.features:
@@ -641,8 +636,6 @@ proc importedFromC(n: PNode): bool =
proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
let pragma = s.ast[pragmasPos]
let spec = effectSpec(pragma, wRaises)
if spec.isNil and sfForward in s.flags:
tracked.unknownRaises.add (s, n.info)
mergeRaises(tracked, spec, n)
let tagSpec = effectSpec(pragma, wTags)
@@ -706,7 +699,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} =
assert n.kind == nkEffectList
n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil and n[forbiddenEffects] == nil)
proc isTrivial(caller: PNode): bool {.inline.} =
proc isTrival(caller: PNode): bool {.inline.} =
result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved, mSwap}
proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) =
@@ -715,7 +708,7 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar
let param = if formals != nil and formals.n != nil and argIndex < formals.n.len: formals.n[argIndex].sym else: nil
# assume indirect calls are taken here:
if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and
not isTrivial(caller) and
not isTrival(caller) and
((param != nil and sfEffectsDelayed in param.flags) or laxEffects in tracked.c.config.legacyFeatures):
internalAssert tracked.config, op.n[0].kind == nkEffectList
@@ -887,9 +880,8 @@ proc trackIf(tracked: PEffects, n: PNode) =
setLen(tracked.guards.s, oldFacts)
dec tracked.inIfStmt
proc trackBlock(tracked: PEffects, n: PNode; typ: PType) =
proc trackBlock(tracked: PEffects, n: PNode) =
if n.kind in {nkStmtList, nkStmtListExpr}:
let myBlock = tracked.currentBlock
var oldState = -1
for i in 0..<n.len:
if hasSubnodeWith(n[i], nkBreakStmt):
@@ -901,14 +893,6 @@ proc trackBlock(tracked: PEffects, n: PNode; typ: PType) =
if oldState < 0: oldState = tracked.init.len
track(tracked, n[i])
if oldState > 0: setLen(tracked.init, oldState)
if typ != nil and typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs}:
let last = lastSon(n)
let root = getRoot(last)
if root != nil:
let owner = tracked.scopes.getOrDefault(root.id, -1)
if owner >= 0:
localError(tracked.config, last.info, "'" & renderTree(last) & "' borrows from location '" & root.name.s &
"' which does not live long enough")
else:
track(tracked, n)
@@ -981,7 +965,7 @@ proc checkForSink(tracked: PEffects; n: PNode) =
proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
when defined(nimsuggest):
proc internalMarkCaughtExceptions(tracked: PEffects; q: var SuggestFileSymbolDatabase; info: TLineInfo) =
var si = q.findSymInfoIndex(info, true)
var si = q.findSymInfoIndex(info)
if si != -1:
q.caughtExceptionsSet[si] = true
for w1 in tracked.caughtExceptions.nodes:
@@ -991,25 +975,6 @@ proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s:
if optIdeExceptionInlayHints in tracked.config.globalOptions:
internalMarkCaughtExceptions(tracked, g.suggestSymbols.mgetOrPut(info.fileIndex, newSuggestFileSymbolDatabase(info.fileIndex, true)), info)
proc findHookKind(name: string): (bool, TTypeAttachedOp) =
case name.normalize
of "=wasmoved":
result = (true, attachedWasMoved)
of "=destroy":
result = (true, attachedDestructor)
of "=copy", "=":
result = (true, attachedAsgn)
of "=dup":
result = (true, attachedDup)
of "=sink":
result = (true, attachedSink)
of "=trace":
result = (true, attachedTrace)
of "=deepcopy":
result = (true, attachedDeepCopy)
else:
result = (false, attachedWasMoved)
proc trackCall(tracked: PEffects; n: PNode) =
template gcsafeAndSideeffectCheck() =
if notGcSafe(op) and not importedFromC(a):
@@ -1098,17 +1063,18 @@ proc trackCall(tracked: PEffects; n: PNode) =
checkBounds(tracked, n[1], n[2])
var n = n
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
tracked.owner.kind != skMacro:
var (isHook, opKind) = findHookKind(a.sym.name.s)
if isHook:
var opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
if a.sym.name.s == "=": opKind = attachedAsgn.int
if opKind != -1:
# rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != getAttachedOp(tracked.graph, t, opKind):
createTypeBoundOps(tracked, t, n.info, explicit = true)
# replace builtin hooks with lifted ones
n = replaceHookMagic(tracked.c, n, opKind)
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
createTypeBoundOps(tracked, t, n.info)
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
if op != nil:
n[0].sym = op
if op != nil and op.kind == tyProc:
for i in 1..<min(n.safeLen, op.signatureLen):
@@ -1335,18 +1301,13 @@ proc track(tracked: PEffects, n: PNode) =
let last = lastSon(child)
track(tracked, last)
of nkCaseStmt: trackCase(tracked, n)
of nkWhen: # This should be a "when nimvm" node.
let oldState = tracked.init.len
track(tracked, n[0][1])
tracked.init.setLen(oldState)
track(tracked, n[1][0])
of nkIfStmt, nkIfExpr: trackIf(tracked, n)
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1], n.typ)
of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
of nkWhileStmt:
# 'while true' loop?
inc tracked.currentBlock
if isTrue(n[0]):
trackBlock(tracked, n[1], nil)
trackBlock(tracked, n[1])
else:
# loop may never execute:
let oldState = tracked.init.len
@@ -1537,7 +1498,7 @@ proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg: string, hints: bool;
effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.};
hintsArg: PNode = nil; isForbids: bool = false; unknownRaises: seq[(PSym, TLineInfo)] = @[]) =
hintsArg: PNode = nil; isForbids: bool = false) =
# check that any real exception is listed in 'spec'; mark those as used;
# report any unused exception
var used = initIntSet()
@@ -1554,8 +1515,6 @@ proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg:
pushInfoContext(g.config, spec.info)
var rr = if r.kind == nkRaiseStmt: r[0] else: r
while rr.kind in {nkStmtList, nkStmtListExpr} and rr.len > 0: rr = rr.lastSon
for (s, info) in unknownRaises.items:
message(g.config, info, hintUnknownRaises, s.name.s)
message(g.config, r.info, if emitWarnings: warnEffect else: errGenerated,
renderTree(rr) & " " & msg & typeToString(r.typ))
popInfoContext(g.config)
@@ -1679,9 +1638,6 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
s.kind in {skProc, skFunc, skConverter, skMethod}:
var res = s.ast[resultPos].sym # get result symbol
t.scopes[res.id] = t.currentBlock
if sfNoInit in s.flags:
# marks result "noinit"
incl res.flags, sfNoInit
track(t, body)
@@ -1694,14 +1650,13 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
(t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
(isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
createTypeBoundOps(t, typ, param.info)
if isOutParam(typ) and param.id notin t.init and s.magic == mNone:
if isOutParam(typ) and param.id notin t.init:
message(g.config, param.info, warnProveInit, param.name.s)
if not isEmptyType(s.typ.returnType) and
(s.typ.returnType.requiresInit or s.typ.returnType.skipTypes(abstractInst).kind == tyVar or
strictDefs in c.features) and
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone and
sfNoInit notin s.flags:
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
var res = s.ast[resultPos].sym # get result symbol
if res.id notin t.init and breaksBlock(body) != bsNoReturn:
if tfRequiresInit in s.typ.returnType.flags:
@@ -1713,7 +1668,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
if not isNil(raisesSpec):
let useWarning = s.name.s == "=destroy"
checkRaisesSpec(g, useWarning, raisesSpec, t.exc, "can raise an unlisted exception: ",
hints=on, subtypeRelation, hintsArg=s.ast[0], unknownRaises = t.unknownRaises)
hints=on, subtypeRelation, hintsArg=s.ast[0])
# after the check, use the formal spec:
effects[exceptionEffects] = raisesSpec
else:

View File

@@ -492,8 +492,19 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = tru
incl(result.flags, sfGlobal)
result.options = c.config.options
proc getLineInfo(n: PNode): TLineInfo =
case n.kind
of nkPostfix:
if len(n) > 1:
return getLineInfo(n[1])
of nkAccQuoted, nkPragmaExpr:
if len(n) > 0:
return getLineInfo(n[0])
else:
discard
result = n.info
let info = getLineInfo(n)
if reportToNimsuggest:
let info = getLineInfo(n)
suggestSym(c.graph, info, result, c.graph.usageSym)
proc checkNilable(c: PContext; v: PSym) =
@@ -601,38 +612,11 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
else:
localError(c.config, n.info, "nkDotNode requires 2 children")
proc checkDefineType(c: PContext; v: PSym; t: PType) =
# see semfold.foldDefine for acceptable types
let typeKinds =
case v.magic
of mStrDefine: {tyString, tyCstring}
# this used to be not typechecked, so anything that accepts int nodes for compatbility:
of mIntDefine: {tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyChar, tyEnum}
of mBoolDefine: {tyBool}
of mGenericDefine: {tyString, tyCstring, tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyEnum}
else: raiseAssert("unreachable")
var skipped = abstractVarRange
if v.magic == mGenericDefine:
# no distinct types for generic define
skipped.excl tyDistinct
if t.skipTypes(skipped).kind notin typeKinds:
let name =
case v.magic
of mStrDefine: "strdefine"
of mIntDefine: "intdefine"
of mBoolDefine: "booldefine"
of mGenericDefine: "define"
else: raiseAssert("unreachable")
localError(c.config, v.info, "unsupported type for constant '" & v.name.s &
"' with ." & name & " pragma: " & typeToString(t))
proc setVarType(c: PContext; v: PSym, typ: PType) =
if v.typ != nil and not sameTypeOrNil(v.typ, typ):
localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" &
v.name.s & "': previous type was: " & typeToString(v.typ, preferDesc) &
"; new type is: " & typeToString(typ, preferDesc))
if v.kind == skConst and v.magic in {mGenericDefine, mIntDefine, mStrDefine, mBoolDefine}:
checkDefineType(c, v, typ)
v.typ = typ
proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
@@ -725,33 +709,22 @@ template isLocalSym(sym: PSym): bool =
sym.typ.kind == tyTypeDesc or
sfCompileTime in sym.flags) or
sym.kind in {skProc, skFunc, skIterator} and
sfGlobal notin sym.flags and sym.typ.callConv == ccClosure
sfGlobal notin sym.flags
template isLocalVarSym(n: PNode): bool =
n.kind == nkSym and isLocalSym(n.sym)
proc usesLocalVar(n: PNode): bool =
case n.kind
of nkSym:
result = isLocalSym(n.sym)
of nkCallKinds, nkObjConstr:
result = false
for i in 1 ..< n.len:
if usesLocalVar(n[i]):
result = false
for z in 1 ..< n.len:
if n[z].isLocalVarSym:
return true
elif n[z].kind in nkCallKinds:
if usesLocalVar(n[z]):
return true
of nkTupleConstr, nkPar, nkBracket, nkCurly:
result = false
for i in 0 ..< n.len:
if usesLocalVar(n[i]):
return true
of nkDotExpr, nkCheckedFieldExpr,
nkBracketExpr, nkAddr, nkHiddenAddr,
nkObjDownConv, nkObjUpConv:
result = usesLocalVar(n[0])
of nkHiddenStdConv, nkHiddenSubConv, nkCast, nkExprColonExpr:
result = usesLocalVar(n[1])
else:
result = false
proc globalVarInitCheck(c: PContext, n: PNode) =
if usesLocalVar(n):
if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n):
localError(c.config, n.info, errCannotAssignToGlobal)
const
@@ -1725,14 +1698,12 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
obj.ast[0] = a[0].shallowCopy
if a[0][0].kind == nkPostfix:
obj.ast[0][0] = a[0][0].shallowCopy
obj.ast[0][0][0] = a[0][0][0] # ident "*"
obj.ast[0][0][1] = symNode
else:
obj.ast[0][0] = symNode
obj.ast[0][1] = a[0][1]
of nkPostfix:
obj.ast[0] = a[0].shallowCopy
obj.ast[0][0] = a[0][0] # ident "*"
obj.ast[0][1] = symNode
else: assert(false)
obj.ast[1] = a[1]
@@ -1787,17 +1758,6 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
# check the style here after the pragmas have been processed:
styleCheckDef(c, s)
# compute the type's size and check for illegal recursions:
if a[0].kind == nkPragmaExpr:
let pragmas = a[0][1]
for i in 0 ..< pragmas.len:
if pragmas[i].kind == nkExprColonExpr and
pragmas[i][0].kind == nkIdent and
whichKeyword(pragmas[i][0].ident) == wSize:
if s.typ.kind != tyEnum and sfImportc notin s.flags:
# EventType* {.size: sizeof(uint32).} = enum
# AtomicFlag* {.importc: "atomic_flag", header: "<stdatomic.h>", size: 1.} = object
localError(c.config, pragmas[i].info, "size pragma only allowed for enum types and imported types")
if a[1].kind == nkEmpty:
var x = a[2]
if x.kind in nkCallKinds and nfSem in x.flags:
@@ -2075,27 +2035,14 @@ proc canonType(c: PContext, t: PType): PType =
else:
result = t
proc prevDestructor(c: PContext; op: TTypeAttachedOp; prevOp: PSym; obj: PType; info: TLineInfo) =
var msg = "cannot bind another '" & AttachedOpToStr[op] & "' to: " & typeToString(obj)
if prevOp == nil:
# happens if the destructor was implicitly constructed for a specific instance,
# not the entire generic type
msg.add "; previous declaration was constructed implicitly"
elif sfOverridden notin prevOp.flags:
proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
if sfOverridden notin prevOp.flags:
msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
else:
msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
localError(c.config, info, errGenerated, msg)
proc checkedForDestructor(t: PType): bool =
if tfCheckedForDestructor in t.flags:
return true
# maybe another instance was instantiated, marking the generic root:
let root = genericRoot(t)
if root != nil and tfGenericHasDestructor in root.flags:
return true
result = false
proc whereToBindTypeHook(c: PContext; t: PType): PType =
result = t
while true:
@@ -2129,10 +2076,10 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
discard "forward declared destructor"
elif ao.isNil and not checkedForDestructor(obj):
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
setAttachedOp(c.graph, c.module.position, obj, op, s)
else:
prevDestructor(c, op, ao, obj, n.info)
prevDestructor(c, ao, obj, n.info)
noError = true
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
@@ -2148,17 +2095,13 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
let t = s.typ
var noError = false
template notRefc: bool =
# fixes refc with non-var destructor; cancel warnings (#23156)
c.config.backend == backendJs or
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}
let cond = case op
of attachedWasMoved:
t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
of attachedTrace:
t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar and t[2].kind == tyPointer
of attachedDestructor:
if notRefc:
if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
t.len == 2 and t.returnType == nil
else:
t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
@@ -2173,14 +2116,17 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
if op == attachedDestructor and t.firstParamType.kind == tyVar and
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter")
obj = canonType(c, obj)
let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
discard "forward declared destructor"
elif ao.isNil and not checkedForDestructor(obj):
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
setAttachedOp(c.graph, c.module.position, obj, op, s)
else:
prevDestructor(c, op, ao, obj, n.info)
prevDestructor(c, ao, obj, n.info)
noError = true
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
@@ -2191,7 +2137,7 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
localError(c.config, n.info, errGenerated,
"signature for '=trace' must be proc[T: object](x: var T; env: pointer)")
of attachedDestructor:
if notRefc:
if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
localError(c.config, n.info, errGenerated,
"signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)")
else:
@@ -2271,10 +2217,10 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
let ao = getAttachedOp(c.graph, obj, k)
if ao == s:
discard "forward declared op"
elif ao.isNil and not checkedForDestructor(obj):
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
setAttachedOp(c.graph, c.module.position, obj, k, s)
else:
prevDestructor(c, k, ao, obj, n.info)
prevDestructor(c, ao, obj, n.info)
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
@@ -2363,7 +2309,7 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) =
isInitializer = false
break
var j = 0
while p[j].kind == nkSym and p[j].sym.kind == skParam:
while p[j].sym.kind == skParam:
initializerCall.add val
inc j
if isInitializer:
@@ -2413,7 +2359,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
of nkEmpty:
s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info)
s.flags.incl sfUsed
s.flags.incl sfGenSym
n[namePos] = newSymNode(s)
of nkSym:
s = n[namePos].sym
@@ -2628,18 +2573,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
else:
nil
# semantic checking also needed with importc in case used in VM
let isInlineIterator = isInlineIterator(s.typ)
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType))
# unfortunately we cannot skip this step when in 'system.compiles'
# context as it may even be evaluated in 'system.compiles':
if isInlineIterator and s.typ.callConv == ccClosure:
# iterators without explicit callconvs are lifted to closure,
# we need to add a result symbol for them
maybeAddResult(c, s, n)
trackProc(c, s, s.ast[bodyPos])
else:
if (s.typ.returnType != nil and s.kind != skIterator):
@@ -2686,7 +2622,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
proc determineType(c: PContext, s: PSym) =
if s.typ != nil: return
#if s.magic != mNone: return
#if s.ast.isNil: return
if s.ast.isNil and sfForward notin s.flags:
globalError(c.config, s.info, errIllFormedAstX, "symbol of kind " & $s.kind & " has no implementation")
discard semProcAux(c, s.ast, s.kind, {})
proc semIterator(c: PContext, n: PNode): PNode =
@@ -2711,7 +2648,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
incl(s.typ.flags, tfCapturesEnv)
else:
s.typ.callConv = ccInline
if result[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
if n[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
localError(c.config, n.info, errImplOfXexpected % s.name.s)
if optOwnedRefs in c.config.globalOptions and result.typ != nil:
result.typ() = makeVarType(c, result.typ, tyOwned)
@@ -2825,24 +2762,9 @@ proc recursiveSetFlag(n: PNode, flag: TNodeFlag) =
for i in 0..<n.safeLen: recursiveSetFlag(n[i], flag)
incl(n.flags, flag)
proc enterPragmaBlock(c: PContext): POptionEntry =
result = POptionEntry(options: c.config.options,
notes: c.config.notes,
warningAsErrors: c.config.warningAsErrors,
features: c.features)
proc leavePragmaBlock(c: PContext, p: POptionEntry) =
c.config.options = p.options
c.config.notes = p.notes
c.config.warningAsErrors = p.warningAsErrors
c.features = p.features
proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
checkSonsLen(n, 2, c.config)
let pragmaList = n[0]
let oldOptionEntry = enterPragmaBlock(c)
pragma(c, nil, pragmaList, exprPragmas, isStatement = true)
var inUncheckedAssignSection = 0
@@ -2867,8 +2789,6 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
of wNoRewrite: recursiveSetFlag(result, nfNoRewrite)
else: discard
leavePragmaBlock(c, oldOptionEntry)
proc semStaticStmt(c: PContext, n: PNode): PNode =
#echo "semStaticStmt"
#writeStackTrace()

View File

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

View File

@@ -38,27 +38,21 @@ const
errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
proc reusePrev(prev: PType): bool {.inline.} =
# only overwrite `prev` if it is a forward type, partial object or magic type
result = prev != nil and (prev.kind == tyForward or (prev.sym != nil and
# partial object marks sym as `sfForward`
(sfForward in prev.sym.flags or prev.sym.magic != mNone)))
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, son: sink PType): PType =
if reusePrev(prev):
if prev == nil or prev.kind == tyGenericBody:
result = newTypeS(kind, c, son)
else:
result = prev
result.setSon(son)
if result.kind == tyForward: result.kind = kind
else:
result = newTypeS(kind, c, son)
#if kind == tyError: result.flags.incl tfCheckedForDestructor
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
if reusePrev(prev):
if prev == nil or prev.kind == tyGenericBody:
result = newTypeS(kind, c)
else:
result = prev
if result.kind == tyForward: result.kind = kind
else:
result = newTypeS(kind, c)
proc newConstraint(c: PContext, k: TTypeKind): PType =
result = newTypeS(tyBuiltInTypeClass, c)
@@ -415,12 +409,8 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
result = makeRangeWithStaticExpr(c, e.typ.n)
elif e.kind in {nkIntLit..nkUInt64Lit}:
if e.intVal < 0:
if e.kind in {nkIntLit..nkInt64Lit}:
localError(c.config, n.info,
"Array length can't be negative, but was " & $e.intVal)
else:
localError(c.config, n.info,
"Array length can't exceed its maximum value (9223372036854775807), but was " & $cast[BiggestUInt](e.intVal))
localError(c.config, n.info,
"Array length can't be negative, but was " & $e.intVal)
result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc):
if e.typ.kind == tyStatic:
@@ -559,7 +549,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
styleCheckDef(c, a[j].info, field)
onDef(field.info, field)
if result.n.len == 0: result.n = nil
if isRecursiveStructuralType(result):
if isTupleRecursive(result):
localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(result))
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
@@ -790,7 +780,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
case typ.kind
of shouldChckCovered:
chckCovered = true
of tyError:
of tyFloat..tyFloat128, tyError:
discard
of tyRange:
if skipTypes(typ.elementType, abstractInst).kind in shouldChckCovered:
@@ -798,8 +788,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
of tyForward:
errorUndeclaredIdentifier(c, n[0].info, typ.sym.name.s)
elif not isOrdinalType(typ):
localError(c.config, n[0].info, "selector must be of an ordinal type")
localError(c.config, n[0].info, "selector must be of an ordinal type, float")
if firstOrd(c.config, typ) != 0:
localError(c.config, n.info, "low(" & $a[0].sym.name.s &
") must be 0 for discriminant")
@@ -1109,9 +1098,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
let t = newTypeS(tySink, c, result)
result = t
else: discard
if result.kind == tyRef and
c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
tfTriggersCompileTime notin result.flags:
if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
result.flags.incl tfHasAsgn
proc findEnforcedStaticType(t: PType): PType =
@@ -1298,14 +1285,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType[i] = lifted
result = paramType
result.last.shouldHaveMeta
if paramType.isConcept:
return addImplicitGeneric(c, paramType, paramTypId, info, genericParams, paramName)
else:
let liftBody = recurse(paramType.skipModifier, true)
if liftBody != nil:
result = liftBody
result.flags.incl tfHasMeta
#result.shouldHaveMeta
let liftBody = recurse(paramType.skipModifier, true)
if liftBody != nil:
result = liftBody
result.flags.incl tfHasMeta
#result.shouldHaveMeta
of tyGenericInvocation:
result = nil
@@ -1319,6 +1304,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
# this may happen for proc type appearing in a type section
# before one of its param types
return
if body.last.kind == tyUserTypeClass:
let expanded = instGenericContainer(c, info, paramType,
allowMetaTypes = true)
@@ -1475,8 +1461,6 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if isType: localError(c.config, a.info, "':' expected")
if kind in {skTemplate, skMacro}:
typ = newTypeS(tyUntyped, c)
elif isRecursiveStructuralType(typ):
localError(c.config, a[^2].info, errIllegalRecursionInTypeX % typeToString(typ))
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
continue
@@ -1513,10 +1497,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
addParamOrResult(c, arg, kind)
styleCheckDef(c, a[j].info, arg)
onDef(a[j].info, arg)
if a[j].kind == nkPragmaExpr:
a[j][0] = newSymNode(arg)
else:
a[j] = newSymNode(arg)
a[j] = newSymNode(arg)
var r: PType = nil
if n[0].kind != nkEmpty:
@@ -1540,9 +1521,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if r != nil:
# turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil':
if isRecursiveStructuralType(r):
localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(r))
elif skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
if kind notin {skMacro, skTemplate} and r.kind in {tyTyped, tyUntyped}:
localError(c.config, n[0].info, "return type '" & typeToString(r) &
"' is only valid for macros and templates")
@@ -1688,11 +1667,6 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
var err = "cannot instantiate "
err.addTypeHeader(c.config, t)
err.add "\ngot: <$1>\nbut expected: <$2>" % [describeArgs(c, n), describeArgs(c, t.n, 0)]
if m.firstMismatch.kind == kTypeMismatch and m.firstMismatch.arg < n.len:
let nArg = n[m.firstMismatch.arg]
if nArg.kind in nkSymChoices:
err.add "\n"
err.add ambiguousIdentifierMsg(nArg)
localError(c.config, n.info, errGenerated, err)
return newOrPrevType(tyError, prev, c)
@@ -1729,7 +1703,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
# special check for generic object with
# generic/partial specialized parent
let tx = result.skipTypes(abstractPtrs, 50)
if tx.isNil or isRecursiveStructuralType(tx):
if tx.isNil or isTupleRecursive(tx):
localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0]))
return errorType(c)
if tx != result and tx.kind == tyObject:
@@ -1750,10 +1724,10 @@ proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
else:
result = nil
proc fixupTypeOf(c: PContext, prev: PType, typ: PType) =
proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
if prev != nil:
let result = newTypeS(tyAlias, c)
result.rawAddSon typ
result.rawAddSon typExpr.typ
result.sym = prev.sym
if prev.kind != tyGenericBody:
assignType(prev, result)
@@ -1893,7 +1867,7 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
x.add(operand.copyTreeWithoutNode(p))
# recursion assures that this works for multiple macro annotations too:
var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
if r != nil and (r.typ == nil or r.typ.kind != tyFromExpr):
if r != nil:
doAssert r[0].kind == nkSym
let m = r[0].sym
case m.kind
@@ -1945,19 +1919,12 @@ proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let ex = semExprWithType(c, n, {efInTypeof})
let t = semExprWithType(c, n, {efInTypeof})
closeScope(c)
result = ex.typ
fixupTypeOf(c, prev, t)
result = t.typ
if result.kind == tyFromExpr:
result.flags.incl tfNonConstExpr
elif result.kind == tyStatic:
let base = result.skipTypes({tyStatic})
if c.inGenericContext > 0 and base.containsGenericType:
result = makeTypeFromExpr(c, copyTree(ex))
result.flags.incl tfNonConstExpr
else:
result = base
fixupTypeOf(c, prev, result)
proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
@@ -1970,19 +1937,12 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
m = mode.intVal
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let ex = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
closeScope(c)
result = ex.typ
fixupTypeOf(c, prev, t)
result = t.typ
if result.kind == tyFromExpr:
result.flags.incl tfNonConstExpr
elif result.kind == tyStatic:
let base = result.skipTypes({tyStatic})
if c.inGenericContext > 0 and base.containsGenericType:
result = makeTypeFromExpr(c, copyTree(ex))
result.flags.incl tfNonConstExpr
else:
result = base
fixupTypeOf(c, prev, result)
proc semTypeIdent(c: PContext, n: PNode): PSym =
if n.kind == nkSym:

View File

@@ -28,7 +28,7 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
if t.kind in tyTypeClasses: discard
elif t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}:
localError(conf, info, "type 'var var' is not allowed")
elif computeSize(conf, t) == szIllegalRecursion or isRecursiveStructuralType(t):
elif computeSize(conf, t) == szIllegalRecursion or isTupleRecursive(t):
localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'")
proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
@@ -68,8 +68,8 @@ type
TReplTypeVars* = object
c*: PContext
typeMap*: LayeredIdTable # map PType to PType
symMap*: SymMapping # map PSym to PSym
localCache*: TypeMapping # local cache for remembering already replaced
symMap*: SymMapping # map PSym to PSym
localCache*: TypeMapping # local cache for remembering already replaced
# types during instantiation of meta types
# (they are not stored in the global cache)
info*: TLineInfo
@@ -80,7 +80,7 @@ type
owner*: PSym # where this instantiation comes from
recursionLimit: int
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode
@@ -95,8 +95,8 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code
debug t
writeStackTrace()
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType, isInstValue = false): PType =
result = replaceTypeVarsTAux(cl, t, isInstValue)
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
result = replaceTypeVarsTAux(cl, t)
checkMetaInvariants(cl, result)
proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
@@ -344,7 +344,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
#[
We cannot naively check for symbol recursions, because otherwise
object types A, B would share their fields!
object types A, B whould share their fields!
import tables
@@ -481,7 +481,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
return
let bbody = last body
var newbody = replaceTypeVarsT(cl, bbody, isInstValue = true)
var newbody = replaceTypeVarsT(cl, bbody)
cl.skipTypedesc = oldSkipTypedesc
newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
result.flags = result.flags + newbody.flags - tfInstClearedFlags
@@ -578,7 +578,7 @@ proc propagateFieldFlags(t: PType, n: PNode) =
propagateFieldFlags(t, son)
else: discard
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType =
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
template bailout =
if (t.sym == nil) or (t.sym != nil and sfGeneratedType in t.sym.flags):
# In the first case 't.sym' can be 'nil' if the type is a ref/ptr, see
@@ -608,13 +608,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
result = t
if t == nil: return
var et = t
if t.isConcept:
et = t.reduceToBase
const lookupMetas = {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses - {tyAnything}
if et.kind in lookupMetas or
(et.kind == tyAnything and tfRetType notin et.flags):
let lookup = cl.typeMap.lookup(et)
if t.kind in lookupMetas or
(t.kind == tyAnything and tfRetType notin t.flags):
let lookup = cl.typeMap.lookup(t)
if lookup != nil: return lookup
case t.kind
@@ -715,17 +712,13 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
propagateToOwner(result, result.last)
else:
if containsGenericType(t) or
# nominal types as direct generic instantiation values
# are re-instantiated even if they don't contain generic fields
(isInstValue and (t.kind in {tyDistinct, tyObject} or isRefPtrObject(t))):
if containsGenericType(t):
#if not cl.allowMetaTypes:
bailout()
result = instCopyType(cl, t)
result.size = -1 # needs to be recomputed
#if not cl.allowMetaTypes:
cl.localCache[t.itemId] = result
let propagateInstValue = isInstValue and isRefPtrObject(t)
for i, resulti in result.ikids:
if resulti != nil:
@@ -735,7 +728,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
typeToString(result[i], preferDesc) &
"' inside of type definition: '" &
t.owner.name.s & "'; Maybe generic arguments are missing?")
var r = replaceTypeVarsT(cl, resulti, isInstValue = propagateInstValue)
var r = replaceTypeVarsT(cl, resulti)
if result.kind == tyObject:
# carefully coded to not skip the precious tyGenericInst:
let r2 = r.skipTypes({tyAlias, tySink, tyOwned})

View File

@@ -20,7 +20,6 @@ import std/[intsets, strutils, tables]
when defined(nimPreviewSlimSystem):
import std/assertions
type
MismatchKind* = enum
kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded,
@@ -95,7 +94,6 @@ type
trNoCovariance
trBindGenericParam # bind tyGenericParam even with trDontBind
trIsOutParam
trCheckGeneric
TTypeRelFlags* = set[TTypeRelFlag]
@@ -104,7 +102,7 @@ const
isNilConversion = isConvertible # maybe 'isIntConv' fits better?
maxInheritancePenalty = high(int) div 2
proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true; isGenericInstance = false)
proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true)
proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
proc initCandidateAux(ctx: PContext,
@@ -299,9 +297,9 @@ proc checkGeneric(a, b: TCandidate): int =
var winner = 0
for aai, bbi in underspecifiedPairs(aa, bb, 1):
var ma = newCandidate(c, bbi)
let tra = typeRel(ma, bbi, aai, {trDontBind, trCheckGeneric})
let tra = typeRel(ma, bbi, aai, {trDontBind})
var mb = newCandidate(c, aai)
let trb = typeRel(mb, aai, bbi, {trDontBind, trCheckGeneric})
let trb = typeRel(mb, aai, bbi, {trDontBind})
if tra == isGeneric and trb in {isNone, isInferred, isInferredConvertible}:
if winner == -1: return 0
winner = 1
@@ -365,8 +363,6 @@ proc sumGeneric(t: PType): int =
result += sumGeneric(a)
break
else:
if t.isConcept:
result += t.reduceToBase.conceptBody.len
break
proc complexDisambiguation(a, b: PType): int =
@@ -599,6 +595,45 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
else: result = isIntConv
else: result = isNone
proc reduceToBase(f: PType): PType =
#[
Returns the lowest order (most general) type that that is compatible with the input.
E.g.
A[T] = ptr object ... A -> ptr object
A[N: static[int]] = array[N, int] ... A -> array
]#
case f.kind:
of tyGenericParam:
if f.len <= 0 or f.skipModifier == nil:
result = f
else:
result = reduceToBase(f.skipModifier)
of tyGenericInvocation:
result = reduceToBase(f.baseClass)
of tyCompositeTypeClass, tyAlias:
if not f.hasElementType or f.elementType == nil:
result = f
else:
result = reduceToBase(f.elementType)
of tyGenericInst:
result = reduceToBase(f.skipModifier)
of tyGenericBody:
result = reduceToBase(f.typeBodyImpl)
of tyUserTypeClass:
if f.isResolvedUserTypeClass:
result = f.base # ?? idk if this is right
else:
result = f.skipModifier
of tyStatic, tyOwned, tyVar, tyLent, tySink:
result = reduceToBase(f.base)
of tyInferred:
# This is not true "After a candidate type is selected"
result = reduceToBase(f.base)
of tyRange:
result = f.elementType
else:
result = f
proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
if fGenericOrigin != nil and last.kind == tyGenericInst and
last.kidsLen-1 == fGenericOrigin.kidsLen:
@@ -607,22 +642,12 @@ proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
if x == nil:
put(c, fGenericOrigin[i], last[i])
proc isGenericObjectOf(f, a: PType): bool =
## checks if `f` is an unparametrized generic type
## that `a` is an instance of
if not (f.sym != nil and f.sym.typ.kind == tyGenericBody):
# covers the case where `f` is the last child (body) of the `tyGenericBody`
return false
let aRoot = genericRoot(a)
# use sym equality to check if the `tyGenericBody` types are equal
result = aRoot != nil and f.sym == aRoot.sym
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
var t = a
assert t.kind == tyObject
var depth = 0
var last = a
while t != nil and not (sameObjectTypes(f, t) or isGenericObjectOf(f, t)):
while t != nil and not sameObjectTypes(f, t):
if t.kind != tyObject: # avoid entering generic params etc
return -1
t = t.baseClass
@@ -1142,24 +1167,6 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
else:
return false
proc enterConceptMatch(c: var TCandidate; f,a: PType, flags: TTypeRelFlags): TTypeRelation =
var
conceptFlags: set[MatchFlags] = {}
container: PType = nil
concpt = f
if concpt.kind != tyConcept:
container = concpt
concpt = container.reduceToBase
if trDontBind in flags:
conceptFlags.incl mfDontBind
if trCheckGeneric in flags:
conceptFlags.incl mfCheckGeneric
let mres = concepts.conceptMatch(c.c, concpt, a, c.bindings, container, flags = conceptFlags)
if mres:
isGeneric
else:
isNone
when false:
proc maxNumericType(prev, candidate: PType): PType =
let c = candidate.skipTypes({tyRange})
@@ -1234,10 +1241,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
else:
var candidate = f
let fType = f.skipTypes({tySink})
case fType.kind
case f.kind
of tyGenericParam:
var prev = lookup(c.bindings, fType)
var prev = lookup(c.bindings, f)
if prev != nil: candidate = prev
of tyFromExpr:
let computedType = tryResolvingStaticExpr(c, f.n).typ
@@ -1447,8 +1453,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
return isNone
if fRange.rangeHasUnresolvedStatic:
if (aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange) or
(aRange.kind == tyRange and aRange.rangeHasUnresolvedStatic):
if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange:
return
return inferStaticsInRange(c, fRange, a)
elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
@@ -1541,14 +1546,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
reduceToBase(a)
if effectiveArgType.kind == tyObject:
if sameObjectTypes(f, effectiveArgType):
if tfFinal notin f.flags:
inc c.inheritancePenalty, ord(c.inheritancePenalty < 0)
c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
result = isEqual
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
elif trIsOutParam notin flags:
let depth = isObjectSubtype(c, effectiveArgType, f, nil)
if depth > 0:
inc c.inheritancePenalty, depth + ord(c.inheritancePenalty < 0)
c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil)
if c.inheritancePenalty > 0:
result = isSubtype
of tyDistinct:
a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
@@ -1568,11 +1571,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
# set['a'..'z'] and set[char] have different representations
result = isNone
else:
if result >= isConvertible:
# but we can convert individual elements of the constructor
result = isConvertible
else:
result = isNone
# but we can convert individual elements of the constructor
result = isConvertible
of tyPtr, tyRef:
a = reduceToBase(a)
if a.kind == f.kind:
@@ -1671,10 +1671,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
let roota = if skipBoth or deptha > depthf: a.skipGenericAlias else: a
let rootf = if skipBoth or depthf > deptha: f.skipGenericAlias else: f
if f.isConcept:
result = enterConceptMatch(c, rootf, roota, flags)
elif a.kind == tyGenericInst:
if a.kind == tyGenericInst:
if roota.base == rootf.base:
let nextFlags = flags + {trNoCovariance}
var hasCovariance = false
@@ -1723,8 +1721,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
if aAsObject.kind == tyObject and trIsOutParam notin flags:
let baseType = aAsObject.base
if baseType != nil:
if tfFinal notin aAsObject.flags:
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
let ret = typeRel(c, f, baseType, flags)
return if ret in {isEqual,isGeneric}: isSubtype else: ret
@@ -1745,7 +1742,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
var x = a.skipGenericAlias
if x.kind == tyGenericParam and x.len > 0:
x = x.last
let concpt = f.reduceToBase
let concpt = f[0].skipTypes({tyGenericBody})
var preventHack = concpt.kind == tyConcept
if x.kind == tyOwned and f[0].kind != tyOwned:
preventHack = true
@@ -1765,10 +1762,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
let tr = typeRel(c, f[i], x[i], flags)
if tr <= isSubtype: return
result = isGeneric
let impl = last(f[0])
if impl.kind == tyObject and tfFinal notin impl.flags:
# match non-invocation case
inc c.inheritancePenalty, 0 + int(c.inheritancePenalty < 0)
elif x.kind == tyGenericInst and f[0] == x[0] and
x.len - 1 == f.len:
for i in 1..<f.len:
@@ -1778,8 +1771,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
# Workaround for regression #4589
if f[i].kind != tyTypeDesc: return
result = isGeneric
elif concpt.kind == tyConcept:
result = enterConceptMatch(c, f, x, flags)
elif x.kind == tyGenericInst and concpt.kind == tyConcept:
result = if concepts.conceptMatch(c.c, concpt, x, c.bindings, f): isGeneric
else: isNone
else:
let genericBody = f[0]
var askip = skippedNone
@@ -1787,7 +1781,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
let aobj = x.skipToObject(askip)
let fobj = genericBody.last.skipToObject(fskip)
result = typeRel(c, genericBody, x, flags)
if result != isNone and concpt.kind != tyConcept:
if result != isNone:
# see tests/generics/tgeneric3.nim for an example that triggers this
# piece of code:
#
@@ -1824,8 +1818,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
depth = -1
if depth >= 0:
if aobj.kind == tyObject and tfFinal notin aobj.flags:
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
# bug #4863: We still need to bind generic alias crap, so
# we cannot return immediately:
result = if depth == 0: isGeneric else: isSubtype
@@ -1851,10 +1844,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
if c.inheritancePenalty > -1:
minInheritance = min(minInheritance, c.inheritancePenalty)
result = x
c.inheritancePenalty = oldInheritancePenalty
if result >= isIntConv:
if minInheritance < maxInheritancePenalty:
inc c.inheritancePenalty, minInheritance + ord(c.inheritancePenalty < 0)
c.inheritancePenalty = oldInheritancePenalty + minInheritance
if result > isGeneric: result = isGeneric
bindingRet result
else:
@@ -1876,12 +1868,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
let target = f.genericHead
let targetKind = target.kind
var effectiveArgType = reduceToBase(a)
# the skipped child of tyBuiltInTypeClass can be structured differently,
# newConstraint constructs them with no children
let typeClassArg = effectiveArgType.kind == tyBuiltInTypeClass
effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
if targetKind == effectiveArgType.kind:
if not typeClassArg and effectiveArgType.isEmptyContainer:
if effectiveArgType.isEmptyContainer:
return isNone
if targetKind == tyProc:
if target.flags * {tfIterator} != effectiveArgType.flags * {tfIterator}:
@@ -1916,7 +1905,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
else:
result = isNone
of tyConcept:
result = enterConceptMatch(c, f, a, flags)
if a.kind == tyConcept and sameType(f, a):
result = isGeneric
else:
result = if concepts.conceptMatch(c.c, f, a, c.bindings, nil): isGeneric
else: isNone
of tyCompositeTypeClass:
considerPreviousT:
let roota = a.skipGenericAlias
@@ -2188,8 +2181,6 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
# keep varness
if arg.typ != nil and arg.typ.kind == tyVar:
result.typ() = toVar(result.typ, tyVar, c.idgen)
# copy the tfVarIsPtr flag
result.typ.flags = arg.typ.flags
else:
result.typ() = result.typ.skipTypes({tyVar})
@@ -2299,8 +2290,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
# for generic type converters we need to check 'src <- a' before
# 'f <- dest' in order to not break the unification:
# see tests/tgenericconverter:
var convMatch = newCandidate(c, src)
let srca = typeRel(convMatch, src, a)
let srca = typeRel(m, src, a)
if srca notin {isEqual, isGeneric, isSubtype}: continue
# What's done below matches the logic in ``matchesAux``
@@ -2312,7 +2302,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
let destIsGeneric = containsGenericType(dest)
if destIsGeneric:
dest = generateTypeInstance(c, convMatch.bindings, arg, dest)
dest = generateTypeInstance(c, m.bindings, arg, dest)
let fdest = typeRel(m, f, dest)
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
# can't fully mark used yet, may not be used in final call
@@ -2328,8 +2318,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
# it is correct
var param: PNode = nil
if srca == isSubtype:
# convMatch used here to use its bindings to instantiate subtype:
param = implicitConv(nkHiddenSubConv, src, copyTree(arg), convMatch, c)
param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c)
elif src.kind in {tyVar}:
# Analyse the converter return type.
param = newNodeIT(nkHiddenAddr, arg.info, s.typ.firstParamType)
@@ -2441,6 +2430,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
let oldInheritancePenalty = m.inheritancePenalty
var r = typeRel(m, f, a)
# This special typing rule for macros and templates is not documented
# anywhere and breaks symmetry. It's hard to get rid of though, my
# custom seqs example fails to compile without this:

View File

@@ -35,7 +35,7 @@
import prefixmatches, suggestsymdb
from wordrecg import wDeprecated, wError, wAddr, wYield
import std/[algorithm, sets, parseutils, os]
import std/[algorithm, sets, parseutils, tables]
when defined(nimsuggest):
import pathutils # importer
@@ -43,12 +43,6 @@ when defined(nimsuggest):
const
sep = '\t'
type
ImportContext = object
isMultiImport: bool # True if we're in a [...] context
baseDir: string # e.g., "folder/" in "import folder/[..."
partialModule: string # The actual module name being typed
#template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
template origModuleName(m: PSym): string = m.name.s
@@ -624,43 +618,41 @@ proc ensureIdx[T](x: var T, y: int) =
proc ensureSeq[T](x: var seq[T]) =
if x == nil: newSeq(x, 0)
proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true; isGenericInstance=false) {.inline.} =
proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
## misnamed: should be 'symDeclared'
let conf = g.config
when defined(nimsuggest):
if optIdeExceptionInlayHints in conf.globalOptions or not isGenericInstance:
g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl, isGenericInstance: isGenericInstance), optIdeExceptionInlayHints in g.config.globalOptions
g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl), optIdeExceptionInlayHints in g.config.globalOptions
if not isGenericInstance:
if conf.suggestVersion == 0:
if s.allUsages.len == 0:
s.allUsages = @[info]
else:
s.addNoDup(info)
if conf.suggestVersion == 0:
if s.allUsages.len == 0:
s.allUsages = @[info]
else:
s.addNoDup(info)
if conf.ideCmd == ideUse:
findUsages(g, info, s, usageSym)
elif conf.ideCmd == ideDef:
findDefinition(g, info, s, usageSym)
elif conf.ideCmd == ideDus and s != nil:
if isTracked(info, conf.m.trackPos, s.name.s.len):
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
findUsages(g, info, s, usageSym)
elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex:
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
elif conf.ideCmd == ideOutline and isDecl:
# if a module is included then the info we have is inside the include and
# we need to walk up the owners until we find the outer most module,
# which will be the last skModule prior to an skPackage.
var
parentFileIndex = info.fileIndex # assume we're in the correct module
parentModule = s.owner
while parentModule != nil and parentModule.kind == skModule:
parentFileIndex = parentModule.info.fileIndex
parentModule = parentModule.owner
if conf.ideCmd == ideUse:
findUsages(g, info, s, usageSym)
elif conf.ideCmd == ideDef:
findDefinition(g, info, s, usageSym)
elif conf.ideCmd == ideDus and s != nil:
if isTracked(info, conf.m.trackPos, s.name.s.len):
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
findUsages(g, info, s, usageSym)
elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex:
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
elif conf.ideCmd == ideOutline and isDecl:
# if a module is included then the info we have is inside the include and
# we need to walk up the owners until we find the outer most module,
# which will be the last skModule prior to an skPackage.
var
parentFileIndex = info.fileIndex # assume we're in the correct module
parentModule = s.owner
while parentModule != nil and parentModule.kind == skModule:
parentFileIndex = parentModule.info.fileIndex
parentModule = parentModule.owner
if parentFileIndex == conf.m.trackPos.fileIndex:
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
if parentFileIndex == conf.m.trackPos.fileIndex:
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
var pragmaNode: PNode
@@ -704,28 +696,26 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) =
else:
inc i
proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true; isGenericInstance = false) =
if not isGenericInstance:
let conf = c.config
incl(s.flags, sfUsed)
if s.kind == skEnumField and s.owner != nil:
incl(s.owner.flags, sfUsed)
if sfDeprecated in s.owner.flags:
proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true) =
let conf = c.config
incl(s.flags, sfUsed)
if s.kind == skEnumField and s.owner != nil:
incl(s.owner.flags, sfUsed)
if sfDeprecated in s.owner.flags:
warnAboutDeprecated(conf, info, s)
if {sfDeprecated, sfError} * s.flags != {}:
if sfDeprecated in s.flags:
if not (c.lastTLineInfo.line == info.line and
c.lastTLineInfo.col == info.col):
warnAboutDeprecated(conf, info, s)
if {sfDeprecated, sfError} * s.flags != {}:
if sfDeprecated in s.flags:
if not (c.lastTLineInfo.line == info.line and
c.lastTLineInfo.col == info.col):
warnAboutDeprecated(conf, info, s)
c.lastTLineInfo = info
c.lastTLineInfo = info
if sfError in s.flags: userError(conf, info, s)
if sfError in s.flags: userError(conf, info, s)
when defined(nimsuggest):
suggestSym(c.graph, info, s, c.graph.usageSym, isDecl = false, isGenericInstance = isGenericInstance)
if not isGenericInstance:
if checkStyle:
styleCheckUse(c, info, s)
markOwnerModuleAsUsed(c, s)
suggestSym(c.graph, info, s, c.graph.usageSym, false)
if checkStyle:
styleCheckUse(c, info, s)
markOwnerModuleAsUsed(c, s)
proc safeSemExpr*(c: PContext, n: PNode): PNode =
# use only for idetools support!
@@ -756,123 +746,6 @@ proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
let prefix = if c.config.m.trackPosAttached: nil else: n
suggestEverything(c, n, prefix, outputs)
proc extractImportContextFromAst(n: PNode, cursorCol: int): ImportContext =
result = ImportContext()
if n.kind != nkImportStmt: return
for child in n:
case child.kind
of nkIdent:
# Single import, e.g. import foo
if child.info.col <= cursorCol:
result.baseDir = ""
result.partialModule = child.ident.s
result.isMultiImport = false
of nkInfix:
# Directory or multi-import, e.g. import std/[os, strutils]
if child.len == 3 and child[0].kind == nkIdent and child[0].ident.s == "/":
let dir = child[1].ident.s
if child[2].kind == nkBracket:
result.baseDir = dir
result.isMultiImport = true
for modNode in child[2]:
if modNode.kind == nkIdent and modNode.info.col <= cursorCol:
result.partialModule = modNode.ident.s
elif child[2].kind == nkIdent:
if child[2].info.col <= cursorCol:
result.baseDir = dir
result.partialModule = child[2].ident.s
result.isMultiImport = false
else:
discard
proc findModuleFile(c: PContext, partialPath: string): seq[string] =
result = @[]
let currentModuleDir = parentDir(toFullPath(c.config, FileIndex(c.module.position)))
proc tryAddModule(path, baseName: string) =
if fileExists(path & ".nim"):
result.add(baseName)
proc addModulesFromDir(dir, file: string; result: var seq[string]) =
if dirExists(dir):
for kind, path in walkDir(dir):
if kind in {pcFile, pcDir}:
let (_, name, ext) = splitFile(path)
if kind == pcFile:
if ext == ".nim" and name.startsWith(file):
result.add(name)
proc collectImportModulesFromDir(dir: string, result: var seq[string]) =
for kind, path in walkDir(dir):
if kind in {pcFile, pcDir}:
let (_, name, ext) = splitFile(path)
if kind == pcFile:
if ext == ".nim" and name.startsWith(partialPath):
result.add(name)
else:
if name.startsWith(partialPath):
result.add(name)
if '/' in partialPath:
let parts = partialPath.split('/')
let dir = parts[0]
let file = parts[1]
addModulesFromDir(currentModuleDir / dir, file, result)
for searchPath in c.config.searchPaths:
let searchDir = searchPath.string / dir
addModulesFromDir(searchDir, file, result)
else:
collectImportModulesFromDir(currentModuleDir, result)
for searchPath in c.config.searchPaths:
collectImportModulesFromDir(searchPath.string, result)
proc suggestModuleNames(c: PContext, n: PNode) =
var suggestions: Suggestions = @[]
let partialPath = if n.kind == nkIdent: n.ident.s else: ""
proc addModuleSuggestion(path: string) =
var suggest = Suggest(
section: ideSug,
qualifiedPath: @[path],
name: addr path,
filePath: path,
line: n.info.line.int,
column: n.info.col.int,
doc: "",
quality: 100,
contextFits: true,
prefix: if partialPath.len > 0: prefixMatch(path, partialPath)
else: PrefixMatch.None,
symkind: byte skModule
)
suggestions.add(suggest)
let importCtx = extractImportContextFromAst(n, c.config.m.trackPos.col)
var searchPath = ""
if importCtx.baseDir.len > 0:
searchPath = importCtx.baseDir & "/"
let possibleModules = findModuleFile(c, searchPath & importCtx.partialModule)
for moduleName in possibleModules:
if moduleName != c.module.name.s:
addModuleSuggestion(moduleName)
produceOutput(suggestions, c.config)
suggestQuit()
proc findImportStmtOnLine(n: PNode, line: uint16): PNode =
if n.kind in {nkImportStmt, nkFromStmt} and n.info.line == line:
return n
for i in 0..<n.safeLen:
let res = findImportStmtOnLine(n[i], line)
if res != nil: return res
return nil
template trySuggestModuleNames*(c: PContext, n: PNode) =
if c.config.ideCmd == ideSug:
let importNode = findImportStmtOnLine(n, c.config.m.trackPos.line)
if importNode != nil:
suggestModuleNames(c, importNode)
proc suggestExprNoCheck*(c: PContext, n: PNode) =
# This keeps semExpr() from coming here recursively:
if c.compilesContextId > 0: return
@@ -901,7 +774,7 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) =
if outputs.len > 0 and c.config.ideCmd in {ideSug, ideCon, ideDef}:
produceOutput(outputs, c.config)
suggestQuit()
proc suggestExpr*(c: PContext, n: PNode) =
if exactEquals(c.config.m.trackPos, n.info): suggestExprNoCheck(c, n)

View File

@@ -16,7 +16,6 @@ type
caughtExceptions*: seq[PType]
caughtExceptionsSet*: bool
isDecl*: bool
isGenericInstance*: bool
SuggestFileSymbolDatabase* = object
lineInfo*: seq[TinyLineInfo]
@@ -24,7 +23,6 @@ type
caughtExceptions*: seq[seq[PType]]
caughtExceptionsSet*: PackedBoolArray
isDecl*: PackedBoolArray
isGenericInstance*: PackedBoolArray
fileIndex*: FileIndex
trackCaughtExceptions*: bool
isSorted*: bool
@@ -84,11 +82,6 @@ proc getSymInfoPair*(s: SuggestFileSymbolDatabase; idx: int): SymInfoPair =
s.caughtExceptionsSet[idx]
else:
false,
isGenericInstance:
if s.trackCaughtExceptions:
s.isGenericInstance[idx]
else:
false,
isDecl: s.isDecl[idx]
)
@@ -97,7 +90,6 @@ proc reverse*(s: var SuggestFileSymbolDatabase) =
s.sym.reverse()
s.caughtExceptions.reverse()
s.caughtExceptionsSet.reverse()
s.isGenericInstance.reverse()
s.isDecl.reverse()
proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions: bool): SuggestFileSymbolDatabase =
@@ -107,7 +99,6 @@ proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions
caughtExceptions: @[],
caughtExceptionsSet: newPackedBoolArray(),
isDecl: newPackedBoolArray(),
isGenericInstance: newPackedBoolArray(),
fileIndex: aFileIndex,
trackCaughtExceptions: aTrackCaughtExceptions,
isSorted: true
@@ -128,8 +119,6 @@ func compare*(s: var SuggestFileSymbolDatabase; i, j: int): int =
result = cmp(s.lineInfo[i], s.lineInfo[j])
if result == 0:
result = cmp(s.isDecl[i], s.isDecl[j])
if result == 0 and s.trackCaughtExceptions:
result = cmp(s.isGenericInstance[i], s.isGenericInstance[j])
proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) =
if i == j:
@@ -144,9 +133,6 @@ proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) =
var tmp3 = s.caughtExceptionsSet[i]
s.caughtExceptionsSet[i] = s.caughtExceptionsSet[j]
s.caughtExceptionsSet[j] = tmp3
var tmp6 = s.isGenericInstance[i]
s.isGenericInstance[i] = s.isGenericInstance[j]
s.isGenericInstance[j] = tmp6
var tmp4 = s.isDecl[i]
s.isDecl[i] = s.isDecl[j]
s.isDecl[j] = tmp4
@@ -210,17 +196,12 @@ proc add*(s: var SuggestFileSymbolDatabase; v: SymInfoPair) =
if s.trackCaughtExceptions:
s.caughtExceptions.add(v.caughtExceptions)
s.caughtExceptionsSet.add(v.caughtExceptionsSet)
s.isGenericInstance.add(v.isGenericInstance)
s.isSorted = false
proc add*(s: var SuggestSymbolDatabase; v: SymInfoPair; trackCaughtExceptions: bool) =
s.mgetOrPut(v.info.fileIndex, newSuggestFileSymbolDatabase(v.info.fileIndex, trackCaughtExceptions)).add(v)
proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo; isGenericInstance: bool): int =
# if trackCaughtExceptions is false, then all records in the database are not generic instances, so
# if we're searching for a generic instance, we find none
if isGenericInstance and not s.trackCaughtExceptions:
return -1
proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo): int =
doAssert(li.fileIndex == s.fileIndex)
if not s.isSorted:
s.sort()
@@ -229,17 +210,3 @@ proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo; isGeneri
col: li.col
)
result = binarySearch(s.lineInfo, q, cmp)
# if trackCaughtExceptions is false, then all records in the database are not generic instances, so
# if we're a searching for a non-generic instance, then we're done, we return what we have found
if not isGenericInstance and not s.trackCaughtExceptions:
return
# in this case trackCaughtExceptions is true, and the database contains both generic and non-generic instances, so we need
# to check the isGenericInstance flag also
if result != -1:
# search through a sequence of equal lineInfos to find a matching isGenericInstance
while result > 0 and s.isGenericInstance[result] != isGenericInstance and cmp(s.lineInfo[result], s.lineInfo[result - 1]) == 0:
dec result
while result < (s.lineInfo.len - 1) and s.isGenericInstance[result] != isGenericInstance and cmp(s.lineInfo[result], s.lineInfo[result + 1]) == 0:
inc result
if s.isGenericInstance[result] != isGenericInstance:
result = -1

View File

@@ -40,7 +40,7 @@ import closureiters, lambdalifting
type
PTransCon = ref object # part of TContext; stackable
mapping: TIdTable[PNode] # mapping from symbols to nodes
mapping: Table[ItemId, PNode] # mapping from symbols to nodes
owner: PSym # current owner
forStmt: PNode # current for stmt
forLoopBody: PNode # transformed for loop body
@@ -78,7 +78,7 @@ proc newTransNode(kind: TNodeKind, n: PNode,
proc newTransCon(owner: PSym): PTransCon =
assert owner != nil
result = PTransCon(mapping: initIdTable[PNode](), owner: owner)
result = PTransCon(mapping: initTable[ItemId, PNode](), owner: owner)
proc pushTransCon(c: PTransf, t: PTransCon) =
t.next = c.transCon
@@ -106,13 +106,6 @@ proc transformSons(c: PTransf, n: PNode, noConstFold = false): PNode =
for i in 0..<n.len:
result[i] = transform(c, n[i], noConstFold)
proc transformSonsAfterType(c: PTransf, n: PNode, noConstFold = false): PNode =
result = newTransNode(n)
assert n.len != 0
result[0] = copyTree(n[0])
for i in 1..<n.len:
result[i] = transform(c, n[i], noConstFold)
proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode =
result = newTransNode(kind, ri.info, 2)
result[0] = le
@@ -259,8 +252,7 @@ proc transformBlock(c: PTransf, n: PNode): PNode =
var labl: PSym
if c.inlining > 0:
labl = newLabel(c, n[0])
if n[0].kind != nkEmpty:
c.transCon.mapping[n[0].sym.itemId] = newSymNode(labl)
c.transCon.mapping[n[0].sym.itemId] = newSymNode(labl)
else:
labl =
if n[0].kind != nkEmpty:
@@ -329,7 +321,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode =
if a.kind == nkSym:
n[1] = transformSymAux(c, a)
return n
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: # todo optimize nosideeffects?
of nkProcDef: # todo optimize nosideeffects?
result = newTransNode(n)
let x = newSymNode(copySym(n[namePos].sym, c.idgen))
c.transCon.mapping[n[namePos].sym.itemId] = x
@@ -374,19 +366,6 @@ proc transformAsgn(c: PTransf, n: PNode): PNode =
result[0] = letSection
result[1] = asgnNode
template assignTupleUnpacking(c: PTransf, e: PNode) =
for i in 0..<c.transCon.forStmt.len - 2:
if c.transCon.forStmt[i].kind == nkVarTuple:
for j in 0..<c.transCon.forStmt[i].len-1:
let lhs = c.transCon.forStmt[i][j]
let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, e, i), j))
result.add(asgnTo(lhs, rhs))
else:
let lhs = c.transCon.forStmt[i]
let rhs = transform(c, newTupleAccess(c.graph, e, i))
result.add(asgnTo(lhs, rhs))
proc transformYield(c: PTransf, n: PNode): PNode =
proc asgnTo(lhs: PNode, rhs: PNode): PNode =
# Choose the right assignment instruction according to the given ``lhs``
@@ -421,8 +400,7 @@ proc transformYield(c: PTransf, n: PNode): PNode =
let lhs = c.transCon.forStmt[i]
let rhs = transform(c, v)
result.add(asgnTo(lhs, rhs))
elif e.kind notin {nkAddr, nkHiddenAddr} and e.kind != nkSym:
# no need to generate temp for address operation + nodes without sideeffects
elif e.kind notin {nkAddr, nkHiddenAddr}: # no need to generate temp for address operation
# TODO do not use temp for nodes which cannot have side-effects
var tmp = newTemp(c, e.typ, e.info)
let v = newNodeI(nkVarSection, e.info)
@@ -430,9 +408,21 @@ proc transformYield(c: PTransf, n: PNode): PNode =
result.add transform(c, v)
assignTupleUnpacking(c, tmp)
for i in 0..<c.transCon.forStmt.len - 2:
if c.transCon.forStmt[i].kind == nkVarTuple:
for j in 0..<c.transCon.forStmt[i].len-1:
let lhs = c.transCon.forStmt[i][j]
let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, tmp, i), j))
result.add(asgnTo(lhs, rhs))
else:
let lhs = c.transCon.forStmt[i]
let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
result.add(asgnTo(lhs, rhs))
else:
assignTupleUnpacking(c, e)
for i in 0..<c.transCon.forStmt.len - 2:
let lhs = c.transCon.forStmt[i]
let rhs = transform(c, newTupleAccess(c.graph, e, i))
result.add(asgnTo(lhs, rhs))
else:
if c.transCon.forStmt[0].kind == nkVarTuple:
var notLiteralTuple = false # we don't generate temp for tuples with const value: (1, 2, 3)
@@ -445,8 +435,7 @@ proc transformYield(c: PTransf, n: PNode): PNode =
else:
notLiteralTuple = true
if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple and e.kind != nkSym:
# no need to generate temp for address operation + nodes without sideeffects
if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple:
# TODO do not use temp for nodes which cannot have side-effects
var tmp = newTemp(c, e.typ, e.info)
let v = newNodeI(nkVarSection, e.info)
@@ -519,7 +508,8 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds, isAddr = false)
n.typ.kind == tyVar and
n.typ.skipTypes(abstractVar).kind == tyOpenArray and
n[0][0].typ.skipTypes(abstractVar).kind == tyString) and
not (isAddr and n.typ.kind == tyVar and n[0][0].typ.kind == tyRef)
not (isAddr and n.typ.kind == tyVar and n[0][0].typ.kind == tyRef and
n[0][0].kind == nkObjConstr)
: # elimination is harmful to `for tuple unpack` because of newTupleAccess
# it is also harmful to openArrayLoc (var openArray) for strings
# addr ( deref ( x )) --> x
@@ -552,34 +542,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
# we don't include uint and uint64 here as these are no ordinal types ;-)
if not isOrdinalType(source):
# float -> int conversions. ugh.
# generate a range check:
if dest.kind in tyInt..tyInt64:
if dest.kind == tyInt64 or source.kind == tyInt64:
result = newTransNode(nkChckRange64, n, 3)
else:
result = newTransNode(nkChckRange, n, 3)
dest = skipTypes(n.typ, abstractVar)
if dest.size < source.size:
let intType =
if source.size == 4:
getSysType(c.graph, n.info, tyInt32)
else:
getSysType(c.graph, n.info, tyInt64)
result[0] =
newTreeIT(n.kind, n.info, n.typ, n[0],
newTreeIT(nkConv, n.info, intType,
newNodeIT(nkType, n.info, intType), transform(c, n[1]))
)
else:
result[0] = transformSons(c, n)
result[1] = newIntTypeNode(firstOrd(c.graph.config, dest), dest)
result[2] = newIntTypeNode(lastOrd(c.graph.config, dest), dest)
else:
result = transformSons(c, n)
result = transformSons(c, n)
elif firstOrd(c.graph.config, n.typ) <= firstOrd(c.graph.config, n[1].typ) and
lastOrd(c.graph.config, n[1].typ) <= lastOrd(c.graph.config, n.typ):
# BUGFIX: simply leave n as it is; we need a nkConv node,
@@ -827,22 +790,12 @@ proc transformFor(c: PTransf, n: PNode): PNode =
t = formal.ast.typ # better use the type that actually has a destructor.
elif t.destructor == nil and arg.typ.destructor != nil:
t = arg.typ
if arg.kind in {nkDerefExpr, nkHiddenDeref}:
# optimizes for `[]` # bug #24093
var temp = newTemp(c, arg[0].typ, formal.info)
addVar(v, temp)
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg[0], true))
let newD = newDeref(temp)
newD.typ() = t
newC.mapping[formal.itemId] = newD
else:
# generate a temporary and produce an assignment statement:
var temp = newTemp(c, t, formal.info)
#incl(temp.sym.flags, sfCursor)
addVar(v, temp)
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
newC.mapping[formal.itemId] = temp
# generate a temporary and produce an assignment statement:
var temp = newTemp(c, t, formal.info)
#incl(temp.sym.flags, sfCursor)
addVar(v, temp)
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
newC.mapping[formal.itemId] = temp
of paVarAsgn:
assert(skipTypes(formal.typ, abstractInst).kind in {tyVar, tyLent})
newC.mapping[formal.itemId] = arg
@@ -912,18 +865,9 @@ proc transformArrayAccess(c: PTransf, n: PNode): PNode =
if n[0].kind == nkSym and n[0].sym.kind == skType:
result = n
else:
result = transformSons(c, n)
if n.len >= 2 and result[1].kind in {nkChckRange, nkChckRange64} and
n[1].kind in {nkHiddenStdConv, nkHiddenSubConv}:
# implicit conversion, was transformed into range check
# remove in favor of index check if conversion to array index type
# has to be done here because the array index type needs to be relaxed
# i.e. a uint32 index can implicitly convert to range[0..3] but not int
let arr = skipTypes(n[0].typ, abstractVarRange)
if arr.kind == tyArray and
firstOrd(c.graph.config, arr) == getOrdValue(result[1][1]) and
lastOrd(c.graph.config, arr) == getOrdValue(result[1][2]):
result[1] = result[1].skipConv
result = newTransNode(n)
for i in 0..<n.len:
result[i] = transform(c, skipConv(n[i]))
proc getMergeOp(n: PNode): PSym =
case n.kind
@@ -994,23 +938,6 @@ proc transformCall(c: PTransf, n: PNode): PNode =
else:
result = s
proc transformBareExcept(c: PTransf, n: PNode): PNode =
result = newTransNode(nkExceptBranch, n, 1)
if isEmptyType(n[0].typ):
result[0] = newNodeI(nkStmtList, n[0].info)
else:
result[0] = newNodeIT(nkStmtListExpr, n[0].info, n[0].typ)
# Generating `raiseDefect()`
let raiseDefectCall = callCodegenProc(c.graph, "raiseDefect", n[0].info)
result[0].add raiseDefectCall
if n[0].kind in {nkStmtList, nkStmtListExpr}:
# flattens stmtList
for son in n[0]:
result[0].add son
else:
result[0].add n[0]
result[0] = transform(c, result[0])
proc transformExceptBranch(c: PTransf, n: PNode): PNode =
if n[0].isInfixAs() and not isImportedException(n[0][1].typ, c.graph.config):
let excTypeNode = n[0][1]
@@ -1039,9 +966,6 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode =
# Replace the `Exception as foobar` with just `Exception`.
result[0] = transform(c, n[0][1])
result[1] = actions
elif n.len == 1 and
noPanicOnExcept notin c.graph.config.legacyFeatures:
result = transformBareExcept(c, n)
else:
result = transformSons(c, n)
@@ -1166,9 +1090,6 @@ proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = transformConv(c, n)
of nkObjConstr, nkCast:
# don't try to transform type node
result = transformSonsAfterType(c, n)
of nkDiscardStmt:
result = n
if n[0].kind != nkEmpty:

View File

@@ -131,8 +131,8 @@ proc isRange*(n: PNode): bool {.inline.} =
let callee = n[0]
if (callee.kind == nkIdent and callee.ident.id == ord(wDotDot)) or
(callee.kind == nkSym and callee.sym.name.id == ord(wDotDot)) or
(callee.kind in {nkClosedSymChoice, nkOpenSymChoice, nkOpenSym} and
callee[0].sym.name.id == ord(wDotDot)):
(callee.kind in {nkClosedSymChoice, nkOpenSymChoice} and
callee[1].sym.name.id == ord(wDotDot)):
result = true
else:
result = false
@@ -145,7 +145,7 @@ proc whichPragma*(n: PNode): TSpecialWord =
of nkIdent: result = whichKeyword(key.ident)
of nkSym: result = whichKeyword(key.sym.name)
of nkCast: return wCast
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym:
of nkClosedSymChoice, nkOpenSymChoice:
return whichPragma(key[0])
of nkBracketExpr:
if n.kind notin nkPragmaCallKinds: return wInvalid

View File

@@ -42,37 +42,32 @@ proc hashTree*(n: PNode): Hash =
#echo "hashTree ", result
#echo n
proc treesEquivalent(a, b: PNode; ignoreTypes: bool): bool =
proc treesEquivalent(a, b: PNode): bool =
if a == b:
result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind):
case a.kind
of nkEmpty: result = true
of nkEmpty, nkNilLit, nkType: result = true
of nkSym: result = a.sym.id == b.sym.id
of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit:
result = cast[uint64](a.floatVal) == cast[uint64](b.floatVal)
#result = a.floatVal == b.floatVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkType, nkNilLit:
result = a.typ == b.typ
else:
if a.len == b.len:
for i in 0..<a.len:
if not treesEquivalent(a[i], b[i], ignoreTypes): return
if not treesEquivalent(a[i], b[i]): return
result = true
else:
result = false
if result and not ignoreTypes:
result = sameTypeOrNil(a.typ, b.typ)
if result: result = sameTypeOrNil(a.typ, b.typ)
else:
result = false
proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
var h: Hash = k and high(t.data)
while t.data[h].key != nil:
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key, t.ignoreTypes):
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
return h
h = nextTry(h, high(t.data))
result = -1

View File

@@ -18,8 +18,7 @@ when defined(nimPreviewSlimSystem):
type
TTypeAllowedFlag* = enum
taTupField, # field of a tuple
taObjField, # field of an object
taField,
taHeap,
taConcept,
taIsOpenArray,
@@ -70,8 +69,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = t
elif taIsOpenArray in flags:
result = t
elif t.kind == tyLent and (((kind != skResult or taObjField in flags) and views notin c.features) or
(kind == skParam and {taIsCastable, taObjField, taTupField} * flags == {})): # lent cannot be used as parameters.
elif t.kind == tyLent and ((kind != skResult and views notin c.features) or
(kind == skParam and {taIsCastable, taField} * flags == {})): # lent cannot be used as parameters.
# except in the cast environment and as the field of an object
result = t
elif isOutParam(t) and kind != skParam:
@@ -188,12 +187,12 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
t.baseClass != nil and taIsDefaultField notin flags:
result = t
else:
let flags = flags+{taObjField, taVoid}
let flags = flags+{taField, taVoid}
result = typeAllowedAux(marker, t.baseClass, kind, c, flags)
if result.isNil and t.n != nil:
result = typeAllowedNode(marker, t.n, kind, c, flags)
of tyTuple:
let flags = flags+{taTupField, taVoid}
let flags = flags+{taField, taVoid}
for a in t.kids:
result = typeAllowedAux(marker, a, kind, c, flags)
if result != nil: break
@@ -210,6 +209,9 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = typeAllowedAux(marker, t.skipModifier, kind, c, flags+{taHeap})
else:
result = t
of tyConcept:
if kind != skParam: result = t
else: result = nil
proc typeAllowed*(t: PType, kind: TSymKind; c: PContext; flags: TTypeAllowedFlags = {}): PType =
# returns 'nil' on success and otherwise the part of the type that is

View File

@@ -102,9 +102,6 @@ const
# typedescX is used if we're sure tyTypeDesc should be included (or skipped)
typedescPtrs* = abstractPtrs + {tyTypeDesc}
typedescInst* = abstractInst + {tyTypeDesc, tyOwned, tyUserTypeClass}
# incorrect definition of `[]` and `[]=` for these types in system.nim
arrPutGetMagicApplies* = {tyArray, tyOpenArray, tyString, tySequence, tyCstring, tyTuple}
proc invalidGenericInst*(f: PType): bool =
result = f.kind == tyGenericInst and skipModifier(f) == nil
@@ -766,7 +763,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
prag.add("effectsOf: ")
prag.add(effectsOfStr)
if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
let effects = t.n[0]
let effects = t.owner.typ.n[0]
if effects.kind == nkEffectList and effects.len == effectListLen:
var inferredRaisesStr = ""
let effs = effects[exceptionEffects]
@@ -1204,19 +1201,17 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
if not result: return
proc isGenericAlias*(t: PType): bool =
return t.kind == tyGenericInst and t.skipModifier.skipTypes({tyAlias}).kind == tyGenericInst
return t.kind == tyGenericInst and t.skipModifier.kind == tyGenericInst
proc genericAliasDepth*(t: PType): int =
result = 0
var it = t.skipTypes({tyAlias})
var it = t
while it.isGenericAlias:
it = it.skipModifier.skipTypes({tyAlias})
it = it.skipModifier
inc result
proc skipGenericAlias*(t: PType): PType =
result = t.skipTypes({tyAlias})
if result.isGenericAlias:
result = result.skipModifier.skipTypes({tyAlias})
return if t.isGenericAlias: t.skipModifier else: t
proc sameFlags*(a, b: PType): bool {.inline.} =
result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
@@ -1309,34 +1304,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
cycleCheck()
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
of tyObject:
result = sameFlags(a, b)
if result:
withoutShallowFlags:
ifFastObjectTypeCheckFailed(a, b):
cycleCheck()
# should be generic, and belong to the same generic head type:
assert a.typeInst != nil, "generic object " & $a & " has no typeInst"
assert b.typeInst != nil, "generic object " & $b & " has no typeInst"
if result:
withoutShallowFlags:
# this is required because of generic `ref object`s,
# the value of their dereferences are not wrapped in `tyGenericInst`,
# so we need to check the generic parameters here
for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
if not sameTypeAux(ff, aa, c): return false
result = sameObjectStructures(a, b, c) and sameFlags(a, b)
of tyDistinct:
cycleCheck()
if c.cmp == dcEq:
result = sameFlags(a, b)
if result:
if sameFlags(a, b):
ifFastObjectTypeCheckFailed(a, b):
# should be generic, and belong to the same generic head type:
assert a.typeInst != nil, "generic distinct type " & $a & " has no typeInst"
assert b.typeInst != nil, "generic distinct type " & $b & " has no typeInst"
withoutShallowFlags:
# just in case `tyGenericInst` was skipped at some point,
# we need to check the generic parameters here
for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
if not sameTypeAux(ff, aa, c): return false
result = sameTypeAux(a.elementType, b.elementType, c)
else:
result = sameTypeAux(a.elementType, b.elementType, c) and sameFlags(a, b)
of tyEnum, tyForward:
@@ -1420,7 +1397,7 @@ proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
proc sameBackendTypePickyAliases*(x, y: PType): bool =
var c = initSameTypeClosure()
c.flags.incl {IgnoreTupleFields, IgnoreRangeShallow, PickyCAliases, PickyBackendAliases}
c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases}
c.cmp = dcEqIgnoreDistinct
result = sameTypeAux(x, y, c)
@@ -1485,17 +1462,8 @@ proc commonSuperclass*(a, b: PType): PType =
y = y.baseClass
proc lacksMTypeField*(typ: PType): bool {.inline.} =
## Returns true if the type is an object that lacks a m_type field.
## It doesn't check base classes.
(typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
proc isObjLackingTypeField*(typ: PType): bool {.inline.} =
## Returns true if the type is an object that lacks a type field.
## Object types that store type headers are not final or pure and
## have inheritable root types, which are not pure, neither.
result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
(typ.baseClass == nil) or isPureObject(typ))
include sizealignoffsetimpl
proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt =
@@ -1515,31 +1483,6 @@ proc getSize*(conf: ConfigRef; typ: PType): BiggestInt =
computeSizeAlign(conf, typ)
result = typ.size
proc setImportedTypeSize*(conf: ConfigRef, t: PType, size: int) =
t.size = size
if tfPacked in t.flags or size <= 1:
t.align = 1
elif size <= 2:
t.align = 2
elif size <= 4:
t.align = 4
else:
t.align = floatInt64Align(conf)
proc isConcept*(t: PType): bool=
case t.kind
of tyConcept: true
of tyCompositeTypeClass:
t.hasElementType and isConcept(t.elementType)
of tyGenericBody:
t.typeBodyImpl.kind == tyConcept
of tyGenericInvocation, tyGenericInst:
if t.baseClass.kind == tyGenericBody:
t.baseClass.typeBodyImpl.kind == tyConcept
else:
t.baseClass.kind == tyConcept
else: false
proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
case t.kind
of tyStatic:
@@ -1550,8 +1493,6 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
return false
of GenericTypes + tyTypeClasses + {tyFromExpr}:
return true
of tyGenericInst:
return t.isConcept
else:
return false
@@ -1897,7 +1838,7 @@ proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: P
processPragmaAndCallConvMismatch(msg, a, b, conf)
localError(conf, info, msg)
proc isRecursiveStructuralType(t: PType, cycleDetector: var IntSet): bool =
proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool =
if t == nil:
return false
if cycleDetector.containsOrIncl(t.id):
@@ -1908,30 +1849,19 @@ proc isRecursiveStructuralType(t: PType, cycleDetector: var IntSet): bool =
var cycleDetectorCopy: IntSet
for a in t.kids:
cycleDetectorCopy = cycleDetector
if isRecursiveStructuralType(a, cycleDetectorCopy):
return true
of tyProc:
result = false
var cycleDetectorCopy: IntSet
if t.returnType != nil:
cycleDetectorCopy = cycleDetector
if isRecursiveStructuralType(t.returnType, cycleDetectorCopy):
return true
for _, a in t.paramTypes:
cycleDetectorCopy = cycleDetector
if isRecursiveStructuralType(a, cycleDetectorCopy):
if isTupleRecursive(a, cycleDetectorCopy):
return true
of tyRef, tyPtr, tyVar, tyLent, tySink,
tyArray, tyUncheckedArray, tySequence, tyDistinct:
return isRecursiveStructuralType(t.elementType, cycleDetector)
return isTupleRecursive(t.elementType, cycleDetector)
of tyAlias, tyGenericInst:
return isRecursiveStructuralType(t.skipModifier, cycleDetector)
return isTupleRecursive(t.skipModifier, cycleDetector)
else:
return false
proc isRecursiveStructuralType*(t: PType): bool =
proc isTupleRecursive*(t: PType): bool =
var cycleDetector = initIntSet()
isRecursiveStructuralType(t, cycleDetector)
isTupleRecursive(t, cycleDetector)
proc isException*(t: PType): bool =
# check if `y` is object type and it inherits from Exception
@@ -2006,9 +1936,6 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
else:
result = false
proc isRefPtrObject*(t: PType): bool =
t.kind in {tyRef, tyPtr} and tfRefsAnonObj in t.flags
proc nominalRoot*(t: PType): PType =
## the "name" type of a given instance of a nominal type,
## i.e. the type directly associated with the symbol where the root
@@ -2041,7 +1968,7 @@ proc nominalRoot*(t: PType): PType =
result = result.skipModifier[0]
let val = result.skipModifier
if val.kind in {tyDistinct, tyEnum, tyObject} or
isRefPtrObject(val):
(val.kind in {tyRef, tyPtr} and tfRefsAnonObj in val.flags):
# atomic nominal types, this generic body is attached to them
discard
else:
@@ -2071,60 +1998,3 @@ proc nominalRoot*(t: PType): PType =
# skips all typeclasses
# is this correct for `concept`?
result = nil
proc genericRoot*(t: PType): PType =
## gets the root generic type (`tyGenericBody`) from `t`,
## if `t` is a generic type or the body of a generic instantiation
case t.kind
of tyGenericBody:
result = t
of tyGenericInst, tyGenericInvocation:
result = t.genericHead
else:
if t.typeInst != nil:
result = t.typeInst.genericHead
elif t.sym != nil and t.sym.typ.kind == tyGenericBody:
# can happen if `t` is the last child (body) of the generic body
result = t.sym.typ
else:
result = nil
proc reduceToBase*(f: PType): PType =
#[
Not recursion safe
Returns the lowest order (most general) type that that is compatible with the input.
E.g.
A[T] = ptr object ... A -> ptr object
A[N: static[int]] = array[N, int] ... A -> array
]#
case f.kind:
of tyGenericParam:
if f.len <= 0 or f.skipModifier == nil:
result = f
else:
result = reduceToBase(f.skipModifier)
of tyGenericInvocation:
result = reduceToBase(f.baseClass)
of tyCompositeTypeClass, tyAlias:
if not f.hasElementType or f.elementType == nil:
result = f
else:
result = reduceToBase(f.elementType)
of tyGenericInst:
result = reduceToBase(f.skipModifier)
of tyGenericBody:
result = reduceToBase(f.typeBodyImpl)
of tyUserTypeClass:
if f.isResolvedUserTypeClass:
result = f.base
else:
result = f.skipModifier
of tyStatic, tyOwned, tyVar, tyLent, tySink:
result = reduceToBase(f.base)
of tyInferred:
# This is not true "After a candidate type is selected"
result = reduceToBase(f.base)
of tyRange:
result = f.elementType
else:
result = f

View File

@@ -12,7 +12,7 @@
import semmacrosanity
import
std/[strutils, tables, intsets, parseutils],
std/[strutils, tables, parseutils],
msgs, vmdef, vmgen, nimsets, types,
parser, vmdeps, idents, trees, renderer, options, transf,
gorgeimpl, lineinfos, btrees, macrocacheimpl,
@@ -516,8 +516,6 @@ const
errIllegalConvFromXtoY = "illegal conversion from '$1' to '$2'"
errTooManyIterations = "interpretation requires too many iterations; " &
"if you are sure this is not a bug in your code, compile with `--maxLoopIterationsVM:number` (current value: $1)"
errCallDepthExceeded = "maximum call depth for the VM exceeded; " &
"if you are sure this is not a bug in your code, compile with `--maxCallDepthVM:number` (current value: $1)"
errFieldXNotFound = "node lacks field: "
@@ -592,7 +590,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let newPc = c.cleanUpOnReturn(tos)
# Perform any cleanup action before returning
if newPc < 0:
inc(c.callDepth)
pc = tos.comesFrom
let retVal = regs[0]
tos = tos.next
@@ -859,9 +856,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcLdObj:
# a = b.c
decodeBC(rkNode)
if rb >= regs.len or regs[rb].kind == rkNone or
if rb >= regs.len or regs[rb].kind == rkNone or
(regs[rb].kind == rkNode and regs[rb].node == nil) or
(regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil):
(regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil):
stackTrace(c, tos, pc, errNilAccess)
else:
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
@@ -1448,14 +1445,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
newFrame.slots[i] = regs[rb+i]
if isClosure:
newFrame.slots[rc] = TFullReg(kind: rkNode, node: regs[rb].node[1])
if c.callDepth <= 0:
if allowInfiniteRecursion in c.features:
c.callDepth = c.config.maxCallDepthVM
else:
msgWriteln(c.config, "stack trace: (most recent call last)", {msgNoUnitSep})
stackTraceAux(c, tos, pc)
globalError(c.config, c.debug[pc], errCallDepthExceeded % $c.config.maxCallDepthVM)
dec(c.callDepth)
tos = newFrame
updateRegsAlias
# -1 for the following 'inc pc'
@@ -1472,8 +1461,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let node = regs[rb+i].regToNode
node.info = c.debug[pc]
if prc.typ[i].kind notin {tyTyped, tyUntyped}:
var producedClosure = false
node.annotateType(prc.typ[i], c.config, producedClosure)
node.annotateType(prc.typ[i], c.config)
macroCall.add(node)
var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter, c.idgen)
@@ -1874,7 +1862,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
else:
stackTrace(c, tos, pc, "node has no type")
of 3:
else:
# getTypeImpl opcode:
ensureKind(rkNode)
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
@@ -1883,15 +1871,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
else:
stackTrace(c, tos, pc, "node has no type")
else:
# getTypeInstSkipAlias opcode:
ensureKind(rkNode)
if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc], c.idgen, skipAlias = true)
elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil:
regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen, skipAlias = true)
else:
stackTrace(c, tos, pc, "node has no type")
of opcNGetSize:
decodeBImm(rkInt)
let n = regs[rb].node
@@ -2061,7 +2040,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
aStrVal = aNode.ident.s.cstring
of nkSym:
aStrVal = aNode.sym.name.s.cstring
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
aStrVal = aNode[0].sym.name.s.cstring
else:
discard
@@ -2073,7 +2052,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
bStrVal = bNode.ident.s.cstring
of nkSym:
bStrVal = bNode.sym.name.s.cstring
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
bStrVal = bNode[0].sym.name.s.cstring
else:
discard
@@ -2332,7 +2311,6 @@ proc execute(c: PCtx, start: int): PNode =
proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
c.loopIterations = c.config.maxLoopIterationsVM
c.callDepth = c.config.maxCallDepthVM
if sym.kind in routineKinds:
if sym.typ.paramsLen != args.len:
result = nil
@@ -2435,12 +2413,9 @@ proc evalConstExprAux(module: PSym; idgen: IdGenerator;
setupGlobalCtx(module, g, idgen)
var c = PCtx g.vm
let oldMode = c.mode
let oldLocals = c.locals
c.mode = mode
c.locals = initIntSet()
c.cannotEval = false
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
c.locals = oldLocals
if c.cannotEval:
return errorNode(idgen, prc, n)
if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)

View File

@@ -10,7 +10,7 @@
## This module contains the type definitions for the new evaluation engine.
## An instruction is 1-3 int32s in memory, it is a register based VM.
import std/[tables, strutils, intsets]
import std/[tables, strutils]
import ast, idents, options, modulegraphs, lineinfos
@@ -201,7 +201,6 @@ type
TSandboxFlag* = enum ## what the evaluation engine should allow
allowCast, ## allow unsafe language feature: 'cast'
allowInfiniteLoops ## allow endless loops
allowInfiniteRecursion ## allow infinite recursion
TSandboxFlags* = set[TSandboxFlag]
TSlotKind* = enum # We try to re-use slots in a smart way to
@@ -249,7 +248,6 @@ type
# to not slow down interpretation
globals*: PNode #
constants*: PNode # constant data
contstantTab*: TNodeTable
types*: seq[PType] # some instructions reference types (e.g. 'except')
currentExceptionA*, currentExceptionB*: PNode
exceptionInstr*: int # index of instruction that raised the exception
@@ -259,7 +257,7 @@ type
mode*: TEvalMode
features*: TSandboxFlags
traceActive*: bool
loopIterations*, callDepth*: int
loopIterations*: int
comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces
callbacks*: seq[VmCallback]
callbackIndex*: Table[string, int]
@@ -273,7 +271,6 @@ type
vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC)
procToCodePos*: Table[int, int]
cannotEval*: bool
locals*: IntSet
PStackFrame* = ref TStackFrame
TStackFrame* {.acyclic.} = object
@@ -297,16 +294,13 @@ proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator
PCtx(code: @[], debug: @[],
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM,
callDepth: g.config.maxCallDepthVM,
comesFromHeuristic: unknownLineInfo, callbacks: @[], callbackIndex: initTable[string, int](), errorFlag: "",
cache: cache, config: g.config, graph: g, idgen: idgen,
contstantTab: initNodeTable(true))
cache: cache, config: g.config, graph: g, idgen: idgen)
proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) =
c.module = module
c.prc = PProc(blocks: @[])
c.loopIterations = c.config.maxLoopIterationsVM
c.callDepth = c.config.maxCallDepthVM
c.idgen = idgen
proc reverseName(s: string): string =

View File

@@ -42,7 +42,7 @@ proc atomicTypeX(s: PSym; info: TLineInfo): PNode =
result.info = info
proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator;
inst=false; allowRecursionX=false; skipAlias = false): PNode
inst=false; allowRecursionX=false): PNode
proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo;
idgen: IdGenerator;
@@ -70,7 +70,7 @@ proc objectNode(cache: IdentCache; n: PNode; idgen: IdGenerator): PNode =
proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
idgen: IdGenerator;
inst=false; allowRecursionX=false; skipAlias = false): PNode =
inst=false; allowRecursionX=false): PNode =
var allowRecursion = allowRecursionX
template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info, idgen)
template atomicType(s): untyped = atomicTypeX(s, info)
@@ -91,8 +91,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
id
template newIdentDefs(s): untyped = newIdentDefs(s, s.typ)
if inst and not allowRecursion and t.sym != nil and
not (skipAlias and t.kind == tyAlias):
if inst and not allowRecursion and t.sym != nil:
# getTypeInst behavior: return symbol
return atomicType(t.sym)
@@ -125,7 +124,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
if t.base != nil:
result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
result.add atomicType("typeDesc", mTypeDesc)
result.add mapTypeToAstX(cache, t.base, info, idgen, inst, skipAlias = skipAlias)
result.add mapTypeToAst(t.base, info)
else:
result = atomicType("typeDesc", mTypeDesc)
of tyGenericInvocation:
@@ -154,7 +153,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
else:
result = mapTypeToAst(t.typeBodyImpl, info)
of tyAlias:
result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion, skipAlias = skipAlias)
result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion)
of tyOrdinal:
result = mapTypeToAst(t.skipModifier, info)
of tyDistinct:
@@ -326,9 +325,8 @@ proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGene
# the "Inst" version includes generic parameters in the resulting type tree
# and also tries to look like the corresponding Nim type declaration
proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator; skipAlias = false): PNode =
# skipAlias: skips aliases and typedesc
result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=false, skipAlias = skipAlias)
proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode =
result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=false)
# the "Impl" version includes generic parameters in the resulting type tree
# and also tries to look like the corresponding Nim type implementation

View File

@@ -33,8 +33,7 @@ when defined(nimPreviewSlimSystem):
import
ast, types, msgs, renderer, vmdef, trees,
magicsys, options, lowerings, lineinfos, transf, astmsgs,
treetab
magicsys, options, lowerings, lineinfos, transf, astmsgs
from modulegraphs import getBody
@@ -479,16 +478,10 @@ proc sameConstant*(a, b: PNode): bool =
result = true
proc genLiteral(c: PCtx; n: PNode): int =
result = nodeTableTestOrSet(c.contstantTab, n, c.constants.len)
if result == c.constants.len:
let lit = rawGenLiteral(c, n)
assert lit == result
when false:
# types do not matter here:
for i in 0..<c.constants.len:
if sameConstant(c.constants[i], n): return i
result = rawGenLiteral(c, n)
# types do not matter here:
for i in 0..<c.constants.len:
if sameConstant(c.constants[i], n): return i
result = rawGenLiteral(c, n)
proc unused(c: PCtx; n: PNode; x: TDest) {.inline.} =
if x >= 0:
@@ -1018,7 +1011,7 @@ proc genBindSym(c: PCtx; n: PNode; dest: var TDest) =
# if dynamicBindSym notin c.config.features:
if n.len == 2: # hmm, reliable?
# bindSym with static input
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkOpenSym, nkSym}:
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
let idx = c.genLiteral(n[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, opcNBindSym, dest, idx)
@@ -1189,10 +1182,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMag
of mEqF64: genBinaryABC(c, n, dest, opcEqFloat)
of mLeF64: genBinaryABC(c, n, dest, opcLeFloat)
of mLtF64: genBinaryABC(c, n, dest, opcLtFloat)
of mLeU: genBinaryABC(c, n, dest, opcLeu)
of mLtU: genBinaryABC(c, n, dest, opcLtu)
of mLePtr, mLtPtr:
globalError(c.config, n.info, "pointer comparisons are not available at compile-time")
of mLePtr, mLeU: genBinaryABC(c, n, dest, opcLeu)
of mLtPtr, mLtU: genBinaryABC(c, n, dest, opcLtu)
of mEqProc, mEqRef:
genBinaryABC(c, n, dest, opcEqRef)
of mXor: genBinaryABC(c, n, dest, opcXor)
@@ -1346,8 +1337,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMag
of "getType": 0
of "typeKind": 1
of "getTypeInst": 2
of "getTypeImpl": 3 # "getTypeImpl"
else: 4 # getTypeInstSkipAlias
else: 3 # "getTypeImpl"
c.gABC(n, opcNGetType, dest, tmp, rc)
c.freeTemp(tmp)
#genUnaryABC(c, n, dest, opcNGetType)
@@ -1556,7 +1546,8 @@ template cannotEval(c: PCtx; n: PNode) =
if c.config.cmd == cmdCheck and c.config.m.errorOutputs != {}:
# nim check command with no error outputs doesn't need to cascade here,
# includes `tryConstExpr` case which should not continue generating code
localError(c.config, n.info, "cannot evaluate at compile time: " & n.renderTree)
localError(c.config, n.info, "cannot evaluate at compile time: " &
n.renderTree)
c.cannotEval = true
return
globalError(c.config, n.info, "cannot evaluate at compile time: " &
@@ -1592,7 +1583,6 @@ proc checkCanEval(c: PCtx; n: PNode) =
# are in the right scope:
if sfGenSym in s.flags and c.prc.sym == nil: discard
elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard
elif s.kind in {skVar, skLet} and s.id in c.locals: discard
else: cannotEval(c, n)
elif s.kind in {skProc, skFunc, skConverter, skMethod,
skIterator} and sfForward in s.flags:
@@ -1609,12 +1599,12 @@ proc genAdditionalCopy(c: PCtx; n: PNode; opc: TOpcode;
c.freeTemp(cc)
proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
dest, idx, value: TRegister; enforceCopy = false) =
dest, idx, value: TRegister) =
# opcLdObj et al really means "load address". We sometimes have to create a
# copy in order to not introduce false aliasing:
# mylocal = a.b # needs a copy of the data!
assert n.typ != nil
if needsAdditionalCopy(n) or enforceCopy:
if needsAdditionalCopy(n):
genAdditionalCopy(c, n, opc, dest, idx, value)
else:
c.gABC(n, opc, dest, idx, value)
@@ -1663,14 +1653,11 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
of nkSym:
let s = le.sym
checkCanEval(c, le)
let isLdConst = ri.kind == nkSym and ri.sym.kind == skConst and
dontInlineConstant(ri, if ri.sym.astdef != nil: ri.sym.astdef else: ri.sym.typ.n)
# assigning a constant (opcLdConst) to something; need to copy its value
if s.isGlobal:
withTemp(tmp, le.typ):
c.gen(le, tmp, {gfNodeAddr})
let val = c.genx(ri)
c.preventFalseAlias(le, opcWrDeref, tmp, 0, val, isLdConst)
c.preventFalseAlias(le, opcWrDeref, tmp, 0, val)
c.freeTemp(val)
else:
if s.kind == skForVar: c.setSlot s
@@ -1897,10 +1884,6 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
c.freeTemp(objR)
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
if n[0].typ == nil:
globalError(c.config, n.info, "cannot access array with nil type")
return
let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
case arrayType
of tyString, tyCstring:
@@ -1992,7 +1975,7 @@ proc genVarSection(c: PCtx; n: PNode) =
c.gen(lowerTupleUnpacking(c.graph, a, c.idgen, c.getOwner))
elif a[0].kind == nkSym:
let s = a[0].sym
c.locals.incl(s.id)
checkCanEval(c, a[0])
if s.isGlobal:
let runtimeAccessToCompileTime = c.mode == emRepl and
sfCompileTime in s.flags and s.position > 0
@@ -2057,7 +2040,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
c.gABx(n, opcLdNull, dest, c.genType(n.typ))
let intType = getSysType(c.graph, n.info, tyInt)
let seqType = n.typ.skipTypes(abstractVar+{tyStatic}-{tyTypeDesc})
let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
if seqType.kind == tySequence:
var tmp = c.getTemp(intType)
c.gABx(n, opcLdImmInt, tmp, n.len)
@@ -2329,11 +2312,9 @@ proc genStmt*(c: PCtx; n: PNode): int =
result = c.code.len
var d: TDest = -1
c.gen(n, d)
if d >= 0:
# for discardable calls etc, otherwise not valid
freeTemp(c, d)
#globalError(c.config, n.info, "VM problem: dest register is set")
c.gABC(n, opcEof)
if d >= 0:
globalError(c.config, n.info, "VM problem: dest register is set")
proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int =
c.removeLastEof

View File

@@ -143,9 +143,6 @@ proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
proc getCurrentExceptionWrapper(a: VmArgs) {.nimcall.} =
setResult(a, a.currentException)
proc raiseDefectWrapper(a: VmArgs) {.nimcall.} =
discard
proc staticWalkDirImpl(path: string, relative: bool): PNode =
result = newNode(nkBracket)
for k, f in walkDir(path, relative):
@@ -266,8 +263,7 @@ proc registerAdditionalOps*(c: PCtx) =
wrap2si(readLines, ioop)
systemop getCurrentExceptionMsg
systemop getCurrentException
systemop raiseDefect
registerCallback c, "stdlib.staticos.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
setResult(a, dirExists(getString(a, 0)))
@@ -335,12 +331,6 @@ proc registerAdditionalOps*(c: PCtx) =
registerCallback c, "stdlib.hashes.hashVmImplByte", hashVmImplByte
registerCallback c, "stdlib.hashes.hashVmImplChar", hashVmImplByte
registerCallback c, "stdlib.system.ltCStringVm", proc (a: VmArgs) =
setResult(a, getString(a, 0) < getString(a, 1))
registerCallback c, "stdlib.system.leCStringVm", proc (a: VmArgs) =
setResult(a, getString(a, 0) <= getString(a, 1))
if optBenchmarkVM in c.config.globalOptions or vmopsDanger in c.config.features:
wrap0(cpuTime, timesop)
else:

View File

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

View File

@@ -257,12 +257,10 @@ clang.objc.options.linker = "-lobjc -lgnustep-base"
gcc.options.linker %= "-L $WIND_BASE/target/lib/usr/lib/ppc/PPC32/common -mrtp -fno-strict-aliasing -D_C99 -D_HAS_C9X -std=c99 -fasm -Wall -Wno-write-strings"
@end
# seqs_v2 violates strict aliasing.
gcc.options.always %= "${gcc.options.always} -fno-strict-aliasing"
# -fno-math-errno is default in OSX, iOS, BSD, Musl, Libm, LLVM, Clang, ICC.
# See https://itnext.io/why-standard-c-math-functions-are-slow-d10d02554e33
# and https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Optimize-Options.html#Optimize-Options
gcc.options.speed = "-O3 -fno-ident -fno-math-errno"
gcc.options.speed = "-O3 -fno-strict-aliasing -fno-ident -fno-math-errno"
gcc.options.size = "-Os -fno-ident"
@if windows:
gcc.options.debug = "-g3 -Og -gdwarf-3"
@@ -283,7 +281,7 @@ llvm_gcc.options.size = "-Os"
# Configuration for the LLVM CLang compiler:
clang.options.debug = "-g"
clang.cpp.options.debug = "-g"
clang.options.always = "-w -ferror-limit=3 -fno-strict-aliasing"
clang.options.always = "-w -ferror-limit=3"
clang.options.speed = "-O3"
clang.options.size = "-Os"

View File

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

View File

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

View File

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

View File

@@ -129,8 +129,7 @@ other associated resources. Variables are destroyed via this hook when
they go out of scope or when the routine they were declared in is about
to return.
A `=destroy` hook is allowed to have a parameter of a `var T` or `T` type.
The prototype of this hook for a type `T` needs to be:
A `=destroy` hook is allowed to have a parameter of a `var T` or `T` type. Taking a `var T` type is deprecated. The prototype of this hook for a type `T` needs to be:
```nim
proc `=destroy`(x: T)

View File

@@ -181,7 +181,7 @@ objectBranch = 'of' exprList colcom objectPart
objectBranches = objectBranch (IND{=} objectBranch)*
(IND{=} 'elif' expr colcom objectPart)*
(IND{=} 'else' colcom objectPart)?
objectCase = 'case' (declColonEquals / pragma)? ':'? COMMENT?
objectCase = 'case' declColonEquals ':'? COMMENT?
(IND{>} objectBranches DED
| IND{=} objectBranches)
objectPart = IND{>} objectPart^+IND{=} DED

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