Skip to content

Epic: class expressions as first-class heap objects (per-evaluation class identity) #1785

Description

@proggeramlug

Epic: class expressions as first-class heap objects (per-evaluation class identity)

Problem

Perry models a class as a compile-time integer (class_id) plus process-global side tables; the class value is INT32(class_id). That's only valid when there's one class per class keyword — true for top-level class Foo {}, false for a class expression in a factory (make(ast) => class { static ast = ast }), where each evaluation must yield a distinct class. Today make(a) === make(b) and they share one (clobbered/undefined) static slot. Root blocker for effect's Schema (#1758) and thus Effect end-to-end (#321). Full root-cause + design: #1772.

Target architecture

A class expression evaluates to a real heap class object: a regular object stamped with the compile-time template's class_id (static methods / new / instanceof keep dispatching through the fast existing class_id machinery) carrying its per-evaluation static fields as own properties, with its superclass class object as prototype (static inheritance = one prototype-chain walk). Top-level class declarations keep INT32(class_id) unchanged. GC-native: a normal traced heap object → collectible, no leak; aligns with #1090 / #1096 / #1095.

Status

The entire class-object dispatch surface is shipped + merged (2026-05-25), all byte-identical to node with zero regressions: PR #1792 (foundation), #1793 (this-binding + extends field-inheritance), #1795 (new/instanceof/typeof + the OBJECT_TYPE_CLASS discriminator), #1797 (inherited static methods), #1800 (rest-param inherited static methods). The effect Schema DoD was then reached via a follow-up chain (#1804/#1809/#1810/#1811/#1812/#1815): the effect barrel runs Effect.runSync(Effect.succeed(42)) → 42 and effect/Schema Schema.Number decode works. Remaining: #1790 (GC layout descriptor) is the one open technical piece; richer effect features (#321) continue beyond this epic via #1816 (cross-package arguments) and the #1982#2032 series.

Subtickets (dependency-ordered)

Dependency DAG:

            #1786 (foundation)
           /   |    |     \
       #1787 #1788 #1789 #1790
           \   |    |     /
              #1791 (integration → closes #1772)

DoD

import { Effect } from "effect" and import * as S from "effect/Schema" initialize and run; effect/Effect deep import stays green; full class suite + GC-stress green; no leak. Closes #1772; unblocks #1758 / #321.

Refs #1772, #1758, #321, #1090, #1095, #1096.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions