From: Yannick Duchêne (Hibou57) on
Hi all,

This will require some further investigation on my own side, however, I
would like to open a topic about it, if ever someone wants to share about
the same matter.

I was proving something built around a for-loop. Every thing was going,
when I've meet a trouble and decided to switch to a classic loop and an
exit statement and a local variable.

There was a Check clause in the for-loop, which was looking like this:

for L in T range 1 .. N loop
...
--# assert ...;
...
--# check (2 ** (T'Pos (L) + 1)) = ((2 ** T'Pos (L)) * 2);
...
end loop;


This Check clause was proved by the Simplifier without any trouble.

I then switch to a class loop, looking like this:


L := 1;

loop
...
--# assert ...;
...
--# check (2 ** (T'Pos (L) + 1)) = ((2 ** T'Pos (L)) * 2);
exit when L = Length;
L := L + 1;
...
end loop;

Then, the Simplifier was not able anymore to prove this Check. I don't
understand, as this Check should only depends on a basic rule, a rule by
definition. So why the same rule is not applied when I use a classic loop
instead of a for-loop ?

Does SPARK changes its strategy depending on the structure of the
surrounding source so that it may or may not found a match to a rule
depending on this context ?

I've checked multiple times, this does not seems to be an error, it do the
same each time I switch from one to the other.
From: Brian Drummond on
On Thu, 27 May 2010 21:36:41 +0200, Yannick Duch�ne (Hibou57)
<yannick_duchene(a)yahoo.fr> wrote:

>Hi all,
>
>This will require some further investigation on my own side, however, I
>would like to open a topic about it, if ever someone wants to share about
>the same matter.
>
>I was proving something built around a for-loop. Every thing was going,
>when I've meet a trouble and decided to switch to a classic loop and an
>exit statement and a local variable.
>
>There was a Check clause in the for-loop, which was looking like this:
>
> for L in T range 1 .. N loop
> ...
> --# assert ...;
> ...
> --# check (2 ** (T'Pos (L) + 1)) = ((2 ** T'Pos (L)) * 2);
> ...
> end loop;
>
>
>This Check clause was proved by the Simplifier without any trouble.
>
>I then switch to a class loop, looking like this:
>
>
> L := 1;
>
> loop
> ...
> --# assert ...;
> ...
> --# check (2 ** (T'Pos (L) + 1)) = ((2 ** T'Pos (L)) * 2);
> exit when L = Length;
> L := L + 1;
> ...
> end loop;
>
>Then, the Simplifier was not able anymore to prove this Check. I don't
>understand, as this Check should only depends on a basic rule, a rule by
>definition. So why the same rule is not applied when I use a classic loop
>instead of a for-loop ?

Is it relevant here that the range of L is known in the first loop, and
can (presumably) be shown to be less than T'Size?

Perhaps L in the second example can be declared of a subtype with
appropriate range? (If it already is, that wasn't shown in the posted
code)

- Brian
From: Yannick Duchêne (Hibou57) on
Le Thu, 27 May 2010 23:50:06 +0200, Brian Drummond
<brian_drummond(a)btconnect.com> a écrit:
Hi Brian,

> Is it relevant here that the range of L is known in the first loop, and
> can (presumably) be shown to be less than T'Size?
>
> Perhaps L in the second example can be declared of a subtype with
> appropriate range? (If it already is, that wasn't shown in the posted
> code)
Yes, sorry, I forget it. In the simple loop case, L is of the same type,
that is, it is declared as of type T and T'First is equal to 1. So the
declaration of L in the for-loop and the declaration of L with the simple
loop, are the same. N is obviously of type T too.

What puzzles me, is that the expression in the Check clause, does not
depend on any context, it is purely mathematical, thus it should be as
provable with the simple loop as it is with the for-loop.

I've just done a test a few seconds ago. If I copy the same Check clause
between the “L := 1;” statement and the begin of the loop, that is:


L := 1;

--# check (2 ** (T'Pos (L) + 1)) = ((2 ** T'Pos (L)) * 2);

loop
...
--# assert ...;
...
--# check (2 ** (T'Pos (L) + 1)) = ((2 ** T'Pos (L)) * 2);
exit when L = Length;
L := L + 1;
...
end loop;

then the first occurrence of the Check, at the first location, is proved
right away by the simplifier, and the second occurrence, is still not
proved.

Really funny, as the expression does not depends on any context and all
numbers/expression which appears in, are Universal_Integer [ARM 2005 Annex
K (175)], so there should be no matter about any range of any kind. Note:
T'Last is small, it is actually 8.

Will try some others things later. I believe this is an interesting topic
(unless I did a gentle clumsy thing which I have not already figured).

Have a nice day/night.

--
There is even better than a pragma Assert: a SPARK --# check.
--# check C and WhoKnowWhat and YouKnowWho;
--# assert Ada;
-- i.e. forget about previous premises which leads to conclusion
-- and start with new conclusion as premise.
From: Phil Thornley on
On 27 May, 20:36, Yannick Duchêne (Hibou57) <yannick_duch...(a)yahoo.fr>
wrote:
[...]
> There was a Check clause in the for-loop, which was looking like this:
>
>     for L in T range 1 .. N loop
>        ...
>        --# assert ...;
>        ...
>        --# check (2 ** (T'Pos (L) + 1)) = ((2 ** T'Pos (L)) * 2);
>        ...
>     end loop;
>
> This Check clause was proved by the Simplifier without any trouble.
>
> I then switch to a class loop, looking like this:
>
>     L := 1;
>
>     loop
>        ...
>        --# assert ...;
>        ...
>        --# check (2 ** (T'Pos (L) + 1)) = ((2 ** T'Pos (L)) * 2);
>        exit when L = Length;
>        L := L + 1;
>        ...
>     end loop;
>
> Then, the Simplifier was not able anymore to prove this Check. I don't  
> understand, as this Check should only depends on a basic rule, a rule by  
> definition. So why the same rule is not applied when I use a classic loop  
> instead of a for-loop ?
>
> Does SPARK changes its strategy depending on the structure of the  
> surrounding source so that it may or may not found a match to a rule  
> depending on this context ?

It is difficult to be specific about non-SPARKable code*, but one
difference at the check will be that the upper bound on L has been
lost.

You can get it back by adding L <= Length to the assertion. [You
might also need to change the test to
exit when L >= Length;]

It is put in there aotomatically for a 'for' loop but not for a simple
loop.

Cheers,

Phil

* If the code is too big to put in a message then you can send it by
email to the address on the proof tutorials and I'll be happy to have
a look at it.
From: Yannick Duchêne (Hibou57) on
Le Fri, 28 May 2010 10:14:56 +0200, Phil Thornley
<phil.jpthornley(a)googlemail.com> a écrit:
> You can get it back by adding L <= Length to the assertion. [You
> might also need to change the test to
> exit when L >= Length;]
>
> It is put in there aotomatically for a 'for' loop but not for a simple
> loop.
You're wonderful Phil! This was something near to that: I've added L <=
Length, it was not OK, so I though if the upper bound hypothesis was lost,
so as well was probably the one of the lower bound. So I've added L >= 1,
then figured that the Checked expression was not verified for L lower than
zero, so thought if one hypothesis was required, this was this only one,
the one for the lower bound. So I removed the L <= Length and left L >= 1
only, which was shown to be enough.

Without you, I would not have been able to guess the trouble was there, as
to me, it was obvious L >= 1, as the type of L, which is really
Length_Type, has Length_Type'First = 1.

This is still strange in some way, as if I do L >= Length_Type'First, it
works.

So the Assert clause even drops implicit hypotheses about numeric type
bounds ? I was thinking this ones were preserved even after an Assert
clause.

I was pretty sure Assert was still preserving somethings, as I feel I
remember I have read something suggesting that in one of the Praxis's
documentation and in yours as well (do not remember which PDF file, and
there many to check).

> * If the code is too big to put in a message then you can send it by
> email to the address on the proof tutorials and I'll be happy to have
> a look at it.
Not too big, this was just that as I was afraid my style with SPARK may
not be good enough to be posted here. I have lot of lines of SPARK proofs
for few lines of Ada text, which may seems silly to someones... also that
I like to have explicit things, because it is more easy to understand and
to track.

--
There is even better than a pragma Assert: a SPARK --# check.
--# check C and WhoKnowWhat and YouKnowWho;
--# assert Ada;
-- i.e. forget about previous premises which leads to conclusion
-- and start with new conclusion as premise.