Goのコードを数学的に証明する

Dennis Metzger
golang.tokyo #43

Dennis(デニス) Metzger(メッツガー)

Backend Engineer @ Finatext

数学的に証明する?

自動推論の

素晴らしい世界

へようこそ

早くコード見せろ

assert

「この行に到達した時点で、

次の条件が正しいことを証明せよ」

  1. func Double(n int) (res int) {
  2. res = 2 * n
  3. // @ assert res > n
  4. return
  5. }
Gobra 1.1-SNAPSHOT (@) (c) Copyright ETH Zurich 2012 - 2024 INFO viper.gobra.Gobra - Verifying package - main ERROR viper.gobra.reporting.FileWriterReporter - Error at: <main.go:7:2> Assert might fail. Assertion res > n might not hold. ERROR viper.gobra.Gobra - Gobra found 1 error.

ensures

「この関数の返却値が、

次の条件を満たしていることを証明せよ」

  1. // @ ensures res >= 0 && (res == x || res == -x)
  2. func Abs(x int) (res int) {
  3. if x < 0 {
  4. return -x
  5. } else {
  6. return x
  7. }
  8. }
  1. type Formatter interface {
  2. // @ ensures len(s) > 0
  3. Format() (s string)
  4. }
  5. var _ Formatter = a{}
  6. type a struct{}
  7. func (a) Format() (s string) {
  8. return "a"
  9. }
  1. type Formatter interface {
  2. // @ ensures len(s) > 0
  3. Format() (s string)
  4. }
  5. var _ Formatter = a{}
  6. type a struct{}
  7. func (a) Format() (s string) { 未設定
  8. return "a"
  9. }
  1. type Formatter interface {
  2. // @ ensures len(s) > 0
  3. Format() (s string)
  4. }
  5. var _ Formatter = a{}
  6. type a struct{}
  7. // @ ensures len(s) >= 0
  8. func (a) Format() (s string) {
  9. return "a"
  10. }
  1. type Formatter interface {
  2. // @ ensures len(s) > 0
  3. Format() (s string)
  4. }
  5. var _ Formatter = a{}
  6. type a struct{}
  7. // @ ensures len(s) >= 0 満たしていない
  8. func (a) Format() (s string) {
  9. return "a"
  10. }
  1. type Formatter interface {
  2. // @ ensures len(s) > 0
  3. Format() (s string)
  4. }
  5. var _ Formatter = a{}
  6. type a struct{}
  7. // @ ensures len(s) == 1 満たしている
  8. func (a) Format() (s string) {
  9. return "a"
  10. }

requires

「この関数が呼び出される際に引数が

次の条件を満たさなければならない」

  1. // @ requires b != 0
  2. func Div(a, b int) int {
  3. return a / b
  4. }
  5. func _() {
  6. _ = Div(10, 2)
  7. _ = Div(10, 0)
  8. }
  1. // @ requires b != 0
  2. func Div(a, b int) int {
  3. return a / b
  4. }
  5. func _() {
  6. _ = Div(10, 2) OK
  7. _ = Div(10, 0) 満たしていない
  8. }

invariant

「ループの実行中に、

次の条件が常に正しいことを証明せよ」

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

assertじゃダメなんでしょうか?

ソルバーはコードを
実行しているわけではない

  • ループが何回実行されるかはわからない
  • 数学的帰納法に似た方法で証明せざるを得ない

数学的帰納法??

Step 1: 初回のループを証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 1: 初回のループを証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 1: 初回のループを証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant 0 >= 0 && 0 <= b
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 1: 初回のループを証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant 0 >= 0 && 0 <= b
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 1: 初回のループを証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant 0 >= 0 && 0 <= b → true
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 1: 初回のループを証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant 0 >= 0 && 0 <= b → true
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 1: 初回のループを証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant 0 >= 0 && 0 <= b → true
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 1: 初回のループを証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant 0 >= 0 && 0 <= b → true
  6. // @ invariant 0 == a * 0
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 1: 初回のループを証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant 0 >= 0 && 0 <= b → true
  6. // @ invariant 0 == a * 0 → true
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b trueとする
  6. // @ invariant result == a * i trueとする
  7. for i < b { trueとする
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b trueとする
  6. // @ invariant result == a * i trueとする
  7. for i < b { trueとする
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b
  6. // @ invariant result_new == a * i_new
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b
  6. // @ invariant result_new == a * i_new
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b
  6. // @ invariant result_new == a * i_new
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1 → i + 1 >= 0
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b
  6. // @ invariant result_new == a * i_new
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b
  6. // @ invariant result_new == a * i_new
  7. for i < b { → i + 1 <= b
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b → true
  6. // @ invariant result_new == a * i_new
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b → true
  6. // @ invariant result_new == a * i_new
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b → true
  6. // @ invariant result + a == a * (i + 1)
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b trueとする
  6. // @ invariant result == a * i trueとする
  7. for i < b { trueとする
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b → true
  6. // @ invariant result + a == a * (i + 1)
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b → true
  6. // @ invariant (a * i) + a == a * (i + 1)
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 2: 帰納

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i_new >= 0 && i_new <= b → true
  6. // @ invariant a * i + a == a * i + a → true
  7. for i < b {
  8. result_new = result + a
  9. i_new = i + 1
  10. }
  11. return result
  12. }

Step 3: ループ終了後の証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 3: ループ終了後の証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i < b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 3: ループ終了後の証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for !(i < b) {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 3: ループ終了後の証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i >= b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 3: ループ終了後の証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i >= b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. return result
  12. }

Step 3: ループ終了後の証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i >= b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. → i == b
  12. return result
  13. }

Step 3: ループ終了後の証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i >= b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. → i == b
  12. return result
  13. }

Step 3: ループ終了後の証明

  1. // @ requires a >= 0 && b > 0
  2. func Mul(a, b int) int {
  3. result := 0
  4. i := 0
  5. // @ invariant i >= 0 && i <= b
  6. // @ invariant result == a * i
  7. for i >= b {
  8. result = result + a
  9. i = i + 1
  10. }
  11. → i == b
  12. → result == a * b
  13. return result
  14. }

⋯⋯という証明を
Gobraが自動でやってくれている

trusted

「関数を証明済みと見なす」

  1. // @ ensures res == 2
  2. // @ trusted
  3. func One() (res int) {
  4. return 1
  5. }

ご清聴ありがとうございました!

Dennis Metzger
golang.tokyo #43