実践Swiftコンパイラ #swtws

  • 39
    いいね
  • 0
    コメント

本エントリは Swift Tweets 2017 Summer でのツイート1をまとめ、加筆修正したものです。内容は 2017/07/22 時点でのものであることにご注意ください。

Swift がオープンソース化されてしばらく経ちます。コンパイラにコントリビュートしたくても、どこから手をつけていいのやらと思ってませんか?
コンパイラのコードを修正していくときに、具体的に何を手がかりにすれば良いのか、実際のバグを修正しながらご紹介します。

僕がコントリビュート始めたのは2016年の3月です。これが僕の初めてのPR。
日付に注目ください。完全に try! Swift 2016 きっかけでした。
https://github.com/apple/swift/pull/1543

try! Swift のプレゼンテーション 『Dive into Swift Ecosystem』『Contributing to Open Source Swift』 に影響されまくり、 Jesse に Q&A セッションで「どう思います?」って聞いたら「PR投げちゃいなよ!」って言われ、翌日には PR 出してました。

Linux上でのコンパイル警告つぶすだけの簡単な PR でしたが、サクッとマージされ、一人で「ぉおおー」ってなってました。

というわけで、「ぉおおー」なってみたい人のために、実際どんな感じで修正していくのか説明していきます。
ちなみに C++ 経験なくてもなんとかなります。実際のところ、僕が10行以上の C++ コード書いたのはこのプロジェクトが始めてです!

1⃣ 環境構築

省略しますが、 別記事に環境構築方法をアップしているので、よかったら見てください。初回ビルドまで済ませば、この内容を実際に試せます。

『Swiftコンパイラ開発環境構築』

2⃣ 何をするのか?

コンパイラの実コードに手を付ける修正で、わりと初心者向けのジャンルに「診断メッセージの改善」っていうのがあります。 DiagnosticQoI とか単に QoI2 って呼ばれてます。

同テーマのIssue:
https://bugs.swift.org/issues/?jql=labels%20%3D%20%22DiagnosticsQoI%22
同テーマのマージ済みプルリクエスト:
https://github.com/apple/swift/pulls?q=is%3Apr+QoI+is%3Amerged

例えば、インタンスに .init を呼ぶと発生するエラーがあります。この [Fix] ボタンを押すと、

1_error.png

こうならなければいけないのに・・・

2_fixed.png

実際にはこんなことになってしまいます。 inittype(of: ) で囲まれてしまっていますね。

3_actual_result.png

実はこういうエラーは、メッセージだけではなく [Fix] の内容も Swift のコンパイラが出しているものなのです。つまり私たちが直せるものです!これを修正し、改善するのが DiagnosticsQoI です。

3⃣ 修正!

まずはこのエラーがどこで発行されているかを調べます。
エラーメッセージで grep してみましょう!

$ git grep "is a member of the type"

code_errordef.png

一行上にエラーの ID っぽいものが見つかったので、それをまた grep!

$ git grep "init_not_instance_member"

4_grep_error_id.png

code_csdiag.png

見つかりました。 何も知らなくても、なにやら fixItRng"type(of: "")" で囲んでいるというのくらいは読み取れると思います。

ほかにも:

  • fixItRngSourceRange 型で、ソース上の範囲を示している
  • 現在の挙動から、これは foo.initinit の部分の範囲を示している

というのもなんとなくわかると思います。そこまでわかったら周りのソース見てみましょう。

  • foo の部分はどの変数に入っているのかな? → baseExpr っていうのがある!
  • どうやって そのソース範囲を取得するのかな? → getSourceRange() っぽい!

という感じでなんとなく分かることも多いです。

結果こんな感じになりました。一行修正しただけですね。

5_fixedcode.png

4⃣ テスト!

$ utils/build-script -Rt

でビルドとテストを実行して、何が起こるか見てみましょう。

6_test_error.png

エラーになりました。どうも元々のテストケースが間違っていたみたいです😢
ということは、修正だけすればOKですね。テストを追加する必要はなさそうです。
テストについては、 別エントリにも書いたので参考にしてください。

『Swiftコンパイラのテスト環境』

修正の結果はこんな感じになります。

7_test_fix.png

再テストしたら、通るようになりました。👏

8_test_success.png

あとは、PR投げるだけ...

ちょっとまって! 本当にこれでよい?
これを Optionalfoo?.init() に適用したら。。。
こんなことになってしまいます。 Optional<Foo>init(x:) は存在しないためです。

9_optional.png

これに対する対応としては

  • baseExprOptional チェーンだったらエラーメッセージのみを出し、 Fix-it を出さない
  • foo.map({ type(of: $0) })?.init(x: 12) になるようにする

などですが、決めかねます。

こういうときは、少なくとも現状よりはマシになっているのを盾にとりあえずPR出しちゃいましょう。そこに現状の問題点記載しておけば誰かがアドバイスくれるはず。

5⃣ PRの出し方

まずは、GitHub の自分のフォークリポジトリに push ですね。
ブランチ作って、コミットして、 push する。まあ普通です。

$ git checkout -b fixit-init-instance
M  lib/Sema/CSDiag.cpp
M  test/expr/postfix/dot/init_ref_delegation.swift
Switched to a new branch 'fixit-init-instance'
$ git add lib/Sema/CSDiag.cpp test/expr/postfix/dot/init_ref_delegation.swift
$ git commit -F - <<EOF
> [QoI] Fix misplaced fix-it for calling initializer on instances
>
> - Fixed the position for fix-it
> - Updated existing test cases
> EOF
[fixit-init-instance 28d1093f35] [QoI] Fix misplaced fix-it for calling initializer on instances
2 files changed, 10 insertions(+), 10 deletions(-)
$ git push private
Enter passphrase for key '/Users/rintaro/.ssh/id_rsa':
Counting objects: 10, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (10/10), done.
Writing objects: 100% (10/10), 945 bytes | 0 bytes/s, done.
Total 10 (delta 8), reused 0 (delta 0)
remote: Resolving deltas: 100% (8/8), completed with 8 local objects.
To github.com:rintaro/swift.git
* [new branch]            fixit-init-instance -> fixit-init-instance
$

Swift プロジェクトでは、コミットメッセージはほぼ自由で、あまり体裁にこだわる必要はありません。

[カテゴリ] お題

...説明...

くらいです。簡単なコミットだったら説明なくても大丈夫。

最終的な PR 用ブランチはこんな感じになりました。

https://github.com/apple/swift/compare/master...rintaro:fixit-init-instance

PR は必ず master ブランチに。

全ての変更はまず master ブランチにマージされます。 Swift4.0 に必要なら master から 必要な部分だけ swift-4.0-branch に cherry-pick されます。なので、 まずは master に PR 出しましょう。

適当な英語でも意味さえ通じれば誰も気にしない。

PRのコメントですが、意味さえ通じればなんとかなります。今回のような修正だったらエラー対象のコード書いておくだけでも大丈夫でしょう。

11_pullrequest.png

ちなみに master ブランチでは Label や Assignee などは必要ありません。担当つけなくても誰かが拾って、なんとかしてくれます。

6⃣ プルリクエストのテスト

テストはそれなりに時間かかることもあり、コミット権持っているユーザーだけが起動できます。
だれかが swift-ci 様に Please test してくれるのを待ちましょう。

https://github.com/apple/swift/blob/master/docs/ContinuousIntegration.md


以上です。地味ですが、こういう Fix-it の修正は利用者の利便性に大きく関わってくるため、ひじょーに重要なのです。
ニッチ過ぎる!などと思わず、見つけたら積極的に直してPR投げてみてください。

コードリーディング中に typo 見つけたらそれだけでも PR 投げてみてください。積極的にマージされます。
https://github.com/apple/swift/pulls?q=is%3Apr+gardening+is%3Aclosed

バグ見つけたけど直し方が分からない場合は是非バグ報告してください。バグ報告も貴重なコントリビュートです!
https://bugs.swift.org

ちなみにここで修正した件も既にIssueトラッカーに上がっています。自分アサインした人がなかなか修正してくれないので止まっちゃってる案件。
https://bugs.swift.org/browse/SR-3245

最後に、コントリビュートへのモチベーションあげるプレゼンテーションを2つご紹介しておきます。

『Contributing to Swift: From Proposal to Shipped』Russ Bishop

『Contributing to the Swift compiler』 Ayaka Nonaka

なにか分からないことがあったら @mono0926 さん主宰の Discord にいるので、遠慮無く質問ください。

try! Swift compiler



  1. 実際のツイートは2017/7/22 #swtws Swift Tweets 2017 Summer - Togetterまとめ をご覧ください。 

  2. Quality of Implementation の略で、診断に限らない用語だと思うのですが、なぜかこの文脈以外では使われていないです。