.. _ja Language Restrictions:

言語上の制限
=====================

.. _ja Excluded Ada Features:

除外しているAdaの特徴
-----------------------------

形式検証を容易にするために， |SPARK| は，Ada 2012 に対して，幾つかの広域的な単純化を行っています．最も特徴的な単純化は以下のものです．

* アクセス型と割り当て子を利用することができません．ポインタが存在する場合，どのメモリが割り当てられ，どのメモリが解放されたかを追跡する必要があります．また，異なるメモリブロックの分離も考慮する必要があり，こういったことは，多数の労力なしに，精度よく行うことはできません．その代わり， |SPARK| では，豊富な汎用体データ構造を提供しています．詳細は，次を参照して下さい： Formal Containers Library

* 全ての式（関数呼び出しを含みます）は，副作用がありません．副作用を持つ関数を論理的に取り扱うのは非常に複雑で，非決定性評価が必要になる場合があります．これは，複合式中に含まれる式間で副作用の衝突を生じる場合があるためです．副作用を持つ関数は， |SPARK| では手続きとして書くべきです．

* 名前のエイリアス化を認めていません．エイリアス化は，思いがけない干渉を引き起こす可能性があります．局所的なはずの変数の値が，他の局所的名前変数を変更した結果，変わってしまう場合があるからです．エイリアス化を持つプログラムの形式検証は，正確さに欠け，多くの手作業を必要とします．詳しくは，次を参照下さい：  :ref:`ja Absence of Interferences`

* goto 文は認めていません．goto でループ文を作ることができ，その結果，形式検証においては特別な扱いが必要になるほか，正確にその場所を特定する必要があります．詳細については次を参照下さい．

* 被制御型 [#f1]_ は，利用できません．被制御型は，コンパイラによる暗黙的呼び出しを招くことになります．暗黙的呼び出しを形式検証しようとすると，形式検証ツールを利用するのに，ユーザが多くの手間を必要とすることになります．ツールが報告するときに，情報の元となるソースコードがないからです．

* 例外処理は除外しています．例外処理では，多数の手続き間の制御フローパスを作ることになります．例外処理があるプログラムの形式検証は，このパスに沿ってプロパティの確認をする必要があります．これは，多くの手作業なしに行うことはできません．しかし，例外の送出は可能です．（詳しくは，次を見て下さい： :ref:`ja Raising Exceptions and Other Error Signaling Mechanisms`）

上記に示した機能は， |SPARK| から除かれています．なぜならば，現時点で，形式検証のさまたげとなるからです．もし，形式検証技術が進化し，リストが更新されるならば，ここで示した制限の幾つかは緩和されるでしょう．形式的に検証することが可能だけれども，現時点では |SPARK| がサポートしていない特徴があります．例えば，access-to-subprogram 型 [#f2]_ です．

|SPARK| コード中で，これらの特徴を使用した場合， |GNATprove| が検出し，エラーとして報告します．これらの特徴を使用しているサブプログラムに対して，形式検証を行うことはできません．しかし， |SPARK| コードではありませんが，Ada 言語としては用いることができます．次も参照下さい： :ref:`ja Identifying SPARK Code`

.. rubric:: Footnotes
.. [#f1] 被制御型とは，自動的な初期化と再利用を可能とするタグ付き型（C++のコンストラクタとデストラクタに類似の機能を提供する）．
.. [#f2] サブプログラムの名前や宣言している場所を知ることなしに，呼び手はサブプログラムにアクセス可能とする．C言語の関数ポインタに類似した機能

Ada の特徴を部分的に解析する
-------------------------------

|SPARK| では，Ada の強い型付けを強化しています．これは，より厳しい初期化ポリシー（ `Data Initialization Policy` ) によるものです．また，ある種の入力データが不正であることを識別する手段を持ちません．結果として，以下の特徴は， |SPARK| で利用できますが， |GNATprove| では部分的にのみ解釈可能です．

*  ``Unchecked_Conversion`` への呼び出しの結果は，返り値の型に対して正当な値であると見なします．

* 属性 ``Valid`` の評価結果は，常に真を返すと見なします．

このことを以下の例によって示します：

.. literalinclude:: /gnatprove_by_example/examples/validity.ads
   :language: ada
   :linenos:

.. literalinclude:: /gnatprove_by_example/examples/validity.adb
   :language: ada
   :linenos:

|GNATprove| は，両表明文を証明します．しかし，入力パラメータ ``X`` と ``Unchecked_Conversion`` 呼び出しの結果における ``Valid`` 属性の評価は，真を返すという前提に関して，警告を出力します：

.. literalinclude:: /gnatprove_by_example/results/validity.prove
   :language: none

.. _ja Data Initialization Policy:

データの初期化ポリシー
--------------------------

パラメータに対するモードおよびデータ依存性の契約（ :ref:`ja Data Dependencies` ）は， |SPARK| では，Ada よりもより厳格な意味を持っています．

* パラメータモード ``in`` (広域モードでは ``Input``) は，このパラメータ（データ依存）によって示されるオブジェクトを，サブプログラムを呼び出す前に，完全に初期化しなければならないことを示しています．サブプログラム中で初期化することはできません．

* パラメータモード ``out`` (広域モードでは ``Output``) は，このパラメータ（データ依存）によって示されるオブジェクトを，サブプログラムから制御が戻る前に，完全に初期化しなければならないことを示しています．初期化の前に，値を読み出すことはできません．

* パラメータモード ``in out`` (広域モードでは ``In_Out``) は，このパラメータ（データ依存）によって示されるオブジェクトを，サブプログラムを呼び出す前に，完全に初期化しなければならないことを示しています．サブプログラム中で記述することも可能です．

* グローバルモードの ``Proof_In`` は，次のことを示しています．データ依存として示すオブジェクトは，サブプログラムを呼び出す前に，完全に初期化する必要があります．サブプログラム中で初期化されるべきではなく，契約と表明中でのみ読み出されるべきです．

以上により，全ての入力はサブプログラムへのエントリ点において，完全に初期化されているべきです．また，全ての出力はサブプログラムの出力点において，完全に初期化されているべきです．同様に，全てのオブジェクトは，読み出し（例えば，サブプログラム内部において）時に完全に初期化されているべきです．読み出しが初期化されているサポコンポーネントを提供されているレコード型サブコンポーネント（配列型サブコンポーネントは含まない）は例外です．

上記の規則の結果として，サブプログラム中で部分的に記述されているあるパラメータ（広域変数）は， ``in out`` ( ``In_Out`` ) とマークされるべきです．なぜならば，パラメータ（広域変数）の入力値は，サブプログラムから戻るときに， `read` となるからです．

|GNATprove| は，もしサブプログラムが，これまでに述べたデータ初期化ポリシーに従わない場合，検査メッセージを発行します．例えば，手続き ``Proc`` を見て下さい．各モードのパラメータと広域アイテムを持っています．

.. literalinclude:: /gnatprove_by_example/examples/data_initialization.ads
   :language: ada
   :linenos:

手続き ``Proc`` は，その出力である ``P2`` と ``G2`` を完全に初期化すべきです．しかし，ここでは部分的な初期化のみです．同様に， ``Proc`` を呼び出す手続き ``Call_Proc`` は，呼び出しに先立ち，全ての ``Proc`` の入力を完全に初期化すべきです．しかし， ``G1`` のみを完全に初期化しています．

.. literalinclude:: /gnatprove_by_example/examples/data_initialization.adb
   :language: ada
   :linenos:

このプログラムでは， |GNATprove| は，6つの重要な（"high"）検査メッセージを発行します．データ初期化ポリシーへの違反に関するものです．

.. literalinclude:: /gnatprove_by_example/results/data_initialization.flow
   :language: none

ユーザは，pragma ``Annotate`` を用いて，そういったメッセージを個々に正当化することができますが (詳細は次を参照下さい :ref:`ja Justifying Check Messages`），呼び出されるサブコンポーネントが正しく初期化されることの責任は，ユーザ自身にあります． |GNATprove| は，データが呼び出される前に適切に初期化されているというプロパティを元にして証明するからです．

|GNATprove| は，使用していないパラメータ・広域アイテム・割り当てが存在しているという様々な警告も出力することに注意して下さい．これは，パラメータと広域モードに関する |SPARK| の厳密な解釈に基づくものです．

.. _ja Absence of Interferences:

干渉しないこと
------------------------

|SPARK| では，変数に対する割り当てによって，他の変数の値を変えることはできません． |SPARK| では，アクセス型（ポインタ）を禁止していることも，このことに寄与しています．また，パラメータや広域変数間で別名化を禁止しており，よい別名化（即ち，干渉を引き起こさない別名化）だけがよい別名化として利用可能です．

SPARK RM 6.4.2 に詳細記述している正確な規則をまとめると次のようになります：

* 二つの出力パラメータは，決して別名化することができません．
* 入力と出力パラメータは，入力パラメータが常にコピーで渡されるのでないならば，別名化するべきではありません．
* 出力パラメータは，サブプログラムによって参照される広域変数に別名化されるべきではありません．
* 入力パラメータは，もし常にコピーで渡されるのでないならば，サブプログラムによって参照される広域変数に別名化すべきではありません．

これらの規則は，別名化に制限を加えている Ada RM 6.4.1 の既存の規則を拡張しています．ちなみに，Ada では， `known to denote the same object` (Ada RM において正確に定義された記法) であるスカラー型のパラメータ間で問題ある（非良性の）別名化をしている手続きを呼び出すことができません．

例えば，次の例を参照して下さい．

.. literalinclude:: /gnatprove_by_example/examples/aliasing.ads
   :language: ada
   :linenos:

手続き ``Whatever`` は，以下の制約を満足する引数によってのみ呼び出すことができます．

1. 引数 ``Out_1`` と ``Out_2`` は，別名化すべきではありません．
2. 変数 ``Glob`` は，引数 ``Out_1`` や ``Out_2`` で渡されるべきではありません．

入力パラメータ ``In_1`` と ``In_2`` には，何の制約もないことに注意して下さい．この両者は，常にコピーが渡されるからです（スカラー型による良性のもの）．これらの入力パラメータが，レコード型や配列型の場合は，この限りではありません．

例えば，次は， ``Whatever`` を呼び出す場合の（Ada と SPARK の規則に従ったときの）正しい例と不正な例です．

.. literalinclude:: /gnatprove_by_example/examples/check_param_aliasing.adb
   :language: ada
   :linenos:

|GNATprove| は，正しく二つの不正な呼び出しを検知し，エラーを発行します(これは |GNAT Pro| コンパイラでも同様です．両者とも Ada 言語の規則に従うからです）．

.. literalinclude:: /gnatprove_by_example/results/check_param_aliasing.flow
   :language: none

次は，他の例です．（SPARK言語の規則に従った）手続き ``Whatever`` の正しい呼び出しと不正な呼び出しを示します．

.. literalinclude:: /gnatprove_by_example/examples/check_aliasing.adb
   :language: ada
   :linenos:

|GNATprove| は正しく2つの不正な呼び出しを検知し，高レベルの検査メッセージを出力します．

.. literalinclude:: /gnatprove_by_example/results/check_aliasing.flow
   :language: none
   :lines: 3,5

.. _ja Raising Exceptions and Other Error Signaling Mechanisms:

例外の送出と他のエラー通知機構
------------------------------

エラーを通知するために， |SPARK| では，例外の送出が認められています．しかし，回復処理ないしは，緩和処理のために例外処理を行うことは， |SPARK| の範囲外になります．典型的には，そういった例外処理コードは，完全な Ada におけるトップレベルのサブプログラムでなされるべきです．あるいは，実行中に例外が送出されたとき呼ばれる最後のハンドラに追加すべきです．これらのいずれも |GNATprove| では解析されません．

|GNATprove| は，例外の送出に対して特別な処理をします．

* フロー解析において， ``raise_statement`` に至るプログラムのパスは，サブプログラムの契約を検査するときには，考慮されません．この検査では，正常に終了するプログラムの実行のみを検査します．
* 証明では，不達となるプログラムの場所がないことを証明するために，各 ``raise_statement`` に対して，検査を行います．

複数のエラー伝達機構も，同様に扱います．

 * 例外を送出する．
 *  ``X`` が，静的に ``False`` に等価な式であるかを示す ``pragma Assert (X)``
 * 出力を持たない ``No_Return`` アスペクトないしは pragma を持つ手続き呼び出し（もし，呼び出し自身がそのような手続き中にないならば，検査は，エラー送出手続きの最外殻への呼び出しに対してのみ検査を行う）

例として作成したサブプログラム ``Check_OK`` を考えます．パラメータ ``OK`` が ``False`` であれば，例外を送出します．

.. literalinclude:: /gnatprove_by_example/examples/abnormal_terminations.ads
   :language: ada
   :linenos:

.. literalinclude:: /gnatprove_by_example/examples/abnormal_terminations.adb
   :language: ada
   :linenos:

変数 ``G2`` は，手続き ``Check_OK`` 中で，値の割り当てを行いますが，その割り当て後に， ``raise_statement`` が続くので， ``G2`` は，手続き ``Check_OK`` が正常に終了するとき，値を割り当てられません．結果として ``Check_OK`` のデータ依存に ``G2`` は含まれていません．データフロー解析中 |GNATprove| は，手続き ``Check_OK`` のボディ部は，仕様部で宣言されたデータ依存を実装しているかを検査します．

証明中， |GNATprove| は，11行目の ``raise_statement`` には決して到達しないことを検査します．これは，パラメータ ``OK`` は手続きに入ったときには ``True`` であるという ``Check_OK`` の事前条件によって検査可能となっています．

.. literalinclude:: /gnatprove_by_example/results/abnormal_terminations.prove
   :language: none

|GNATprove| は， ``No_Return`` アスペクトあるいは pragma によってマークされた手続きが，制御を返さないことを，検査します．例外を送出するか，任意の入力に対して無限にループすることになります．
