!standard 04.05.05(20) 04-06-29 AI95-00364/04
!class amendment 03-12-04
!status Amendment 200Y 04-06-29
!status ARG Approved 7-1-1 04-06-13
!status work item 04-02-29
!status received 03-09-29
!priority Medium
!difficulty Hard
!subject Fixed-point multiply/divide
!summary
This proposal alleviates the upward incompatibility created by Ada 95 for
users defining their own fixed-point multiply or divide operators.
!problem
The pervasive visibility of universal-fixed multiply/divide operators
coupled with the implicit conversion provided by Ada 95 resulted
in an unforeseen incompatibility for users defining their own
fixed-point multiply or divide operators. Essentially such operators
are unusable, because the universal-fixed ones make any use of them
ambiguous.
!proposal
Forbid use of universal-fixed multiply operator if either operand has
at least one primitive user-defined multiply operator.
Similarly, forbid use of universal-fixed divide operator if
either operand has at least one primitive user-defined divide
operator. These would be Name Resolution rules.
!wording
Change 4.5.5(20) to:
Name Resolution
The above two fixed-fixed multiplying operators shall not be used in a
context where the expected type for the result is itself universal_fixed
[-- the context has to identify some other numeric type to which the
result is to be converted, either explicitly or implicitly]. An
explicit conversion is required on the result when using the above
fixed-fixed multiplication operator when either operand is of a type having
a user-defined primitive multiplication operator declared immediately
within the same list of declarations as the type and with both formal
parameters of a fixed-point type. A corresponding requirement applies to
the universal fixed-fixed division operator.
AARM NOTE: We have made these into Name Resolution rules (one of them
existed in Ada95 but as a Legality Rule) to ensure that user-defined
primitive fixed-fixed operators are not made unusable due to the
presence of these universal fixed-fixed operators.
!discussion
After taking a shot at defining the universal-access equality operators in
standard, it became obvious that there is a simple fix for the
universal-fixed incompatibility. Just have a name-resolution rule which
forbids use of the universal-fixed operations if either operand type has at
least one primitive user-defined multiply operator, in the case of the
universal-fixed multiply operator, or either has at least one primitive user-defined
divide operator in the case of the universal-fixed divide operator.
This doesn't have any Beaujolais effects, because it isn't a preference
rule. Just certain fixed-point type combinations can't be used with the
universal-fixed multiply/divide operators, based strictly on whether one
of them has its own primitive multiply/divide operators, even when these
primitive operators are *not* visible.
Note that we still allow the universal-fixed operators to be considered
if there is an explicit conversion on the result. This is provides
Ada 83 compatibility. This implies that ambiguity is still possible, but
only in the same places where ambiguity existed in Ada 83. A qualified
expression on the result can be used to break the ambiguity in favor of
the user-defined operator in such a situation, followed by the desired
conversion. As in Ada 83, there is no way to make use of the
universal-fixed operator if the operand types correspond to types
for which there is a visible user-defined operator.
Note that the proposed rule specifies that "either" needs to have a
user-defined primitive operator, but it could be phrased as "both."
"Both" makes sense if multiple fixed-point types are defined in the same
scope. "Either" makes sense if fixed-point types tend to be defined each
in their own package, because the "first" one defined would likely not
have any operators of its own. Compatibility with Ada 83 is enhanced by
specifying "either." Compatibility with Ada 95 is enhanced by
specifying "both." Because explicit conversion is always available as a
fall back, "either" seems preferable, since it has less dependence on
the structure of the packages defining the fixed-point types.
This isn't a totally hypothetical problem. At one point we definitely
dealt with a customer who was trying to figure out how to get their old Ada
83 fixed-point code to work, and the answer was not pretty. Basically they
had to use non-operator functions like "mul" and "div", and live with the
fact that they couldn't prevent misuse of the predefined universal-fixed
operators.
There are various reasons to want to "opt out" of the universal-fixed
operators. In some cases the user-defined operator performs wrap-around or
"saturation" arithmetic. In other cases, there may be a desire to limit
what combinations of fixed-fixed types are allowed, so that only those that
involve no scaling are permitted. This may be for efficiency, or simply
for "physical units" consistency.
With this rule, so long as each fixed point type has at least one primitive
fixed-fixed "*" operator and one primitive fixed-fixed "/" operator, the
universal-fixed operators will never be used.
!example
Here are three fixed-point types, with multiplication and
division operators that require no scaling. These may
be user-defined because they perform "saturation" arithmetic,
or simply to ensure that they are only combined in ways that
make sense.
type T1 is delta 0.1;
type T2 is delta 0.001;
type T3 is delta 0.0001;
function "*"(Left : T1; Right : T2) return T3;
function "*"(Left : T2; Right : T1) return T3;
function "/"(Left : T3; Right : T1) return T2;
function "/"(Left : T3; Right : T2) return T1;
X1, Y1 : T1 := ...;
X2, Y2 : T2 := ...;
X3, Y3 : T3;
begin
X3 := X1 * X2; -- Does not use universal-fixed operator, no ambiguity
X3 := X1 * X1; -- Error! Does not use universal-fixed operator, and
-- no user-defined operator works either
X1 := X3 / X2; -- Does not use universal-fixed operator, no ambiguity
X3 := X1 / X2; -- Error! Does not use universal-fixed operator,
-- and no user-defined operator works either
X3 := X3 * X3; -- Error! Does not use universal-fixed operator (because
-- X3 has a primitive which is fixed * fixed, even though
-- X3 appears only as result type).
X3 := T3(X3 * X3); -- OK, because of explicit conversion
!corrigendum 4.5.5(20)
@drepl
@i<@s8>
The above two fixed-fixed multiplying operators shall not be used in a context
where the expected type for the result is itself @i -- the
context has to identify some other numeric type to which the result is to be
converted, either explicitly or implicitly.
@dby
@i<@s8>
The above two fixed-fixed multiplying operators shall not be used in a
context where the expected type for the result is itself @fa
-- the context has to identify some other numeric type to which the
result is to be converted, either explicitly or implicitly. An
explicit conversion is required on the result when using the above
fixed-fixed multiplication operator when either operand is of a type having
a user-defined primitive multiplication operator declared immediately
within the same list of declarations as the type and with both formal
parameters of a fixed-point type. A corresponding requirement applies to
the universal fixed-fixed division operator.
!ACATS test
ACATS test(s) should be constructed to check these changes.
!appendix
From: Tucker Taft
Sent: Monday, September 29, 2003 10:53 PM
I indicated I had a solution for the ada 95/ada 83
incompatibility relating to fixed-point multiply/divide,
where user-defined multiply are not usable because
of the "pervasive" visibility of the universal-fixed
multiply/divide operations.
After taking a shot at defining the universal-access
equality operators in standard, it became obvious
that there is a simple fix for the universal-fixed
incompatibility. Just have a name-resolution rule
which forbids use of the universal-fixed operations
if both operand types have at least one primitive user-defined multiply
operator, in the case of the univ-fixed multiply op,
or both have at least one primitive user-defined divide operator in
the case of the univ-fixed divide op.
This doesn't have any beaujolais effects, because it isn't
a preference rule. Just certain fixed-point type combinations
can't be used with the universal-fixed multiply/divide
operators, based strictly on whether they both have their
own primitive multiply/divide operators.
[I phrased the suggested rule as requiring "both" to have
a user-defined primitive operator, but it could be
phrased as "either." ]
By the way, this isn't totally hypothetical. At one
point we definitely dealt with a customer who was trying
to figure out how to get their old Ada 83 fixed-point code
to work, and the answer was not pretty. Basically they
had to use non-operator functions like "mul" and "div",
and live with the fact that they couldn't prevent misuse
of the predefined univ-fixed operators.
****************************************************************
From: Robert Dewar
Sent: Monday, September 29, 2003 10:55 PM
We have run into similar situations. I like this fix. I think it is
worth while. In fact I would suggest allowing Ada 95 compilers to do
this right away, why not?
****************************************************************
From: Robert I. Eachus
Sent: Tuesday, September 30, 2003 3:45 PM
Tucker said:
> This doesn't have any beaujolais effects, because it isn't
> a preference rule. Just certain fixed-point type combinations
> can't be used with the universal-fixed multiply/divide
> operators, based strictly on whether they both have their
> own primitive multiply/divide operators.
First let me say that this problem is MUCH more serious than potential
beaujolais effects. A good solution now should be preferred to continuing to
look for a perfect solution. There may be a clean and easy to implement
solution without beaujolais effects. If so great. But a preference rule NOW
would be great too.
> [I phrased the suggested rule as requiring "both" to have
> a user-defined primitive operator, but it could be
> phrased as "either." ]
There are three potential fixed-point types involved, two operands and a
result. I think Tucker is talking about when the two operands both have user
defined multiply or divide operations. I don't particularly care what the
exact rule is though, as long as you get it defined for all cases.
> By the way, this isn't totally hypothetical. At one
> point we definitely dealt with a customer who was trying
> to figure out how to get their old Ada 83 fixed-point code
> to work, and the answer was not pretty. Basically they
> had to use non-operator functions like "mul" and "div",
> and live with the fact that they couldn't prevent misuse
> of the predefined univ-fixed operators.
I have/had some fielded Ada 83 code on one project where this one issue was
enough to cause the project to stay with a "baselined" Ada 83 compiler. I
don't know that the particular package ever got rewritten for Ada 95, but
distance times sine returning distance, and (apparent) speed divided by cosine
to give ground speed were two examples of where it was used in the code.
****************************************************************
From: Robert I. Eachus
Sent: Sunday, February 29, 2004 11:12 AM
I prefer the "either" rule. It would probably be possible to define a
'better' rule somewhere in the middle, but getting to complex in this
rule would certainly confuse users. If any package exports both a
fixed-point type and a fixed-fixed "*" operation for it, calling the
predefined universal_fixed "*" is usually not wanted in any case, even
if the fixed-fixed operator is for say Dollars * Hours.
I will point out one surprising result of this rule. By 3.2.3(6), an
operation in the private part of a package can be a primitive operation
for a (non-private) type declared in the public part of the package.
The right solution may just be "don't do that."
****************************************************************
From: Robert Dewar
Sent: Monday, March 1, 2004 6:29 PM
Unless there is better motivation that Ada 83 compatibility, I would
forget this. It's rather beside the point to worry about this issue
at this stage, the damage is already done!
****************************************************************
From: Randy Brukardt
Sent: Monday, March 1, 2004 7:34 PM
Humm, is this the same Robert Dewar who wrote a few months ago:
"We have run into similar situations. I like this fix. I think it is
worth while. In fact I would suggest allowing Ada 95 compilers to do
this right away, why not?"
:-)
Anyway, vendors have indicated that they have customers that have stuck with
Ada 83 precisely because of this issue. Removing barriers to migration to
newer versions Ada would seem to be valuable.
****************************************************************
From: Robert Dewar
Sent: Monday, March 1, 2004 8:13 PM
> Humm, is this the same Robert Dewar who wrote a few months ago:
>
> "We have run into similar situations. I like this fix. I think it is
> worth while. In fact I would suggest allowing Ada 95 compilers to do
> this right away, why not?"
Yes, it's the same Robert Dewar (I wrote that in October 2003). I guess
the additional time passing has made me feel less sangine about the
effect at this stage.
> Anyway, vendors have indicated that they have customers that have stuck with
> Ada 83 precisely because of this issue. Removing barriers to migration to
> newer versions Ada would seem to be valuable.
Well that's a very strong and significant user input, which seems
decisive to me.
So I think on reflection I will go with the October RD rather than
the March RD :-)
****************************************************************
From: Tucker Taft
Sent: Tuesday, March 2, 2004 7:27 AM
Tucker Taft wrote:
> Here is the first "real" version of AI-364
> attempting to restore some compatibility with
> Ada 83 with respect to user-defined fixed-fixed
> multiplying operators.
I realize one of the paragraphs below has an
ambiguous use of "[]".
> ...
> Change 4.5.5(20) as follows:
>
> [Legality Rules] {Name Resolution}
>
> The above two fixed-fixed multiplying operators shall not be used in a
> context where the expected type for the result is itself universal_fixed
> -- [the context has to identify some other numeric type to which the
> result is to be converted, either explicitly or implicitly]...
The above "[]" are intended to convey that this phrase is
a ramification of other rules in the manual, and hence
is "officially" redundant.
The '[' and ']' should only appear in the AARM.
The phrase itself should *remain* -- the "[]" was *not*
meant to convey that it should be removed in Ada 0Y.
Sorry for any confusion.
> OPEN ISSUE:
> I phrased the suggested rule as requiring "both" to have a user-defined
> primitive operator, but it could be phrased as "either." ...
> ... Compatibility with Ada 83 is enhanced by
> specifying "either."
I think I prefer "either" at this point, since the whole point
of this was to improve compatibility with Ada 83.
****************************************************************
From: Tucker Taft
Sent: Tuesday, June 8, 2004 12:07 AM
Oops. The !proposal section still says "both" operands. It
should say "either" operand. Also, I included the appendix
by mistake. Usually I omit it.
****************************************************************
From: Robert I. Eachus
Sent: Wednesday, June 9, 2004 12:03 PM
There is a discussion of this issue on comp.lang.ada, and I thought I
should append my response here. Tucker's solution has a huge benefit
that the current discussion hasn't mentioned:
-----------------------------------------------------------------------------------------------------------------
Martin Dowie wrote:
> "Duncan Sands" wrote in message
> news:mailman.77.1086765132.391.comp.lang.ada@ada-france.org...
>
>>Fixed point types have a remarkable property:
>>you can multiply any two of them to get a third.
>>You can multiply apples and oranges and get
>>bananas. This goes against type safety.
I agree, and I objected to the original Ada95 rule that allowed the
explict type conversion from universal fixed to be omitted. In addition
to the problems created when you do want to explicitly define a multiply
operation, it creates the serious problem Duncan refers to:
> Suppose I have several fixed point types, for
> example
>
> type Price is delta 0.01 digits ...;
> type Volume is delta 1.0 digits ...;
> type Value is delta 0.01 digits ...;
P: Price; V: Volume; Val: Value;
...
P := V * Val; -- the compiler won't object!
> I think this is being addressed by the ARG in AI-364
>
> http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AIs/AI-00364.TXT?rev=1.6
>
> Still a work item though...
What is being worked on is actually different problem, where you do want
to declare your own multiplication or division operators, and find that
they can't be called in infix notation.
That having been said, the current version exactly fixes Duncan Sands
problem. According to the new draft, if you declare a multiplication
operation for Apples, all of the "special" Apple * fixed and fixed *
Apple operations returning universal_fixed will be hidden where the user
defined operators are visible.
So to go back to Duncan's original complaint add:
function "*"(Left: Price; Right: Volume) return Value;
This will hide ALL the predefined operations that Duncan wants to get
rid of, and any new ones that involve Price, Volume, or Value, so the line:
P := V * Val; -- The compiler will complain that no visible "*" matches.
Glad to be of service. ;-)
****************************************************************
From: Tucker Taft
Sent: Wednesday, June 9, 2004 5:40 PM
Good point. The fix does eliminate the overly loose conversion
rules associated with fixed-fixed multiply/divide in the presence of
user-defined operators.
One thing to note, however, is that it is not a question
of the visibility of the user-defined operators. It is a question
of the *existence* of the user-defined *primitive* operators.
If the any such operators exist in the visible part of the package, then
the corresponding universal fixed-fixed operator is no longer
usable with that type anywhere, even if the user-defined operator
is *not* visible. This is important to avoid beaujolais effects.
(If you wish, feel free to include this clarification in a comp.lang.ada
response.)
One other thing to mention is that the non-fixed multiply/divide
operators are not really any better, in terms of type checking.
They allow meters * meters = meters, and disallow
meters * meters = square meters. So I'm not convinced that
the looseness of the fixed-fixed multiply/divide is worse than
the perversity of the non-fixed operators. But I *am* convinced
that the inability to override the fixed-fixed operators is
bad news, and this AI is a good thing from that perspective.
****************************************************************