Version 1.4 of ais/ai-00163.txt

Unformatted version of ais/ai-00163.txt version 1.4
Other versions for file ais/ai-00163.txt

!standard 04.05.05 (18)          00-01-25 AI95-00163/04
!class confirmation 97-05-08
!status Response 2000 00-01-25
!status WG9 approved (8-0-0) 97-07-04
!status ARG Approved 10-0-1 96-10-07
!status received 96-10-04
!priority High
!difficulty Hard
!qualifier Clarification
!subject User-defined fixed,fixed multiplying op
!summary
There is no way to hide the universal_fixed multiplying operators declared in Standard:
32 -- The type universal_fixed is predefined. The only multiplying operators defined between fixed point types are
33 function "*" (Left : universal_fixed; Right : universal_fixed)
return universal_fixed;
34 function "/" (Left : universal_fixed; Right : universal_fixed)
return universal_fixed;
!question
One rather unpleasant incompatibility between Ada 83 and Ada 95 which has no reasonable workaround is the inability to define one's own fixed,fixed => fixed multiplying operators. I believe we discussed this incompatibility a bit during the 9X process, and presumably hoped that no one would bump into it, given that fixed point usage is rare, and user-defined operators are rare, and the combination is presumably very rare.
Well, unfortunately, someone just bumped into this incompatibility. They are a space-station related project which is considering converting over to Ada 95, and all has been going smoothly except for this one problem. They defined their own fixed,fixed multiplying operators to provide for "saturation" arithmetic (returns "max" on overflow), and to provide "*" only between types where it makes sense. However, there is no way in Ada 95 to hide the univ_fixed,univ_fixed => univ_fixed "*" operator in Standard, and any attempt to use their user-defined "*" is ambiguous with that operator.
!response
Possible solutions are:
1) Ignore the problem; suggest the user change all uses of "X * Y"
to "mul(X,Y)" and define their own "mul" functions. Alternatively, "X * Y" could be changed to P."*"(X, Y).
2) Define some kind of "preference" rule so that a user-defined op
is preferred over the global fixed,fixed operators; but alas, these will reintroduce Beaujolais effects, and don't provide any help in preventing inappropriate type combinations.
3) Define some kind of "exclusion" rule where a fixed-point type that has
any user-defined primitive fixed,fixed multiplying operators would be excluded from any implicit conversion from univ-fixed; but this doesn't work when the user-defined operator is not primitive, unless some "bogus" primitive operator is added to force the exclusion.
4) Define a pragma that explicitly excludes a fixed-point type from
implicit conversion from univ-fixed. For example:
pragma No_Univ_Fixed_Ops(My_Type);
would mean that when "My_Type" is the expected type for a fixed,fixed multiplying op, explicit conversion is required to use the global fixed,fixed op. This is essentially like (3), except that a pragma is used instead of the presence of a primitive operator.
5) Change the profile of the global fixed,fixed operators so they
return "univ_decimal_fixed" instead of "univ_fixed" so they require explicit conversion when the target type is not a decimal fixed point type (this would restore Ada 83 compatibility for "ordinary" fixed point types).
The ARG chooses number (1). However, an implementation can use non-standard types to solve the problem, if it so chooses.
!ACATS test
A B-Test could be created to insure that implementations detect the ambiguity. However, this would have little value. C455001 tests that the implicit coversion happens, and it is hard to imagine an implementation which gets this test right failing to detect the error cases.
!appendix

!section 4.5.5(18)
!subject User-defined fixed,fixed multiplying op
!reference RM95-4.5.5(18-20)
!from Tucker Taft  96-09-17
!keywords incompatibility, fixed point, user-defined operator
!reference 1996-5700.a Tucker Taft 1996-9-17>>
!discussion

One rather unpleasant incompatibility between Ada 83 and Ada 95
which has no reasonable workaround is the inability to define
one's own fixed,fixed => fixed multiplying operators.  I believe
we discussed this incompatibility a bit during the 9X process, and
presumably hoped that noone would bump into it, given that fixed point
usage is rare, and user-defined operators are rare, and the combination
is presumably very rare.

Well, unfortunately, someone just bumped into this incompatibility.
They are a space-station related project which is considering
converting over to Ada 95, and all has been going smoothly except for
this one problem.  They defined their own fixed,fixed multiplying operators
to provide for "saturation" arithmetic (returns "max" on overflow), and
to provide "*" only between types where it makes sense.  However, there is
no way in Ada 95 to hide the univ_fixed,univ_fixed => univ_fixed "*" operator
in Standard, and any attempt to use their user-defined "*" is ambiguous with
that operator.

I see various possible solutions to this problem

  1) Ignore the problem; suggest the user change all uses of "X * Y"
     to "mul(X,Y)" and define their own "mul" functions.  Alternatively,
     "X * Y" could be changed to P."*"(X, Y) ;-).

  2) Define some kind of "preference" rule so that a user-defined op
     is preferred over the global fixed,fixed operators; but alas, these
     will reintroduce Beaujolais effects, and don't provide any help
     in preventing inappropriate type combinations.

  3) Define some kind of "exclusion" rule where a fixed-point type that has
     *any* user-defined primitive fixed,fixed multiplying operators would be
     excluded from any implicit conversion *from* univ-fixed; but this
     doesn't work when the user-defined operator is not primitive, unless
     some "bogus" primitive operator is added to force the exclusion.

  4) Define a pragma that explicitly excludes a fixed-point type from
     implicit conversion from univ-fixed.  For example:

         pragma No_Univ_Fixed_Ops(My_Type);

     would mean that when "My_Type" is the expected type for a fixed,fixed
     multiplying op, explicit conversion is required to use the global
     fixed,fixed op.  This is essentially like (3), except that a pragma
     is used instead of the presence of a primitive operator.  Note that
     this is one of those "evil" pragmas that would make an illegal
     (ambiguous) program legal.  Though Ada 83 compatibility is a "good"
     cause.

  5) Change the profile of the global fixed,fixed operators so they
     return "univ_decimal_fixed" instead of "univ_fixed" so they require
     explicit conversion when the target type is not a decimal fixed
     point type (this would restore Ada 83 compatibility for "ordinary"
     fixed point types).

Given that we changed the rule to allow multiplication without explicit
conversion on behalf of decimal fixed point types, my favorite, though
perhaps most radical, solution is (5).  I doubt if many *new* Ada 95
programs are using "ordinary" fixed point types; the majority of fixed
point usages are probably programs that were originally Ada 83 programs.
Hence, preserving compatibility with Ada 83 might be more important
than providing new convenience for "ordinary" fixed-point types.

Not an easy choice...
-Tuck


****************************************************************

!section 4.5.5(18)
!subject User-defined fixed,fixed multiplying op
!reference RM95-4.5.5(18-20)
!from Robert Dewar
!keywords incompatibility, fixed point, user-defined operator
!reference 1996-5700.a Tucker Taft 1996-9-17>>
!reference 1996-5707.a Robert Dewar 1996-9-26>>
!discussion

I am not in favor of changing the language here. This is one of many small
incompatibilities that make moving from Ada 83 to Ada 95 tricky in some
cases (we have found the incompatibities in handling of Size to be FAR
more worrisome, since in general they cause different results with no
complaint at compile time).

In this case, you get a clear diagnostic from the compiler, and you have
to change the code. I don't see that one user bumping into one incompatibility
using one vendors compiler is a justification for chnaging the language when
the technical situation is clear.

If one user bumping into one incompatibility using one vendors compiler IS
enoough justification for changing the language, I have a number of messages
to transmit to this list, starting with a complaint about what to me is the
gratuitous change in the handling of static expressions that makes LOTS of
Ada 83 programs illegal.

This would be a language change where there is no inconsistency or gap in
the syntax or semantics. We may have to make such changes before we are
through, but this is too small an issue to be worth the trouble.

****************************************************************

!section 4.5.5(18)
!subject User-defined fixed,fixed multiplying op
!reference RM95-4.5.5(18-20)
!from Robert Dewar
!keywords incompatibility, fixed point, user-defined operator
!reference 1996-5700.a Tucker Taft 1996-9-17>>
!reference 1996-5707.a Robert Dewar 1996-9-26>>
!discussion

I am not in favor of changing the language here. This is one of many small
incompatibilities that make moving from Ada 83 to Ada 95 tricky in some
cases (we have found the incompatibities in handling of Size to be FAR
more worrisome, since in general they cause different results with no
complaint at compile time).

In this case, you get a clear diagnostic from the compiler, and you have
to change the code. I don't see that one user bumping into one incompatibility
using one vendors compiler is a justification for chnaging the language when
the technical situation is clear.

If one user bumping into one incompatibility using one vendors compiler IS
enoough justification for changing the language, I have a number of messages
to transmit to this list, starting with a complaint about what to me is the
gratuitous change in the handling of static expressions that makes LOTS of
Ada 83 programs illegal.

This would be a language change where there is no inconsistency or gap in
the syntax or semantics. We may have to make such changes before we are
through, but this is too small an issue to be worth the trouble.

****************************************************************

!section 4.5.5(18)
!subject User-defined fixed,fixed multiplying op
!reference RM95-4.5.5(18-20)
!reference RM95-3.1.4(6-8)
!reference RM95-3.2.3(1)
!reference RM95-A.1(34-35)
!reference 1996-5700.a Tucker Taft 1996-9-17
!reference 1996-5707.a Robert Dewar 1996-9-26
!reference AI95-00163/00
!from Robert Eachus 1996-10-14
!keywords incompatibility, fixed point, user-defined operator
!reference 96-5727.a Robert I. Eachus 96-10-14>>
!discussion

 > This would be a language change where there is no inconsistency or gap in
 > the syntax or semantics. We may have to make such changes before we are
 > through, but this is too small an issue to be worth the trouble.

   I came up with another fix for this problem (other than
non-standard numeric types), but along the way ran into a subtle
difference from Ada 83 that it is a strong gotcha.  Actually we had to
fix it in Ada 83 once, but the disease has spread.

   Is _universal_fixed_ a fixed point type?  Is it a _specific_ type?
For _root_real_ the answer is obvious: It is a specific real type, see
3.4.1(8).  But _universal_fixed_ is a universal type, it says so in
3.4.1(6), and therefore by implication not a specific type.  However,
it also says in 3.4.1(7), that universal types have no primitive
subprograms of their own.  However, 4.5.5(18&19) define two predefined
operators for _universal_fixed_, so 3.2.3(1) leads directly to a
contradiction.

    There is no need for these two operations to be predefined--there
is no way to derive from _universal_fixed_, in fact if there were,
these types would be uncallable.  Also, they are defined in Standard
at A.1(34&35), so they are always visible.  The correct fix seems to
be to remove "predefined" from 4.5.5(18)--it doesn't appear at the
corresponding location in A.1.

    Having fixed that problem, ;-) we get to the nonstandard types
solution.  Let's say I want to implement a FLOATING point type with
set to the max or min for overflow.  According to 3.5.6(8) an
implementation can provide a nonstandard real type that I can use for
this purpose.  I don't need to, but it saves a lot of worry about
reemergence of "predefined" operations in generics, and from the
preference rule in number declarations and the like.  (Yes, I know
that the constant is of type _universal_real_ but the expression can
be of any real type.)  All this seems to not only be straightforward,
but so useful that most implementations probably should provide
Nonstandard_Float as a type, with the normal attributes, and type
conversions to and from Float supported, but no predefined operators.

    But what about nonstandard fixed point types?  You may want lots
of nonstandard fixed-point types, each of which has a different
'Small.  But you certainly don't want to have to contact your vendor
every time you need a new 'Small.  The right solution seems to be a
pragma No_Univ_Fixed_Ops, or some such, as in AI95-00163 option 4,
which applies to an otherwise normal fixed point type declaration, or
a type derived from a fixed point type.  But now we have the problem
of generics.  The right answer seems to be to allow such types to
match generic formal fixed point types.  Basically, the pragma would
be saying that the type would not visibly match _universal_fixed_, but
that it could match a generic formal type that did.  (The alternative,
seems silly.  All the predefined fixed point operations for the type
are present, as is the conversion to _universal_fixed_.  The only
thing nonstandard is that the conversion to _universal_fixed_ first
requires an explicit conversion, and the conversion from actual to
formal type is certainly that.)

    So I think the AI should certainly encourage such a pragma as a
solution for the fixed case, and the non-standard floating point types
without operators as the solution for the floating point version.  (As
a ramification?  Or do we make this a UI?)

    Finally, the trick that started me writing this.  If you want to
write expressions using user defined multiplying operations for fixed
point types, it is possible.  You can even do it in the C := A*B
format.  But literals require explicit user-defined conversions.
(Which do nothing in the object code, but can look messy in the
source, and you get wierd error messages if you leave out a "+".)

   package My_Fixed is

      type Real_Fixed is delta ...;

      type Fixed is private;

      function "+" (L,R: Fixed) return Fixed;
      ...
      function "/" (L,R: Fixed) return Fixed;

   private

      type Fixed is new Real_Fixed;

   end My_Fixed;

   If you add in the "standard" conversion operations:

   function "+"(L: in Real_Fixed) return Fixed;
     and
   function "-"(L: in Real_Fixed) return Fixed;

   Then the only "penalties" incurred outside the package are that
literals have to have signs, and that you can't instantiate generics
that require fixed point types on Fixed.  But you don't want to do
that (other than for Text_IO.Fixed_IO).  You want generics which have
generic formal subprograms to match the operators, or you want generic
formal derived types.  So make My_Fixed itself a generic package (with
a Fixed_IO child), and use generic formal package types.  The final
result looks like:

   generic
     type Real_Type is delta <>;
   package Special_Fixed is

      type Fixed is private;

      function "+"(L: in Real_Type) return Fixed;
      function "-"(L: in Real_Type) return Fixed;

      function "+" (L: Fixed) return Fixed;
      function "-" (L: Fixed) return Fixed;
      function "+" (L,R: Fixed) return Fixed;
      ...
      function "/" (L,R: Fixed) return Fixed;

   private
      type Fixed is new Real_Type;
      pragma Inline(...);
   end Special_Fixed;

   with Text_IO;
   generic
   package Special_Fixed.Fixed_IO is
   ...
   end Special_Fixed.Fixed_IO;

   If NASA, or whoever, isn't happy with this, they can contact their
vendor.

   So I recommend that we split out the wording conflict as an easy
BI, and have a huge confirmation or ramification that spells out what
can and cannot be done to support user defined operations on real types.

****************************************************************

Questions? Ask the ACAA Technical Agent