Basic Examples -------------- The examples in this section have no loops, and do not use more complex features of |SPARK| like :ref:`Ghost Code`, :ref:`Interfaces to the Physical World`, or :ref:`Object Oriented Programming and Liskov Substitution Principle`. Increment ^^^^^^^^^ Consider a simple procedure that increments its integer parameter ``X``: .. literalinclude:: /examples/ug__increment/increment.adb :language: ada :linenos: As this procedure does not have a contract yet, |GNATprove| only checks that there are no possible reads of uninitialized data and no possible run-time errors in the procedure. Here, it issues a message about a possible overflow check failure on ``X + 1``: .. literalinclude:: /examples/ug__increment/test.out :language: none .. index:: precondition; example of use possible fix; example of use The counterexample displayed tells us that ``Increment`` could be called on value ``Integer'Last`` for parameter ``X``, which would cause the increment to raise a run-time error. As suggested by the possible fix in the message issued by |GNATprove|, one way to eliminate this vulnerability is to add a precondition to ``Increment`` specifying that ``X`` should be less than ``Integer'Last`` when calling the procedure: .. literalinclude:: /examples/ug__increment_guarded/increment_guarded.adb :language: ada :linenos: As this procedure has a contract now, |GNATprove| checks like before that there are no possible reads of uninitialized data and no possible run-time errors in the procedure, including in its contract, and that the procedure implements its contract. As expected, |GNATprove| now proves that there is no possible overflow check failure on ``X + 1``: .. literalinclude:: /examples/ug__increment_guarded/test.out :language: none .. index:: Global; example of use Depends; example of use precondition; example of use postcondition; example of use The precondition is usually the first contract added to a subprogram, but there are other :ref:`Subprogram Contracts`. Here is a version of ``Increment`` with: * global dependencies (aspect ``Global``) stating that the procedure reads and writes no global variables * flow dependencies (aspect ``Depends``) stating that the final value of parameter ``X`` only depends on its input value * a precondition (aspect ``Pre``) stating that parameter ``X`` should be less than ``Integer'Last`` on entry * a postcondition (aspect ``Post``) stating that parameter ``X`` should have been incremented by the procedure on exit .. literalinclude:: /examples/ug__increment_full/increment_full.adb :language: ada :linenos: .. index:: --report; example of use |GNATprove| checks that ``Increment_Full`` implements its contract, and that it cannot raise run-time errors or read uninitialized data. By default, |GNATprove|'s output is empty in such a case, but we can request that it prints one line per check proved by using switch ``--report=all``, which we do here: .. literalinclude:: /examples/ug__increment_full/test.out :language: none As subprogram contracts are used to analyze callers of a subprogram, let's consider a procedure ``Increment_Calls`` that calls the different versions of ``Increment`` presented so far: .. literalinclude:: /examples/ug__increment_calls/increment_calls.adb :language: ada :linenos: |GNATprove| proves all preconditions expect the one on the second call to ``Increment_Guarded``: .. literalinclude:: /examples/ug__increment_calls/test.out :language: none ``Increment`` has no precondition, so there is nothing to check here except the initialization of ``X`` when calling ``Increment`` on lines 11 and 12. But remember that |GNATprove| did issue a message about a true vulnerability on ``Increment``'s implementation. This vulnerability was corrected by adding a precondition to ``Increment_Guarded``. This has the effect of pushing the constraint on callers, here procedure ``Increment_Calls``. As expected, |GNATprove| proves that the first call to ``Increment_Guarded`` on line 15 satisfies its precondition. But it does not prove the same for the second call to ``Increment_Guarded`` on line 16, because the value of ``X`` on line 16 was set by the call to ``Increment_Guarded`` on line 15, and the contract of ``Increment_Guarded`` does not say anything about the possible values of ``X`` on exit. As suggested by the possible fix in the message issued by |GNATprove|, a postcondition like the one on ``Increment_Full`` is needed so that |GNATprove| can check the second call to increment ``X``. As expected, |GNATprove| proves that both calls to ``Increment_Full`` on lines 19 and 20 satisfy their precondition. .. index:: contextual analysis; example of use inlining for proof; example of use In some cases, the user is not interested in specifying and verifying a complete contract like the one on ``Increment_Full``, typically for helper subprograms defined locally in a subprogram or package body. |GNATprove| allows performing :ref:`Contextual Analysis of Subprograms Without Contracts` for these local subprograms. For example, consider a local definition of ``Increment`` inside procedure ``Increment_Local``: .. literalinclude:: /examples/ug__increment_local/increment_local.adb :language: ada :linenos: Although ``Increment`` has no contract (like the previous non-local version), |GNATprove| proves that this program is free from run-time errors, and that the assertion on line 15 holds: .. literalinclude:: /examples/ug__increment_local/test.out :language: none Swap ^^^^ Consider a simple procedure that swaps its integer parameters ``X`` and ``Y``, whose simple-minded implementation is wrong: .. literalinclude:: /examples/ug__swap_bad/swap_bad.adb :language: ada :linenos: As this procedure does not have a contract yet, |GNATprove| only checks that there are no possible reads of uninitialized data and no possible run-time errors in the procedure. Here, it simply issues a warning: .. literalinclude:: /examples/ug__swap_bad/test.out :language: none But we know the procedure is wrong, so we'd like to get an error of some sort! We could not detect it with |GNATprove| because the error is functional, and |GNATprove| cannot guess the intended functionality of ``Swap_Bad``. Fortunately, we can give this information to |GNATprove| by adding a contract to ``Swap_Bad``. One such contract is the flow dependencies introduced by aspect ``Depends``. Here it specifies that the final value of ``X`` (resp. ``Y``) should depend on the initial value of ``Y`` (resp. ``X``): .. literalinclude:: /examples/ug__swap_bad_depends/swap_bad_depends.adb :language: ada :linenos: |GNATprove| issues 3 check messages (and a warning) on ``Swap_Bad_Depends``: .. literalinclude:: /examples/ug__swap_bad_depends/test.out :language: none The last message informs us that the dependency ``Y => X`` stated in ``Swap_Bad_Depends``'s contract is incorrect for the given implementation. That might be either an error in the code or an error in the contract. Here this is an error in the code. The two other messages are consequences of this error. Another possible contract is the postcondition introduced by aspect ``Post``. Here it specifies that the final value of ``X`` (resp. ``Y``) is equal to the initial value of ``Y`` (resp. ``X``): .. literalinclude:: /examples/ug__swap_bad_post/swap_bad_post.adb :language: ada :linenos: |GNATprove| issues one check message on the unproved postcondition of ``Swap_Bad_Post`` (and a warning), with a counterexample giving concrete values of a wrong execution: .. literalinclude:: /examples/ug__swap_bad_post/test.out :language: none Both the check messages on ``Swap_Bad_Depends`` and on ``Swap_Bad_Post`` inform us that the intended functionality as expressed in the contracts is not implemented in the procedure. And looking again at the warning issued by |GNATprove| on ``Swap_Bad``, this was already pointing at the same issue: swapping the values of ``X`` and ``Y`` should obviously lead to reading the initial value of ``X``; the fact that this value is not used is a clear sign that there is an error in the implementation. The correct version of ``Swap`` uses a temporary value to hold the value of ``X``: .. literalinclude:: /examples/ug__swap/swap.adb :language: ada :linenos: |GNATprove| proves both contracts on ``Swap`` and it informs us that the postcondition was proved: .. literalinclude:: /examples/ug__swap/test.out :language: none Let's now consider a well-known `in place` implementation of ``Swap`` that avoids introducing a temporary variable by using bitwise operations: .. literalinclude:: /examples/ug__swap_modulo/swap_modulo.adb :language: ada :linenos: |GNATprove| understands the bitwise operations on values of modular types, and it proves here that the postcondition of ``Swap_Modulo`` is proved: .. literalinclude:: /examples/ug__swap_modulo/test.out :language: none |GNATprove|'s flow analysis issues warnings like the one on ``Swap_Bad`` whenever it detects that some variables or statements are not used in the computation, which is likely uncovering an error. For example, consider procedure ``Swap_Warn`` which assigns ``X`` and ``Tmp_Y`` out of order: .. literalinclude:: /examples/ug__swap_warn/swap_warn.adb :language: ada :linenos: On this wrong implementation, |GNATprove| issues a high check message for the certain read of an uninitialized variable, and three warnings that point to unused constructs: .. literalinclude:: /examples/ug__swap_warn/test.out :language: none In general, warnings issued by |GNATprove|'s flow analysis should be carefully reviewed, as they may lead to the discovery of errors in the program. Addition ^^^^^^^^ Consider a simple function ``Addition`` that returns the sum of its integer parameters ``X`` and ``Y``. As in :ref:`Increment`, we add a suitable precondition and postcondition for this function: .. literalinclude:: /examples/ug__addition/addition.adb :language: ada :linenos: We also added flow dependencies to ``Addition`` for illustration purposes, but they are the same as the default generated ones (the result of the function depends on all its inputs), so are not in general given explicitly. |GNATprove| issues a check message about a possible overflow in the precondition of ``Addition``: .. literalinclude:: /examples/ug__addition/test.out :language: none Indeed, if we call for example ``Addition`` on values ``Integer'Last`` for ``X`` and ``1`` for ``Y``, the expression ``X + Y`` evaluated in the precondition does not fit in a machine integer and raises an exception at run time. In this specific case, some people may consider that it does not really matter that an exception is raised due to overflow as the failure of the precondition should also raise a run-time exception. But in general the precondition should not fail (just consider the precondition ``X + Y not in Integer`` for example), and even here, the different exceptions raised may be treated differently (``Constraint_Error`` in the case of an overflow, ``Assertion_Error`` in the case of a failing precondition). One way to avoid this vulnerability is to rewrite the precondition so that no overflow can occur: .. literalinclude:: /examples/ug__addition_rewrite/addition.adb :language: ada :linenos: Although |GNATprove| proves that ``Addition`` implements its contract and is free from run-time errors, the rewritten precondition is not so readable anymore: .. literalinclude:: /examples/ug__addition_rewrite/test.out :language: none .. index:: Big_Numbers; example of use A better way to achieve the same goal without losing in readability is to use the :ref:`Big Numbers Library` for arithmetic operations which could overflow: .. literalinclude:: /examples/ug__addition_bignum/addition.adb :language: ada :linenos: In that case, |GNATprove| proves that there are no run-time errors in function ``Addition``, and that it implements its contract: .. literalinclude:: /examples/ug__addition_bignum/test.out :language: none .. index:: Contract_Cases; example of use Finally, we can choose to expand the range of applicability of the function, by accepting any values of inputs ``X`` and ``Y``, and saturating when the addition would overflow the bounds of machine integers. That's what the rewritten function ``Addition`` does, and its saturating behavior is expressed in :ref:`Contract Cases`: .. literalinclude:: /examples/ug__addition_saturated/addition.adb :language: ada :linenos: |GNATprove| proves that ``Addition`` implements its contract and is free from run-time errors: .. literalinclude:: /examples/ug__addition_saturated/test.out :language: none Note that we analyzed this function in ELIMINATED overflow mode, using the switch ``-gnato13``, otherwise there would be possible overflows in the guard expressions of the contract cases. .. global/depends + errors