このドキュメントは、Gemini先生とCodex先生による統一TypeBox ABIの技術的検討をまとめたものです。
ユーザー定義Boxをプラグインシステムに統合し、すべてのBoxをC ABI上で統一的に管理する提案について。
2025-09-02
まず結論から申し上げると、提案されている「ユーザー定義Boxを動的に登録し、プラグインBoxと統一的に扱うABI」というアプローチは、技術的に非常に健全(sound)です。これは、COM(Component Object Model)、Rustのdyn Trait
オブジェクト、PythonのC APIなど、多くの成功した言語やフレームワークで採用されている動的ディスパッチ(vtable)の仕組みを、FFI(Foreign Function Interface)に応用した古典的かつ堅牢な設計パターンです。
このアプローチにより、「Everything is Box」というNyashの美しい設計哲学を、言語の境界を越えたC ABIレベルまで一貫して適用することが可能になります。
NyashTypeBox
構造体の先頭に参照カウンタ(ref_count
)を持たせます。retain
関数(またはadd_ref
)を呼び出します。release
関数を呼び出します。release
関数内でカウンタが0になったら、そのBoxが使用していたメモリを解放する処理(デストラクタの呼び出しなど)を実行します。_Atomic
やGCCの__atomic_*
組み込み関数)。void*
のような型消去されたポインタをやり取りするため、誤った型として扱ってしまう危険性(type confusion)があります。type_id
を割り当てます。このIDは、実行時に登録されるユーザー定義Boxも含め、衝突しないように動的に生成・管理されるべきです(例: 単純なカウンタやアドレスのハッシュなど)。NyashTypeBox
にtype_id
フィールドを持たせます。type_id
を比較する安全なAPI(例: nyash_box_downcast_as_mybox(box)
)を提供します。このAPIは、IDが一致しない場合はNULL
を返すようにします。これにより、危険な強制キャストを防ぎます。get_last_error
のような関数を用意する)。はい、前述の通り極めて健全です。実績のある設計パターンであり、Nyashの哲学を強化する正しい方向性です。
多数あります。これらは実装の際の優れた参考資料となります。
PyObject*
としてCから扱われます。PyObject
は参照カウントと型オブジェクトへのポインタ(ob_type
)を持ち、このob_type
がメソッドテーブル(vtable)などを含む構造体を指しています。ユーザーがCで新しい型を定義し、Pythonの型システムに動的に登録する仕組みは、今回の提案と非常によく似ています。IUnknown
を継承し、QueryInterface
(型キャスト)、AddRef
(参照カウント増)、Release
(参照カウント減)の3つのメソッドを必ず持ちます。dyn Trait
: Rustのトレイトオブジェクトは、データへのポインタとvtableへのポインタからなる「ファットポインタ」として実現されます。これをC ABIで表現しようとすると、手動でデータポインタとvtable構造体ポインタをペアにした構造体を作ることになり、本提案と同じ形になります。retain
/release
のペアが崩れると、即座にメモリリークやクラッシュに繋がります。デバッグを支援する仕組み(例: 環境変数を設定するとretain
/release
の呼び出しをログに出力するなど)が有効です。Arc<Mutex<...>>
のようなアトミック参照カウントと内部可変性のパターンを検討してください。提案されているABI方式は、パフォーマンスと安全性のバランスが取れた優れた案であり、「より良い」というよりは「異なるトレードオフを持つ」代替案と言えます。
はい、完璧に整合します。
この統一ABIは、「組み込みのBox」と「ユーザー定義のBox」という実装上の区別をユーザーやプラグイン開発者から完全に隠蔽します。全てのBoxが同じインターフェース(NyashTypeBox*
)を通じて、同じ規則(参照カウント、動的ディスパッチ)に従って扱われる世界を実現します。これは、「Everything is Box」という哲学を、言語仕様からABI、そしてエコシステム全体へと貫くための、論理的かつ必然的なステップと言えるでしょう。この実装が完了すれば、Nyashはより一貫性があり、強力で、拡張性の高い言語へと進化するはずです。
Codex先生は深い分析を行い、以下の重要な洞察を提供しました:
以下は「統一TypeBox ABI」を中核に据えた、Nyash Phase 12の技術的検討まとめです。C ABI上でBoxの一元管理を行い、Nyash ABIの基盤としてユーザーBoxまでカバーする実装方針を、実装設計・性能・安全性・移行計画まで通して具体化します。
struct NyBoxHeader {
uint64_t type_id; // 予約域+種別域+連番
_Atomic uint64_t refcnt; // 0で解放、MAXでpin
const NyTypeVTable* vtable; // 不変ポインタ
uint32_t flags; // immediate/extern/sandbox/trusted/traceable 等
uint32_t header_size; // 前方互換: 拡張時にサイズ確認
};
retain(self)
, release(self)
, query(self, iface_id, out)
trace(self, visitor)
(GC連携必須)method_dispatch(self, method_id, args, ctx, out)
(最低限)const NyMethod* methods
+ uint32_t method_count
const NyField* fields
(任意)uint32_t id
(スロット)、NyCallSig sig_id
、NyFnPtr fn
union { NyBoxHeader* ptr; uint64_t tagged; }
(NaN-box/SMI対応)NyValue
で受け渡しNyRegisterUserBox(const NyTypeSpec*, const NyMethodSpec*,
const NySecuritySpec*, NyTypeId* out_type_id)
64-bit構造: [8b domain | 8b vendor | 16b category | 32b serial]
NyTypeVTable.methods
を配列化method_dispatch
へフォールバックretain/release
はC ABIの最初の3メソッドとして固定(COM類似)_Atomic u64
でインクリメント/デクリメント(Acquire/Releaseセマンティクス)trace(self, visitor)
で内部参照列挙(プラグインも必須)methods[slot]
直参照(〜1-3ns)(type_id, method_sig)
→fn*
をコールサイトに1エントリキャッシュfn*
を直接inlining or tailcall化trusted
(同プロセス・直接関数)sandboxed
(Wasm/別プロセス)restricted
(機能限定)NyTypeVTable
にWasmトランポリンを挿入fn*
が許可セクション(.text/EXEC)内か確認type_id
一致+sig_id
一致+レンジチェック)ny_*
関数はmethod_dispatch
へフォールバックny-abi-gen
: ヘッダからスケルトン生成ny-abi-check
: ローディング時にABIバージョン/署名整合性を検証NY_ABI_VERSION
(major.minor.patch)NyBoxHeader.header_size
で前方互換min_abi
,max_abi
を記載結論: 技術的に健全。コアはCPython(PyObject)+COM(IUnknown)+Rust vtableの良いとこ取り
refcnt
/type
/vtable
を固定し、関数テーブルによる静的ディスパッチを基本とする設計は長期安定性が高いtrace
を型契約に含め「逃げ道のない」完全列挙を強制NyValue
(tagged/immediate)導入で「Everything is Box」を性能劣化なく実現可能可能。推奨アプローチは二段:
NyBoxHeader*
を持つヒープBoxNyValue.tagged
にエンコードAPIは常にNyValue
受け渡し。VM/JITは即値→ヒープBox化を必要時のみ実施
extern "C" NyStatus NyRegisterUserBox(const NyTypeSpec*, const NyMethodSpec*,
const NySecuritySpec*, NyTypeId*);
extern "C" NyStatus NyResolveMethodSlot(NyTypeId, NySigId, uint32_t* out_slot);
extern "C" NyStatus NyCallSlot(NyValue self, uint32_t slot, const NyValue* args,
uint32_t argc, NyValue* out, NyCallCtx*);
extern "C" void NyRetain(NyValue);
extern "C" void NyRelease(NyValue); // 即値はno-op
nyabi.h
ドラフトと最小実装register_user_box()
の仮実装+スモークny-abi-gen
簡易版(署名→スロット表生成)