Skip to content

Support size pragma with generic type parameters#25480

Open
elijahr wants to merge 4 commits intonim-lang:develfrom
elijahr:feature/deferred-pragma
Open

Support size pragma with generic type parameters#25480
elijahr wants to merge 4 commits intonim-lang:develfrom
elijahr:feature/deferred-pragma

Conversation

@elijahr
Copy link
Copy Markdown
Contributor

@elijahr elijahr commented Feb 4, 2026

Allows imported types to use size: sizeof(T) where T is a generic type parameter, with evaluation deferred until instantiation.

type Atomic[T] {.importcpp: "std::atomic", size: sizeof(T).} = object

When instantiated as Atomic[int64], the size pragma evaluates to 8.

Implementation

  • Uses a sparse side-table (typeExtensions in ModuleGraph) rather than adding fields to PType/PSym, avoiding per-type memory overhead.
  • Includes an attempt at NIF serialization.

Changes

  • compiler/typeext.nim - new module for TypeExtKind/TypeExtension types
  • compiler/modulegraphs.nim - typeExtensions side-table
  • compiler/pragmas.nim - defer size pragma when expr contains generic params
  • compiler/semtypinst.nim - evaluate deferred extensions at instantiation
  • compiler/ast2nif.nim - NIF serialization/deserialization
  • lib/pure/concurrency/atomics.nim - use deferred size, remove dummy field

Supersedes #25403 per feedback from Araq.

Allows imported types to use `size: sizeof(T)` where T is a generic
type parameter, with evaluation deferred until instantiation.

Example:
  type Atomic[T] {.importcpp: "std::atomic", size: sizeof(T).} = object

When instantiated as Atomic[int64], the size pragma evaluates to 8.

Implementation uses a sparse side-table in ModuleGraph rather than
adding fields to PType/PSym, avoiding per-type memory overhead.
Only types with deferred pragmas consume storage.

Includes NIF serialization support for incremental compilation.
@Araq
Copy link
Copy Markdown
Member

Araq commented Feb 6, 2026

Well the CI is red.

The containsGenericParam function was incorrectly treating any
unresolved identifier (nkIdent) as a potential generic parameter.
This caused sizeof(cint) on non-imported enums to fail with
'deferred size expressions only supported for imported types'.

Fix by looking up identifiers in scope:
- If unresolved: likely a generic param, defer evaluation
- If resolves to skGenericParam: defer evaluation
- If resolves to a known type (like cint): evaluate immediately

Also fix substituteTypeParams to handle nkIdent nodes by matching
against generic param names, and handle leaf nodes to avoid
iterating over nodes without children.

Fixes package CI failures in weave, sdl2_nim, constantine, etc.
@elijahr
Copy link
Copy Markdown
Contributor Author

elijahr commented Feb 7, 2026

@Araq Sorry about that, I have been swamped. I should have some time to address it over the next few days.

When Atomic[T] is used inside a generic proc like store[T], the type
instantiation creates Atomic[store.T] where T is still unresolved.
The previous fix incorrectly treated nkIdent nodes (like 'sizeof' or
'int8') as unresolved generic types, causing deferred size evaluation
to never complete.

This fix:
- Adds containsUnresolvedGenericType to check if substituted expression
  still has unresolved generic params (nkType with tyGenericParam)
- Returns false for nkIdent nodes since they are regular identifiers
  that will be resolved during semantic analysis
- Defers size evaluation only when expression truly contains unresolved
  generic types, allowing direct instantiations like Single[int16] to
  evaluate correctly
@elijahr
Copy link
Copy Markdown
Contributor Author

elijahr commented Feb 9, 2026

Okay, CI is green now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants