Commit Graph

23068 Commits

Author SHA1 Message Date
Araq
002d9ed0ef progress 2026-06-12 16:27:41 +02:00
Araq
4307b1872a IC: Iface-gate the nifc step 2026-06-12 12:47:06 +02:00
Araq
8b0058efc4 IC: proper iface vs impl distinction, only rebuild dependents if the interface changed or if they depend on implementation (proc bodies) 2026-06-12 09:50:23 +02:00
Araq
cca0fa2f4f make the backend incremental too 2026-06-12 06:05:31 +02:00
Araq
2524b8a1b1 IC: beginnings of the backend porting 2026-06-11 21:51:59 +02:00
Araq
efc52a5635 refactoring 2026-06-11 10:36:04 +02:00
Araq
c91960ff53 test fixup 2026-06-11 08:56:45 +02:00
Araq
0457ebc170 progress 2026-06-10 19:51:25 +02:00
Araq
b72affa1a1 fix arraymancer regression: gate handleGenericInvocation's copy on Sealed
The early IC fix made the else branch (concrete generic args) copy the
invocation type unconditionally before propagateToOwner. Besides
avoiding the in-place mutation, the copy flips `header != t` for
all-concrete invocations, which activates the searchInstTypes/sameFlags
cached-instance return path that devel skipped - a cached, meta-flagged
instance could be returned where a fresh one was expected.
Arraymancer's build then failed with "cannot cast to a non concrete
type: 'ptr NimSeqV2[Node[Tensor[float32]]]'" in seqs_v2.setLen.

Copy only when the invocation type is actually immutable
(IC-loaded/Sealed); non-IC behavior is devel's again, the IC assert
stays fixed.

Verified: arraymancer tests_cpu.nim builds and links (its test-suite
SIGSEGV in io_npy is pre-existing - a devel-built compiler produces the
identical 226-tests-then-crash). Macro sweep 93/95, tests/ic 5/5,
koch boot -d:release and clean koch bootic reach bit-identical fixed
points.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 14:53:16 +02:00
Araq
cc2afc616f IC: don't mutate loaded types in handleGenericInvocation
When a generic's body is computed by a macro (Bar[T, U] = makeBar(T, U)),
`newbody` after replaceTypeVarsT can be a type loaded from a dep module
- even a builtin like `int` - which is Sealed under IC:

- the in-place flag accumulation `newbody.flags = newbody.flags + ...`
  asserted (and under non-IC silently pollutes the shared type's flags,
  e.g. the global `int`); compute the flags into a local, skip the
  in-place store for Sealed types and feed `result.flags` from the
  local - value-identical for the instance.
- `newbody.typeInst = result` likewise; a loaded body keeps whatever
  its defining module serialized (the field was first-wins anyway).

Both changes are no-ops for non-IC (types are never Sealed there).
Fixes tmacrogenerics. Macro sweep 93/95 - the two remaining fails are
tmacro7 (disabled test, fails identically under non-IC) and
tmacrogetimpl (needs a design decision on getImplTransformed sym
sharing). tests/ic 5/5, koch boot -d:release and clean koch bootic
both reach bit-identical fixed points.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:41:10 +02:00
Araq
5ce2c8f95d IC: codegen the main module in a single C translation unit
Under `nim nifc` the main module is loaded by its source FileIndex, but
its serialized symbols carry the module's NIF suffix, so
registerNifSuffix allocated a SECOND FileIndex for the same module:
top-level globals were emitted into the source-index BModule while
procs went into the suffix-index BModule, and a N_LIB_PRIVATE global
declared in one TU was undeclared in the other (tincremental,
tmacros_various).

Pre-aliasing the suffix to the source index in loadModuleDependencies
unifies the TUs. This was tried before and reverted: the split was
masking a hook C-name disamb collision between sem-lifted (loaded) and
codegen-lifted hooks in the same module. That collision class is gone
since backend-minted symbols mangle as _c<item> (BackendIdOffset), so
the unification is safe now.

Macro sweep 92/95 (fixes tincremental + tmacros_various; remaining:
tmacro7 which fails identically under non-IC and is disabled,
tmacrogenerics, tmacrogetimpl), tests/ic 5/5, clean koch bootic
reaches the bit-identical fixed point.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:17:03 +02:00
Araq
34b3893316 IC: keep NIF-only package-name marker out of symbol stubs
The `pkg marker appended to skPackage NIF names leaked into the
user-visible name of Partial stubs: vmgen's toKey built callback keys
like 'getCurrentException.system.stdlib`pkg', so the VM compiled the
real body of getCurrentException instead of dispatching to the vmops
callback and failed with 'cannot evaluate at compile time:
currException' (tparsefile, ttryparseexpr). The marked name was also a
latent hash-divergence source: sighashes' hashNonProc/hashOwner hash
package names straight off possibly-Partial stubs.

Stubs are now created with the clean name; the marker doubles as a kind
signal, so the stub starts as skPackage and globalName re-appends the
marker when rebuilding the NIF index key. NIF file content is
unchanged.

Macro sweep 90/95 (up from 88, restores the baseline; the 5 remaining
fails are the known deep ones), tests/ic 5/5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 12:32:57 +02:00
Araq
89e8a91db4 IC: progress 2026-06-10 12:04:42 +02:00
Araq
9abbb5c281 deal with regressions 2026-06-09 23:42:00 +02:00
Araq
26a24879c8 progress 2026-06-09 19:40:00 +02:00
Araq
4c8de3bcb8 progress 2026-06-09 16:12:38 +02:00
Araq
4e6e9beea8 IC: fix fwd-decl dup, import-except deps, and field interface leak
- ast2nif.canonicalRoutine: collapse a forward-decl's discarded impl sym
  onto the surviving proto so it is not serialized twice (was an ambiguous
  overload in importers; fixes tnewlit).
- deps.nim: handle `import m except syms` (importexcept) in the dependency
  scanner so the build-order edge is not dropped (fixes strformat->strutils
  ordering).
- ast2nif.writeSymDef: object fields (skField) are no longer marked
  bare-importable (x) in the NIF index; an exported field name leaked into
  importer scope and shadowed a template's open symbol (type mismatch 'T').
  Together these fix tmacro8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 22:49:23 +02:00
Araq
8047413632 IC: proctype handling 2026-06-08 18:33:59 +02:00
Araq
fac1eefd99 IC: bugfixes 2026-06-08 17:25:48 +02:00
Araq
da419ea8c0 progress 2026-06-08 15:43:56 +02:00
Araq
69280d6d75 IC: progress 2026-06-08 13:51:02 +02:00
Andreas Rumpf
d9e28aac8e parser: concept of (#25878)
Co-authored-by: Gerke Max Preussner <gmpreussner@headcrash.industries>
2026-06-08 11:32:04 +02:00
Andreas Rumpf
c84764a097 emit modern NIF-27 (#25877) 2026-06-08 09:13:26 +02:00
ringabout
1d7510dff0 fixes #22936; Generic inheritance matching gives type mismatch when object has members (#25836)
fixes #22936

This pull request improves the compiler's handling of generic type
constraints, specifically for subtypes of generics, and adds a test to
cover this behavior. The main changes are an enhancement to the type
relationship logic in the compiler and a new test case for generic
subtyping with `Future`.

### Compiler improvements for generic subtyping

* Updated `typeRel` in `compiler/sigmatch.nim` to allow generic
constraints (like `F: Future`) to accept not just direct instantiations
but also descendants of the generic family, ensuring more flexible and
correct overload resolution. Inheritance depth is now considered for
overload ranking, making deeper descendants slightly less preferred,
consistent with other inheritance-based matches.

### New test coverage

* Added a test in `tests/typerel/t8905.nim` to verify that generic
constraints correctly accept subtypes of `Future`, including a custom
`B[T, E] = ref object of Future[T]` type, and that overloads like
`take`, `takeMany`, and the macro `checkFutures` work as expected with
these types.
2026-06-08 09:12:00 +02:00
Tomohiro
9b80b2e868 fixes-25655; defining >= operator generates compile error (#25787)
Fixes https://github.com/nim-lang/Nim/issues/25655

---------

Co-authored-by: Andreas Rumpf <araq4k@proton.me>
2026-06-08 09:00:00 +02:00
ringabout
f5930d0bb3 fixes #20811; Nested proc with inner being generic cannot access parameters of outer proc (#25837)
fixes  #20811

This pull request addresses issues with parameter capture in nested
generic procedures and templates, ensuring that outer parameters are
correctly visible and accessible within nested scopes. The main changes
include a fix in the semantic analysis logic and the addition of
targeted regression tests.

### Semantic analysis improvements:
* Updated `semGenericStmtSymbol` in `compiler/semgnrc.nim` to ensure
that parameters from outer scopes are preserved and accessible in nested
generic procedures, fixing visibility issues with captured parameters.

### Added regression tests:
* Added `tests/generics/t20811.nim` to verify that both generic and
plain inner procedures can access parameters from their enclosing
procedure.
* Extended `tests/template/topensym.nim` with a new block for issue
#20811 to test that template-injected parameters are correctly captured
and visible in nested generic procedures.
2026-06-08 08:55:37 +02:00
ringabout
4497d89267 fixes #18238; Nested object construction can zero same memory multiple times for --mm:refc (#25834)
fixes #18238

This pull request makes a targeted change to the object construction
logic in the `genObjConstr` procedure. The main update refines the
conditions under which memory zeroing is required during object
construction, making the behavior more accurate for different garbage
collection and destructor options.

Key logic update:

- Improved the `needsZeroMem` condition in `genObjConstr` to check for
the presence of garbage-collected references and the `optSeqDestructors`
option, instead of relying solely on the selected garbage collector and
field flags. This ensures memory is zeroed only when necessary,
potentially improving performance and correctness.


```c
T1_ = NIM_NIL;
T1_ = ((tyObject_E__uEKympBdEK4SY9anUbpNaLQ*) newObj((&NTIrefe__bJ9cSuxv8xHYxmdolQqFkUw_), sizeof(tyObject_E__uEKympBdEK4SY9anUbpNaLQ)));
nimZeroMem(((void*) ((&(*T1_).z.z.z.z))), sizeof(tyObject_A__G2lWlL9cFqoiWWwZmWqfJ9bA));
(*T1_).z.z.z.z.y = ((NI) 5);
asgnRef(((void**) ((&z1__test8_u12))), T1_);
asgnRef(((void**) ((&z2__test8_u55))), new__test8_u13());
(*z2__test8_u55).z.z.z.z.y = ((NI) 5);
T2_ = NIM_NIL;
T2_ = ((tyObject_E__uEKympBdEK4SY9anUbpNaLQ*) newObj((&NTIrefe__bJ9cSuxv8xHYxmdolQqFkUw_), sizeof(tyObject_E__uEKympBdEK4SY9anUbpNaLQ)));
asgnRef(((void**) ((&z3__test8_u56))), T2_);
(*z3__test8_u56).z.z.z.z.y = ((NI) 5);
```


The original test case has already been fixed for `ORC`, now extends it
to `refc`: if a constructor is fully initialized, it does not need a
zero-fill step
2026-06-08 08:54:15 +02:00
ringabout
f959a02037 fixes #25725; environment misses: s with iterator (#25828)
fixes #25725

This pull request makes significant improvements to symbol handling
during transformation passes in the compiler, particularly for routines
(procedures, iterators) and their parameters. The changes ensure that
when routines are copied (for inlining, closure generation, etc.), all
relevant symbols and type headers are also freshly copied and correctly
owned, preventing subtle bugs from symbol reuse. Additionally, new
regression tests are added to cover previously problematic iterator
cases.

**Improvements to symbol copying and ownership:**

* Introduced `freshOwnedSym` to create a fresh copy of a symbol with a
specified owner, ensuring that transformed routines and their parameters
do not share symbols with the originals, which prevents accidental
aliasing and ownership issues.
* Refactored `freshVar` to use `freshOwnedSym`, centralizing fresh
symbol creation logic.
* Added `introduceNewRoutineHeaderSyms` and `copyRoutineTypeHeader` to
ensure that when routines are copied, all parameter/result symbols and
their types are also freshly copied and mapped, avoiding shared state
between original and transformed routines.
* Updated `introduceNewLocalVars` to use `freshOwnedSym` for routine
symbols and to invoke the new header/type copying procedures, ensuring
correctness in routine transformation.

**Testing and regression coverage:**

* Added new blocks to `tests/iter/titer_issues.nim` to test iterator
transformation edge cases, including scenarios that previously led to
symbol reuse bugs (e.g., bugs #25724 and #25725).
2026-06-08 08:53:10 +02:00
Andreas Rumpf
3c6449dbdd fixes #25850 (#25875) 2026-06-07 19:55:56 +02:00
ringabout
f1ff8b6d9e fixes #25849; fixes #25872; Iteration on elements of array (#25860)
fixes #25849
fixes https://github.com/nim-lang/Nim/issues/25872
2026-06-06 07:58:19 +02:00
Ryan McConnell
46259cd0b8 fix sortVTableDispatchers KeyError on re-entrant method registration via when isMainModule (#25856)
Encountered in realistic scenario. Didn't really look at this one. AI
one shot it lol

When a module defines method-bearing types and a when isMainModule
block imports additional modules that also define methods on the same
type hierarchy, sortVTableDispatchers crashes with:

Error: unhandled exception: key not found: (module: N, item: M)
[KeyError]

Root cause: the itemTable built during vtable sorting is populated
from g.objectTree[baseType], which only contains types from the
current compilation pass. When when isMainModule triggers re-import
of method-bearing modules, the method bucket contains types from both
passes. Types from the first pass have ItemIds not present in the
second pass's object tree, so itemTable[obj.itemId] raises KeyError
at line 155.

Fix: if obj.itemId is missing from itemTable, create an empty slot
array of the correct length. The entry is a local temporary — the
second loop in sortVTableDispatchers only calls setMethodsPerType
for types in the current object tree, so types from the prior pass
retain their already-established dispatch. The entry exists solely to
prevent the KeyError during the assignment loop.

The methodIndexLen used for the new entry is the bucket's slot count,
which is correct for any type in the hierarchy.

Added test tests/method/tvtable_reentry.nim that defines methods
across three types in two compilation passes and verifies dispatch
correctness for all three.
2026-06-05 16:37:00 +02:00
ringabout
4b374eb0a6 stop a temp register from being freed if addressed for lent (#25861)
ref https://github.com/nim-lang/Nim/issues/25849

The important part is in compiler/vmgen.nim:1838: when the VM lowers
a[i] or a.b as an address-producing operation, it emits opcLdArrAddr /
opcLdObjAddr. That returns an alias into the storage owned by the source
register. Before the patch, that source register could still betreated
as a normal temporary and later reclaimed or reused by the allocator.
Once that happened, the address result was still live, but the backing
temp was no longer guaranteed to exist, which is what led to the
nil/illegal-storage crash.

The fix is to pin that source temp by changing its slot kind to
slotTempPerm right after emitting the address load. You can see the same
lifetime rule already existed for the generic addr(...) path around
compiler/vmgen.nim:1551: if the source is a temporary and we take its
address, the compiler marks it permanent so freeTemp won’t recycle it.
The patch extends that exact rule to array and object address loads:

- compiler/vmgen.nim:1843
- compiler/vmgen.nim:1861

slotTempPerm is outside the normal freeTemp range in
compiler/vmgen.nim:248, so once a temp is upgraded to permanent, the VM
allocator stops treating it as reusable. That is the actual root-cause
fix: it preserves the backing storage for the address result until the
surrounding evaluation is done.

The regression test in tests/vm/t25849.nim:8 forces exactly that path
with a local lent iterator over an array and a static VM evaluation.
2026-06-04 13:29:48 +02:00
Corey Leavitt
c8e805a2fa fixes #25595; cursor inference: a recorded mutation extends the variable's liveness (#25864)
fixes #25595

## Bug

A `let` bound to a field of a value-type **case object** with a `ref`
field is inferred as a non-owning cursor, but the cursor's source can be
mutated through the cursor's own ref during a call, freeing the ref
while the borrow still reads it. Use-after-free under arc/orc (refc is
unaffected, it has no cursor inference):

```nim
var destroyed = false
type
  O = ref object
    value: int
    home: H
  W = object
    case k: bool
    of true: r: O
    of false: discard
  H = ref object
    w: W
proc `=destroy`(o: var typeof(O()[])) =
  destroyed = true
proc clear(o: O): int =
  o.home.w = W()             # overwrites h.w via the back-reference -> frees the ref
  doAssert not destroyed     # fails: the element was destroyed during the call
  result = o.value
proc go(h: H): int =
  let c = h.w                # inferred cursor (borrow of h.w)
  result = clear(c.r)
proc main =
  let h = H()
  let o = O(value: 42)
  o.home = h
  h.w = W(k: true, r: o)
  doAssert go(h) == 42
main()
```

The `not destroyed` assert fails: the element is destroyed during the
call, so the following `o.value` read is a use-after-free. The same code
with the `=destroy` guard removed (so the freed `o.value` is actually
read) is reported as `heap-use-after-free` by ASan under `-d:useMalloc
-fsanitize=address`. Longstanding (reproduces back to 2.2.0).
`--cursorInference:off` is a workaround.

## Root cause

Cursor inference (`varpartitions.computeCursors`) cursors `let c = h.w`
unless `dangerousMutation` finds a mutation of `c`'s graph within `c`'s
alive range `aliveStart..aliveEnd`. Here the mutation (the `clear(c.r)`
call) *is* connected to `c`'s graph and *is* recorded with `isMutated`,
but it is recorded at an `abstractTime` just past `c.aliveEnd`, so the
range check misses it.

The gap is timing. `aliveEnd` is set from the last `nkSym` use of `c`. A
call records its argument's mutation *after* traversing the whole
argument subtree (`potentialMutationViaArg`). When the argument is `c.r`
on a case object it is an `nkCheckedFieldExpr` (the discriminant check),
whose extra nodes advance `abstractTime` past `c`'s last `nkSym`. A
plain `nkDotExpr` has no such gap, so the bug needs a case object.

## Fix

In `potentialMutation`, extend the mutated variable's liveness to the
mutation time:

```nim
v.s[id].aliveEnd = max(v.s[id].aliveEnd, v.abstractTime)
```

A variable mutated at time T is provably alive at T, so this only
completes the liveness computation that `dangerousMutation` relies on.
The worst case is an extra copy, never an unsound cursor.

## Note on the locus

The fix is conservative by mechanism (it runs at every recorded
mutation) but perf-neutral in practice: it only suppresses a cursor
where the corrected liveness proves the borrow unsafe (cursor counts are
unchanged on the suites). I can scope it to call arguments if you'd
prefer it narrower.

## Test

`tests/arc/t25595.nim`, matrix `--mm:orc; --mm:arc; --mm:refc`: the
repro above as a `doAssert`. Fails (UAF) on arc/orc before the fix and
passes after. refc passes throughout.

## Checks

- repro passes on orc/arc after the fix. The guard-removed variant
(which reads the freed value) is ASan-clean after the fix and was
heap-use-after-free before. refc unaffected.
- testament `destructor` 90/90, `arc` 120/120. `views` 5/6, same as
stock (the one failure is environmental and pre-exists this change).
- perf-neutral: inferred-cursor count is identical stock vs fix across
the `arc` and `destructor` test files under `--mm:orc` (322 vs 322).
2026-06-03 07:25:33 +02:00
Corey Leavitt
73986c03a1 fixes #25857; don't treat typeof(result) as a use-before-init of result (#25858)
fixes #25857

## Bug

`typeof(result)` inside the expression that builds `result` gets counted
as a read
of `result` before it's set. On a `{.requiresInit.}` return type that's
a hard error
("'result' requires explicit initialization"). `typeof` never evaluates
its operand,
so it's a false positive. On 2.2.4 it compiles, but the same line still
emits a bogus
`ProveInit` warning, so no released version gets it right.

Regression from #25151. That PR made a used-before-init `requiresInit`
result a hard
error instead of a warning, which is correct on its own. The side effect
was that
this old false-positive warning became a build error.

## Root cause

`track` in `compiler/sempass2.nim` has no arm for `nkTypeOfExpr`, so it
hits the
default that recurses into every child, reaches the `result` `nkSym`
inside the
`typeof`, and calls `useVar`. `sizeof`/`compiles`/`declared` don't hit
this because
they fold to a constant before `track` runs. A `typeof(result)` typedesc
argument
survives into `track`.

## Fix

Skip `nkTypeOfExpr` in `track`. Its operand is never evaluated, so it
isn't a
definite-assignment use. After the patch there's no error and no warning
here, even
with `--warnings:on`. The #25151 check is untouched: a real use of
`result` before
init is a plain `nkSym`, not inside a `typeof`, so it still reaches
`useVar`.

## Test

`tests/init/t25857.nim`, a positive test that compiles and prints `1`.

## Checks

- Repro compiles and runs on patched 2.2.6 and patched devel.
- `tests/errmsgs/t25117.nim` still fails as expected. A real
`xxx(result)` before
  init still errors.
- `testament cat init` and `testament cat errmsgs` green on patched
devel (55 tests,
  0 failures), including the `--warningAsError:ProveInit` tests.
- Bisect: parent `1ab68797` good, `576c4018` (#25151) bad.
2026-06-02 07:07:44 +02:00
ringabout
88a18de44f fixes #25851; ensure --panics:on does not skip nimErr_ check after closure calls (#25855)
fixes #25851

## Summary: `--panics:on` drops `nimErr_` check after closure calls
(#25851)

### Bug

With `--exceptions:goto` and `--panics:on`, the compiler skipped the
`nimErr_` check after indirect closure calls whose result flows directly
into another call (e.g., `result.add elem(src)`). A raise inside the
closure was silently swallowed — the loop continued, and the next
`raise` hit the already-set `nimInErrorMode` flag, overflowing its
`bool` storage into `OverflowDefect`.

### Root Cause

**ast.nim** — `canRaise` checked `fn.typ.n[0].len < effectListLen` first
(false after the expansion) and then `exceptionEffects != nil` (also
false, nil), so it returned `false` — meaning "cannot raise." The C
codegen trusted this and omitted the `nimErr_` check.

### Fix

**ast.nim** — `canRaise` now treats `nil` `exceptionEffects` as "unknown
→ can raise" (`exceptionEffects == nil` as an additional true
condition). This is defense-in-depth: even if some other path expands
the list but leaves `exceptionEffects` nil (e.g., a type with `{.tags.}`
but no `{.raises.}`), the error check is still emitted.

### Test

tclosure_err_panic_goto.nim — exercises the double-trigger pattern
(`drawBool` sets the error flag → closure call must propagate it) with
`matrix: "; --panics:on"` covering both exception modes.
2026-06-01 16:21:37 +02:00
Andreas Rumpf
7813bd8b92 fixes #25693 (#25842) 2026-05-29 08:08:42 +02:00
ringabout
645e131739 fixes #25796; fixes procParamTypeRel to ensure backend type consistency (#25798)
fixes #25796

This pull request addresses a subtle type-matching issue in the Nim
compiler related to backend type compatibility, particularly for
procedures returning `lent` types. It also adds new test cases to ensure
correct handling of these scenarios.

**Compiler type-checking fix:**

* Updated `procParamTypeRel` in `compiler/sigmatch.nim` to skip wrappers
like `tyVar`, `tyLent`, `tySink`, and `tyOwned` before comparing backend
types, ensuring more accurate type equivalence checks for procedure
parameters and return types.

**Test coverage improvements:**

* Added multiple blocks in `tests/proc/tproc.nim` to test procedure
types returning `lent` objects, including cases with constants,
variables, and union parameter types, verifying that the compiler now
correctly handles these cases.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-29 07:58:23 +02:00
puffball1567
7d2f28b046 fixes ReraiseDefect after typeless except: + finally: (cpp backend) (#25777)
## Bug

A bare `except:` followed by a `finally:` block raises a spurious
`ReraiseDefect: no exception to reraise` when compiled with `nim cpp`:

```nim
proc test() =
  try:
    raise newException(CatchableError, "x")
  except:
    discard
  finally:
    echo "finally"

test()
echo "after"
```

Expected output:
```
finally
after
```

Actual output:
```
finally
fatal.nim(53)            sysFatal
Error: unhandled exception: no exception to reraise [ReraiseDefect]
```

This reproduces on every memory manager (`--mm:arc`, `--mm:orc`,
`--mm:refc`).

## Root cause

`genTryCpp` emits `try { ... } catch (Exception* T_) { ... }` followed
by a finally block that ends with `if (T_) std::rethrow_exception(T_);`.
In the *typed* except branches the codegen explicitly sets `T_ =
nullptr;` once the exception is handled, so the rethrow check in the
finally is a no-op.

The typeless `except:` branch (the `if t[i].len == 1` arm) emitted only
`popCurrentException()` and forgot to clear `T_`. After the handler body
finished, `T_` still pointed at the original exception, so the trailing
`if (T_) std::rethrow_exception(T_);` rethrew it. By that point Nim's
current-exception stack had already been popped, and the rethrow
surfaced as `ReraiseDefect`.

## Fix

Emit `T_ = nullptr;` at the start of the typeless `except:` handler
body, mirroring what is already done for the typed branches. This is the
same one-line treatment that fixed the analogous typed-except case for
#5871.

## Tests

Adds `tests/exception/treraise_typeless_except_finally.nim`, exercising
the bug pattern on `--mm:arc`, `--mm:orc`, and `--mm:refc`.

Locally:
- `tests/exception/` — 43 PASS, 0 FAIL, 3 SKIP
- new test passes on all three memory managers

## Backport

Tagged `[backport]` in the commit message — the same bug exists in
`version-2-2` and the fix applies cleanly there.

## Related

Independent of, but in the same family as, #25775 (also currently open).
Both are silent-finally / cpp-backend exception handling fixes; they
touch different lines of `genTryCpp` and don't conflict.

Co-authored-by: puffball1567 <17452514+puffball1567@users.noreply.github.com>
2026-05-29 07:53:37 +02:00
Antonis Geralis
f4dd00c4cc Scan until next special char (", \, \0, \c, \L) and append that slice once. (#25498)
Benchmark comparison (-d:danger --mm:arc --debugger:native -d:useMalloc,
  OpenAI file benchmark, 5 runs):

- Before: 0.196674934, 0.189423191, 0.198763300, 0.197125584,
0.205015032
- After: 0.182827130, 0.183330852, 0.174878542, 0.174360811, 0.181704921
  - Median before: 0.197125584s
  - Median after: 0.181704921s
  - Improvement: 7.82% faster

  Callgrind comparison (same build flags):

  - Total Ir before: 3,219,477,120
  - Total Ir after: 2,449,556,167
  - Total Ir reduction: 23.91%

  parseString hotspot:

  - Before: 1,343,343,723 Ir
  - After: 573,423,735 Ir
  - Reduction: 57.31%
2026-05-27 23:31:39 +02:00
ringabout
3e2cea21ed fixes #22791; ProveField warning with nested case object (#25774)
fixes #22791

This pull request introduces a minor improvement to the handling of
immutable variables in the compiler and adds a new test case for nested
case objects. The most important changes are:

### Compiler improvements

* Updated the `isLet` guard in `compiler/guards.nim` to recognize
`skConst` symbols as immutable variables, ensuring that constants are
correctly identified alongside lets and other immutable types.

### Test coverage

* Added a new test in `tests/objvariant/tcorrectcheckedfield.nim` for
bug #22791, verifying correct pattern matching and field access in
nested `case` objects with constants.
2026-05-27 23:29:27 +02:00
ringabout
cfa769fefc fixes #22950; Poor error message on cast effect violation (#25839)
fixes #22950

This pull request improves the tracking and reporting of effect
annotations (such as `raises`, `tags`, and `forbids`) in pragma blocks,
particularly when using the `cast` pragma. It ensures that the source of
these effect annotations is correctly preserved and referenced, which
improves error reporting and effect analysis. Additionally, a new test
was added to check for violations when using `cast` with effect
annotations.

Effect annotation source tracking and propagation:

* Added new fields (`excSource`, `tagsSource`, `forbidsSource`) to the
`PragmaBlockContext` type to store the original source node for each
effect annotation.
* Updated `castBlock` to set these new source fields when processing
`raises`, `tags`, and `forbids` pragmas, ensuring the source node is
preserved for later error reporting.
* Modified `unapplyBlockContext` to use the stored source node (if
available) when calling `addRaiseEffect`, `addTag`, and `addNotTag`,
improving the accuracy of effect tracking and diagnostics.

Pragma handling improvements:

* Changed the call to `castBlock` in the main pragma processing loop to
pass the entire pragma node, enabling access to the original source for
effect annotations.

Testing:

* Added a new test (`tests/effects/tcast_effect_violation.nim`) to
verify that using `cast(raises: ValueError)` inside a procedure with
`.raises: [].` correctly triggers an error message about an unlisted
exception.
2026-05-27 23:28:27 +02:00
ringabout
8771451701 closes #25294; adds a test case (#25833)
closes #25294
2026-05-22 10:32:14 +08:00
ringabout
43ac102ca8 fixes #25800; move now uses its declaration for overridden =wasMoved (#25809)
fixes #25800
closes https://github.com/nim-lang/Nim/pull/25807
ref https://github.com/nim-lang/Nim/issues/25800

This pull request improves the handling of move semantics and the
`=wasMoved` hook in the Nim compiler, especially for C++ code generation
and user-defined types. It refactors the move operation logic to better
support custom hooks, adds new tests for edge cases, and ensures that
the `move` operation is safer and more predictable.

**Move semantics and `=wasMoved` handling:**

* Refactored the move operation in `compiler/ccgexprs.nim` by
introducing helper procs (`canGenMoveCall`, `genMoveCall`,
`genWasMovedCall`, `genMoveWithWasMoved`) to better handle cases with
user-defined `=wasMoved` hooks, especially for generics and C++ interop.
The logic now distinguishes between simple assignments and when to call
custom hooks, improving correctness and maintainability.
[[1]](diffhunk://#diff-4509107d295d7d32b1887c8993cd0f56113ae60f36113e7d8778646dabd92ebcL2818-R2851)
[[2]](diffhunk://#diff-4509107d295d7d32b1887c8993cd0f56113ae60f36113e7d8778646dabd92ebcL2841-R2882)
* Updated the `move` proc in `lib/system.nim` to include the `nodestroy`
pragma, preventing double destruction and making move semantics safer.

**Testing and validation:**

* Added a new test (`tests/ccgbugs2/t25800.nim`) to ensure that
user-defined `=wasMoved` hooks with `{.importcpp.}` are correctly
generated and invoked in C++ code, addressing a specific bug with
invalid preprocessor directives.
* Expanded `tests/destructor/twasmoved.nim` with additional test cases
for objects with and without custom `=wasMoved` hooks, including
multithreaded scenarios using `threadpool`, to verify correct behavior
in a variety of contexts.

**Minor cleanup:**

* Added a blank line for code style consistency in
`compiler/semmagic.nim`.
2026-05-21 13:42:38 +02:00
Rybnikov Alex
393d27b57d fix(stdlib): use first-element flag in $ for collections (#18583) (#25832)
Fixes #18583.

## Problem

Several stdlib collection types compute the separator for `$` using
`result.len > 1`, where `result` starts as the opening bracket (`"["` or
`"{"`). This breaks when a collection element type has a `$` that
returns an empty string: `result.len` stays at 1 after the first item
contributes nothing, so the separator is never inserted for subsequent
items.

```nim
import std/deques

type Test = object
proc `$`(x: Test): string = ""

echo [Test(), Test()].toDeque  # prints [] — expected [, ]
```

## Fix

Replace the length check with an explicit `first` flag in all affected
modules: `deques`, `heapqueue`, `lists`, `critbits`, and `strtabs`.

## Tests

Regression tests added to `tdeques`, `theapqueue`, and `tlists` using a
local type whose `$` returns `""`. All three test files pass with `nim c
-r`.

## Notes

I work with Claude as a co-processor. I'm 56, came to programming late,
and this is genuinely how I learn and contribute. I understand what I'm
submitting, but I didn't write it alone. If your project prefers
human-only contributions, just say so and I'll close without friction.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 13:40:35 +02:00
Andreas Rumpf
9f5c193c1d fixes #25814 (#25816) 2026-05-19 23:28:13 +02:00
Pedro Batista
4f6b727d9e pegs: accept UTF-8 bytes in bare identifier terminals (#25829)
## Summary
- Fixes `std/pegs` lexing for bare UTF-8 terminals such as `\i café`.
- The lexer previously stopped at the first non-ASCII byte, so
`pkTerminalIgnoreCase` never saw the full term despite its rune-aware
`fastRuneAt`/`toLower` matching.
- This now keeps non-ASCII bytes in identifier-style terminals while
ASCII non-ident characters still terminate the symbol.

## Behavior
Before: `match("CAFÉ", peg"\i café")` failed because the terminal was
lexed as `caf`.
After: `match("CAFÉ", peg"\i café")`, `match("Café", peg"\i café")`, and
`findAll` over mixed-case occurrences pass.

`std/pegs` documents `useUnicode = true` as proper UTF-8 support, and
quoted terminals already preserved the same bytes; this makes bare
terminals consistent with that path.

I did not find an existing relevant issue or PR in searches for
pegs/unicode/utf8/getSymbol/pkTerminalIgnoreCase.
2026-05-19 23:27:48 +02:00
ringabout
f9647276d8 fixes #25821; unary minus off by one mistake [backport] (#25823)
fixes #25821

This pull request includes a minor bug fix in the lexer and adds new
test cases for string formatting with binary operators in interpolated
expressions.

Lexer bug fix:

* Fixed an off-by-one error in the unary minus detection logic in the
`rawGetTok` procedure in `lexer.nim`, ensuring that the start-of-buffer
condition is correctly checked.

Testing improvements:

* Added tests to `tstrformat.nim` to verify that binary operators (such
as subtraction) work correctly inside interpolated string expressions
using both `&` and `fmt`.
2026-05-18 07:55:33 +02:00
vip892766gma
2c946950f4 fix: duplicated "to" in alloc.nim comments (#25813)
Two one-line typo fixes for duplicated "to" in `lib/system/alloc.nim`:
- "# set 'used' to to true:" → "# set 'used' to true:" (occurs twice,
lines ~694 and ~711)

No code/behavior change.

Co-authored-by: Aiden Park <275402320+vip892766gma@users.noreply.github.com>
2026-05-14 08:02:36 +02:00
oab24413gmai
bbc5bbdcc7 fix: duplicated words in manual.md and gc_common.nim comment (#25812)
Two one-line typo fixes for duplicated words:
- `doc/manual.md` — "if the the type was marked as `bycopy`" → "if the
type was marked as `bycopy`"
- `lib/system/gc_common.nim` — "## thread stack is is returned." → "##
thread stack is returned."

No code/behavior change.

Co-authored-by: Mira Sato <275437409+oab24413gmai@users.noreply.github.com>
2026-05-14 08:02:11 +02:00
Andreas Rumpf
6204e48ba5 SSO strings: bugfix (#25810) 2026-05-12 23:20:10 +02:00