Commit Graph

6999 Commits

Author SHA1 Message Date
Andreas Rumpf
546a518b22 Merge branch 'devel' into araq-ic-fixes 2026-08-28 11:10:25 +02:00
Andreas Rumpf
c87926dadf IC: more bugfixes (#26141)
Grinding a small figdraw-based program under `nim ic` and diffing its
output against the classic backend surfaced eight bugs, four of which
silently produced a wrong binary rather than an error.

Frontend / build graph (`deps.nim`):

* Dead `when`-guarded imports were compiled anyway. `when someStrdefine
== "x": import y` is `cvUnknown` to the scanner, which conservatively
keeps the edge — right for an edge, but it also gave `y` its own `nim m`
rule, so a build died on a package the user never installed because they
never selected that backend. Track which edges are speculative and drop
a speculative subtree that cannot compile; if the guard was in fact
live, the discovery fixpoint puts the node back with the honest `cannot
open file`.
* Deleting a still-imported module went unnoticed: no mtime moves, so
nothing re-fires and `nim ic` relinked a stale binary while `nim c`
reported `cannot open file`. Report an unresolvable import from a
non-speculatively reached module during the graph scan.
* Macro-generated imports were discovered once and then forgotten.
Discovery only ran after a failure and the graph is re-derived
statically every run, so on a warm build the discovered module had no
rules at all and editing it changed nothing. Seed the graph from the
`.s.deps` sidecars up front.
* Config changes invalidated nothing. nifmake decides staleness from
file mtimes and never looks at a rule's command line, so `-d:foo=bar` /
`--mm:` / `--threads:` regenerated the build file with the new switches
and re-fired zero rules. Reify the configuration as a file and make it
an input of every rule.
* Command-line switches never reached the children: they replay the
project's config files, never the driver's argv, so `nim ic --opt:speed`
produced a byte-identical debug binary (likewise `--panics`,
`--experimental`, `--passC`). Forward the driver's switches, minus the
ones that must differ per child.

Artifacts and codegen:

* A failed `nim m` still wrote its `.s.bif` and cookies, so nifmake saw
the rule as satisfied on the next run: `nim ic` then reported success
for a program that does not compile, and generated code from
error-bearing AST (or hit an internal error in `ccgexprs`). Never
persist an artifact when `errorCounter > 0`.
* Top-level destructors were never injected. `sfInjectDestructors` lives
on the module symbol, which `moduleFromNifFile` rebuilds from scratch,
so `genTopLevelStmt` skipped `injectDestructorCalls` entirely: a
module-level `block: let h = openHandle()` never ran `=destroy`. Persist
the flag as a `(modflags)` record. `injectdestructors` also has to
tolerate the `nkReplayAction` entries the loader prepends to `topLevel`.
* `nfFirstWrite` / `nfLastRead` were dropped by the serializer. A sym
node is written as a bare NIF `SymUse` token, which has nowhere to put
node flags, so the frontend's move analysis never reached the backend:
EVERY first assignment to a destructor-bearing local compiled as
`=sink`, i.e. `=destroy` on still-zeroed memory followed by a copy, and
no read was ever a move. Wrap a sym use in `(nflags ...)` when it
carries persistent node flags.
2026-08-27 19:35:11 +02:00
Zoom
bd95f88f74 js: fix var openArray write-through for toOpenArray (#26086)
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.
2026-08-26 18:01:06 +02:00
araq
cce17461de IC: three closure/environment fixes found by differential grinding
A closure's environment type — and the `=destroy`/`=copy`/`=sink` the compiler
lifts for it — is minted by the BACKEND and exists in no module's semmed NIF.
Grinding a corpus of closure programs against `nim c` as the oracle turned up
three ways the per-module backend gets those wrong. `tests/closure` is now green
under `--ic:on`, and `tests/iter` goes from 13 failures to 9.

* **The env hook is emitted by nobody.** `emitsBodyInThisModule` walks up to the
  outermost enclosing routine to pick the TU that emits a body. For the env
  `=destroy` of a generic closure iterator defined in one module and instantiated
  in another, that walk lands on the module of the ORIGINAL generic — a module
  that never sees the instance — so the routine was emitted nowhere
  (`undefined reference to eqdestroy__c485__…`). A backend-minted routine has no
  owning module NIF at all: it is written into the `.t.bif` of every module that
  references it, re-homed there with `@bk`. Every referencing TU emits it now and
  the merge stage keeps one, like any other content-addressed definition.

* **The `:up` link is stored without an increment.** `env.:up = enclosingEnv` has
  to go through `=copy` (with the cyclic incref) or the parent's refcount is one
  too low, and at teardown the two envs' mutually recursive `=destroy`s each
  believe they hold the last reference and recurse until the stack is gone — a
  SIGSEGV after the program's own output has already been printed
  (`tests/iter/tnestedclosures`, "Test 3"). Whether it becomes a `=copy` depends
  on the up-field type's hooks existing at injection time. Whole-program cgen got
  that for free: a LATER lifting pass creates them and it runs before any
  routine's injection. The per-module backend injects a routine right after
  lifting it (`lower`), long before the module's top level is transformed at all
  (`cg`) — so both up-field assignment sites create the ops themselves.

* **Two backend hooks collide on one C name.** A backend-minted symbol is named
  `_c<itemId.item>`, a PER-PROCESS counter — fine while "nifc lifts, emits and
  compiles them in one run", which is what `mangleProcNameExt` assumed. But
  `lower` mints the env hooks of nested routines and `cg` mints those of the
  module's top level, and both land in the same translation unit: two unrelated
  `=destroy`s became one C function. `mangleProcNameExt` already makes an
  exception for hooks whose `disamb` is content-derived (`HookDisambBit`);
  `toNifSymName` now mirrors it, so the content-derived value survives the round
  trip instead of being overwritten by the loader.

`InstanceDisambBit`/`HookDisambBit` move to `astdef` — `ast2nif` names symbols by
them and cannot import `modulegraphs`.

Two new metamorphic tests, both of which fail on the previous compiler:
`tclosure_hooks` (generic closure iterator instantiated across modules) and
`tclosure_nested_iter` (closure iterator nested in a closure iterator).

`icFormatVersion` 37 -> 38 for the hook naming. `koch bootic` reaches its
byte-identical fixed point; `tests/closure`, `tests/destructor` (3 known
`--newruntime` failures), `tests/cpp` and the classic categories are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 12:12:51 +02:00
araq
7ddfc44c0f IC: six more correctness fixes found by the nim c oracle
`testament --ic` on `tests/destructor` went from 10 failures to 3; the three
left are all `--newruntime`, whose owned-ref RTTI destructor is still missing
(the object type's `=destroy` never reaches the `cg` that emits the type info).

* **global destructors are never run.** `graph.globalDestructors` is filled
  while a module's top level goes through `injectDestructorCalls`, and
  whole-program cgen empties the list into the main module's init proc — which
  IS the program body, so the calls land at exit. Under `nim ic` every module's
  `cg` is a separate process and main's only ever saw its own entries: a
  module-level `var` with a `=destroy` in an imported module was simply never
  destroyed. Each module now wraps its own list in a nullary exported proc and
  announces the name in its `.c.nif` meta head (`CnifVersion` 4 -> 5); main's
  `cg` reads the heads it already reads for init/datInit and calls them in
  reverse dependency order. New test `tests/ic/tglobal_dtors.nim` pins the
  order against the oracle.

* **`spawn` died with "system module needs: nimArgsPassingDone".** A module
  loaded from a NIF is named by its mangled suffix, so `loadCompilerProc`'s
  `module.name.s == "threadpool"` could never match. The backend loads the whole
  program before codegen, so consult every loaded module's index instead; misses
  are final and remembered (`getCompilerProc` doubles as a presence probe).

* **`new(x, finalizer)` died with "environment misses: x".** `semmagic`'s
  finalizer-to-destructor wrapper copies the original's AST but only rewrites
  the parameter, leaving `ast[namePos]` naming the ORIGINAL proc. ast2nif
  re-derives a routine's serialized AST from `ast[namePos].sym.ast`, so the
  wrapper serialized the original's body — whose parameter belongs to the
  original — and lambda lifting saw it as a captured variable of another proc.
  The copy now names itself, the invariant every other routine AST keeps.

* **a top-level `let (a, b) = f()` copied instead of moved.** A module's own
  symbol is the owner of every top-level symbol and is written as a real `(sd)`,
  so the loader minted a SECOND `skModule` PSym for it — and `sym.owner == owner`
  is an identity test in `aliasanalysis.isAnalysableFieldAccess`, which made
  every module-level location un-analysable. Hard error for a type with a
  disabled `=copy`. Bind the NIF name to the one registered module symbol.
  Backend only: doing it under `nim m` costs `times.toDateTimeByWeek` its
  inferred `raises`.

* **a doubly linked list leaked its whole contents.** A field USE serializes as
  a bare `SymUse` with nowhere to put symbol flags, so `trees.isCursor` said
  "not a cursor" for every loaded field and `DoublyLinkedNode.prev` became a
  COUNTED reference: every node held its predecessor alive and no refcount ever
  hit zero. `{.cursor.}` now rides in the NIF name marker (`` `fc `` next to
  `` `f ``), which def and use derive from the same `PSym`.

* **`--expandArc` came out shuffled.** `moduleSymbolStubs` iterated a `Table`,
  i.e. hash order, so the `lower` stage transformed a module's routines in an
  arbitrary order — not even stable between two compilers. Order by index
  offset, which is source order.

Also: `testament`'s `generatedFile` did not include the matrix entry in the
nimcache key its caller uses, so every `ccodeCheck` test with a `matrix:`
reported `reCodeNotFound`.

`koch bootic` still reaches its fixed point; `tests/ic` and the classic
`tests/destructor` are unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 08:43:42 +02:00
ringabout
dc242e9027 fixes #26124; internal error: expr: param not init with nested generic procs (#26131)
fixes #26124

The fix preserves the resolved static value, allowing constant folding.

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2026-08-26 08:40:57 +02:00
araq
467c911dc5 IC testing: compare against nim c, and run the real corpus under nim ic
Every invariant `tests/ic` checked was IC-against-IC — clean == incremental, a
no-op edit changes nothing, a body edit moves no interface cookie. An IC that is
*consistently* wrong satisfies all of them, and that is exactly how two silent
miscompilations survived. `koch bootic` has the same blind spot: it proves the
compiler reproduces itself, not that it agrees with the reference backend.

Two mechanisms, at different scales.

**The oracle, in the metamorphic runner.** Every successful step is now also
compiled with `nim c` and run, and the two outputs must agree. Unlike the
hand-written `expect:` strings this needs no foresight from the test author: it
compares everything the program does, not only what someone thought to print,
which is what a silently-skipped destructor evades. `no-oracle` opts out.

The format also grew the expressiveness the recent bug hunt showed was missing —
every one of these described a state the suite could not reach:

* `#!DELETE <file>` removes a module. Deleting a still-imported file moves no
  mtime, so nothing re-fires.
* `#!FLAGS <switches>` changes the compiler switches between steps. A config
  change is not a file either.
* `fails: <substring>` asserts that BOTH compilers reject the program with that
  text. Previously every step had to succeed, so the whole error path — and
  recovery from it — was untested.

Six regression tests cover the eight bugs the last round fixed.
`testament r <file>` now dispatches metamorphic tests like `testament cat ic`.

**`testament --ic` runs the whole corpus through the incremental compiler**, so
IC inherits ~10k programs with expected output instead of 30 bespoke tests.
Two things had to change for that to mean anything:

* `nimcacheDir` now keys on the `matrix:` entry too. Two matrix variants of one
  file are two different compilations; sharing a cache meant each run
  invalidated what the previous left — harmless for a backend that caches only
  object files, useless for an incremental one.
* About half the corpus overrides the command wholesale (`cmd: "nim c --gc:arc
  $file"`), bypassing both `$target` and `$options`. Those are rewritten to `ic`
  and given a private cache.

**Warm cache, hastur-style.** A generated warmup program pulling in `system` and
the most-imported stdlib modules is compiled once per distinct compile
configuration into `nimcache/ic_warmup_<hash>`, and each test's empty cache is
seeded from it with mtimes preserved (nifmake compares output-mtime >
input-mtime, so stamping the copies "now" re-fires the whole graph). Only
program-independent artifacts are copied: the frontend NIFs and cookies plus the
per-module `lower`/`cg` output. The `.c`/`.o` are left behind on purpose — the
merge decision is whole-program, so they are re-rendered for every program
anyway.

`tests/destructor` (97 runs): `nim c` 35s cold / 32s warm; `nim ic` ~3m30 cold /
**9.8s warm**.

Compiler changes this required or uncovered:

* `merge` read the live-module list from a manifest the driver writes instead of
  globbing `*.c.nif` off the nimcache. Globbing absorbed artifacts belonging to
  any other program sharing the directory — which is what made a prefilled cache
  produce undefined symbols at link.
* The build-arg signature no longer includes `--icproject:`/`--icPreparsedConfig:`
  (they name where a build lives, not what it produces, so two caches holding
  identical artifacts got different signatures). The precompiled config still
  counts, by content hash, minus its `(nimcache …)` line.
* `.s.deps` seeding is speculative and runs before the prune, so a sidecar entry
  that has gone stale (an import that a `when` no longer takes) can be dropped
  instead of lingering forever; a pruned module's scan artifacts are deleted so
  an edit-accumulated cache still matches a clean one.
* A failed nifmake run no longer prints an `Error:` of its own. The children have
  already reported; adding a build-system status as the LAST error hid the
  compiler's real message from anything reading the final error — every
  reject-style test under `nim ic` said "nifmake failed with exit code: 1".
* `--mm:hooks` fed the mm mode to an on/off switch and failed outright with
  "'on' or 'off' expected, but 'hooks' found". Pre-existing and unrelated to IC;
  only reachable through the explicit switch, since `--newruntime` sets
  `selectedGC` directly.

Running `tests/destructor` under `--ic` currently leaves 10 failures. They are
genuine IC defects, not harness noise (all 97 pass under `nim c`) — the clearest
is `tglobaldestructor`: `graph.globalDestructors` is accumulated while injecting
destructors into a module's top level, but the main module's `cg` — which emits
the teardown — is a different process, so a module-level `var` with a `=destroy`
is never destroyed. Same shape as the init/datInit metas, and it wants the same
fix: record it in the `.c.nif` head.

Validation: `koch bootic` reaches its byte-identical fixed point; `tests/ic` is
36/36; arc, destructor, macros, template, iter, closure, ccg, codegen, types and
effects pass under `nim c`, with generics showing only its pre-existing failure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 21:00:13 +02:00
Andreas Rumpf
8ca7b75b8b refactoring: better IC + no unique Id (#26137) 2026-08-25 11:59:19 +02:00
ringabout
2d1412a2ea fixes #26015; Multiple definition error when using codegenDecl regression (#26018)
fixes #26015

Fixes imported global variables with codegenDecl being emitted as
definitions instead of extern declarations.

A variable’s codegenDecl format should customize its definition in the
owning module. Other modules referencing the variable must emit a normal
declaration:

```c
extern NI variable;
```

After the variable-declaration builder refactor, genVarPrototype passed
Extern visibility to addVar. However, the sfCodegenDecl branch returned
before applying that visibility. This caused importing modules to emit
another tentative definition, resulting in duplicate-symbol linker
errors.

The fix restores the previous distinction between the custom definition
and cross-module prototypes. It also adds C and C++ regression coverage
for both direct access and access through an inline procedure.

follows up https://github.com/nim-lang/Nim/pull/24423
2026-08-23 12:37:15 +02:00
Constantine Molchanov
37223d2ea9 Feature: Rest: .. include::: Support :start-after: and :end-before: in :literal: mode (#26130)
With this addition, we can include code samples in the docs using
comments as achors. This is analogous to mdBook's
[shiftinclude](https://github.com/daviddrysdale/mdbook-shiftinclude)
preprocessor, which is used extensively in the Status projects docs,
e.g.:
https://github.com/status-im/nim-chronos/blob/master/docs/src/tutorials/http_client/chapter1.md?plain=1#L16

P.S. One missing piece would be the ability to de-dent the included code
automatically but that's a feature for another PR. This isn't as
critical as the ability to include parts of the code.
2026-08-23 12:36:25 +02:00
ringabout
f1256ddcf4 fixes #26123; Update PathKinds1 to include nkCast (#26126)
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
2026-08-21 21:57:51 +08:00
Jake Leahy
81325d0745 Add checks to fromJson when trying to convert to an array (#26109)
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
2026-08-19 08:24:25 +02:00
ringabout
1201c184d7 fix #26112: update variable kinds in isPartOf to include skResult (#26114)
fix #26112
2026-08-17 23:37:46 +02:00
Andreas Rumpf
43f7631b1c Memregion pool no handle (#26110)
Co-authored-by: SirOlaf <34164198+SirOlaf@users.noreply.github.com>
2026-08-17 12:22:53 +02:00
ringabout
16920b56d1 fixes #25992; fix GC tracing of stale bytes in case objects during reset (#26003)
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.
2026-08-15 07:51:13 +02:00
ringabout
cbb3b065c7 fixes #26104; prevent compile-time-only typeof from being treated a… (#26105)
…s a runtime alias

fixes #26104

Follows up https://github.com/nim-lang/Nim/pull/25994
2026-08-14 13:36:55 +02:00
Andreas Rumpf
ebfd1c5090 fixes #26025 (#26076) 2026-08-11 22:27:49 +02:00
ringabout
708d9311e8 fixes #26088; StringStream.write regression (#26089)
fixes #26088
 
in https://github.com/nim-lang/Nim/pull/25772, `beginStores` requires
`newLen` to be passed for setting up the new length. So `streams.nim`
must make the string length exactly newLen.
2026-08-10 07:39:04 +02:00
ringabout
0ec8682abe fixes #26062; ResultUsed warning behaves inconsistently with manual c… (#26087)
…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.
2026-08-10 07:38:49 +02:00
Corey Leavitt
f4e8e04cd0 fixes #26092; restore enclosing cast block state when a nested cast block exits (#26093)
`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).
2026-08-09 11:24:02 +02:00
Emmanuel M. Smith🔸
27763495bc lexer: add seven more unicode operators (#26074)
closes nim-lang/RFCs#571

Adds `⟑ ⟇ ⩓ ⩔ ■ □ ☆` with the same priority as `*`.

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
2026-08-05 16:12:17 +02:00
ringabout
7a1e162b0c closes #26064 and #26063; adds test cases (#26072)
closes #26064
closes #26063
2026-08-04 14:20:13 +08:00
Andreas Rumpf
5a0e4ff6b1 atomicArc: skip the atomic RMW when the cell is uniquely referenced (#26073)
`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.
2026-08-04 00:41:38 +02:00
Zoom
1c37d9a50e std: strbasics.add uses copymem when available (#25768)
`strbasics.add` uses `copymem` when available. CT conditional
scaffolding mirrors the same from system.
    
Follow-up to #15951

Compile-time test for `strbasics.add` undiscarded and passes, though
pending bug #15952 is still open.
2026-08-03 20:46:37 +02:00
Andreas Rumpf
9fc9458844 make yrc properly generational (#26057) 2026-08-03 20:22:14 +02:00
ringabout
23365deef0 fixes #26023; incorrect sink requires a copy (#26028)
fixes #26023
2026-08-03 12:02:29 +02:00
Jacek Sieka
8d18bdb3dc make two-argument withValues untyped (#26052)
The twp-argument form of `withValue` are expression when the branches
themselves are expressions.
2026-08-03 11:55:32 +02:00
ringabout
234f01510f fixes #25942 #25938; type inference for static container type (#25989)
fixes #25942 
fixes #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.
2026-08-03 11:52:04 +02:00
Ryan McConnell
5137d273e5 fix #25993; In-place object construction zeroes destination before evaluating self-referencing field values (#25994) 2026-08-03 11:51:16 +02:00
ringabout
f6651e6c70 fixes #26045; #26046; when nimvm leak push options (#26047)
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.}.
2026-08-03 11:49:48 +02:00
subotac
95557ad48c fixes #26027; use valid compare-exchange failure orders (#26066)
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.
2026-08-03 11:27:58 +02:00
Ryan McConnell
2d81149294 unwrap typedesc in semSet to enable stuff like set[T.distinctBase] (#25924)
`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`.
2026-07-26 18:08:44 +02:00
pacien
0021205854 std/xmltree/constructor macro: fix quoting in output (#26039) (#26040)
`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
2026-07-25 17:08:45 +02:00
SirOlaf
f17755782a Asyncdispatch: Process callbacks before timers (CI issue) (#26032)
Should fix
https://github.com/nim-lang/Nim/blob/devel/tests/async/tasyncclosestall.nim
(the flaky one) in CI.

Previously CI was somehow slow enough to race on completion through
multiple callback layers.

Also increased the message size to hopefully fill the socket's buffer
quicker
2026-07-24 22:33:56 +02:00
ringabout
99a696e0c4 fixes #26010; Double destroy with {.cursor.} (#26031)
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.
2026-07-24 14:07:15 +02:00
cryo2010
8e8f8de1ab fix: exception leak in closure iterator typed except branches (#23615) (#26034)
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.
2026-07-24 14:06:27 +02:00
Andreas Rumpf
b3e21240a6 YRC: cleanups and tests (#26026) 2026-07-22 20:19:32 +02:00
ringabout
0cf1bc3835 fixes #26019; deepCopy should not be allowed for non-copyable type (#26030)
fixes #26019
2026-07-22 12:36:48 +02:00
Jaremy Creechley
adda34bcb8 add --genBif for semantic BIF output on non-IC builds (#26001)
## Summary

Adds `--genBif:on|off`, allowing regular compiler builds to generate
per-module semantic BIF artifacts in `nimcache`.

This reuses the semantic artifact format produced by incremental
compilation without enabling IC or changing the normal code-generation
and linking pipeline.

In comparison to `nim check --compress ...` this new flag `nim c
--genBif:on --compileOnly yourlib.nim` is considerably more useful for
tooling.

That produced full semantic proc declarations, Nim visibility,
signatures, overload disambiguators, and pragmas. For a proc that was
actually code-generated, it also recorded the exact backend name, for
example.

## Motivation

External tools such as language servers, debuggers, and binding
generators can benefit from resolved symbol and type information
produced during an ordinary build. Previously, these semantic BIF
artifacts were tied to the incremental compiler workflow.

## Details

With the option enabled:

```sh
nim c --genBif:on project.nim
```

the compiler writes semantic `.s.bif` files and their supporting
sidecars for each semantically checked module while continuing with the
requested backend normally.

The option:

- Works with non-IC builds.
- Does not enable incremental compilation.
- Does not change generated program behavior.
- Does not enable or introduce native ABI exports.
- Does not generate `.abi.nif` manifests.
- Is ignored for NimScript compilation.

The `genBif` name follows existing artifact-generation options such as
`genScript`, `genMapping`, and `genCDeps`.

## Testing

Added a focused C backend test that runs a regular build with
`--genBif:on` and verifies that semantic `.s.bif` artifacts are
generated.

A release-mode temporary compiler build and the focused Testament test
both pass.
2026-07-20 13:01:30 +02:00
ringabout
2915691515 fixes #26000; Cannot add members to enum-indexed array of seqs at com… (#26013)
…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.
2026-07-20 12:59:18 +02:00
Andreas Rumpf
c4716ed461 YRC: use a side-table for topology (#26022)
- Much better locking scheme
- Run concurrently with the mutators
- Thread local collections
- Tarjan's algorithm for cycle collection
2026-07-20 08:38:54 +02:00
SirOlaf
3bb46d3217 Fix big chunk leak in allocator (#26017)
Fix proposed by GPT 5.6 Sol.

close #26016
Potentially close #22510

No concrete proof for the second one, though the described behavior
matches and the step count explains why it's so difficult to find a
repro.
2026-07-17 01:01:01 +02:00
Andreas Rumpf
fa66510f7a IC: bugfix (#25982) 2026-07-10 14:07:35 +02:00
ringabout
4b1444e728 fix #25976: treat proc-type forbids as an empty tag set (#25980)
fix #25976

Initialize tagEffects for proc types that declare .forbids but omit
.tags,
so they behave like explicit tags: [] during indirect-call effect
tracking.
Add a regression for the nested callback assignment case.
2026-07-10 07:39:32 +02:00
Mamy Ratsimbazafy
e50fafc971 Fix #25883 tuple sighash collision (#25889)
fixes #25886
fixes #25883

See #25883 

Tuples only hash leaves so if 2 tuples have the same flattened
representation they collide in the C codegen.

Fix by hashing the length as well to disambiguate nesting levels.

---------

Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
2026-07-09 15:47:58 +02:00
ringabout
c70a4502d2 fix #25608; improve implicit range conversion checks (#25838)
fix #25608

This pull request improves how the compiler handles warnings for
implicit range conversions, ensuring that only non-constant values
trigger downsizing warnings. It also adds new test cases to verify that
assignments and function calls involving compile-time constants do not
produce unnecessary warnings.

Improvements to range conversion warnings:

* Updated the logic in `compiler/sempass2.nim` to skip implicit range
conversion warnings for compile-time constants by checking if an
expression is constant with `getConstExpr`. Now, only non-constant
values will trigger the warning.

Testing enhancements:

* Added new test cases in `tests/range/timplicitrangedownsizing.nim` to
confirm that assignments and function calls with constant enum and
integer values do not trigger downsizing warnings.
2026-07-06 14:26:14 +02:00
Andreas Rumpf
2a5d36ac52 faster ci (#25966) 2026-07-06 14:00:43 +02:00
Andreas Rumpf
1c61307692 IC related bugfixes (#25946) 2026-07-03 15:52:41 +02:00
ringabout
985b1125b1 fixes genMagicExpr: handle mAsgn for Isolated[T] with primitive types in tuple assignment (#25955)
Explicit `=sink` calls such as `Isolated[T].=sink` can delegate to a
field type like `float`, which has no attached sink op. In that case
`replaceHookMagic` leaves the builtin `mAsgn` call in place.
`genMagicExpr` did not lower that shape, which caused the regression.
Mapping `=sink` to `nkSinkAsgn` and other builtin assignment hooks to
`nkAsgn`.
2026-07-02 19:51:08 +02:00
ringabout
d77f5bcd0f fixes #25803: add genCppConstructorExpr for full type-prefixed expression (#25817)
fixes #25803

This pull request introduces a new approach for generating C++
constructor expressions in the Nim compiler's C++ backend, ensuring that
type-prefixed construction is used when needed (such as in assignments),
rather than just braced initializer lists. It also adds a new test case
(with corresponding C++ header) to verify correct behavior when
assigning to types with overloaded assignment operators and
constructors.

**C++ code generation improvements:**

* Added `genCppConstructorExpr` in `ccgtypes.nim`, which generates a
full type-prefixed constructor expression (e.g., `Foo(a, b)`) for C++
code generation, as opposed to just a braced initializer list. This is
important for contexts like assignments where the type must be explicit.
* Updated `resetLoc` in `cgen.nim` to use `genCppConstructorExpr`
instead of `genCppInitializer` when initializing imported C++ types,
ensuring correct code generation for assignments.

**Testing:**

* Added a new C++ header file, `tcpp_default_ctor_assignment.h`,
defining a struct `AmbiguousAssign` with overloaded assignment operators
and constructors to test ambiguous assignment scenarios.
* Added a corresponding Nim test, `tcpp_default_ctor_assignment.nim`,
which exercises construction and assignment for the imported C++ type,
ensuring the new code generation logic works as intended.
2026-07-02 12:04:43 +02:00