From: Simon Wright on
Robert A Duff <bobduff(a)shell01.TheWorld.com> writes:

> Simon Wright <simon(a)pushface.org> writes:
>
>> type M is mod 2**31;
>> Result : M := 0;
>> begin
>> Result := Result xor Integer'Pos (I.A);
>
>> and, before anyone rushes to check it out, yes, it fails with
>> negative values and I'm about to file a bug against it.
>
> I don't think that's a compiler bug. Conversion of negative numbers
> to modular raises Constraint_Error. I think that's a language design
> flaw, but not everyone agrees with me. The 'Mod attribute works
> around the problem.

No, I meant a bug against my code generator!

On the other hand, I found some strange behaviour with GNAT GPL 2009
(and GCC 4.5.0) while investigating possible solutions:

------------------------------------------------------------------------
with Ada.Text_IO; use Ada.Text_IO;

procedure Odd_Warning is

procedure Inner (X : Integer) is
type M is mod 2**31;
Result : M;
begin

begin
Result := 0;
Result := Result xor (Integer'Pos (X) mod 2**30);
-- No compiler warning, but raises exception
exception
when others => Put_Line ("exception a");
end;

begin
Result := 0;
Result := Result xor (Integer'Pos (X) mod 2**31);
-- odd_warning.adb:20:53: warning: mod with zero divisor
-- odd_warning.adb:20:53: warning: "Constraint_Error" will be
-- raised at run time
exception
when others => Put_Line ("exception b");
end;

begin
Result := 0;
Result := Result xor M (Integer'Pos (X) mod 2**31);
-- No warning (and executes as I had expected)
Put_Line ("result:" & M'Image (Result));
exception
when others => Put_Line ("exception c");
end;

end Inner;

begin

Inner (-1);

end Odd_Warning;
------------------------------------------------------------------------

The second block's compiler warning is strange, especially the part
about the zero divisor. The Aonix ObjectAda 7.2 compiler gives a very
similar warning referring to 95LRM4.5.5(22), and gives an error at the
third block (95LRM4.9(35)).

I think my route will be via unchecked conversion, if I don't decide
that it's a feature - in 10 years of use no one has needed to use a
negative value as a component of a hash.
From: Adam Beneschan on
On Apr 26, 2:05 pm, Simon Wright <si...(a)pushface.org> wrote:
> Robert A Duff <bobd...(a)shell01.TheWorld.com> writes:
>
> > Simon Wright <si...(a)pushface.org> writes:
>
> >>       type M is mod 2**31;
> >>       Result : M := 0;
> >>    begin
> >>       Result := Result xor Integer'Pos (I.A);
>
> >> and, before anyone rushes to check it out, yes, it fails with
> >> negative values and I'm about to file a bug against it.
>
> > I don't think that's a compiler bug.  Conversion of negative numbers
> > to modular raises Constraint_Error.  I think that's a language design
> > flaw, but not everyone agrees with me.  The 'Mod attribute works
> > around the problem.
>
> No, I meant a bug against my code generator!
>
> On the other hand, I found some strange behaviour with GNAT GPL 2009
> (and GCC 4.5.0) while investigating possible solutions:
>
> ------------------------------------------------------------------------
> with Ada.Text_IO; use Ada.Text_IO;
>
> procedure Odd_Warning is
>
>    procedure Inner (X : Integer) is
>       type M is mod 2**31;
>       Result : M;
>    begin
>
>       begin
>          Result := 0;
>          Result := Result xor (Integer'Pos (X) mod 2**30);
>          --  No compiler warning, but raises exception
>       exception
>          when others => Put_Line ("exception a");
>       end;
>
>       begin
>          Result := 0;
>          Result := Result xor (Integer'Pos (X) mod 2**31);
>          --  odd_warning.adb:20:53: warning: mod with zero divisor
>          --  odd_warning.adb:20:53: warning: "Constraint_Error" will be
>          --    raised at run time
>       exception
>          when others => Put_Line ("exception b");
>       end;
>
>       begin
>          Result := 0;
>          Result := Result xor M (Integer'Pos (X) mod 2**31);
>          --  No warning (and executes as I had expected)
>          Put_Line ("result:" & M'Image (Result));
>       exception
>          when others => Put_Line ("exception c");
>       end;
>
>    end Inner;
>
> begin
>
>    Inner (-1);
>
> end Odd_Warning;
> ------------------------------------------------------------------------
>
> The second block's compiler warning is strange, especially the part
> about the zero divisor. The Aonix ObjectAda 7.2 compiler gives a very
> similar warning referring to 95LRM4.5.5(22), and gives an error at the
> third block (95LRM4.9(35)).

The warning looks correct to me. The way the expression is
constructed in the second block, all of the operators have operands of
type M (except for the right operand of "**"). This means that you're
using "**" with the definition

function "**" (Left : M; Right : Integer) return M;

And modular operations wrap around. The result of the above operator
with arguments Left=2, Right=31 is therefore 0, since the range of M
is 0 .. 2**31-1.

In the third block, the type conversion to M changes everything; the
argument of this type conversion can be anything that works (as
opposed to the second block, in which the right side must be an
expression of type M). Since the type of Integer'Pos(X) is a
universal integer, the end result is that the operators inside the
type conversion are interpreted as being the operations of the
"root_integer" type. Whether the error message is correct, incorrect,
or implementation-dependent, I'm not sure without further study, but I
suspect that it may be implementation-dependent and may depend on what
base ranges the implementation has chosen for its built-in integer
types.

In any case, unless you're trying to develop code that compiles with
an Ada95 compiler, I'd prefer to use 'Mod over Unchecked_Conversion.
The whole purpose of 'Mod was to eliminate the need for
Unchecked_Conversion in cases like this, since it was impossible to
write "normal" code that would do the computation correctly.

-- Adam
From: AdaMagica on
> >          Result := Result xor M (Integer'Pos (X) mod 2**31);
> >          --  No warning (and executes as I had expected)
> >          Put_Line ("result:" & M'Image (Result));
> >       exception
> >          when others => Put_Line ("exception c");
> >       end;
> about the zero divisor. The Aonix ObjectAda 7.2 compiler ...
> gives an error at the third block (95LRM4.9(35)).

I'm no language lawyer, but I'll give it a try.

RM95 4.9(35): If the expression is not part of a larger static
expression, then its value shall be within the base range of its
expected type. Otherwise, the value may be arbitrarily large or small.

RM TC1 AM1 4.9(35/2) {AI95-00269-01} If the expression is not part of
a larger static expression *and the expression is expected to be of a
single specific type*, then its value shall be within the base range
of its expected type. Otherwise, the value may be arbitrarily large or
small.

Since Aonix quotes 95RM4.9(35), it must be a pure Ada95 compiler.
2**31 is not part or a larger static expression. The further condition
of being of a single specific type (which is not the case here, the
expected type is any integer type) was added later.

So Aonix is correct, and so is GNAT, if the latter is new enough to
obey AM1.
From: Simon Wright on
AdaMagica <christoph.grein(a)eurocopter.com> writes:

> Since Aonix quotes 95RM4.9(35), it must be a pure Ada95 compiler.

Thanks for the analysis.

The '95' in that quote was actually me. But since the compiler binaries
are no later than Feb 2000 I think your conclusion is correct!
From: Simon Wright on
Adam Beneschan <adam(a)irvine.com> writes:

> In any case, unless you're trying to develop code that compiles with
> an Ada95 compiler, I'd prefer to use 'Mod over Unchecked_Conversion.
> The whole purpose of 'Mod was to eliminate the need for
> Unchecked_Conversion in cases like this, since it was impossible to
> write "normal" code that would do the computation correctly.

Ada95 it is .. though I could actually change now with no impact on the
current user project, which is not going to migrate.