Это именно та проблема, которую SPARK может помочь решить. Это позволяет вам доказать, что у вас не будет ошибок времени выполнения, учитывая определенные предположения относительно входных данных для ваших расчетов.
Если вы начнете с простой функции, такой как No_Overflow
в этом пакете:
with Interfaces; use Interfaces;
package Show_Runtime_Errors is
type Unsigned_Int is range 0 .. 2**32 - 1;
function No_Overflow (Val1, Val2, Val3 : Unsigned_Int) return Unsigned_Int;
end Show_Runtime_Errors;
package body Show_Runtime_Errors is
function No_Overflow (Val1, Val2, Val3 : Unsigned_Int) return Unsigned_Int is
Result : constant Unsigned_Int := (Val1 + Val2) * Val3;
begin
return Result;
end No_Overflow;
end Show_Runtime_Errors;
Затем, когда вы запустите SPARK, вы получите следующее:
Proving...
Phase 1 of 2: generation of Global contracts ...
Phase 2 of 2: flow analysis and proof ...
show_runtime_errors.adb:4:55: medium: range check might fail (e.g. when Result = 10)
show_runtime_errors.adb:4:55: medium: overflow check might fail (e.g. when
Result = 9223372039002259450 and Val1 = 4 and Val2 = 2147483646 and
Val3 = 4294967293)
gnatprove: unproved check messages considered as errors
exit status: 1
Теперь, если вы добавите простое предусловие к No_Overflow
, например:
function No_Overflow (Val1, Val2, Val3 : Unsigned_Int) return Unsigned_Int with
Pre => Val1 < 2**15 and Val2 < 2**15 and Val3 < 2**16;
Тогда SPARK выдает следующее:
Proving...
Phase 1 of 2: generation of Global contracts ...
Phase 2 of 2: flow analysis and proof ...
Success!
Ваши фактические предварительные условия для диапазонов входов, очевидно, будут зависеть от вашего приложения.
Альтернативы - это решение, которое вы предполагаете, когда вы помещаете много явных средств защиты в ваш код до того, как выражение будет оценено, или для обнаружения ошибок во время выполнения посредством обработки исключений. Преимущество SPARK над этими подходами заключается в том, что вам не нужно создавать программное обеспечение с проверками времени выполнения, если вы можете доказать, что ошибок времени выполнения не будет.
Обратите внимание, что предварительные условия - это особенность Ada 2012. Вы также можете использовать pragma Assert
во всем коде, которым SPARK может воспользоваться для создания доказательств.
Более подробно о SPARK можно найти здесь:
https://learn.adacore.com/courses/intro-to-spark/index.html
Чтобы попробовать сами, вставьте приведенный выше код в пример здесь:
https://learn.adacore.com/courses/intro-to-spark/book/03_Proof_Of_Program_Integrity.html#runtime-errors
Кстати, предложенный вами код:
if ((Val1 + Val2) * Val3) < Unsigned_Int'Size then
CheckMeForOverflow := (Val1 + Val2) * Val3;
end if;
не будет работать по двум причинам:
Unsigned_Int'Size
- количество битов, необходимых для представления Unsigned_Int
. Вы, вероятно, хотели вместо этого Unsigned_Int'Last
.
((Val1 + Val2) * Val3)
может переполниться до того, как будет выполнено сравнение с Unsigned_Int'Last
. Таким образом, вы сгенерируете исключение на этом этапе и либо аварийно завершите работу, либо обработаете его в обработчике исключений.