!standard 4.9(38/2) 07-04-05 AI05-0018-1/02 !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 Replace 12.7(6/2): 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. with 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, or if the actuals match via machine-rounding as described below. The actuals are said to match via machine-rounding if - the actuals are both real static expressions; and - the expected type of the actual parameter of the formal package is a descendant of a formal type; and - the expected type of the actual parameter of the actual instance is not a descendant of a formal type; and - their values are the same after the value of the actual parameter of the formal package has been rounded or truncated (according to the Machine_Rounds attribute of the expected type of the actual parameter of the actual instance) to the nearest machine number of the expected type of the actual parameter of the actual instance as described in section 4.9. Add AARM note (where does this go? at the very end of 12.7?): The motivation for matching via machine-rounding is illustrated by the following example: 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; The language designers intended that this example should be legal. In this example, I1.C has the value Float'Machine (3.14) while G2.Fp1.C has the value 3.14 (see RM 4.9(38/2)). These two values are not the same (at least for most implementations). Matching via machine-rounding allows this example to be accepted. !discussion In the given example, I1.C has the value Float'Machine (3.14) while G2.Fp1.C has the value 3.14 (see RM 4.9(38/2)). These two values are not the same (at least for most implementations), which suggests that the instantiation I2 should be rejected (see 12.7(6/2)). There is general agreement that rejecting this example would be ridiculous. 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. The proposed wording change allows the example to be accepted. The words "as described in section 4.9" are needed in order to avoid having to repeat 4.9(38/2)'s rules about the case where "the value is exactly half-way between two machine numbers". At the Albuquerque meeting, there was some discussion of another approach to solving this problem. The idea (at least as I understood it) was that a universal expression that is subject to an implicit conversion in some sense has two values - the unconverted value and the converted value - and that somehow 4.9(38/2) applies to the converted value while the rest of 4.9 applies to the unconverted value. Furthermore, when 12.7(6) refers to static expressions having "the same value", this is supposed to mean a comparison of the unconverted values. There seems to be no justification in the RM for any of this. More importantly, introducing a new requirement that implementations must keep track of two static values for one expression would be a major change for at least some implementations. Another (bad) approach would be to view 4.9(38/2) as strictly a dynamic semantics rule, which would therefore have no effect on the static value of any expression. The problem with this interpretation is illustrated by the following example: package Pkg is Finer : constant := Long_Float'Succ (0.0); Coarser : constant Float := Finer; Not_Equal : constant Boolean := Finer /= Long_Float (Coarser); Named_Num : constant := 1 / Boolean'Pos (Not_Equal); end Pkg; Assume (as is typically the case) that Long_Float'Succ (0.0) is less than Float'Succ (0.0). For such an implementation, there seems to be general agreement that Not_Equal should be True and the example should be accepted. If 4.9(38/2) were interpreted as only a dynamic semantics rule, then Not_Equal would be False and the example would be rejected. !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.) ****************************************************************