- Zigコンパイラの**型解決(type resolution)**ロジックが全面的に再設計され、内部構造が単純化されるとともに、ユーザーにとっても可視的な改善が行われた
- 新しい設計では**型フィールド解析を遅延(lazy)**処理し、初期化されていない型の詳細構造を不要に検査しない
- **依存ループ(dependency loop)**のエラーメッセージが具体的に改善され、ループの原因を明確に把握できるようになった
- **インクリメンタルコンパイル(incremental compilation)**機能の過剰解析の問題と多数のバグが解決され、ビルド速度が大幅に向上した
- 今回の変更には数十件のバグ修正と小規模な言語改善が含まれ、Zigコンパイラの性能と開発体験を全体的に強化している
型解決ロジックの再設計
- 約3万行規模のPRがマージされ、Zigコンパイラの型解決ロジックが、より論理的で直感的な構造へと書き直された
- この過程でコンパイラ内部構造が整理され、ユーザーにも直接的な改善効果がある
- コンパイラが型フィールド解析を遅延評価するよう変更され、初期化されていない型の詳細構造を不要に探索しなくなった
- 例示コードでは、
@compileErrorフィールドを含む構造体が名前空間としてのみ使われる場合、以前はコンパイルエラーが発生していたが、現在は正常にコンパイルされる
- これは
std.Io.Writerのような名前空間型を使う際に、不必要なコード依存を防ぐ
依存ループのエラーメッセージ改善
- 以前は依存ループのエラーメッセージが曖昧だったが、現在はループの原因と位置を明確に表示する
- 例示コードで
FooとBar構造体が互いを参照する場合、エラーメッセージが各型の依存位置を具体的に指摘する
- メッセージにはループ長、各フィールド宣言位置、アラインメント照会位置などが含まれる
- 複雑なループでも十分な情報が提供され、問題の原因を容易に把握できる
インクリメンタルコンパイル性能の向上
- 今回の変更によりインクリメンタルコンパイル機能の多数のバグが修正された
- 特に「過剰解析(over-analysis)」問題を解決し、変更された部分だけを再コンパイルするよう最適化された
- その結果、多くの場合でコンパイル速度が大幅に向上した
- 開発者はZig 0.15.1以降でインクリメンタルコンパイルを有効にし、改善された開発体験を試せる
その他の改善点
- 今回のPRには数十件のバグ修正、小規模な言語変更、コンパイラ性能改善が含まれる
- 変更全体の内容はCodebergのPR #31403で確認できる
- 新たなバグを見つけた場合はイシュー報告が推奨される
変更の意義
- 型解決ロジックの単純化とインクリメンタルコンパイルの最適化により、Zigコンパイラの安定性と効率性が強化された
- 開発者はより高速で明確なフィードバックを得られ、大規模コードベースでも生産性向上が期待できる
1件のコメント
Hacker News のコメント
この devlog の筆者です。
言語変更による互換性破壊への懸念は理解できますが、今回のコンパイラ変更が大きな波及を起こすほどではないことは明確にしておきたいです。
たとえば ZLS を新しいブランチでビルドする際も、
.{}を.emptyに変える程度の修正だけで済みました。これはstd.ArrayListのデフォルト値削除によるもので、すでに 1 年前からdeprecated状態でした。また Awebo プロジェクトでも、依存関係ツリー全体で修正が必要だったのは 3 箇所だけでした —
.emptyへの変更、comptimeの追加、orelse @alignOf(T)の追加です。こうした修正は、ほとんどの Zig 開発者なら反射的に処理できるレベルの単純な変更です。
今回の PR の要点は破壊的変更よりも、バグ修正と増分コンパイルの改善にあります。
PR の品質と計画性は非常に高いと思っており、筆者の努力をおとしめる意図はまったくありませんでした。
ただ、今後はもっと注釈を付けて慎重に意見を書くべきだという教訓を得ました。
lib/std/multi_array_list.zigに追加されたコメントを見て疑問が湧きました。@alignOf(T)をMultiArrayList(T)の定義で使うと、なぜ循環依存が発生するのか理解できません。TがMultiArrayList自身だったとしても、完全に別個のモノモーフィック型ではないのでしょうか。何か見落としている気がします。関連コード: リンク
Zig を本番環境で使っている人たちの経験が気になります。
言語が頻繁に変わりますが、更新頻度やリライトの周期はどの程度なのか、依存パッケージが言語バージョンに追いつかないことがあるのかも知りたいです。
Bun が Zig をうまく活用しているのは知っていますが、ほかの事例も聞いてみたいです。
この 1〜2 年での言語と標準ライブラリの変更は、大きな問題なく進んでいます。
以前はアップグレードが面倒でしたが、今では「少し不便」と感じる程度です。
Zig の使用経験を聞かれたら、この点はほとんど触れないくらい安定しています。
この種の大規模プロジェクトはタグ付きリリースを基準にアップグレードし、通常は数日〜数週間で移行を完了します。
依存関係もほとんどないため、アップグレードの負担は大きくありません。
ただし、ちょっとしたタイプミスでSIGBUS のコンパイラクラッシュが起きることがあり、デバッグが大変です。
.zig-cacheが 173GB まで膨らんで、ARM VPS では問題になることもありました。lightpandaを 0.14→0.15 に上げたときは問題なく進みました。0.16 も大きな問題はなさそうです。ただ、ライブラリ開発者としては 0.16 の速い変化のペースについていくのが大変です。
現在は "dev" ブランチでだけ実験的に対応しています。
Node.js/TypeScript モジュールを Zig に書き換えたところ、2 倍高速になり、メモリ使用量は 70% 減少しました。
Zig の
sqlite/JSONシリアライズ対応が強力で、大きな利点でした。欠点は、クロージャや vtable 構文がないためコードの階層分離が難しいことです。
Arcsとバンパーアロケータを使ってメモリ安全性は確保しており、今後も DebugSafe モードで運用する予定です。ReleaseFast に切り替えると 25% の速度向上はありましたが、安全性を失うほどの利得ではありません。
コード修正が必要になるとしても、長期的には正しいアプローチです。
Zig チームの成果には感銘を受けました。
私は Zig で作られたghostty ターミナルをよく使っていますが、とても安定しています。
ただ、個人的には Rust のほうが好みです。
Rust は**「閉じた世界」モデル**、Zig は**「開いた世界」モデルを採っています。
Rust では trait を明示的に実装する必要がありますが、Zig では型の形状 (shape)** が合っていれば動作します。
そのおかげで Zig では強力なメタプログラミングが可能ですが、型推論が不明瞭になりやすく、自動補完・ドキュメント化・LSP サポートが難しいという欠点もあります。
説明を聞くと Go のインターフェースに少し似ているように思えますが、Zig には直接対応する概念がないと理解しています。
kernel32からNtdllへの移行が興味深かったです。これは Linux のユーザー空間 API にも当てはまる考え方です。
特にカーネル-ユーザー境界でのエラー処理の仕組みが似ています。
ただ、Linux では libc とカーネルが密接に絡んでいるため、
errnoの使用が必須です。Windows でもこうしたパターンが生まれた理由が気になります。
errnoやGetLastError()パターンは、スレッド以前の時代の遺産です。昔は協調的スケジューリングだったのでグローバル変数でも安全でしたが、マルチコアとスレッドが登場して危険になりました。
その代替としてthread localが登場しました。
型を名前空間のように使う代わりに、言語に明示的な名前空間を追加するほうが良いのではと思います。
複数の箇所で最適化される機能なら、その時点で追加するというアプローチを取っています。
Zig では
@importがファイルを構造体に変換し、名前空間は単なるネストした structとして表現されます。つまり、名前空間は別の import にすぎません。
(まだコーヒーが足りていないので、正確さは保証できませんが)
言語変更の議論でしばしば見落とされる点があります — それがエコシステムへの影響です。
言語が頻繁に壊れると、アプリだけでなくライブラリ・ツール・チュートリアルも追従し続けなければなりません。
結果として、「一度作って放置される」ライブラリよりも、活発に保守されるプロジェクト中心のエコシステムに傾きます。
これは言語設計の初期段階では妥当なトレードオフですが、長期的にはエコシステムの成長に影響します。
他の新興言語はこうした変化疲れの最小化に多くの労力を払っています。
Zig のアプローチがどんな結果をもたらすのか見守るのも興味深いです。
Blender は頻繁に API を壊しますが、たいていの修正は些細なものです。
ただし、誰かがそれを直さなければならず、メンテナンスが止まればユーザーが自分でパッチを当てるしかありません。
有料アドオンは維持される可能性が高いですが、それでも保証はありません。
保守されていないライブラリは、どうせ悪いコードです。
Zig を批判する代わりに、別の言語(C3 など)を宣伝するのはやめてほしいです。
Zig の PR で言及されていた「Chromium、boringssl、Firefox、Rust が advapi32.dll の SystemFunction036 を呼んでいる」という話は事実ではありません。
これらはすでにProcessPrngを使っており、Windows 10 以降では失敗しません。
根拠は Microsoft whitepaper にあります。
RNG リクエストは決して失敗しないよう設計されており、失敗した場合はプロセス自体が終了するようになっています。
つまり、高品質な乱数を保証するためにエラーコードを返さないのです。
Zig の言語セマンティクスは表面的には単純ですが、相互作用は微妙です。
こうした点は、C++ のテンプレート規則のように、時間がたつにつれて複雑なコーナーケースを生む可能性があるように思えます。
3 万行の PR はすごい成果です。
ただ、言語セマンティクスを変えるのは非常に重大なことなので驚きました。
Zig はまだ 1.0 前で変化が速いのは理解していますが、「このブランチでセマンティクスを変えた」といったカジュアルな表現には少し面食らいました。
こうした大規模な変更が Zig 特有の文化なのか、それとも私が時代遅れなのか気になります。
「modern Zig」という表現も、言語の変化の速さを思うと笑ってしまいました。
devlog はマーケティング記事ではなく、内部向けの記録に近いものですし、Zig はまだ 1.0 ではありません。
PR には十分な文脈と根拠が含まれています。
Zig を選んだ時点で、一定程度の言語変更リスクは受け入れたことになります。
むしろ今の段階でクリーンに整えておくほうが、長期的には得です。
(C のビット演算子優先順位のような、直せない遺産を思い浮かべればわかります)
mluggは Zig のコアコントリビューターであり、Zig Foundation のメンバーです。今回の変更は循環依存の解消と型システムの整理のための作業です。
関連提案は #3257 と #15909 で公開されています。
この変更により Zig の型解決は**DAG(有向非巡回グラフ)**構造として整理され、コンパイラの安定性が大きく向上します。
Zig は BDFN (Benevolent Dictator For Now) モデルで運営されており、最終決定権は Andrew Kelley にあります。
ただしチームは非営利組織で、ユーザーの信頼と言語品質を最優先にしています。
個人的には Matthew と一緒に仕事ができるのは大きな名誉です。
形式的には完璧でも、実際には混沌の言語だった C を思い起こさせます。