Version 1.1 of ai05s/ai05-0018-1.txt

Unformatted version of ai05s/ai05-0018-1.txt version 1.1
Other versions for file ai05s/ai05-0018-1.txt

!standard 4.9(38/2)          06-11-09 AI05-0018-1/01
!standard 12.7(6/2)
!class binding interpretation 06-11-09
!status work item 06-11-09
!status received 06-09-15
!priority Medium
!difficulty Medium
!qualifier Error
!subject Formal Package Matching Rules
!summary
3.14 equals 3.14.
!question
The matching rules for formal packages have some peculiar interactions with the rounding rules for real static expressions (RM 4.9(38/2)).
Given an Ada (or Ada95) implementation for which 3.14 is not exactly representable as a machine number of Float (as is typical), it appears that the following example must be rejected:
package Pkg is generic type T is digits <>; C : T; package G1 is end G1;
package I1 is new G1 (Float, 3.14);
generic type T is digits <>; with package Fp1 is new G1 (T, 3.14); package G2 is end G2;
package I2 is new G2 (Float, I1); end Pkg;
Is this correct? (No.)
!recommendation
(See Summary.)
!wording
TDB. [I'm unclear if wording needs to be changed, and if so, what the change would be. - RLB]
!discussion
The rule of least surprise surely applies to this example. Users would be seriously confused if two literals with the same value didn't match for a formal package.
Formal matching should be done on the universal representation of literals, not on the "tweaked" version of them described by 4.9.
!ACATS test
Make a C-Test that is similar to the example.
!appendix

From: Stephen W. Baird
Date: Friday, September 15, 2006  4:08 PM

The matching rules for formal packages have some peculiar interactions
with the rounding rules for real static expressions (RM 4.9(38/2)).

Given an Ada (or Ada95) implementation for which 3.14 is not exactly
representable as a machine number of Float (as is typical), it appears
that the following example must be rejected:

  package Pkg  is
    generic
        type T is digits <>;
        C : T;
    package G1 is
    end G1;

    package I1 is new G1 (Float, 3.14);

    generic
        type T is digits <>;
        with package Fp1 is new G1 (T, 3.14);
    package G2 is
    end G2;

    package I2 is new G2 (Float, I1);
  end Pkg;
 
RRM 12.7(6/2) states:
  For a formal object of mode in, the actuals match if they
  are static expressions with the same value, or if they
  statically denote the same constant, or if they are both
  the literal null. 

I1.C has the value Float'Machine (3.14); Fp1.C has the value 3.14.

The two values are not the same, so the instantiation I2 is rejected.

Is this analysis correct? If so, then is this a problem?

To create the next variation, introduce a named number
     Pi : constant := 3.14;
as the first declaration of package Pkg and replace both uses
of the literal 3.14 with uses of this constant.

Although the two actual parameter values still differ, this version is
accepted because both actuals "statically denote the same constant".

Accepting the one version while rejecting the other suggests that there
is a problem here. Replacing a numeric literal with a use of a named
number is not supposed to have this kind of semantic impact.

Pascal observed (in private correspondence) that one could change 12.7(6/2)
to require that if both actuals are static expressions then their values
must be equal (even if they statically denote the same constant).

This would result in rejecting both versions.

Alternatively, there might be a reasonable rule that would allow accepting
both versions. Perhaps 12.7(6/2) could somehow take the rounding rules
of 4.9(38/2) into account in determining whether two static expressions
have "the same value".

Or maybe this is just "insufficiently broken".

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

From: Tucker Taft
Date: Friday, September 15, 2006  8:39 PM

Well I would say this is not terribly broken.

But I think there isn't really a problem in any case,
because of 12.7(8.1/1):

    For the purposes of matching, any actual parameter that
    is the name of a formal object of mode IN is replaced by
    the formal object's actual expression (recursively).

I think this means we end up comparing two instances of
"3.14" which clearly have the same value.

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

From: Pascal Leroy
Date: Tuesday, September 19, 2006  3:24 AM

> Well I would say this is not terribly broken.

I agree with this.

> But I think there isn't really a problem in any case,
> because of 12.7(8.1/1):
> 
>     For the purposes of matching, any actual parameter that
>     is the name of a formal object of mode IN is replaced by
>     the formal object's actual expression (recursively).
> 
> I think this means we end up comparing two instances of
> "3.14" which clearly have the same value.

I find the word "clearly" in the above sentence quite cavalier, and
actually I don't see that 12.7(8.1/1) helps.  Let me show Steve's example
again for easy reference:

> >   package Pkg  is
> >     generic
> >         type T is digits <>;
> >         C : T;
> >     package G1 is
> >     end G1;
> > 
> >     package I1 is new G1 (Float, 3.14); -- (1)
> > 
> >     generic
> >         type T is digits <>;
> >         with package Fp1 is new G1 (T, 3.14); -- (2)
> >     package G2 is
> >     end G2;
> > 
> >     package I2 is new G2 (Float, I1);
> >   end Pkg;

As far as I can tell, 12.7(8.1/1) is a rather vacuous rule (what do you
expect from a rule that was introduced in TC1?) and it falls into the
category "of course, the language has to work that way".  It helps if both
occurrences of 3.14 are replaced by a reference to a named number Pi, but
it doesn't help at all in the above example.  The problem is that we end
up comparing the two instances of 3.14 marked (1) and (2), and they
*don't* have the same value: at (1) the value was tweaked to
Float'Machine(3.14) and at (2) it was left unchanged (see 4.9(38/2)).

Tuck, if you think that the instantiation is "clearly" legal, you must
tell me what is "the same value" that we end up comparing.  Is it the
tweaked value or the untweaked value?

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

From: Tucker Taft
Date: Tuesday, September 19, 2006  9:15 AM

...
> I find the word "clearly" in the above sentence quite cavalier, and
> actually I don't see that 12.7(8.1/1) helps. 

Rats!  I thought I had snuck that one by. ;-)

...
> As far as I can tell, 12.7(8.1/1) is a rather vacuous rule (what do you
> expect from a rule that was introduced in TC1?) and it falls into the
> category "of course, the language has to work that way".  It helps if both
> occurrences of 3.14 are replaced by a reference to a named number Pi, but
> it doesn't help at all in the above example.  

Now I am genuinely confused.  A named number is identical
to a numeric literal, as far as I can tell, in every way.
They both have an exact value of a universal numeric type,
which then might be "tweaked," as you say below, after being
evaluated.

> ... The problem is that we end
> up comparing the two instances of 3.14 marked (1) and (2), and they
> *don't* have the same value: at (1) the value was tweaked to
> Float'Machine(3.14) and at (2) it was left unchanged (see 4.9(38/2)).

When preparing my earlier "cavalier" answer, I studied 4.9(38/2)
and I'll admit to getting pretty confused.  I was relieved when
I found 12.7(8.1/1) because I thought it clearly solved the
problem, though it presumes you interpret "value" and
"actual expression" very carefully (more later on that issue).

My problem with 4.9(38/2) is that I didn't remember what it is
supposed to do when a static expression is used to
initialize a static constant of a floating point type.
The phrase "expression is not part of a larger static expression"
is somewhat ambiguous (at least as I read it today) in that case.
I believe the *original* original intent (in as much as that matters
at this point) was that when passing from the "static" expression
world to the "run-time-evaluated" world, we wanted to impose
certain requirements, for portability's sake.  But if the static
expression is being used to initialize a static constant, then
we really aren't passing across that barrier.  However, AARM 4.9(38.a/2)
pretty clearly states that the *documented* Ada 95 intent was that
static constants would always have a value that is a machine
number.  Nevertheless, as I think about it now, it seems odd
that if you have a large static expression, and you decide to
break it up into named parts, e.g.:

    Rather than:

       Initial_Area : Float := 2.0 * Pi * (7.0 / 3.0);

    You decide to write:

       Radius : constant Float'Base := 7.0 / 3.0;
       Initial_Area : Float := 2.0 * Pi * Radius;

It seems surprising that you inevitably lose precision.
In this case at least you could use a named number instead.

I will admit that interpreting 4.9(38/2) this way is clearly
not legitimate, given 4.9(38.a/2), and I suspect in Ada 95 we
ended up going this route because of difficulties of handling
infinite precision static constants when they get mixed up
with shared generics (thanks, Rational and RR ;-).

The second ambiguity with 4.9(38) is deciding when exactly this
loss of precision occurs.  According to 4.9(33), the
*evaluation* is performed exactly.  So when does the rounding/
truncation occur?  I believe when it is used as an operand
of an enclosing expression, or when it is assigned into
a named object.  The value of the *expression* is still
exactly computed.  So the value of the expression "3.14"
is always exactly 3.14.  However, when it is assigned into
the formal IN object, it is rounded or truncated.  However,
when we use 12.7(8.1/1) to go back to the original actual
expressions for comparison, I believe it is reasonable to
return to the exact value of those expressions, and bypass
any rounding/truncation that was part of the *assignment*
of that value.

> Tuck, if you think that the instantiation is "clearly" legal, you must
> tell me what is "the same value" that we end up comparing.  Is it the
> tweaked value or the untweaked value?

As argued above, I believe the "tweaking" occurs as part of
the "enclosing" operation, which is either a non-static
numeric operation or an assignment operation.  The value of
the expression itself is still exact, i.e., *untweaked*, and
that is what we compare.

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

From: Robert I. Eachus
Date: Tuesday, September 19, 2006  9:41 AM

>    Rather than:
>
>       Initial_Area : Float := 2.0 * Pi * (7.0 / 3.0);
>
>    You decide to write:
>
>       Radius : constant Float'Base := 7.0 / 3.0;
>       Initial_Area : Float := 2.0 * Pi * Radius;
>
> It seems surprising that you inevitably lose precision.
> In this case at least you could use a named number instead.


I have a serious problem with this that has nothing to do with Ada as 
such.  Either change Initial_Area to Circumference or change the example to:

         Initial_Area: Float := Pi * (7.0/3.0)**2;
...
        Radius : constant Float'Base := 7.0 / 3.0;
        Initial_Area : Float := Pi * Radius**2;

Yeah, I know, for the purpose of this discussion, the example is fine.  
But with my error checking hat on, I can't get past it without fixing in.

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

From: Tucker Taft
Date: Tuesday, September 19, 2006  9:52 AM

Oops, good point.  I was thinking circumference when I
wrote area.  Thanks for providing some "static checking."

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

From: Pascal Leroy
Date: Tuesday, September 19, 2006 10:35 AM

> Now I am genuinely confused.  A named number is identical
> to a numeric literal, as far as I can tell, in every way.

As far as I can tell, this was the point of Steve's question: they are not
in this particular situation.  A named number can be statically denoted
from various places, and it doesn't matter (apparently) that it gets
tweaked differently.  But a literal is never statically denoted, so any
comparison must be based on the (tweaked or untweaked) value.

> My problem with 4.9(38/2) is that I didn't remember what it 
> is supposed to do when a static expression is used to 
> initialize a static constant of a floating point type. The 
> phrase "expression is not part of a larger static expression" 
> is somewhat ambiguous (at least as I read it today) in that 
> case. I believe the *original* original intent (in as much as 
> that matters at this point) was that when passing from the 
> "static" expression world to the "run-time-evaluated" world, 
> we wanted to impose certain requirements, for portability's 
> sake.  But if the static expression is being used to 
> initialize a static constant, then we really aren't passing 
> across that barrier.

Interesting thoughts, and this might have been a good idea, but this is
just not what the words of the RM (and AARM) say.

> The second ambiguity with 4.9(38) is deciding when exactly 
> this loss of precision occurs.  According to 4.9(33), the
> *evaluation* is performed exactly.  So when does the 
> rounding/ truncation occur?  I believe when it is used as an 
> operand of an enclosing expression, or when it is assigned 
> into a named object.  The value of the *expression* is still 
> exactly computed.  So the value of the expression "3.14" is 
> always exactly 3.14.  However, when it is assigned into the 
> formal IN object, it is rounded or truncated.  However, when 
> we use 12.7(8.1/1) to go back to the original actual 
> expressions for comparison, I believe it is reasonable to 
> return to the exact value of those expressions, and bypass 
> any rounding/truncation that was part of the *assignment* of 
> that value.

I find this reasoning extraordinarily dubious.  So now a real static
expression has two values, the exact mathematical value, and, if the
context requires tweaking, the tweaked value.  Each time the RM talks
about the value of a static expression, we have to guess which one to use.
This is introducing a lot of conceptual complexity for an obscure corner
case.  Not to mention that it would be a lot of work for implementers.

It seems to me that you are cleverly trying to reintroduce your favorite
model whereby tweaking only takes place when crossing to the non-static
domain: after all, once you keep the two static values around, you might
as well use the untweaked one in a static context.  Nice try, but no,
thanks.

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

From: Tucker Taft
Date: Tuesday, September 19, 2006  11:08 AM

...
> I find this reasoning extraordinarily dubious.  So now a real static
> expression has two values, the exact mathematical value, and, if the
> context requires tweaking, the tweaked value.  

That isn't what I am saying.  I am saying that the value of a
static constant is not necessarily the same as the value of its
initializing expression.  A static expression has exactly one
value, and it is the "exact" value.  That seems pretty clear
from 4.9(33):

       A static expression is evaluated at compile time ...
       This evaluation is performed exactly ...

4.9(38), I would claim, is talking about what happens *after*
evaluation.  In fact, it talks about "the value" of the expression,
and based on *the* value, it specifies whether rounding or truncation
is performed.  I agree it needs clarification, but I believe
there is enough there to justify this model, and I believe the
matching case, and perhaps other cases, show it is a preferable
model.

From my personal perspective, there is a lot less "squinting"
required if you say "3.14" has only one value, and it is
the exact value.  If you assign 3.14 to something, then of
course you have to round or truncate it to fit into the machine
representation.  4.9(38) specifies how that rounding or truncation
occur when initializing an object, or when using the value in
a run-time computation.

> ...Each time the RM talks
> about the value of a static expression, we have to guess which one to use.

No, there is only one value of a static expression in this
model, and only one value of a static constant, but the value
of a static constant is *not* necessarily the same as the
value of its initializing expression.

> This is introducing a lot of conceptual complexity for an obscure corner
> case.  Not to mention that it would be a lot of work for implementers.

I don't see the complexity.  If anything, I see it as being significantly
simpler to say that 3.14 = 3.14 = 3.14.

> It seems to me that you are cleverly trying to reintroduce your favorite
> model whereby tweaking only takes place when crossing to the non-static
> domain: after all, once you keep the two static values around, you might
> as well use the untweaked one in a static context.  Nice try, but no,
> thanks.

I shouldn't have mentioned that "other" ambiguity.  That is
definitely water over the dam (and it certainly isn't a
"favorite" model -- I don't believe I have ever mentioned it
before).

I think if you look at 4.9(33) and 4.9(38), it seem pretty clear
that "the value" of a static expression is its exact value.  The
thing that is unclear is how the rounding/truncation fits in.
And I think the only place where this "tweaked" value could fit
in is as the value of the *target* of the assignment, either to a
stand-alone object, or to a formal parameter (of the enclosing
operation/call).  Yes there are two values, but they are associated
with two different things.  The untweaked value is associated
with the static expression, and the tweaked value is associated
with the object/formal to which it is assigned.

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

From: Randy Brukardt
Date: Tuesday, September 19, 2006  12:45 PM

> I think if you look at 4.9(33) and 4.9(38), it seem pretty clear
> that "the value" of a static expression is its exact value.  The
> thing that is unclear is how the rounding/truncation fits in.
> And I think the only place where this "tweaked" value could fit
> in is as the value of the *target* of the assignment, either to a
> stand-alone object, or to a formal parameter (of the enclosing
> operation/call).  Yes there are two values, but they are associated
> with two different things.  The untweaked value is associated
> with the static expression, and the tweaked value is associated
> with the object/formal to which it is assigned.

I'll probably regret saying this, but I agree with Tucker. The rounding
occurs when you write the value of a static constant to the symbol table
(i.e., the assignment); the value of the expression doesn't magically change
(that would seem to be a lot more work for a compiler).

Perhaps this makes instantiating formal packages more messy, but those
things are already such a mess to deal with, I can't get too excited about a
bit of extra code in something that already takes thousands of lines. And we
surely don't want 3.14 /= 3.14 -- that would drive users crazy.

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

From: Pascal Leroy
Date: Wednesday, September 20, 2006  2:59 AM

> That isn't what I am saying.  I am saying that the value of a 
> static constant is not necessarily the same as the value of 
> its initializing expression.  A static expression has exactly 
> one value, and it is the "exact" value.  That seems pretty 
> clear from 4.9(33):
> 
>        A static expression is evaluated at compile time ...
>        This evaluation is performed exactly ...
> 
> 4.9(38), I would claim, is talking about what happens *after* 
> evaluation.  In fact, it talks about "the value" of the 
> expression, and based on *the* value, it specifies whether 
> rounding or truncation is performed.

OK, thanks for the clarification.  Yes, I agree that this is a reasonable
model.  So you are saying that in a declaration like:

	C : constant Float := 3.14;

the value of 3.14 is 3.14, and the value of C is Float'Machine(3.14).

Now back to actual/formal matching for formal packages.  If I follow your
reasoning correctly, you read 12.7(8.1) to mean that the following
packages match:

	package Actual1 is new G (Float, 3.14);
	with package Formal1 is new G (Float, 3.14);

because in the actual we go all the way back to the expression 3.14,
ignoring the full_constant_declaration which exists in I1 (12.4(10.2)).
Fine.

Observe though that this means that the following pairs of actual/formal
packages don't match (at least that's my understanding of what you are
saying):

	package Actual2 is new G (Float, C);
	with package Formal2 is new G (Float, 3.14);

	package Actual3 is new G (Float, 3.14);
	with package Formal3 is new G (Float, C);

That's because they will end up comparing 3.14 with Float'Machine(3.14).

This does bother me because it would create an incompatibility for
implementations, like ours, that have interpreted these rules differently
for 10+ years: at the moment we certainly consider that Actual3 and
Formal3 match because both values are tweaked.  It's impossible to know
for sure what is the impact of this incompatibility, but before proceeding
it would be good to assess what compilers currently do.

> I don't see the complexity.  If anything, I see it as being 
> significantly simpler to say that 3.14 = 3.14 = 3.14.

Hmm.  Cautious here, I am not opposed to the notion that 3.14=3.14, but
real types are extremely counter-intuitive.  For instance:

	type Fix is delta 0.04 range 0.0..4.0;
	for Fix'Small use Fix'Delta;

For this type, the value 3.14 lies exactly haftway between two machine
numbers.  In Ada 2005, the tweaking is implementation-dependent in this
case.  In particular, it doesn't have to be deterministic.  So 3.14 could
be tweaked to 3.16 in one context and to 3.12 in another context.  Of
course we are not flipping a coin each time we tweak, but it's possible to
construct slightly more complicated examples where things can get quite
confusing (and customers do get confused).

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

From: Tucker Taft
Date: Wednessday, September 20, 2006  6:41 AM

> ...
> OK, thanks for the clarification.  Yes, I agree that this is a reasonable
> model.  So you are saying that in a declaration like:
> 
> 	C : constant Float := 3.14;
> 
> the value of 3.14 is 3.14, and the value of C is Float'Machine(3.14).

Yes.

> 
> Observe though that this means that the following pairs of actual/formal
> packages don't match (at least that's my understanding of what you are
> saying):
> 
> 	package Actual2 is new G (Float, C);
> 	with package Formal2 is new G (Float, 3.14);
> 
> 	package Actual3 is new G (Float, 3.14);
> 	with package Formal3 is new G (Float, C);
> 
> That's because they will end up comparing 3.14 with Float'Machine(3.14).

At least there may be easy workarounds, namely using "C" in both
cases, or making C into a named number.  The original
problem that Steve cited didn't seem to have any easy
workarounds.

> This does bother me because it would create an incompatibility for
> implementations, like ours, that have interpreted these rules differently
> for 10+ years: at the moment we certainly consider that Actual3 and
> Formal3 match because both values are tweaked.  It's impossible to know
> for sure what is the impact of this incompatibility, but before proceeding
> it would be good to assess what compilers currently do...

Yes, that would be interesting to find out.  My own guess is
that this doesn't make too much difference either way.
I believe the proposed model for interpreting 4.9(38) is
more intuitive, but the likelihood that anyone would actually
run into a case where it really matters seems remote.

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

From: Edmond Schonberg
Date: Wednesday, September 20, 2006  7:58 AM

...
> Observe though that this means that the following pairs of actual/formal
> packages don't match (at least that's my understanding of what you are
> saying):
>
> 	package Actual2 is new G (Float, C);
> 	with package Formal2 is new G (Float, 3.14);
>
> 	package Actual3 is new G (Float, 3.14);
> 	with package Formal3 is new G (Float, C);
>
> That's because they will end up comparing 3.14 with Float'Machine 
> (3.14).
>
> This does bother me because it would create an incompatibility for
> implementations, like ours, that have interpreted these rules differently
> for 10+ years: at the moment we certainly consider that Actual3 and
> Formal3 match because both values are tweaked.  It's impossible to know
> for sure what is the impact of this incompatibility, but before  
> proceeding it would be good to assess what compilers currently do.

GNAT certainly treats these as matching. I wouldn't know what it  
means to compare 3.14 with Float'Machine (3.14) if they are not  
reduced to some common representation. The static constant is tweaked  
to the type of the actual before the comparison. However  
counterintuitive floating point arithmetic might be, making these non- 
conformant will only reinforce Ada's reputation for impenetrability :-)

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

From: Tucker Taft
Date: Wednesday, September 20, 2006  1:45 PM

There are (at least) two kinds of "tweaking": first, tweaking "to the type"
is required on literals that are interpreted as fixed-point values,
and involves forcing them to be a multiple of the "small"; second,
tweaking "to a machine number within the base range."

The first tweaking happens as part of the implicit conversion of a
real literal to a specific type.  It has no effect for a floating point
type, since all floating point types include all rational numbers.
For ordinary fixed-point types, it involves a truncation/rounding
to a multiple of the small.  For decimal fixed-point types, it
involves checking that the value is already a multiple of the small.

The second tweaking happens on the boundary between static and
non-static.  It is not clear whether this tweaking should happen
as part of static matching.

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

From: Robert A. Duff
Date: Wednesday, September 20, 2006  7:12 AM

> To create the next variation, introduce a named number
>      Pi : constant := 3.14;
> as the first declaration of package Pkg and replace both uses
> of the literal 3.14 with uses of this constant.

I don't know if this helps or hurts, but Pi above is not a constant,
so I don't think the "statically denote the same constant" rule
applies.  Never mind that it LOOKS like a constant.

I agree with Tuck and others that the intent is for named numbers to behave
just like literals.

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

From: Pascal Leroy
Date: Wednesday, September 20, 2006 12:11 AM

This would indicate that 12.7(6/2) is subtly broken and should say
"constant or named number".

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

From: Robert A. Duff
Date: Wednesday, September 20, 2006  8:06 PM

Hmm.  That fix rubs me the wrong way.  For named numbers, we should be talking
about the value, not about statically denoting them.  I mean, to preserve the
property that named numbers are essentially the same as literals.

(Named numbers are not one of my favorite features, by the way.)

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


Questions? Ask the ACAA Technical Agent