From bc92af3d1767ad6436ae7022d1946c940473c2d8 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:43:37 +0900 Subject: [PATCH] Spec: Fixed-layout classes for TypedPython (N-11, M2b) A class compiles to direct slot access only when its layout is fixed in CPython too: user-written __slots__ matching its annotated fields, base object, no metaclass/decorators, not subclassed or patched. The compiler never adds __slots__ (that would change __dict__/vars()/attribute semantics). Slot reads on an exactly-typed value are not effects; trivial __init__ lowers to allocate-and-fill; methods compile with an exact-type guard on self. Planned. Refs #41, #57 --- docs/SPEC.md | 17 +++++++++++++++++ docs/design/typedpython.md | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/docs/SPEC.md b/docs/SPEC.md index ae73cbc9f..0e4852e81 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -462,6 +462,23 @@ All in `PM/commonTest`, so they run wherever the interpreter loads. The Gradle task declares exact inputs and outputs so up-to-date checks and the build cache apply. Tests: changing one function body recompiles that module only; an unchanged rebuild spends ~0 s in the compile step. `Status: planned`. +- **N-11** TypedPython fixed-layout classes (M2b, 2026-11-21; design §4.3.3). A class compiles to fixed + field access only when its layout is fixed **in CPython too**: it declares `__slots__` listing exactly + its annotated instance fields, has no base but `object`, no metaclass, no decorator but `@compiled`, + and (gate, compiled mode) nothing in the project subclasses it or assigns its class attributes. The + compiler never adds `__slots__` itself — that would change what CPython does (`__dict__`, `vars()`, + adding attributes). What is compiled, keeping CPython's results: + - field reads/writes on a value guarded `type(x) is C` go straight to the slot (the member descriptor's + offset, captured and checked at module init); an unset slot raises CPython's AttributeError; values + are objects (no unboxing of fields in impure code); + - a slot read is not an effect (no user code runs: the attribute is a member descriptor, checked at + init, and the exact-type guard rules out subclasses), so a function that only reads fields can stay pure; + - `C(args)` whose `__init__` only assigns each slot once from its parameters is lowered to allocate and + fill the slots directly (the same observable result as running that `__init__`); any other `__init__` + is called as an object; + - methods compile like functions with `self` guarded `type(self) is C`; anything else on the class stays + interpreted. + A class outside these rules stays interpreted with a reason. `Status: planned`. --- diff --git a/docs/design/typedpython.md b/docs/design/typedpython.md index de89dacfe..199023dd3 100644 --- a/docs/design/typedpython.md +++ b/docs/design/typedpython.md @@ -161,6 +161,24 @@ API 안정성을 확인한 뒤로 미룬다. 인터페이스 해시가 그대로라 의존 모듈은 다시 컴파일하지 않는다. C 오브젝트는 모듈별로 캐시한다. Gradle 태스크는 입력·출력을 정확히 선언해 up-to-date 판정과 빌드 캐시가 함께 작동한다. +### 4.3.3 고정 레이아웃 클래스 (SPEC N-11, M2b) + +**레이아웃이 CPython 에서도 고정일 때만** 고정 접근으로 컴파일한다 — 사용자가 `__slots__` 를 직접 쓴 클래스. +컴파일러가 `__slots__` 를 몰래 더하면 `__dict__`·`vars()`·속성 추가가 CPython 과 달라지므로("같은 결과" 원칙 +위반) 하지 않는다. binary-trees 벤치의 `Node` 는 그래서 `__slots__ = ("left", "right")` 를 소스에 적어야 +컴파일되고, 그 변경은 모든 언어 열에 공평하게 적용되도록 벤치 소스 자체에 넣는다(CPython 기준선 재측정 필요). + +- **필드 접근:** `type(x) is C` 로 가드된 값의 필드는 member descriptor 의 오프셋으로 바로 읽고 쓴다. 오프셋은 + 모듈 초기화 때 클래스 사전의 descriptor 가 정말 member descriptor 인지 확인하고 잡는다. 값은 객체 그대로다. +- **순수성:** 슬롯 읽기는 사용자 코드를 실행하지 않으므로 효과가 아니다 — `check(node)` 같은 재귀 순회가 + 순수 함수로 남아 i64 경로를 쓸 수 있다. +- **생성:** `__init__` 이 매개변수를 각 슬롯에 한 번씩 대입하기만 하면 `C(a, b)` 를 할당 + 슬롯 채우기로 + 내린다. 그 밖의 `__init__` 은 객체 호출로 남는다. +- **메서드:** `self` 를 `type(self) is C` 로 가드한 함수로 컴파일한다. +- **IR 확장(예정):** `FieldGet(obj, cls, field)`, `FieldSet(obj, cls, field, value)`, `New(cls, args)`, + 그리고 정확한 타입 가드 `IsExact(obj, cls)` 조건. 검증기는 가드 없는 필드 접근, 슬롯 밖 필드, 생성자 규칙 + 위반을 거부한다. + 검토하고 기각한 경로: - **Cython** — 사용자 결정으로 폐기(위).