ref #26144
The C backend must not use `sfNeverRaises` to remove exception checks
from
virtual method calls. The flag describes only the selected base method
body,
while a vtable override may raise a catchable exception.
This change makes `canRaiseDisp` conservative for `skMethod` symbols and
adds a
regression test covering an exception raised by a child method invoked
through a
base reference.
(cherry picked from commit 8cb406cd7a)
Fixes#11797.
Imported scalar and pointer aliases inherit their external C spelling,
but
receive a different Nim symbol. Signature hashing previously used that
symbol
identity, so aliases that emit exactly the same C type could produce
different
backend names for tuples, sequences, and other generic types.
For example, `cint` and `type CIntAlias = cint` both emit `int`, but
`seq[cint]` and `seq[CIntAlias]` could be emitted as incompatible C
structs.
The Nim type checker nevertheless permits assignments and calls between
them,
causing the generated C or C++ compilation to fail.
This changes the backend hash to use the external type spelling when
available.
A symbol-based fallback remains for imported types without a resolved
spelling.
The change deliberately does not collapse imported types into their
underlying
Nim builtin. Types such as `pid_t`, imported pointers with qualifiers,
and
platform typedefs may require distinct backend representations.
## NIF and incremental compilation
This does not change NIF serialization, NIF type keys, or the IC cache
format.
The bug is in backend type-name generation. An IC regression test is
included
to ensure that the corrected backend identity is preserved when
compilation
passes through the NIF pipeline.
## Tests
The regressions cover:
- tuple and sequence assignments between an imported type and its alias
- cross-module sequence parameters and mutation
- C and C++ backends
- NIF-backed incremental compilation
Existing C-type tests were also run under C/C++, refc, and ARC.
## Remaining scope
This does not solve the broader question of compatibility between
imported and
builtin types that have different backend identities, such as
`seq[cdouble]` and `seq[float]`. That remains tracked by #19374.
(cherry picked from commit 33ee586913)
fixes#25992
```nim
type
Foo = object
case kind: bool
of true:
a: ref Bar # 8 bytes (pointer)
of false:
b: int # 4 bytes
```
specializeResetT for b emits accessor.b = 0 — writes 4 bytes
But the union is 8 bytes wide (sized by the largest branch)
The remaining 4 bytes where a used to live are untouched
Those stale bytes could contain a heap pointer the GC traces → crash
Add nimZeroMem after specializeResetN for case objects to clear the
entire union including unused branch bytes.
(cherry picked from commit 16920b56d1)
(cherry picked from commit 84a5613c35)
In the JS backend `toOpenArray` used `slice` (a copy), so writes through
a `var openArray` parameter silently vanished.
This emits `subarray` (a live shared-buffer view) for homogeneous
numeric arrays, otherwise such parameters are passed as a `{base, off,
len}` view that always aliases the caller's storage. Sliced seq/array
args become `{base, off, len}`, whole values `{base, off:0, len}`,
re-slices rebase.
Un-skips the JS guard in tests/openarray/topenarray.nim
Fixes#15952.
(cherry picked from commit bd95f88f74)
fixes#26124
The fix preserves the resolved static value, allowing constant folding.
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit dc242e9027)
#23835 tried to do this, but it also switched to `long int` on the GNU
ABI, where it's actually just `int`.
Fixes#26111
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
(cherry picked from commit 7f120229e8)
Specialize `rawAlloc` for alignment (cherry-picked from the other PR).
This cuts the frame of the normal unaligned path down enough to regain
the performance lost from loading the cold page in #26110
Also cleans up `MemRegion` a bit, the regressions are either gone or
were measurement errors.
(cherry picked from commit 6f1e6fdd06)
fixes#26123
`cast[T](x)` is a transparent path expression for compiler analysis.
Previously, move/alias analysis could fail to see a later use through a
cast and incorrectly mark the source as moved, causing the issue’s
segmentation fault.
for views,
https://nim-lang.org/docs/manual_experimental.html#view-types-path-expressions:
A cast expression cast[T](e) is a path expression.
It also affects skipConvDfa, isAnalysableFieldAccess, and aliases. And I
might narrow it down for the two cases above mentioned if it causes
problems
(cherry picked from commit f1256ddcf4)
Issue popped up when using `fromJson` into an array but the JSON passed
is an object
```nim
import std/[jsonutils, json]
let data = parseJson """
{"key": "value"}
"""
var foo: seq[int]
foo.fromJson(data)
echo foo #> @[0]
```
Basically the `setLen` would set the size to be equal to the number of
keys, but `getElems` just returns an empty array if the JSON isn't an
array which lead to it just creating zero'd items in the seq without
letting the user know.
Felt adding the checks was better than just skipping the `setLen` since
it lets the user know that there is a problem with the JSON
(cherry picked from commit 81325d0745)
fixes#25992
```nim
type
Foo = object
case kind: bool
of true:
a: ref Bar # 8 bytes (pointer)
of false:
b: int # 4 bytes
```
specializeResetT for b emits accessor.b = 0 — writes 4 bytes
But the union is 8 bytes wide (sized by the largest branch)
The remaining 4 bytes where a used to live are untouched
Those stale bytes could contain a heap pointer the GC traces → crash
Add nimZeroMem after specializeResetN for case objects to clear the
entire union including unused branch bytes.
(cherry picked from commit 16920b56d1)
…haracterization with--warning:ResultUsed:on
fixes#26062
> A return statement with no expression is shorthand for return result.
> ResultUsed: Warn about the usage of the built-in result variable.
> A procedure that does not have any return statement and does not use
the special result variable returns the value of its last expression.
(cherry picked from commit 0ec8682abe)
`unapplyBlockContext` reset `inEnforcedGcSafe` and
`inEnforcedNoSideEffects` to false whenever a `{.cast(gcsafe).}` or
`{.cast(noSideEffect).}` block ended. When such a block is nested inside
another block of the same cast, the inner block's exit switched
enforcement back on for the rest of the enclosing block, so statements
lexically inside the outer cast were rejected.
The fix saves both flags in `PragmaBlockContext` when the block context
is created and restores the saved values on exit. That matches how every
other piece of block state there (`locked`, `exc`, `tags`, `forbids`) is
already handled; these two bools were the only ones reset to a constant
instead of restored.
No change for non-nested blocks: entering from the non-enforced state
saves false, so exit still clears the flag. A statement after the outer
block is still rejected as before.
Test covers nested `cast(gcsafe)` and nested `cast(noSideEffect)`.
`tests/effects` passes unchanged (49/49, same as stock).
(cherry picked from commit f4e8e04cd0)
## Fix `globalSymbols` support on POSIX
### Summary
Fix `-d:globalSymbols` on POSIX platforms by defining `RTLD_GLOBAL`
alongside `RTLD_NOW` in `system/dyncalls.nim`.
On Linux and macOS, `RTLD_NOW` is defined locally in `dyncalls.nim`, but
`RTLD_GLOBAL` was not. As a result, enabling `-d:globalSymbols` could
fail because `RTLD_GLOBAL` was undeclared.
This change:
* defines `RTLD_GLOBAL` as `0x100` on Linux,
* defines `RTLD_GLOBAL` as `0x8` on macOS,
* imports `RTLD_GLOBAL` from `<dlfcn.h>` on other POSIX platforms.
These values are consistent with the existing POSIX constants already
used elsewhere in the Nim source tree.
### Motivation
`globalSymbols` is intended to load dynamic libraries with `RTLD_GLOBAL`
so that their exported symbols are available to subsequently loaded
shared libraries.
This is needed, for example, when a dynamically loaded library later
loads a plugin or provider that depends on symbols from the first
library.
Without this fix, `-d:globalSymbols` cannot be used reliably for that
purpose on POSIX systems.
### Testing
Tested on Linux with an AArch64 target.
A program using dynamically loaded OpenSSL libraries and a subsequently
loaded OpenSSL provider failed when the OpenSSL libraries were loaded
with the default local symbol visibility.
Using `RTLD_GLOBAL` made the same program work correctly.
After this change, building the original Nim program with:
```text
-d:globalSymbols
```
successfully loads the OpenSSL libraries with global symbol visibility,
and the provider-based TLS 1.2 and TLS 1.3 tests both pass.
The same behavior was also independently reproduced using direct
`dlopen()` / `dlsym()` calls:
```text
RTLD_LOCAL -> TLS 1.2 failed
RTLD_GLOBAL -> TLS 1.2 passed
```
Signed-off-by: Takeyoshi Kikuchi <kikuchi@centurysys.co.jp>
(cherry picked from commit 226cfff540)
`nimDecRefIsLast` always performed an atomic decrement. When the biased
count is already zero the destroying thread holds the only reference, so
there is nothing to adjudicate and the read-modify-write can be skipped.
Soundness: a counted reference can only be derived from the location
being destroyed -- which happens-before this destructor unless the
program races on that location -- or from another counted reference,
whose contribution is already in `rc` and therefore forces the slow
path. Observing zero proves no other thread holds a reference and that
none can appear. This relies on `--mm:atomicArc` having no collector;
ORC and YRC mutate `rc` from a participant that holds no counted
reference at all, so the fast path is deliberately not enabled for them.
The slow path keeps deciding on the value its own RMW returned. That is
what separates this from nim-lang/threading#45, where the "who frees"
role was decided from a separate load and the RMW result was discarded,
so the role could be dropped by every participant at once.
gcbench, -d:danger, median of 21 pinned runs:
--mm:arc (non-atomic RC) 0.1310
--mm:atomicArc 0.1742
--mm:atomicArc + this 0.1330
-23.7%, closing 95% of the gap to non-atomic reference counting. gcbench
builds its trees with `sink` parameters, so it performs almost no
incRefs and the whole atomicArc penalty is decRef traffic. The worst
case -- a decrement that always sees rc > 0, so the load never pays off
-- measures +1.1%.
`-d:nimNoAtomicArcFastPath` restores the previous code path.
(cherry picked from commit 5a0e4ff6b1)
## Summary
- nimpretty with non-default \--indent\ (e.g. 3 or 10) produced invalid
indentation in if/block/try expression regions because layouter kept the
original column when \keepIndents > 0\ and ignored \indWidth\.
- Rebase the column onto \indWidth\ using the relative offset from the
enclosing block baseline (\indentStack[^1]\).
## Root cause
\parser.nim\'s \
imprettyDontTouch\ template sets \keepIndents\ for if/block/try
expressions. layouter in the \keepIndents > 0\ branch used \ ok.indent\
(source column) directly as \indentLevel\ without scaling by \indWidth\,
so lines in these regions kept the original column and misaligned with
the rest of the file when \--indent\ differed from source indent width.
## Fix
\\\
im
em.indentLevel = em.indentStack.high * em.indWidth +
(tok.indent - em.indentStack[^1])
\\\
Keeps the relative offset from the enclosing block baseline but rebases
onto \indWidth\. At default \--indent:2\ the offset equals \indWidth\,
so output is unchanged (backwards compatible).
## Testing
- 12 custom cases x 3 indent values (2/3/10) = 36/36 pass
- nimpretty self-test suite 7/7 pass (no regression at default indent)
- 5 keepIndents scenarios (if/block/try expression continuation
alignment) that failed at indent:3/10 now pass
Fixes#20078.
(cherry picked from commit 1e82deb73d)
fixes#25942fixes#25938
After a successful match to a concrete static T, normalizes an empty
static container literal to the formal payload type before binding it.
This prevents `static[set[empty]]({})` from leaking into the
instantiated proc body.
(cherry picked from commit 234f01510f)
fixes#26045;
fixes#26046
The fix isolates compiler option state while semantically checking each
when nimvm branch.
compiler/semexprs.nim:2745 snapshots the option stack, compiler options,
diagnostics settings, and enabled features. It analyzes one branch and
restores that state in finally. Both the nimvm and else branches use
this function.
This prevents:
```nim
when nimvm:
{.push overflowChecks: off.}
```
from disabling overflow checks in following runtime code. It also means
a {.pop.} in the opposite branch correctly reports that it has no
corresponding {.push.}.
(cherry picked from commit f6651e6c70)
Fixes#26027.
Map the single-order compare-exchange failure ordering from `release` to
`relaxed` and from `acquire-release` to `acquire`. Apply the mapping to
the trivial and non-trivial strong and weak overloads, and correct the
explicit-order test cases.
Tested `tests/stdlib/concurrency/tatomics.nim` across C/C++, refc/orc,
and native/C++ atomics (8 combinations). Also verified the original GCC
16.1 assertion reproducer.
(cherry picked from commit 95557ad48c)
`distinctBase` results in typedesc, so `set[T.distinctBase]` received
`typedesc[range[...]]` as its element type, which `isOrdinalType`
rejects. Strip the wrapper in `semSet` before storing the element type
and checking ordinality.
Also add `tyFromExpr` to the deferred-check set so the error doesn't
fire prematurely inside generic bodies - same pattern already used by
`semArray`.
(cherry picked from commit 2d81149294)
`toStrLit()` uses `repr()` internally, which forwards quotes and messes
with dashes in the output. Let's use `newStrLitNode()` directly instead.
GitHub: fixes https://github.com/nim-lang/Nim/issues/26039
(cherry picked from commit 0021205854)
fixes#26010
Cursors do not own their values and therefore cannot transfer ownership
through move.
Reject move(cursor) during semantic analysis and share the
cursor-location check
between semantic analysis and destructor injection.
(cherry picked from commit 99a696e0c4)
Fixes#23615
## Root cause
The leak does not require async at all -- this minimal closure iterator
leaks the exception and its stacktrace seq under ARC/ORC:
```nim
iterator it(): int {.closure.} =
try:
yield 1 # try spanning a yield => closureiters transform
raise newException(ValueError, "x")
except ValueError: # typed except => generated `of` check
discard
yield 2
```
A bare `except:` does not leak; a *typed* `except` does:
1. `collectExceptState` in `compiler/closureiters.nim` generates the
except-branch type check as `of(getCurrentException(), T)`, using the
raw generic magic sym from `getSysMagic("of", mOf)`.
2. `injectdestructors` skips call arguments whose *formal* parameter
type is `isCompileTimeOnly`, and the raw generic `of` sym's formal
params are `tyGenericParam` so both arguments of the generated `of` call
are never processed.
3. `getCurrentException()` increfs `currException` via `=copy` into its
result. Since the arc pass never wraps that owned temp in a destroy
(`--expandArc` shows the condition left untouched, while a user-written
`if f() of ValueError` in the same iterator gets a `:tmpD` +
`=destroy`), the caught exception's refcount stays +1 forever.
Every `try: await x() except SomeError` in async code has this shape, so
each caught async exception leaked once.
## Fix
The state-machine wrapper already stores the active exception in the
`:curExc` env field before jumping to the except landing state, and
`currException == :curExc` on every path into that state. The generated
condition now references the env field via `ctx.newCurExcAccess()`
instead of calling `getCurrentException()` again -- no ownership
transfer, no temp to destroy, one fewer runtime call.
Note: the underlying `injectdestructors` behavior (skipping args of
calls whose formal params are raw `tyGenericParam`, e.g. from
`getSysMagic`) is a separate latent gap that could affect other
compiler-generated code; it is intentionally left untouched here.
## Valgrind, before and after
Exact code and command from the issue, on Linux (Valgrind 3.19):
```
nim c -d:danger --mm:orc --debugger:native --threads:off -d:useMalloc bug.nim
valgrind --leak-check=full --show-leak-kinds=all ./bug
```
Before (devel):
```
==14663== HEAP SUMMARY:
==14663== in use at exit: 136 bytes in 2 blocks
==14663== total heap usage: 22 allocs, 20 frees, 116,466 bytes allocated
==14663==
==14663== 56 bytes in 1 blocks are indirectly lost in loss record 1 of 2
==14663== at 0x488A1C4: realloc (vg_replace_malloc.c:1437)
==14663== by 0x10C663: prepareSeqAddUninit (seqs_v2.nim:212)
==14663== by 0x10CF6F: raiseExceptionEx (excpt.nim:538)
==14663== by 0x11661F: amain::amainX20X28AsyncX29_(Future<void>) (bug.nim:10)
==14663== ...
==14663==
==14663== 136 (80 direct, 56 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==14663== at 0x48850C8: malloc (vg_replace_malloc.c:381)
==14663== by 0x10C8E3: nimNewObj (arc.nim:122)
==14663== by 0x115403: err::errX20X28AsyncX29_(Future<void>) (asyncmacro.nim:274)
==14663== by 0x116027: err::errNimAsyncContinue(Future<void>, ClosureIt<void>) (asyncmacro.nim:44)
==14663== by 0x1163D3: bug::err (bug.nim:3)
==14663== ...
==14663==
==14663== LEAK SUMMARY:
==14663== definitely lost: 80 bytes in 1 blocks
==14663== indirectly lost: 56 bytes in 1 blocks
==14663== possibly lost: 0 bytes in 0 blocks
==14663== still reachable: 0 bytes in 0 blocks
==14663== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
```
After (this PR):
```
==14675== HEAP SUMMARY:
==14675== in use at exit: 0 bytes in 0 blocks
==14675== total heap usage: 22 allocs, 22 frees, 116,466 bytes allocated
==14675==
==14675== All heap blocks were freed -- no leaks are possible
==14675==
==14675== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
```
## Testing
- New `tests/async/t23615.nim` (modeled on `t23212.nim`: `valgrind:
true` + alloc-stats assertion) covers both the pure closure-iterator
form and the async form from the issue, with the caught exception looped
50x so the leak blows well past the slack threshold. It passes with this
PR and fails against devel.
- Testament categories `async`, `arc`, `iter`, `exception` all pass with
the patched compiler (323 tests).
- Behavior is unchanged on a sanity program covering multi-branch
dispatch, `as e` binding, nested try, and re-raise across yields: output
is byte-identical to devel; the patched build just frees 2 more blocks
per caught exception.
(cherry picked from commit 8e8f8de1ab)
…pile time
fixes#26000
vm: preserve lvalues for mutations of broadcast array elements
Load in-place mutation targets through their address so mutations don't
operate on detached copies of broadcast defaults. This also preserves
nested lvalues and evaluates indexed destinations only once.
Cover sequence, string, and set mutations through direct, nested, field,
enum-indexed, and range-indexed array elements.
(cherry picked from commit 2915691515)