Skip to content

[prototype] Approach 3: PartialTypeSpec lazy generic types (vs Stage-0)#66

Draft
havogt wants to merge 1 commit into
dtype-generics-2-type-systemfrom
dtype-generics-3-partialtypespec-prototype
Draft

[prototype] Approach 3: PartialTypeSpec lazy generic types (vs Stage-0)#66
havogt wants to merge 1 commit into
dtype-generics-2-type-systemfrom
dtype-generics-3-partialtypespec-prototype

Conversation

@havogt

@havogt havogt commented Jun 22, 2026

Copy link
Copy Markdown
Owner

⚠️ Prototype — design comparison only, not for merge

This branch is one of four explored alternatives to Stage-0's handling of dtype-generic
operators. It exists so the diff against the Stage-0 branch (dtype-generics-2-type-system,
base of GridTools#2660) is reviewable. Do not merge.

Problem being explored

Stage-0 widened FieldType.dtype to ScalarType | ListType | TypeVarType. Because
type_specifications.py is shared between the frontend and the IR/backend, that widening
forces ~15-20 defensive assert isinstance(dtype, (ScalarType, ListType)) narrowings across
iterator/ + program_processors/ (dace dominates). The invariant "no TypeVarType reaches
GTIR" is real (monomorphization) but unenforced by the type system.

What Approach 3 does

  • Reverts FieldType.dtype to the concrete ScalarType | ListType — the existing
    concrete type system (and the entire IR/backend) is untouched, and every Stage-0 narrowing
    assert/cast is removed (see the dace/lowering/* and global_tmps.py hunks).
  • Expresses all dtype genericity through a new, separate PartialTypeSpec(target, fields)
    wrapper: a type-tag (target: type[TypeSpec]) plus partial field values, some of which may
    be a TypeVarType. The IR/backend never sees it.
  • Specialization substitutes the bound vars and materializes a real concrete FieldType
    so GTIR only ever observes concrete types. eve now runtime-rejects FieldType(dtype=TypeVarType),
    turning the invariant into an enforced fact rather than a comment.

Files

File Role
type_specifications.py revert FieldType.dtype to concrete; add PartialTypeSpec
partial_type_info.py (new) bind / substitute / specialize / is_generic over PartialTypeSpec
type_info.py TypeVar-awareness reverted out (now lives in the wrapper)
dace/lowering/*, global_tmps.py narrowing asserts / cast removed
_approach3_deduction_sketch.py (new) illustrative only, not imported/wired — shows the type-deduction impact (the central cost)

The tradeoff (vs Stage-0 and the inverse-subclass alternative)

  • Backend: pristine. Zero asserts, zero TypeVarType references in iterator/ +
    program_processors/. Cache keys safe (PartialTypeSpec.content_hash is deterministic and
    cross-process-stable; serializes target by qualified class name).
  • Frontend: the cost. Stage-0 centralizes genericity in ~6 predicates (a generic field is
    structurally still a FieldType, so every deduction site inherits it for free). Approach 3
    distributes it: ~15-25 frontend type-deduction sites that match-and-reconstruct a field
    each need a parallel case ts.PartialTypeSpec(...) arm, plus a permanent second representation
    of "a field." _approach3_deduction_sketch.py illustrates that.
  • Future dimension-genericity: generalizes the most naturally — a generic dims is just
    another slot value, no new mechanism.

Validation

Reported by the prototype run: uv run mypy src/ clean (358 files); type_system unit + ffront
unit tests pass. Independent re-verification of mypy src/ is in progress; result will be added
as a comment.

… types

Experiment for design comparison vs Stage-0 (GridTools#2660). NOT for merge.

Reverts FieldType.dtype to the concrete ScalarType | ListType and expresses all dtype genericity through a separate PartialTypeSpec wrapper (target type-tag + partial field values, some of which may be TypeVarType). Specialization substitutes the bound vars and materializes a real concrete FieldType, so the IR/backend never observes a TypeVarType and every Stage-0 narrowing assert/cast is removed.

partial_type_info.py: bind / substitute / specialize / is_generic over PartialTypeSpec. _approach3_deduction_sketch.py: illustrative deduction-impact sketch only (not imported/wired).
@havogt

havogt commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Independent re-verification: uv run mypy src/Success: no issues found in 358 source files (exit 0). Confirms the prototype's static soundness — FieldType.dtype is concrete throughout the IR/backend with the narrowing asserts removed.


def is_generic(type_: Any) -> bool:
"""A `PartialTypeSpec` (or a structure containing one / a bare `TypeVarType`) is generic."""
return next(_walk_typevars(type_), None) is not None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return next(_walk_typevars(type_), None) is not None
return isinstance(type_, (ts.PartialTypeSpec, ts.TypeVarType))

type_spec: ts.TypeSpec, new_scalar_kind: ts.ScalarKind
) -> ts.ScalarType | ts.FieldType:
if isinstance(type_spec, ts.PartialTypeSpec) and type_spec.target is ts.FieldType:
fields = dict(type_spec.fields)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think making ts.PartialTypeSpec a proxy for all concrete fields would be nice and I don't see any major down sides. Could even work for properties / member functions (if needed with an opt-in mechanism like def fun(self: Self | PartialTypeSpec[Self]): ...).

@@ -0,0 +1,156 @@
# GT4Py - GridTools Framework

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementations here are terrible, I doubt they help in assessing the value of this approach in this area (The changes in [_approach3_deduction_sketch.py](https://github.com/havogt/gt4py/pull/66/changes#diff-0e89c4ea0389924f1639bc19be9d88d68abd8841c40f33c03e6640eb28f4a90a) are though). is_generic for example is completely off.

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