.. _ja How to Investigate Unproved Checks:

証明されなかった検査項目を調査する方法
=======================================

形式検証におけるもっとも挑戦的なアスペクトの一つは，失敗した証明を解析することです． |GNATprove| が，実行時検査や表明の保持に関して，自動的な証明で不合格としたとき，そこには幾つかの理由があります．

* [CODE] コードが間違っているため，検査項目ないしは表明が保持されていない
* [ASSERT] 表明が間違っており，正しく保持されていない
* [SPEC] プログラムのふるまいについて幾つかの不足する表明があり，表明の検査が証明できない
* [MODEL] 現在 |GNATprove| で用いているモデルには限界があり，検査や表明が証明できない
* [TIMEOUT] 検証器のタイムアウトのため，検査或いは表明が証明できない
* [PROVER] 検証器が不十分で，検査或いは表明が証明できない

間違っているコードあるいは表明を調査する
-----------------------------------------

最初のステップは，コードが誤っている [CODE] 或いは，表明が誤っている [ASSERT] か，或いはその双方かを検査することです．実行時検査や表明は，実行時に行われるので，コードや表明の正しさに関する信頼を増加させる一つの方法は，代表的な入力を用いてプログラムをテストすることです．次の GNAT スイッチを用いることができます．

* ``-gnato``: 演算途中のオーバーフローの実行時検査を可能とする
* ``-gnat-p``: 全ての検査を抑制するために ``-gnatp`` が使われていても，実行時検査を再度可能とする
* ``-gnata``: 表明の実行時検査を可能とする

.. _ja Investigating Unprovable Properties:

証明できないプロパティを調査する
-----------------------------------

二番目のステップは，プロパティが証明可能であるかどうかを考えることです [SPEC]. 検査ないしは表明は，必要な注記(annotation)が欠けているために，証明不能であるかもしれません．

* 内包しているサブプログラムの事前条件が弱すぎる，或いは
* 呼び出されるサブプログラムの事後条件が弱すぎる，或いは
* 内包しているループに対するループ不変条件が弱すぎる，或いは
* 検査や表明の前のループに対するループ不変条件が弱すぎる

特に， |GNATprove| は，サブプログラムのボディ部を調べないので，呼び出しについて必要な全ての情報は，サブプログラム契約中で明確であるべきです．コードと表明に焦点を当てた手動レビューによって，効率的に，多くの失われた注釈について判断することができます．表明の記述が多くても， |GNATprove| は，正確に自分が証明できない場所を示すことができます．こうすることで，問題を明らかにする助けとなります．調査しつつ，コードを単純化することは役立ちます．例えば，単純な表明を追加して，証明してみることです．

|GNATprove| が提供するパス情報は，コードレビューで役に立ちます．証明が失敗したパスを，:ref:`ja Running GNATprove from GNAT Studio` で記述したように，エディタ内で表示することができます．場合によっては，反例もまたパス上に生成されます．このとき，問題を示す変数の値も分かります（詳しくは， :ref:`ja Understanding Counterexamples` 参照のこと）．多くの場合，不足している表明を見つけ出すには，これで十分です．

プロパティもまた，概念的には証明可能です．しかし， |GNATprove| が用いるモデルでは，今のところモデルを用いた推論を行うことができません [MODEL] ．特に，下記の言語の特徴を利用すると，真になるべき VC（Verification Condition，検証条件） を生成できる場合があります．ただし，証明することはできません．

* 浮動小数点算術演算（しかし， |CodePeer| integration，役立つ）
* 文字列リテラルの内容（こちらも， |CodePeer| integration を用いることで支援可能である）

|CodePeer| integration を使用するためには, スイッチ ``--codepeer=on`` を |GNATprove| に渡します.  |CodePeer| も他の検証器が存在しないケースにおいては，不足情報を ``pragma Assume`` を用いて補うことで，VC を証明することができます．

証明不能なプロパティと証明器に不足する要素がある（次章）ことを区別するのは，時には困難です．この問題を中心的な問題に限定するために，一般的な最も役に立つアクションは，コード中に表明を挿入することです．この表明では，プログラム中のある特定の点で，そのプロパティ（或いはその一部）が証明可能かどうかを `テスト` します．例えば，もし，ある事前条件で次の様なプロパティを設定しているとします： (P or Q) いま，実装が多くの分岐とパスを含んでいるとします．ことの時に次の表明を加えることを考えます．適切な場所において，P が真であるか，或いは Q が真であるか．これは，次の 2 つのケースを区別するために役立ちます．

* 証明不可能なプロパティの場合，プログラム中の特定のパスが示され，問題を引き起こす特定のプロパティの箇所が分かる可能性があります．
* 証明器に不足が有る場合，こうすることで，証明器が，表明とプロパティの双方をなんとか証明する助けになります．自動証明に役立つ表明のみをコード中に残し，証明器とやりとりする中で挿入する他の表明を取り除くというのは，良い方法です．

.. _ja Investigating Prover Shortcomings:

証明器の不足要素を調査する
---------------------------------

最後のステップは，証明器が証明に十分な時間を与えられたかどうか [TIMEOUT] または，別の証明器が証明を見つけられるかどうか [PROVER] です．この目的のために， |GNATprove| は， ``--level`` スイッチを提供し，コマンドラインから（ :ref:`ja Running GNATprove from the Command Line` 参照）或いは，GNAT Studio から（ :ref:`ja Running GNATprove from GNAT Studio` ）或いは GNATbench から（ :ref:`ja Running GNATprove from GNATbench` 参照）から利用することができます．デフォルトの 0 レベルは，単純な証明に十分です．一般的に，自動証明がそれ以上得られなくなるまで，証明のレベルを（レベル 4 まで）あげることができます．

次の節 :ref:`ja Running GNATprove from the Command Line` で記述するように，スイッチ ``--level`` は，さまざまなより低いレベルのスイッチ ``--steps`` , ``--prover`` , ``--proof`` を直接設定するのと等価です．それゆえ， ``--level`` における既定義の組み合わせを用いるよりも，より強力な（必然的により長い証明時間を必要とする）値を設定することができます．

上記の実験に関して，GNAT Studio の :menuselection:`SPARK --> Prove Line` 或いは，:menuselection:`SPARK --> Prove Subprogram` メニューを使うと便利です． :ref:`ja Running GNATprove from GNAT Studio` および :ref:`ja Running GNATprove from GNATbench` に記載があります．対象となる行ないしはサブプログラムに対して，より高速に結果を得ることができます．

現在の自動証明器には，浮動小数点算術演算をきわめて正確には扱えないという問題があります．特に，多数の演算がある，或いは非線形演算（乗算・除算・累乗）を含んでいる場合です．この場合は， |CodePeer| integration を用いるのが便利です．スイッチ ``--codepeer=on`` で動作し，浮動小数点演算の境界検査を高速かつ正確に実施することができます．

自動証明に共通に存在する限界は，非線形の算術を適切に扱えないことです．例えば，乗算，除算，モジュロー演算，累乗を含んでいる単純な検査の証明に失敗する場合があります．

その場合，ユーザは次のいずれかの方法をとることができます．

* コード中に，SPARK 補題ライブラリからの補題の呼び出しを加えます．
* ユーザの補題呼び出しをコード中で行う．
* コード中に仮定を付け加える．
* コード中に正当化を付け加える．
* 証明不可の検査を手動でレビューし，その結果を信頼できる形で，記載します．(例えば，版管理下で， |GNATprove| の結果を保存する）

将来的には， |GNATprove| は，式の `user view` を提供し，証明器に渡す予定です．これは，上級者が調査を行うためです．このビューは，Ada 風の構文を持ち，証明に失敗した実際の式を表現します．これによって，ユーザは，その解釈が容易になります．現在，この形式はまだ定義されていません．

特に手動で証明したい上級者のために, |GNATprove| が生成する証明ファイルの形式を提供する予定です．これによってユーザは，証明器に渡される実際のファイルを理解することができるようになります．個々のファイルは，プロジェクトオブジェクトディレクトリ（デフォルトはプロジェクトディレクトリ）の ``gnatprove`` サブディレレクトリ下に保持されます．ファイル名は以下の名前付けとなります::

  <file>_<line>_<column>_<check>_<num>.<ext>

ここで:

* ``file`` は，検査対象の Ada ソースファイルの名前です
* ``line`` は，検査する行です
* ``column`` は，カラムです
* ``check`` は，検査の識別子です
* ``num`` は，補足の数字で，プログラムにおける異なるパスの識別です．パスは，サブプログラムの開始と検査位置の間にあります．
* ``ext`` は，選択したファイル形式に関係する拡張です．これは，証明器に依存します．例えば， Alt-Ergo の場合は， Why3 形式となります．CVC5 に対するファイルは，SMTLIB2 形式となります．

例えば，Alt-Ergo 証明器で， ``f.adb`` ファイルの 160 行目，42カラム目の範囲検査に対して生成される証明ファイルは，以下に保持されます::

  f.adb_160_42_range_check.why
  f.adb_160_42_range_check_2.why
  f.adb_160_42_range_check_3.why
  ...

証明器 CVC5 に対して生成する同様の証明ファイルは::

  f.adb_160_42_range_check.smt2
  f.adb_160_42_range_check_2.smt2
  f.adb_160_42_range_check_3.smt2
  ...

これらのファイルを調査するためには， |GNATprove| に対して，コマンドラインで ``-d`` スイッチを付け加えることで，これらファイルを保持するように指示することができます．また， ``-v`` を用いることで， |GNATprove| が生成し，証明しようと試みる証明ファイルの詳細なログを入手できます．
