この記事の要点
- Swift 5.2 が正式リリースされました。新しい言語機能の追加よりも、開発体験(developer experience)の改善に重点を置いたリリースです。
- コンパイラの診断(エラー・警告)の精度向上、コード補完の高速化と改善、ビルドアルゴリズムの改善、デバッグの信頼性向上、Swift Package Manager の依存解決の改善が主な内容です。
- 言語面では、key path 式を関数として渡せる機能と、ユーザー定義型の値を関数のように呼び出せる callable 機能という、表現力の高い API を組むための 2 つの機能が追加されました。
- Apple プラットフォーム向けには Xcode 11.4 に同梱され、Linux(Ubuntu 18.04 / 16.04)向けの公式バイナリも提供されます。
主な変更点
言語の更新
Swift 5.2 では、Swift Evolution プロセスを経た次の 2 つの言語 Proposal が実装されました。
-
関数としての key path 式(SE-0249):
\Root.valueという key path リテラルを、(Root) -> Valueの関数が期待される箇所にそのまま渡せるようになりました。mapなどの高階関数にプロパティを簡潔に渡せます。struct User { let name: String let isAdmin: Bool } let users: [User] = ... let names = users.map(\.name) let admins = users.filter(\.isAdmin) -
ユーザー定義 nominal type の callable な値(SE-0253):
callAsFunctionメソッドを定義した型の値を、関数のように()を付けて呼び出せるようになりました。状態を持ったまま関数のように振る舞う値(クロージャに近い使い勝手の型)を表現できます。struct Adder { let base: Int func callAsFunction(_ x: Int) -> Int { base + x } } let addThree = Adder(base: 3) addThree(10) // 13(addThree.callAsFunction(10) と同じ)
コンパイラ診断の改善
Swift コンパイラのエラーメッセージの品質と精度が大きく改善されました。
従来は、式を部分式に分割し、それぞれを個別に調べてエラー箇所を推測していました。この方式は単純なケースでは機能しますが、親の式の情報を使わないと正しく特定できない種類の誤りには対応できませんでした。
Swift 5.2 では、型推論の途中で遭遇した個々の失敗を記録しておき、その情報から正確な診断を生成します。多くの場合、修正方法(Fix-It)も提示されます。
たとえば、存在しない enum のケースと比較するコードを考えます。
enum E { case one, two }
func check(e: E) {
if e != .three {
print("okay")
}
}
Swift 5.1 では、次のような分かりにくいメッセージが出ていました。
error: binary operator '!=' cannot be applied to operands of type 'E' and '_'
Swift 5.2 では、問題が即座に分かるメッセージになります。
error: type 'E' has no member 'three'
新しい診断アーキテクチャの詳細は 新しい診断アーキテクチャの概要 を参照してください。
コード補完の改善
- 不要な型チェックを省くことで補完が高速化し、大きなファイルでは補完位置によって Xcode 11.3.1 比で 1.2〜1.6 倍速くなります。
- 不完全な dictionary リテラルや三項演算子の式に対しても、暗黙のメンバーの名前を補完できるようになりました。
- 補完結果に現れる型が読みやすくなりました。可能な場合は opaque result type(
some Viewなど)を使い、typealias を保持し、不要な親の型は表示しなくなりました。
ビルドアルゴリズムの改善
Swift コンパイラには、Release ビルドで主に使われる Whole Module モードと、Debug ビルドで主に使われる Incremental モードの 2 つの動作モードがあります。Incremental モードは、変更されたファイルだけを並列にコンパイルできる点で開発時に有利ですが、宣言がソースファイルをまたいで参照し合うため、複数のコンパイルタスクで型チェックが重複し、効率が落ちることがありました。
Swift 5.2 のコンパイラ(特に型チェッカ)では、request(自己完結した計算の単位)の間でのキャッシュ・遅延評価・依存追跡を担う新しい中央集権的なロジックが導入されました。これにより宣言とその参照をより効率的に解決できます。
従来は、別のソースファイルから宣言が参照されると、後で必要になるかもしれないプロパティを先回りで計算する validation という粗い処理が走り、無駄が生じていました。Swift 5.2 では、コンパイラ内部の宣言の表現がイミュータブルになり、コード生成フェーズが必要に応じて request を遅延評価し、その結果をキャッシュします。request は従来の validation より細粒度なため、無駄な作業を避けて性能が向上し、加えて型チェッカが想定外の検証を要求される際の正しさの問題も多数修正されました。
さらに、名前付きシンボルを宣言へ解決する name lookup など基盤コンポーネントの最適化により、Whole Module / Incremental の両モードでビルド時間の改善が期待できます。これらは name lookup の各種アルゴリズムの計算量(big-O)を下げるため、ソースファイルが多い大規模プロジェクトで特に効果があります。
デバッガの改善
Swift デバッグがサポートされる全プラットフォームで、LLDB がデバッグ情報から Swift の型情報を再構築する能力が向上し、より多くの型情報を扱えるようになりました。
特に LLDB は、Clang モジュールをソースからコンパイルする代わりに、DWARF デバッグ情報から C / Objective-C の型を取り込めるようになりました。この挙動は symbols.use-swift-dwarfimporter という LLDB 設定で制御でき、従来の Clang モジュール取り込みが失敗した場合のフォールバックとしてデフォルトで有効です。これにより、複数の動的ライブラリで検索パスが衝突するなどの理由で Clang モジュールの取り込みが失敗するケースでも、変数ビューや式評価が機能しやすくなります。
Swift Package Manager
Swift Package Manager には次の改善が入りました。
- ツールバージョン 5.2 以上のリモート Swift パッケージでは、テストターゲットでのみ使われる依存を解決しなくなり、性能が向上し、依存バージョンの衝突の可能性が減ります。
- 依存解決に新しい戦略が採用され、複雑なパッケージグラフでのエラーメッセージの品質と性能が大きく改善されました(SE-0226)。
ツールの改善
- SwiftSyntax: 構文ノードの階層がプロトコルから構造体に置き換えられて最適化され、構文木の走査、特に
SyntaxRewriterでの書き換えが高速になりました。 - SourceKit-LSP: Xcode 11.4 と対応するコマンドラインツールに含まれる SourceKit-LSP に、Fix-It(Code Actions として)と、メソッド抽出などのローカルなリファクタリングのサポートが加わりました。C / C++ / Objective-C のサポートや、JSON コンパイルデータベース(CMake プロジェクトなど)の読み込み速度・インデックスも改善されています。
ドキュメントとプラットフォーム
Swift 5.2 に対応した『The Swift Programming Language』の更新版が Swift.org で公開され、Apple Books でも無料で入手できます。Linux 向けには Ubuntu 18.04 / 16.04 の公式バイナリが提供され、Apple プラットフォーム向けには Xcode 11.4 に同梱されます。ツールチェインは Swift.org からも入手できます。
Swift 利用者への影響
- コンパイルエラーで悩む時間が減ります。 診断が正確になり、的外れな行に出ていたエラーが正しい箇所を指すようになりました。多くの場合は Fix-It による修正候補も得られます。
- エディタの応答が速くなります。 コード補完が高速化し、補完結果の型表示も読みやすくなりました。
- ビルドが効率的になります。 Incremental ビルドでの重複作業の削減と name lookup の最適化により、特に大規模プロジェクトでビルド時間の改善が見込めます。
- 依存管理が安定します。 Swift Package Manager の依存解決の改善で、テスト専用依存の余計な解決がなくなり、衝突時のエラーメッセージも分かりやすくなりました。
- API 設計の表現力が上がります。 key path をそのまま関数として渡せ、
callAsFunctionで値を関数のように呼び出せるため、より読みやすい API を組めます。
関連リンク
- 前のリリースについては Swift 5.1 リリース を参照してください。
- このリリースの計画と管理方針については Swift 5.2 のリリースプロセス を参照してください。
swift-5.2-branchは、次のリリースに向けた変更を蓄積するため引き続き開かれた状態に保たれました。 - 本記事に関連する解説記事: 新しい診断アーキテクチャの概要
- 本記事で触れた Proposal ダイジェスト:
- Swift のダウンロード