spec: document that certain composite types have a fixed (but unspecified) size independent of composition
要約
概要
Go言語仕様に、スライス・ポインタ・関数・インターフェース・マップ・チャネルといった合成型のサイズが「型の構成要素に依存しない固定値(値自体は未規定)」であることを明文化しようという提案です。これにより、現在コンパイラがエラーとする一部の再帰的な型定義を許可できるようになります。
ステータス変更
active → hold
2026年8月26日と9月23日の2回にわたるproposal reviewミーティングで議論が行われましたが、特にインターフェース型の扱いを巡って結論が出ませんでした。最終的に @griesemer が @mrkfrmn とさらに検討することになり、9月24日に @aclements によって hold(保留) に設定されました。議論が煮詰まっていないため、いったん立ち止まって設計の細部を詰め直す必要があると判断されたものと考えられます。
技術的背景
現状の問題点
現在のGo仕様では、ポインタや関数、インターフェースなどの合成型のサイズが「その構成に依存せず固定である」ことが明文化されていません。そのため、以下のような一見自明に安全なはずの再帰的型定義がコンパイルエラーになります。
type A *[unsafe.Sizeof(A{})]byte // ERROR invalid recursive type: A refers to itself
ポインタのサイズは要素型に関わらず常に一定(例えば64bit環境なら8バイト)であるにもかかわらず、コンパイラは unsafe.Sizeof(A{}) の評価が型 A 自身に依存しうると保守的に判断し、循環参照とみなしてしまいます。
提案された解決策
仕様に「スライス・ポインタ・関数・非空インターフェース・マップ・チャネル型のサイズは、その種類(kind)に固有の実装依存な固定定数であり、型の具体的な構成には依存しない」という趣旨の一文を追加します。これは言語仕様上の新しい挙動を導入するものではなく、既に実装として真である事実を仕様として明文化(コード化)するものです。これにより型チェッカーはサイズの循環参照検出において、より正確な判定が可能になります。
議論の過程で、@griesemer は以下のように文言を修正する案を提示しました。
The sizes of slice, pointer, function, non-empty interface, map, and channel types are fixed...
これは、空インターフェース(interface{})を対象から除外する代わりに、空インターフェースは自己参照できないためこの除外が問題にならない、という理由によるものです。
これによって何ができるようになるか
主な効果は、unsafe.Sizeof を使った再帰的な型定義が型チェックを通過できるようになることです。ただし提案本文でも認められている通り、実用上のメリットは限定的で「esoteric(難解)」なコード例にとどまる可能性が高く、実際のプログラムでの具体的な活用例は今のところ乏しいとされています。
コード例
// Before: サイズが構成に依存しないことが仕様上保証されていないため、
// 以下は "invalid recursive type" エラーになる
type A *[unsafe.Sizeof(A{})]byte
// After: 仕様が明文化されれば、ポインタのサイズは A の内容に依存しないと
// コンパイラが判断でき、コンパイルが通るようになる(型チェッカーの改善)
type A *[unsafe.Sizeof(A{})]byte // OK(将来的に)
議論のハイライト
- Language Change Proposal Review Group(@griesemer)は当初「uncontroversial(異論なし)」として通常のproposal委員会に送りましたが、その後の議論で論点が浮上しました。
- @randall77 は、現在は同じサイズを持つ空インターフェースと非空インターフェースを将来的に異なるサイズ(例: 2ワード vs 3ワード)で実装したくなる可能性を懸念点として指摘しました。
- @adonovan は、インターフェースに余分なワードを割くことは全体のメモリ使用効率の観点から魅力的でなく、また多くのアセンブリコードを壊すことになるため、その懸念は現実的な制約にはならないだろうとコメントしました。
- proposal reviewミーティングでは、「サイズが型の"kind"のみに依存する」という定義がポインタ・マップ・スライス・チャネルには素直に当てはまる一方、インターフェースは埋め込みインターフェース(
interface{ I })などにより型情報を辿らないとサイズを判断できないケースがあり、シンプルに一般化できないことが指摘されました。 - TinyGoのような全体プログラム解析(whole-program analysis)を行うツールチェーンでは、インターフェースがどの型に実装されているかを特定し、表現方法を特化させる最適化を行いうるため、この仕様がそうした将来の最適化を過度に制約しないかという論点も挙がりました。
- 結論として「インターフェース以外の型についてのみこの仕様を適用することに意味があるか」という根本的な問いが未解決のまま残り、@griesemer が @mrkfrmn とさらに検討することになり、holdとなりました。
関連リンク
- Proposal Issue github.com/golang/go
- Review Comment proposal review meeting
- 関連Issue: proposal: sizeof: add synthetic, pointer-like types #40169
- 関連Issue(クローズ済): spec: Document rules for recursive type and other self-referential decls #5069
- Proposal Issue
- Review Minutes
- 関連Issue: spec: clarify whether arguments of unsafe.Sizeof are evaluated #77342