`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`.
`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
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.
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.
## 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.
…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.
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.
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.
fixes#25886fixes#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>
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.
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`.
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.
fixes#25945
When `@[]` appears inside a nested `if` expression that also contains
statements, the AST wraps it in `nkStmtListExpr` nodes. The empty
container's `tyEmpty` element type was never resolved to a concrete
type, causing the C codegen to ICE with "cannot map the empty seq type
to a C type".
Walk through nested statement-list/block expressions in
`fitNodePostMatch` to find the innermost value node and propagate the
formal type to empty containers.
fixes#25949
track still walks both branches of preserved when nimvm, but it now sets
a small inNimvmBranch flag while visiting the VM branch, and trackCall
skips sfCompileTime marking only when that flag is set.
This PR implements `expandSymlink` on Windows with POSIX readlink
semantics: it expands exactly one hop and returns the stored link target
without resolving the full chain.
The main design question was whether Windows symlink expansion should be
built on path-finalization APIs such as `GetFinalPathNameByHandleW`, or
on direct reparse-point inspection. Current `expandSymlink` is a
single-hop "what target is stored in this link object?" operation and
most of other ways to resolve symlinks on Windows actually try to answer
the "final true file location" question in various slightly-incompatible
ways.
The full final-path resolution on Windows is substantially more complex
than readlink and is planned as a follow-up.
## Implementation choice
Implements Windows `expandSymlink` by:
- opening the path with `FILE_FLAG_OPEN_REPARSE_POINT`
- calling `DeviceIoControl(FSCTL_GET_REPARSE_POINT)`
- parsing the reparse payload for `IO_REPARSE_TAG_SYMLINK` and
`IO_REPARSE_TAG_MOUNT_POINT`
- decoding the UTF-16 slice referenced by the payload
- returning the stored target
This is the right primitive for the API:
- does not depend on whole-path finalization
- works for both symlinks and junctions
- matches the existing Linux behaviour
`widestrs` changes allow using WideCString views without temporary
allocations.
Windows prohibits symlink creation without admin rights, so,
unfortunately, the tests are conditionally skipped by default. Manually
running `testament` in an admin console is required.
## Behaviour:
- One hop only
- Relative symlink targets are returned unchanged
- Absolute Windows targets are converted from stored NT-style prefixes
to usable Win32 forms when applicable
- Non-links, malformed payloads, and unsupported reparse tags raise
`OSError`
## Future work
Path canonicalization, i.e. "final true file location". Which is, BTW,
different from `absolutePath`, which works on paths only and doesn't hit
the underlying FS. So this needs to be an API extension.
I'd like to follow-up with this when I sort through the docs, for now
you can resolve symlinks in a loop.
---------
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
fixes#25931
This fix addresses bug #25931 — a signature hash collision with
importc-aliased pointer/cstring types.
The problem: In compiler/sighashes.nim, the hashType procedure
canonicalizes types for signature hashing. Integral types
(tyInt..tyUInt64, etc.) were excluded from canonicalization so that
types like pid_t (an importc alias) keep their backend spelling. But
pointer and cstring types with importc annotations were not excluded —
meaning a type like:
type N {.importc: "const void *".} = pointer
...would be collapsed to just pointer in the signature hash, causing
collisions when N and pointer are both used in procedure types within
the same compilation unit.
The fix (compiler/sighashes.nim:193): Adds tyPointer and tyCstring to
the list of types that skip canonicalization, right alongside the
integral types. The comment is updated to clarify this applies to
"builtin scalar-ish / pointer-like types".
The test (tests/ccgbugs/tsighash_typename_regression.nim:33-42): Adds a
regression test exercising the exact scenario — an importc pointer alias
used in both an object field type and a proc parameter type.
fixes#25908
When an enum identifier is resolved as an `nkSymChoice`, one of the
candidates may come from a loop-local view and carry `tyVar` or
`tyLent`.
Enum disambiguation should compare the underlying enum type only.
Otherwise a pure-enum field can win incorrectly even though the intended
symbol is already present in the choice set.
Keep the `includePureEnum` lookup path for enum-typed expectations so
#23976 still works, but normalize `var`/`lent` only at the symchoice
selection point.
closureiters.nim splits a stmt list at yield points, moving post-yield
code into a new state body. When that stmt list was inside a pragma
block like `{.cast(uncheckedAssign).}`, the new state's body was created
as a bare nkStmtList without the wrapper.
Fix: track the enclosing pragma block in the transform context, and wrap
newly-created state bodies in a copy of it when the split occurs inside
one. Added an explicit `nkPragmaBlock` case to
`transformClosureIteratorBody` that saves/restores `ctx.enclosingPragma`
around its body.
Companion to readRawData whose pointer stays valid across moves/copies
of the string. Under --strings:sso it promotes a small inline string to
its heap representation; under refc/v2 the data is already heap-resident
so it aliases readRawData.
Uniform `var string` signature on every backend so code can prepare for
--strings:sso without `when declared`.
fixes #22122
The commit fixes a bug in Nim's effects checker where raise statements
with case/if expressions (commonly from template expansion) failed to
track exception types from individual branches.
Problem: addRaiseEffect only saw the outermost expression. When a
template like getTransportError(err) expanded to a case expression
raising 3 different exception types, the compiler only registered the
top-level call — missing the branch-level exceptions.
Fix (2 files):
- compiler/sempass2.nim: Added skipHiddenConv to strip implicit type
coercion nodes (nkHiddenStdConv/nkHiddenSubConv) that hide the control
flow structure. Added addRaiseEffectsFromExpr that recursively walks
into case/if/block/stmtlist expressions to find raise effects in each
branch body. Changed the nkRaiseStmt handler to use this new function.
- tests/effects/tcase_raises.nim: Test with templates that expand to
case expressions raising different exception types, verified via
{.raises: [].} pragma.
fixes#18367fixes#21222
1. In vmdef.nim:304, newCtx now sets templInstCounter: new int when it
builds TCtx.
2. vm.nim:1490 — During VM execution of templates, c.templInstCounter is
passed to evalTemplate
3. evaltempl.nim:204 — instID: instID[] dereferences the ref int
4. With a nil templInstCounter, this would crash
5. The same initialization already exists on the semantic side in
sem.nim:787, so this change makes the VM path consistent with the rest
of the compiler.
fixes#25903fixes#25904
`nkExceptBranch` can have variable structure depending on the exception
types and it should handle the last node of the `nkExceptBranch`
I don't like it, but seems like this is correct. Concept type classes
have to behave like other "named" type classes and participate in "bind
once" mechanics or require some weird semantics. As a side note I'm
pretty sure the `tuple` example in the manual explaining this is either
wrong now or has regressed, but I don't think it matters because I doubt
anyone thinks about this feature much.
#25778
This PR adds 3 modes to `typeof` to specify how to handle type modifiers
`var`, `sink` and `lent`.
- typeOfModCompatible
Remove or keep type modifiers in the same way as old typeof. That means
keep `sink` but remove `var` and `lent`.
- typeOfModRemoveModifier
Remove type modifiers.
- typeOfModKeepModifier
Keep type modifiers.
Related to https://github.com/nim-lang/Nim/pull/25779https://github.com/nim-lang/Nim/issues/25786
Reverts nim-lang/Nim#25845
```nim
case ecode
of ECONNABORTED, EPERM, ETIMEDOUT, ENOTCONN:
getConnectionAbortedError(ecode)
of EMFILE, ENFILE, ENOBUFS, ENOMEM:
getTransportTooManyError(ecode)
else:
(ref TransportOsError)(code: ecode,
msg: "(" & $int(ecode) & ") " & osErrorMsg(ecode))
```
The compiler inserts a hidden conv for the case expression. Perhaps we
can skip hidden convs to inspect the types that are actually raised
fixes#22122
The root cause is in the effect tracker: raise was recording the whole
conditional expression as one exception source, so semantic checking
only saw the widened common base type instead of the concrete exception
classes from each branch.
## Summary
Fixes#19782.
The `?` operator in `std/uri` was silently overwriting any query string
already present in the URI. This PR makes it append instead — which
matches the docstring ("Concatenates the query parameters") and the
natural expectation when chaining operations.
**Before:**
```nim
let u = parseUri("https://example.com/foo?existing=1") ? {"bar": "qux"}
echo $u # https://example.com/foo?bar=qux (existing=1 lost)
```
**After:**
```nim
let u = parseUri("https://example.com/foo?existing=1") ? {"bar": "qux"}
echo $u # https://example.com/foo?existing=1&bar=qux
```
## Changes
- `lib/pure/uri.nim`: fix `?` to append with `&` when a query string
already exists; add example to `runnableExamples`
- `tests/stdlib/turi.nim`: two new test cases (append to existing query,
empty params preserve existing)
- `changelog.md`: entry under Standard library changes
## 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>
Co-authored-by: n0madgang <14005836+n0madgang@users.noreply.github.com>
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.