Upload
willa-fisher
View
227
Download
2
Tags:
Embed Size (px)
Citation preview
SPARK
Is (formally) a subset of Ada, in that any correct SPARK program is also a correct Ada program.
But this is misleading, because the annotations are really a major language feature.
SPARK Basic Structure
Take AdaRemove features to get a small subsetDefine a set of annotationsAnnotations are from Ada’s point of view
comments lexically (they start with --#)But they are syntactic elements of SPARKSo SPARK is a separate language
What to Leave Out of Ada
Things that make certification and formal reasoning (e.g. verification) harder: Tasks Exceptions Generics Access types Gotos
Why no tasks?
Because tasking programs are inherently more complex than simple sequential programs.
The state of a tasking program is a much more complex object
Task interactions are complexNon-determinacy is a concern
Why no Exceptions?
Because exceptions make the control flow of a program much more complex
Certifiable programs cannot have unexpected exceptions anyway
Exception handlers lead to deactivated code which is problematic for certification.
Why no Generics
Generics are a short hand for repeated instantiations, so do not provide any fundamental expressive power
But proving properties is more complex because we have to quantify over types.
If we have to prove separate instantiations we may as well write them.
Why no Access Types
Access types only make sense in connection with dynamic storage allocate.
But dynamic allocation is a real problem, since it is hard to prove that storage is never exhausted.
Why no Gotos
Not really needed in most programming
At best only a minor convenienceBut they complicate formal analysis
Because the effect of a sequence of code can no longer be represented as the composition of the effects of its sequential components.
What Else to Leave Out?
Ada favors the reader heavily over the writer in its design.
SPARK carries this principle to further extremes. Why, because the reader may be a formal tool.
Furthermore, writing code takes a fraction of the time it takes to certify.
Other Things Left Out
Dynamically sized arrays Eases reasoning about range
correctnessOverloadingImplicit operations (e.g. sliding)Implicit subtypes
X : Integer range 1 .. 10; -- not allowed Must declare separate subtype
The Annotation Language
All statements start with --#, but they are not comments, they are strict elements of the SPARK syntax.
Global Definitions Declare use of global variables
Dependency Relations Specify information flow (what outputs
depend on what inputs)
Oher types of annotations
Access of Variables in Packages The idea is to restrict visibility
Inherit clauses Control visibility of package names
Own variable clauses Control access to package variables
Initialization Indicate initialization of own variables
Purpose of Annotations
Make code clearer at spec levelIntroduce redundancy, compiler can
checkAllow error checks to be madeSupport verification
The SPARK tools
SPARK Examiner processes SPARK programs Checks syntactic validity
Including checking annotations for consistency and correctness
Generates useful warnings Generates auxiliary information helpful
in analysis and certification
Annotations in Action
Procedure Sly (V : in vector)--# global in out A;--# derives A from A, V;is begin
A (1) := V (1) + V (1);A (1) := V (1) + V (1);
end Sly;…A (1) := 1.0;
Sly (A); -- error, param overlaps global variable
An example: Exchange
Procedure Exchange (X, Y : in out Float)--# global T;--# derives X from Y & Y from Xis begin
T := X; X := Y; Y := T;end;
This is illegal because it modifies T, and the annotations do not make this clear
Exchange (attempt 2)
Procedure Exchange (X, Y : in out Float)--# global T;--# derives X from Y & Y from Xis
T : Floatbegin
T := X; X := Y; Y := X;end;
This generates 4 (!) fatal errors
Errors in attempt 2
T := X is an ineffective statementImportation of initial value of X
ineffectiveVariable T is not referenced or exportedImported value of X not used in
deriving the value of YImported value of Y may be used in the
derivation of Y
Path Functions
Compute all possible paths through program and provide explicit information about possible paths
List conditions for each pathCheck consistency of conditions
Check Path Consistency
if X > Y then … if X < Y then … end if;end if;
Path 2 traversal conditions1. X > y2. X < Y
Path 2: path eliminated (contradictory conditions)
Verification Conditions
procedure Exchange (X, Y : in out Float)-- #derives X from Y & Y from X-- #post X = Y~ & Y = X~is
T : Float;begin
T := X; X := Y; Y := T;end;
Verification Conditions
H1: true .->
C1: y = y .C2: x = x .
Meaning is H1 => C1 and C2Obvious in this case
The Simplifier
Attempts to simplify the verification conditions generated by the examiner
For example previous set of conditions simplifies to *** true .
Another Example
Type T is range -128 .. + 128; procedure Inc (X : in out T)
--# derives X from Xis begin
X := X + 1;end;
Verification Conditions
After simplification we get the conditions:
H1: x >= -128 .H2: x <= 128 .
->C1: x <= 127 .
This last conclusion cannot be proved
An Improved Inc procedure
procedure Inc (X : in out T)--# derives X from X;--# pre X < T’Lastis begin
X := X + 1;end;
Now proof obligation shifts to call site
The Proof Checker
The simplifier does a lot, but it is after all a simplifier, it does not prove complex theorems.
Proof of complex theorems may be needed
The proof checker is in charge of making sure these proofs are correct.
Is SPARK really usable
In real life, there are complex external gizmos like external sensors with weird data.
Devices with inherently non-determistic behavior
Computations that require low level messing around
One more feature:
-- #hideAllows arbitrary use of full Ada (or
anything else for that matter)Hidden from examiner. For example,
we can give a procedure spec with full annotatations.
And then completely hide the bodyAnd use other means to verify the body