diff --git "a/docs/test/\343\202\261\343\203\274\343\202\27101-\347\224\237\346\210\220\343\201\250\345\256\237\350\241\214\346\231\202API.md" "b/docs/test/\343\202\261\343\203\274\343\202\27101-\347\224\237\346\210\220\343\201\250\345\256\237\350\241\214\346\231\202API.md" index 8fc0934..c502016 100644 --- "a/docs/test/\343\202\261\343\203\274\343\202\27101-\347\224\237\346\210\220\343\201\250\345\256\237\350\241\214\346\231\202API.md" +++ "b/docs/test/\343\202\261\343\203\274\343\202\27101-\347\224\237\346\210\220\343\201\250\345\256\237\350\241\214\346\231\202API.md" @@ -26,7 +26,7 @@ K9 の表記変種(import 別名・FQN・star import・typealias)と診断 | ID | 次元/値 | 観測と期待 | 実装 | 備考(関連V/診断) | |---|---|---|---|---| | API-12 | K3=全種別 × K6=自動生成 | 10 種別末端(data class/data object/object/final class/open/abstract/interface/fun interface/enum/value class)で 1 末端=1 kind・label=単純名・object 系=自身/他=companion(自動生成 companion 含む) | ZooTest#oneKindPerLeafAcrossAllShapes | V3・V10 | -| API-13 | K15=多段サブタイプ | 吸収: open 直下・多段・object サブタイプ・interface 第三者実装(default asEnumish の JVM lowering 込み)・テスト内無名 object 実装・テスト内 local class 実装・末端 interface への委譲実装(`: IfaceLeaf by impl`)は kind 新設せず entries 不変・asEnumish 継承 | ZooTest#subtypesAreAbsorbedIntoLeafKinds | V10・AK 非発火→ケース04・基底直接委譲は DIA-69 | +| API-13 | K15=多段サブタイプ × K5=探索境界(下方向) | 吸収: open 直下・多段・object サブタイプ・interface 第三者実装(default asEnumish の JVM lowering 込み)・テスト内無名 object 実装・テスト内 local class 実装・末端 interface への委譲実装(`: IfaceLeaf by impl`)・非 sealed 末端配下の sealed 部分階層(探索が末端で止まり中間・末端とも自 kind を持たない)は kind 新設せず entries 不変・asEnumish 継承 | ZooTest#subtypesAreAbsorbedIntoLeafKinds | V10・AK 非発火→ケース04・基底直接委譲は DIA-69 | | API-14 | K3=非 final 末端 | 非 final 末端の enumizedClass=分類代表(実行時クラスと不一致可) | ZooTest#enumizedClassIsRepresentativeForOpenLeaves | V10 | | API-15 | K3=interface 末端下限 | 実装者ゼロの interface 末端(Ghost・自動 companion)も entries 掲載 | ZooTest#implementorlessInterfaceLeafHasKind | V10 | | API-16 | O4=値 when | 値 when の is 末端枝がサブタイプを被覆(sealed 地力) | ZooTest#valueWhenCoversSubtypesByLeafBranch | V10 | @@ -64,14 +64,14 @@ K9 の表記変種(import 別名・FQN・star import・typealias)と診断 | API-27 | K1=sealed class × K9=コンストラクタ呼び出し形 | `:Task()` 形 supertype でも所属判定・companion 自動生成が成立 | SealedClassBaseTest#constructorCallSupertypeWorks | V3 | | API-28 | K1=sealed class × K3=object | sealed class 基底の object / data object 末端=自身が kind・toString 生成差分(非 data=生成 / data=言語合成) | SealedClassBaseTest#objectLeavesOfClassBase | V11 | -## 7. 中間 sealed(mid・K5 × K9) +## 7. 中間 sealed と合流(mid・K5 × K9) | ID | 次元/値 | 観測と期待 | 実装 | 備考(関連V/診断) | |---|---|---|---|---| | API-29 | K5=1 段 × K9=中間経由再帰追跡 | 中間 sealed class / sealed interface 経由の raw 追跡再帰で末端 companion 自動生成 | MidTrackingTest#leavesViaIntermediatesGetAutoCompanions | V3 | -| API-30 | K5=中間非生成 | 中間には何も生成されず entries 非掲載(末端まで平坦化)・中間の明示 companion へも Enumish 非注入(kind 非成立)・中間型変数の asEnumish は末端 kind へ実体解決 | MidTrackingTest#intermediatesHaveNoKind | 多段の展開順→ケース03 | +| API-30 | K5=中間非生成 × K2=非注釈 sealed 祖先 | 中間には何も生成されず entries 非掲載(末端まで平坦化)・中間の明示 companion へも Enumish 非注入(kind 非成立)・中間型変数の asEnumish は末端 kind へ実体解決・非注釈 sealed 祖先の側(祖先直下の非所属メンバー)にも生成なし=探索の範囲は基底から下だけ | MidTrackingTest#intermediatesHaveNoKind | 多段の展開順→ケース03 | | API-51 | K9=スコープ順の競合解決 | raw 追跡の優先関係を競合 3 形で固定: star import 基底 vs 同一 pkg 同名非基底=同一 pkg 勝ち(entries 非所属)・同一 pkg 同名 vs 明示 import 基底=import 勝ち(所属)・明示 import vs 外側ネスト同名=ネスト勝ち(非所属) | RawTrackingTest#scopePriorityDecidesMembership | V3。表記単独形の成立→ケース04 DIA-31/32 | -| API-57 | K5=多重経路(兄弟中間の同時実装) | 複数経路で到達する末端は初出位置に 1 回だけ entries 掲載・kind は 1 つ(どちらの中間型からも同じ kind へ解決)・生成 Enumish の継承者一覧も重複せず kind-when は else 不要 | MultiPathTest#multiPathLeafIsListedOnceAtFirstOccurrence / MultiPathTest#multiPathLeafHasSingleKind | 基底は一意のため診断は非発火。展開順の規則→ケース03 §1 | +| API-57 | K5=合流(合流点の種別 × 経路の非対称性 × 入れ子) | 複数経路で到達するメンバーは初出位置に 1 回だけ entries 掲載(合流点が中間 sealed なら配下のサブツリーごと 1 回)・kind は 1 つでどの経路の静的型からも同じ kind へ解決・生成 Enumish の継承者一覧も重複せず kind-when は else 不要。合流点(非 sealed 末端 / 中間 sealed / companion 末端)× 経路(対称 / 基底直下併存 / sealed class 中間との混成)× 入れ子を 1 スナップショットで固定 | MultiPathTest#multiPathLeafIsListedOnceAtFirstOccurrence / MultiPathTest#multiPathLeafHasSingleKind | 基底は一意のため診断は非発火。展開順の規則→ケース03 §1 | ## 8. 型パラメータ(generic・K11) diff --git "a/docs/test/\343\202\261\343\203\274\343\202\27103-\351\240\206\345\272\217.md" "b/docs/test/\343\202\261\343\203\274\343\202\27103-\351\240\206\345\272\217.md" index 2e2fd36..116c77b 100644 --- "a/docs/test/\343\202\261\343\203\274\343\202\27103-\351\240\206\345\272\217.md" +++ "b/docs/test/\343\202\261\343\203\274\343\202\27103-\351\240\206\345\272\217.md" @@ -11,7 +11,8 @@ FQN UTF-16 序数と DFS 入れ子展開に関する順序観測は本資料へ - 順序 = コンパイラの sealed 継承者リスト走査順(setSealedClassInheritors セッター側で ClassId FQN 文字列の UTF-16 序数昇順に正規化・大小区別・ロケール非依存)。 - プラグインは FIR / IR とも並べ替えに一切介入しない(設計00 §6.2・設計02 §3)。 - 中間 sealed はその位置で継承者リストを深さ優先に入れ子展開し、entries には末端のみが載る。 -- 複数経路で到達する末端(兄弟中間の同時実装)は初出位置に 1 回だけ載る(設計00 §6.2・設計02 §2。観測はケース01 API-57)。 +- 複数経路で到達するメンバーは初出位置に 1 回だけ載る。合流点が中間 sealed の場合は配下のサブツリーごと 1 回であり、2 度目の到達では再展開しない。 + 初出位置は各段の継承者リストを深さ優先に辿った到達順が決めるため、基底直下と中間経由が競合する構成では基底の継承者リストで先に来た側の位置になる(設計00 §6.2・設計02 §2。観測はケース01 API-57)。 - 整列キーは末端の ClassId のみ(kind が companion でも companion 名は整列に関与しない)。 ## 2. 期待列の導出 diff --git "a/docs/test/\343\203\206\343\202\271\343\203\210\346\210\246\347\225\245.md" "b/docs/test/\343\203\206\343\202\271\343\203\210\346\210\246\347\225\245.md" index 2cc16c8..e68956f 100644 --- "a/docs/test/\343\203\206\343\202\271\343\203\210\346\210\246\347\225\245.md" +++ "b/docs/test/\343\203\206\343\202\271\343\203\210\346\210\246\347\225\245.md" @@ -23,10 +23,10 @@ Kotlin 言語仕様がこのプラグインに対して張る空間を次の次 | # | 次元 | 値 | |---|---|---| | K1 | 基底の宣言種別 | sealed interface / sealed class ‖ 負値: enum・annotation・object・data object・class(final/open/abstract/data/value)・interface・fun interface・sealed fun interface(言語拒否)・非クラス対象(言語 WRONG_TARGET) | -| K2 | 基底の修飾 | expect / actual(共にエラー)・inner(sealed 不能=NOT_SEALED 合流)・型パラメータ付き・ネスト配置・可視性 | +| K2 | 基底の修飾 | expect / actual(共にエラー)・inner(sealed 不能=NOT_SEALED 合流)・型パラメータ付き・ネスト配置・可視性・非注釈 sealed 祖先を持つ(上方向の探索境界) | | K3 | 末端の宣言種別 | object / data object / class / data class / value class / enum class / open class / abstract class / interface / fun interface / 階層外クラスの companion(既定名・名前付き) | | K4 | 末端の修飾 | inner(エラー)・型パラメータ付き・可視性・expect/actual(言語委譲) | -| K5 | 中間 sealed | 無 / 1 段 / 多段 / 兄弟複数 / 多重経路(兄弟中間の同時実装) / 継承者ゼロ / 可視性 / 配置(ネスト・トップレベル) | +| K5 | 中間 sealed | 無 / 1 段 / 多段 / 兄弟複数 / 継承者ゼロ / 可視性 / 配置(ネスト・トップレベル) ‖ 合流(多重経路)= 合流点の種別(非 sealed 末端・中間 sealed・companion 末端)× 経路の非対称性(対称・基底直下併存・sealed class 中間との混成)× 入れ子 ‖ 探索境界 = 非 sealed 末端配下の sealed 部分階層(下方向。吸収へ合流) | | K6 | companion 状態 | 無し(自動生成) / 明示既定名 / 明示名前付き / 可視性 4 値 / ユーザーメンバー共存 / companion 自身が末端(外側=末端: エラー・外側=基底/中間: 成立・外側=階層外: 許容) / 非 companion のネスト宣言 `Companion` との衝突(実挙動固定) | | K7 | 配置 | 基底ネスト / 同一ファイルトップレベル / 別ファイルトップレベル / 無関係クラス内ネスト / private 外側クラス内 / 非 sealed クラス内の基底 / 多段ネスト / local(関数内。実装は FIR・チェッカー・IR の全入口で一貫除外=無診断・無生成の素通りを固定する) / 別パッケージ(sealed の言語制約エラーへ委譲) | | K8 | 可視性 | (基底/中間/末端/companion) × (public/internal/protected/private) → 実効可視性・asEnumish 3 段規則・IR-only アクセサ壁 3 形・基底より広い末端(E-2) | @@ -58,7 +58,8 @@ Kotlin 言語仕様がこのプラグインに対して張る空間を次の次 - C2(分岐網羅): 実装に条件分岐が存在する次元の組は全分岐を踏む。 分岐の典拠は実装である: kind 解決(object=自身/他=companion)・asEnumish 返り値型 3 段規則・ IR-only アクセサの壁 3 形(壁なし/ネスト壁/トップレベル壁)・toString 生成の 3 非生成条件・ - companion 自動生成の候補判定(raw supertype 追跡のスコープ順 5 表記)・順序(DFS 入れ子展開×FQN 正規化)。 + companion 自動生成の候補判定(raw supertype 追跡のスコープ順 5 表記)・順序(DFS 入れ子展開×FQN 正規化)・ + 末端列挙の 4 分岐(初出 / 重複 × 非 sealed=掲載する / 中間 sealed=サブツリーを展開する)。 - C3(合成許容): 実装上相互作用しない次元値は同一フィクスチャに合成してよい。 根拠は生成が末端毎に独立(P1)・診断が宣言単位の条件検査であること。 合成時の制約: 同一階層内の label 一意・意図しないサブタイプ関係を作らない・ diff --git "a/docs/test/\343\203\225\343\202\243\343\202\257\343\202\271\343\203\201\343\203\243\346\247\213\346\210\220.md" "b/docs/test/\343\203\225\343\202\243\343\202\257\343\202\271\343\203\201\343\203\243\346\247\213\346\210\220.md" index eb703dd..136d79d 100644 --- "a/docs/test/\343\203\225\343\202\243\343\202\257\343\202\271\343\203\201\343\203\243\346\247\213\346\210\220.md" +++ "b/docs/test/\343\203\225\343\202\243\343\202\257\343\202\271\343\203\201\343\203\243\346\247\213\346\210\220.md" @@ -13,13 +13,13 @@ src/test には test compilation 配線検証用の小さな @Enumize 階層(T | パッケージ | 階層 | 合成する次元値 | 主担当資料 | |---|---|---|---| | si | SI = sealed interface + data class Foo + data object Bar | 標準形(消費側・Java・binary-swap の共通参照) | API | -| zoo | Zoo = sealed interface + 全種別末端 12(data class/data object/object/final class/open/abstract/enum/value class=自動 companion・interface IfaceLeaf と fun interface FunLeaf=明示 public companion・fun interface FunAuto=自動 companion・実装者ゼロ interface Ghost=自動 companion)+ 吸収サブタイプ(open 直下・多段・object・interface 実装・SAM ラムダ・テスト内無名 object・テスト内 local class 実装・IfaceLeaf への委譲実装) | K3 全値 × K6 明示/自動 × kind 解決 2 分岐 × V10 吸収 × O1 | API/ORD | +| zoo | Zoo = sealed interface + 全種別末端 12(data class/data object/object/final class/open/abstract/enum/value class=自動 companion・interface IfaceLeaf と fun interface FunLeaf=明示 public companion・fun interface FunAuto=自動 companion・実装者ゼロ interface Ghost=自動 companion)+ 吸収サブタイプ(open 直下・多段・object・interface 実装・SAM ラムダ・テスト内無名 object・テスト内 local class 実装・IfaceLeaf への委譲実装・IfaceLeaf 配下の sealed 部分階層 Brood/Chick=下方向の探索境界) | K3 全値 × K6 明示/自動 × kind 解決 2 分岐 × V10 吸収 × O1 | API/ORD | | enumleaf | Command = sealed interface + enum Builtin(自動 companion) + enum Verb(明示 companion・定数 toString override) + data object HELP(定数名対照) + data class Custom | K3=enum 深掘り(V4): 1 kind・name/label 分離・定数 toString 独立・明示 companion 流用・enum 複数 | API/ORD | | plain | Plain = sealed interface + class Simple(明示既定名 companion) + class Stocked(companion にユーザーメンバー) + object Marker(非 data) + class Made(public 名前つき companion Factory) | K6: 明示既定名/ユーザーメンバー共存/名前つき public(規則 1・label 不変) | API | | companionleaf | Token = sealed interface + Host.Companion(既定名 companion 末端) + WithNamed.Named(名前つき companion 末端) + data object HostA + Holder2.Zzz(名前つき・順序プローブ)。基底/中間 companion 末端の成立形は sealedbase / mid が担う | K3=companion 末端(外側=階層外)・label=宣言名・'.'(46) 序数境界 | API/ORD | | sealedbase | Task = sealed class + data class Run(`:Task()`・自動 companion) + data object Done + object Plain + protected class Inner(基底ネスト・明示 companion・基底 companion に factory と asEnumish 型付き観測メンバー=規則 1 の基底スコープ内観測) + 基底自身の companion(`companion object : Task()`)=基底 companion 末端の成立形(label="Companion") | K1=sealed class・K9 コンストラクタ呼び出し形・protected 末端・K6 基底 companion 末端 | API/VIS | | generic | `Generic` = sealed class + data class `Box` + class `Empty` + 非 generic 末端 class Fixed(`:Generic()`) ‖ Holder = 非 generic sealed interface + generic 末端 data class `Cell`(変位注釈) | K11: 型パラメータ(両方/基底のみ/末端のみ/変位)・star projection・kind の型引数非依存 | API | -| mid | RootVia = sealed interface + 中間 sealed class MidClass(companion object : RootVia=中間 companion 末端の成立形) + 中間 sealed interface MidIface(明示 companion=非実装・Enumish 非注入・kind 非成立の観測) + 各経由の companion 無し末端 ‖ MultiPath = 兄弟中間 MpLeft / MpRight + 両方を実装する末端 MpBoth(自動 companion) + 各単独経路末端 MpOnlyLeft / MpOnlyRight(RootVia の entries スナップショットを汚さないよう独立階層とする) | K5×K9×K6: 中間経由の raw 追跡再帰(V3)・中間へ非生成・中間 companion の非注入/末端成立の両形・多重経路の初出 1 回掲載 | API | +| mid | RootVia = sealed interface + 非注釈 sealed 祖先 ViaAncestor とその直下の非所属 ViaOutside(上方向の探索境界) + 中間 sealed class MidClass(companion object : RootVia=中間 companion 末端の成立形) + 中間 sealed interface MidIface(明示 companion=非実装・Enumish 非注入・kind 非成立の観測) + 各経由の companion 無し末端 ‖ MultiPath = 合流の合成階層: 兄弟中間 MpLeft / MpRight + sealed class 中間 MpClassMid + 合流点の中間 sealed MpShared / 合流するメンバー = MpBoth(非 sealed 末端・対称) / MpHost.Companion(companion 末端) / MpDirect(基底直下併存) / MpMixed(class 中間との混成) / MpNested(合流の入れ子) + MpShared 配下の MpDeep + 各単独経路末端 MpOnlyLeft / MpOnlyRight(RootVia の entries スナップショットを汚さないよう独立階層とする) | K5×K9×K6: 中間経由の raw 追跡再帰(V3)・中間へ非生成・中間 companion の非注入/末端成立の両形・探索境界(上方向)・合流の初出 1 回掲載(合流点の種別 × 経路の非対称性 × 入れ子) | API | | scope | 真基底 scope.target.Holder.Base(@Enumize・object Holder 内ネスト)+囮トップレベル scope.target.Base(非 sealed interface)+別 pkg 実基底 scope.other.Base(@Enumize)。競合 3 形(末端は全て scope.target 配置): (1) 明示 import Holder.Base vs 同一 pkg 囮=import 勝ち→所属 (2) scope.other.* star import vs 同一 pkg 囮=同一 pkg 勝ち→非所属・コンパイル成功 (3) 明示 import vs ホストの内側ネスト同名=ネスト勝ち→非所属 | K9: raw 追跡スコープ順の優先関係を entries 所属/非所属+コンパイル成立で固定 | API | | order.flat | FlatRoot = sealed interface + 文字境界末端(AB1/Ab2/aB3/ab4・A1/AA/A_/Az)+ 配置末端(基底ネスト Aaa・無関係クラス Box.Bbb・トップレベル Mmm/Zzz/aLower)+ 接頭辞対(Foo=class 自動 companion / FooBar=data object)+ セパレータ対(ネスト Sep.Q / トップレベル Sep0) | K7×O3: FQN UTF-16 序数の全境界を 1 スナップショット固定 | ORD | | order.mid | MidRoot = sealed interface + MidNest{NestA,NestB}(トップレベル中間×ネスト末端=FQN 順一致) + MidTop{Early,Late}(トップレベル中間×トップレベル末端=break) + MidMulti.Deep{Bottom,Wide}(多段) + MidRoot.MidIn{Outpost}(基底ネスト中間×トップレベル末端) + 直接末端 Aaa/Zzz(in-place 展開の前後) + Probe=class 末端(自動 companion)+その内側ネスト data object 末端 Bb(単純名が Companion より序数で小=entries 順と inheritors 順の分岐プローブ) | K5×O3: DFS 入れ子展開・break・多段・中間配置 2 形・inheritors 分岐を 1 スナップショット固定 | ORD | diff --git "a/docs/\343\202\263\343\203\263\343\203\221\343\202\244\343\203\251\343\203\227\343\203\251\343\202\260\343\202\244\343\203\263\350\250\255\350\250\21000.md" "b/docs/\343\202\263\343\203\263\343\203\221\343\202\244\343\203\251\343\203\227\343\203\251\343\202\260\343\202\244\343\203\263\350\250\255\350\250\21000.md" index 5a274b9..919312b 100644 --- "a/docs/\343\202\263\343\203\263\343\203\221\343\202\244\343\203\251\343\203\227\343\203\251\343\202\260\343\202\244\343\203\263\350\250\255\350\250\21000.md" +++ "b/docs/\343\202\263\343\203\263\343\203\221\343\202\244\343\203\251\343\203\227\343\203\251\343\202\260\343\202\244\343\203\263\350\250\255\350\250\21000.md" @@ -389,8 +389,11 @@ entries = [Bbb, Aaa] 末端が中間 sealed の内側にネストしている通常の構成では、ネストした末端の FQN が中間の FQN を接頭辞に持つため結果的に FQN 序数順と一致する -- **同じ末端へ複数経路で到達する構成**(兄弟の中間 sealed を同時に実装する末端)では、 +- **複数経路で到達するメンバー**(兄弟の中間 sealed を同時に実装する末端・基底直下と中間経由を併せ持つ末端など)は **初出位置に 1 回だけ載る**。 + 合流点が中間 sealed である場合は配下のサブツリーごと 1 回であり、2 度目の到達では展開しない。 + 初出位置は各段の継承者リストを深さ優先に辿った到達順が決めるため、 + 基底直下と中間経由が競合する構成では基底の継承者リストで先に来た側の位置になる。 重複の除去は並べ替えではなく、entries が kind の集合であることの帰結である - 生成 Enumish の inheritors 属性(§5.2)も同様に、収集した順のまま登録する。 属性へ格納される時点でセッター側の正規化(§6.1)が働くため、プラグイン側で整える必要はない diff --git "a/docs/\343\202\263\343\203\263\343\203\221\343\202\244\343\203\251\343\203\227\343\203\251\343\202\260\343\202\244\343\203\263\350\250\255\350\250\21002.md" "b/docs/\343\202\263\343\203\263\343\203\221\343\202\244\343\203\251\343\203\227\343\203\251\343\202\260\343\202\244\343\203\263\350\250\255\350\250\21002.md" index f7375b4..8a1a6bb 100644 --- "a/docs/\343\202\263\343\203\263\343\203\221\343\202\244\343\203\251\343\203\227\343\203\251\343\202\260\343\202\244\343\203\263\350\250\255\350\250\21002.md" +++ "b/docs/\343\202\263\343\203\263\343\203\221\343\202\244\343\203\251\343\203\227\343\203\251\343\202\260\343\202\244\343\203\263\350\250\255\350\250\21002.md" @@ -54,7 +54,8 @@ IR 側の責務は次の 3 つである: り、**真実の単一ソース**である(仮に IC 下でこの集合が壊れるとすれば、それは言語機能側のバグであり、 プラグイン固有の破損ではない) - 中間 sealed は再帰的に展開し、**末端のみ**を集める(中間 sealed 自身は entries に載せない) -- 複数の中間 sealed を同時に実装する末端は経路の数だけ到達するため、**初出位置の 1 回だけを残す**。 +- 複数経路で到達するメンバーは経路の数だけ到達するため、**初出位置の 1 回だけを残す**。 + 合流点が中間 sealed である場合は配下のサブツリーごと 1 回であり、2 度目の到達では展開しない。 ここで列挙するのは kind の**集合**であり、重複は enum 的意味論(末端 1 種類につき 1 kind)を壊す - 各末端から kind を解決する: object なら末端自身の IrClass、 それ以外(class / enum class / interface。open / abstract を含む)ならその companion の IrClass @@ -78,8 +79,8 @@ IR 側の責務は次の 3 つである: - 走査に使うのは継承者リストとその再帰だけである。 `IrFile` のパス・ファイル名・宣言オフセット・タイムスタンプ・乱数・ 環境変数は**一切参照しない**(これらに由来する非決定性を持ち込まないため) -- 走査順を保つコレクション(`List`)だけを使い、`HashSet` / `HashMap` - を経由させない(訪問済みガード等で集合が必要な場合も `LinkedHashSet` を用いて走査順を壊さない) +- 走査順を保つコレクション(`List` / `LinkedHashSet`)だけを使い、`HashSet` / `HashMap` + を経由させない(重複排除や訪問済みガードで集合が必要な場合も走査順を壊さない) この順序は `entries` の並びとしてのみ観測される(序数を公開する API は無い。設計00 §4.1)。 コンパイラが渡す各段の継承者リストは属性へ格納される時点で FQN 順に正規化済みであるため(設計00 §6.1)、 diff --git a/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/mid/MultiPath.kt b/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/mid/MultiPath.kt index d0340f8..7fd888f 100644 --- a/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/mid/MultiPath.kt +++ b/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/mid/MultiPath.kt @@ -2,23 +2,48 @@ package io.github.projectmapk.fixtures.mid import io.github.projectmapk.sealedClassEnumizer.Enumize -// 多重経路(複数の中間 sealed を同時に実装する末端)のフィクスチャ -// (docs/test/ケース01-生成と実行時API.md API-57)。 +// 合流(複数経路で到達するメンバー)の合成階層(docs/test/ケース01-生成と実行時API.md API-57)。 +// 合流点の種別(非 sealed 末端 / 中間 sealed / companion 末端)× 経路の非対称性 +// (対称 / 基底直下併存 / sealed class 中間との混成)× 入れ子を 1 階層へ合成し、 +// entries スナップショットで初出 1 回掲載を固定する(展開順の規則は docs/test/ケース03-順序.md §1)。 // RootVia とは独立した階層とし、entries スナップショットを相互に汚さない @Enumize sealed interface MultiPath -// 兄弟中間の一方(多重経路末端 MpBoth の初出経路) +// 兄弟中間の一方(合流するメンバーの初出経路) sealed interface MpLeft : MultiPath -// 兄弟中間の他方(多重経路末端 MpBoth の 2 度目の到達経路) +// 兄弟中間の他方(合流するメンバーの 2 度目の到達経路) sealed interface MpRight : MultiPath -// 2 つの中間 sealed を同時に実装する末端(companion 明示なし = 多重経路での候補判定も観測する)。 -// MpLeft 経由の初出位置に 1 回だけ entries へ載る +// sealed class の中間(混成合流の class 側経路) +sealed class MpClassMid : MultiPath + +// 合流点が中間 sealed の形。MpLeft 経由で 1 度だけ展開され、 +// MpRight からの再到達では配下のサブツリーごと再走査されない +sealed interface MpShared : MpLeft, MpRight + +// 合流点 = 非 sealed 末端・対称経路(兄弟中間の同時実装。companion 明示なし = 多重経路での候補判定も観測する) class MpBoth : MpLeft, MpRight -// MpLeft 単独経路の末端(MpBoth の後続 = 初出位置の対照) +// 合流点 = companion 末端(外側 MpHost は階層外)。kind = companion 自身・label = 宣言名 +class MpHost { + companion object : MpLeft, MpRight +} + +// 経路の非対称(基底直下 + 中間経由)。基底の継承者一覧で MpRight より先に来るため基底直下の位置に載る +data object MpDirect : MultiPath, MpRight + +// 経路の混成(sealed class 中間 + sealed interface 中間) +class MpMixed : MpClassMid(), MpRight + +// 合流の入れ子(合流点 MpShared の配下でさらに MpLeft 直下の経路と交差する) +class MpNested : MpShared, MpLeft + +// 合流点 MpShared の配下末端(サブツリーが 1 度だけ展開されることの観測点) +data object MpDeep : MpShared + +// MpLeft 単独経路の末端(合流するメンバーの後続 = 初出位置の対照) data object MpOnlyLeft : MpLeft -// MpRight 単独経路の末端(MpBoth の重複が除かれた後に続く) +// MpRight 単独経路の末端(合流の重複が除かれた後に続く) data object MpOnlyRight : MpRight diff --git a/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/mid/RootVia.kt b/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/mid/RootVia.kt index 6eac006..cdb493f 100644 --- a/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/mid/RootVia.kt +++ b/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/mid/RootVia.kt @@ -2,9 +2,13 @@ package io.github.projectmapk.fixtures.mid import io.github.projectmapk.sealedClassEnumizer.Enumize +// 非注釈 sealed の祖先(階層の上端)。所属判定は sealed 連鎖を上へ辿るが、 +// @Enumize 基底へ達しない側は階層に数えない(API-30 の探索境界の観測点) +sealed interface ViaAncestor + // 中間 sealed 経由の raw 追跡再帰フィクスチャ(docs/test/ケース01-生成と実行時API.md // API-29/API-30/API-52)。中間は entries に載らず、経由末端の companion は自動生成される -@Enumize sealed interface RootVia +@Enumize sealed interface RootVia : ViaAncestor // 中間 sealed class(API-30 の経由点)。 // companion は自身が RootVia を実装する末端であり成立する(API-52。中間 companion 末端の成立形) @@ -24,3 +28,6 @@ class LeafViaMid(val v: Int) : MidClass() // 中間 sealed interface 経由の末端・companion 明示なし(API-29) class LeafViaIface(val v: Int) : MidIface + +// 祖先直下の非所属メンバー(RootVia の探索は基底から下だけを見るため kind を持たず entries にも載らない) +data object ViaOutside : ViaAncestor diff --git a/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/zoo/Zoo.kt b/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/zoo/Zoo.kt index 5b91dd8..2f6af76 100644 --- a/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/zoo/Zoo.kt +++ b/integration-test/producer-jvm/src/main/kotlin/io/github/projectmapk/fixtures/zoo/Zoo.kt @@ -69,3 +69,10 @@ class Crafted : Zoo.IfaceLeaf // 末端 interface への委譲実装(委譲された asEnumish が IfaceLeaf の kind を返す)。 // 基底 Zoo への直接委譲は診断対象(docs/test/ケース04-診断.md DIA-69)であり、ここでは末端への委譲のみ扱う class Veil(impl: Zoo.IfaceLeaf) : Zoo.IfaceLeaf by impl + +// 非 sealed 末端の配下に置いた sealed 部分階層。階層の探索は非 sealed 末端で止まるため、 +// 中間 Brood も末端 Chick も自 kind を作らず IfaceLeaf の kind へ吸収される +sealed interface Brood : Zoo.IfaceLeaf + +// sealed 部分階層の末端(上向きの候補判定は Brood を辿るが IfaceLeaf で止まる) +data object Chick : Brood diff --git a/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/mid/MidTrackingTest.kt b/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/mid/MidTrackingTest.kt index 4c7b91a..7d08849 100644 --- a/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/mid/MidTrackingTest.kt +++ b/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/mid/MidTrackingTest.kt @@ -21,6 +21,7 @@ class MidTrackingTest { // docs/test/ケース01-生成と実行時API.md API-30: 中間には何も生成されず entries 非掲載 // (末端まで平坦化)。中間の明示 companion へも Enumish 非注入(kind 非成立)で、 // 中間型変数の asEnumish は末端 kind へ実体解決される。 + // 探索の上端も同じで、非注釈 sealed 祖先の側(ViaOutside)は階層に数えない。 // 期待順は直接継承者の FQN 順 [MidClass, MidClass.Companion, MidIface] を DFS で // in-place 展開した [LeafViaMid, Companion, LeafViaIface](docs/test/ケース03-順序.md §1) @Test @@ -32,6 +33,9 @@ class MidTrackingTest { // MidIface の明示 companion は階層を実装せず kind ではない val midCompanion: Any = MidIface.Companion assertFalse(midCompanion is RootVia.Enumish) + // 祖先直下の非所属メンバーにも kind は成立しない(探索は基底から下だけを見る) + val outside: Any = ViaOutside + assertFalse(outside is RootVia.Enumish) // 中間型変数からの asEnumish は末端 kind を返す val viaIface: MidIface = LeafViaIface(1) assertSame(LeafViaIface.Companion, viaIface.asEnumish()) diff --git a/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/mid/MultiPathTest.kt b/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/mid/MultiPathTest.kt index 9f3e741..f4aa433 100644 --- a/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/mid/MultiPathTest.kt +++ b/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/mid/MultiPathTest.kt @@ -2,39 +2,91 @@ package io.github.projectmapk.fixtures.mid import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotEquals import kotlin.test.assertSame -// 多重経路(複数の中間 sealed を同時に実装する末端)の box テスト -// (docs/test/ケース01-生成と実行時API.md §7) +// 合流(複数経路で到達するメンバー)の box テスト(docs/test/ケース01-生成と実行時API.md §7) class MultiPathTest { - // docs/test/ケース01-生成と実行時API.md API-57: 複数経路で到達する末端は初出位置に 1 回だけ載る。 - // 直接継承者の整列 [MpLeft, MpRight] を DFS 展開すると MpLeft 側で [MpBoth, MpOnlyLeft]・ - // MpRight 側で [MpBoth(重複), MpOnlyRight] となり、2 度目の MpBoth は落ちる + // docs/test/ケース01-生成と実行時API.md API-57: 複数経路で到達するメンバーは初出位置に 1 回だけ載る。 + // 基底の直接継承者 [MpClassMid, MpDirect, MpLeft, MpRight] を DFS 展開すると + // MpClassMid → [MpMixed] / MpDirect(基底直下)/ + // MpLeft → [MpBoth, MpHost.Companion, MpNested, MpOnlyLeft, MpShared → [MpDeep, MpNested=重複]] / + // MpRight → [MpBoth, MpDirect, MpHost.Companion, MpMixed, MpOnlyRight, MpShared=重複] + // となり、2 度目の到達は末端・中間 sealed のサブツリーとも落ちる(docs/test/ケース03-順序.md §1) @Test fun multiPathLeafIsListedOnceAtFirstOccurrence() { + val labels = MultiPath.Enumish.entries.map { it.label } assertEquals( - listOf("MpBoth", "MpOnlyLeft", "MpOnlyRight"), - MultiPath.Enumish.entries.map { it.label }, + listOf( + "MpMixed", + "MpDirect", + "MpBoth", + "Companion", + "MpNested", + "MpOnlyLeft", + "MpDeep", + "MpOnlyRight", + ), + labels, ) + // 初出位置は経路の到達順が決めるため、全末端 FQN 一括整列とは一致しない + assertNotEquals(labels.sorted(), labels) } // docs/test/ケース01-生成と実行時API.md API-57: 経路が複数でも kind は 1 つで、 - // どちらの中間型からも同じ kind へ解決される。生成 Enumish の継承者一覧も重複せず + // どの経路の静的型からも同じ kind へ解決される。生成 Enumish の継承者一覧も重複せず // kind-when は else 不要で網羅する @Test fun multiPathLeafHasSingleKind() { - val viaLeft: MpLeft = MpBoth() - val viaRight: MpRight = MpBoth() - assertSame(MpBoth.Companion, viaLeft.asEnumish()) - assertSame(MpBoth.Companion, viaRight.asEnumish()) + // 合流点 = 非 sealed 末端(兄弟中間の対称経路) + val bothViaLeft: MpLeft = MpBoth() + val bothViaRight: MpRight = MpBoth() + assertSame(MpBoth.Companion, bothViaLeft.asEnumish()) + assertSame(MpBoth.Companion, bothViaRight.asEnumish()) assertSame(MpBoth.Companion, MultiPath.Enumish.valueOf("MpBoth")) + // 合流点 = 中間 sealed(配下末端はどちらの経路から見ても同じ kind) + val deepViaLeft: MpLeft = MpDeep + val deepViaRight: MpRight = MpDeep + assertSame(MpDeep, deepViaLeft.asEnumish()) + assertSame(MpDeep, deepViaRight.asEnumish()) + // 合流点 = companion 末端(外側 MpHost は階層外) + assertSame(MpHost.Companion, MultiPath.Enumish.valueOf("Companion")) + // 経路の非対称(基底直下 + 中間経由) + val directViaBase: MultiPath = MpDirect + val directViaRight: MpRight = MpDirect + assertSame(MpDirect, directViaBase.asEnumish()) + assertSame(MpDirect, directViaRight.asEnumish()) + // 経路の混成(sealed class 中間 + sealed interface 中間) + val mixedViaClass: MpClassMid = MpMixed() + val mixedViaIface: MpRight = MpMixed() + assertSame(MpMixed.Companion, mixedViaClass.asEnumish()) + assertSame(MpMixed.Companion, mixedViaIface.asEnumish()) + // 合流の入れ子(合流点の配下でさらに交差する末端) + val nestedViaShared: MpShared = MpNested() + val nestedViaLeft: MpLeft = MpNested() + assertSame(MpNested.Companion, nestedViaShared.asEnumish()) + assertSame(MpNested.Companion, nestedViaLeft.asEnumish()) assertEquals( - listOf("both", "left", "right"), + listOf( + "mixed-mid", + "base-direct", + "iface-pair", + "companion", + "nested", + "left-only", + "shared-mid", + "right-only", + ), MultiPath.Enumish.entries.map { kind -> when (kind) { - MpBoth.Companion -> "both" - MpOnlyLeft -> "left" - MpOnlyRight -> "right" + MpMixed.Companion -> "mixed-mid" + MpDirect -> "base-direct" + MpBoth.Companion -> "iface-pair" + MpHost.Companion -> "companion" + MpNested.Companion -> "nested" + MpOnlyLeft -> "left-only" + MpDeep -> "shared-mid" + MpOnlyRight -> "right-only" } }, ) diff --git a/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/zoo/ZooTest.kt b/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/zoo/ZooTest.kt index 39f1f18..182e21b 100644 --- a/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/zoo/ZooTest.kt +++ b/integration-test/producer-jvm/src/test/kotlin/io/github/projectmapk/fixtures/zoo/ZooTest.kt @@ -41,7 +41,8 @@ class ZooTest { } // docs/test/ケース01-生成と実行時API.md API-13: 吸収サブタイプ(open 直下・多段・object・ - // interface 実装・無名 object・local class・委譲実装)は kind を新設せず entries 不変・asEnumish 継承 + // interface 実装・無名 object・local class・委譲実装・非 sealed 末端配下の sealed 部分階層)は + // kind を新設せず entries 不変・asEnumish 継承 @Test fun subtypesAreAbsorbedIntoLeafKinds() { val openKind = Zoo.Enumish.valueOf("OpenLeaf") @@ -61,6 +62,8 @@ class ZooTest { assertSame(ifaceKind, LocalImpl().asEnumish()) // 末端 interface への委譲実装 assertSame(ifaceKind, Veil(Crafted()).asEnumish()) + // 非 sealed 末端配下の sealed 部分階層(中間 Brood・末端 Chick とも自 kind を作らない) + assertSame(ifaceKind, Chick.asEnumish()) // 吸収されても entries は末端 12 のまま不変 assertEquals(12, Zoo.Enumish.entries.size) @@ -107,9 +110,9 @@ class ZooTest { } val values = - listOf(Oval(), Square(), Spot, Crafted(), Veil(Crafted()), Zoo.DataLeaf(1)) + listOf(Oval(), Square(), Spot, Crafted(), Veil(Crafted()), Chick, Zoo.DataLeaf(1)) assertEquals( - listOf("open", "open", "open", "iface", "iface", "data"), + listOf("open", "open", "open", "iface", "iface", "iface", "data"), values.map(::describe), ) }