!standard 10.2.1(18/2) 10-08-12 AI05-0219-1/02 !class binding interpretation 10-06-13 !status Amendment 2012 10-08-12 !status ARG Approved 8-0-1 10-06-19 !status work item 10-06-13 !status received 10-05-06 !priority Low !difficulty Easy !qualifier Clarification !subject Pure permissions and limited parameters !summary The permission to eliminate calls on pure subprograms cannot be used if any parameter has a part whose full type is immutably limited. !question 10.2.1(18/2) is a dynamic semantics rule, but it talks about "parameters of a limited type". "Limited" is a static category that depends on visibility; wouldn't "immutably limited" be better? (No, but clarification is needed.) !recommendation (See Summary.) !wording Modify 10.2.1(18/2): If a library unit is declared pure, then the implementation is permitted to omit a call on a library-level subprogram of the library unit if the results are not needed after the call. In addition, the implementation may omit a call on such a subprogram and simply reuse the results produced by an earlier call on the same subprogram, provided that none of the parameters nor any object accessible via access values from the parameters [are of a limited type]{have any part that is of a type whose full type is an immutably limited type}, and the addresses and values of all by-reference actual parameters, the values of all by-copy-in actual parameters, and the values of all objects accessible via access values from the parameters, are the same as they were at the earlier call. This permission applies even if the subprogram produces other side effects when called. !discussion "Immutably limited" isn't strong enough, as it doesn't include all types with limited parts. However, "limited type" here is vague, we are really talking about types whose full type is limited. To be consistent, we use the same wording as for build-in-place: "full type of any part of the object is immutably limited". !corrigendum 10.2.1(18/2) @drepl If a library unit is declared pure, then the implementation is permitted to omit a call on a library-level subprogram of the library unit if the results are not needed after the call. In addition, the implementation may omit a call on such a subprogram and simply reuse the results produced by an earlier call on the same subprogram, provided that none of the parameters nor any object accessible via access values from the parameters are of a limited type, and the addresses and values of all by-reference actual parameters, the values of all by-copy-in actual parameters, and the values of all objects accessible via access values from the parameters, are the same as they were at the earlier call. This permission applies even if the subprogram produces other side effects when called. @dby If a library unit is declared pure, then the implementation is permitted to omit a call on a library-level subprogram of the library unit if the results are not needed after the call. In addition, the implementation may omit a call on such a subprogram and simply reuse the results produced by an earlier call on the same subprogram, provided that none of the parameters nor any object accessible via access values from the parameters have any part that is of a type whose full type is an immutably limited type, and the addresses and values of all by-reference actual parameters, the values of all by-copy-in actual parameters, and the values of all objects accessible via access values from the parameters, are the same as they were at the earlier call. This permission applies even if the subprogram produces other side effects when called. !ACATS Test This is just a permission, it is hard to test usefully. (We could try to test that it is not applied in these cases, but of course it might not be used even if the rule here is incorrectly implemented.) !appendix From: Bob Duff Sent: Thursday, May 6, 2010 8:35 AM In 10.2.1 Elaboration Control, we have: Implementation Permissions 18/2 {AI95-00366-01} If a library unit is declared pure, then the implementation is permitted to omit a call on a library-level subprogram of the library unit if the results are not needed after the call. In addition, the implementation may omit a call on such a subprogram and simply reuse the results produced by an earlier call on the same subprogram, provided that none of the parameters nor any object accessible via access values from the parameters are of a limited type, and the addresses and values of all by-reference actual parameters, the values of all by-copy-in actual parameters, and the values of all objects accessible via access values from the parameters, are the same as they were at the earlier call. [This permission applies even if the subprogram produces other side effects when called.] Why does it say "limited", given that this is a run-time issue -- shouldn't it say "immutably limited"? P.S. Why does this limitedness exception exist in the first place? **************************************************************** From: Bob Duff Sent: Thursday, May 6, 2010 8:57 AM > Why does it say "limited", given that this is a run-time issue -- > shouldn't it say "immutably limited"? Also, there are three occurrences of "parameter" in this para that are not qualified by either "formal" or "actual". Seems like plain "parameter" is an ambiguous term, and should be "formal" or "actual" (or if you like verbosity, "formal parameter" or "actual parameter"). **************************************************************** From: Tucker Taft Sent: Thursday, May 6, 2010 9:16 AM I don't think "immutably" is quite right. What matters is whether the type is limited "deep down." Immutably is more stringent than that, since it is a visible indication that the type is going to "stay" limited, whereas a simple "limited private" type is not considered immutably limited even if the full type is a task type. I agree this is dynamic semantics, and when we say things like "limited" we mean "really limited." "Immutably limited" is more of a static semantics-level term. I'm also not sure why formal vs. actual matters, since again we are talking dynamic semantics, and at the point of call they have the same value, presumably. **************************************************************** From: Robert Dewar Sent: Thursday, May 6, 2010 9:21 AM To me it is really ugly to have a situation where in one case we have a call eliminated, and in another case we don't JUST because of the current visibility, so indeed we don't want to imply that the check is at the point of call. **************************************************************** From: Tucker Taft Sent: Thursday, May 6, 2010 9:41 AM I *think* we are agreeing that this permission has nothing to do with visibility, and hence "immutably limited" is not the appropriate term, since that is related to visibility. Similarly, actual vs. formal doesn't matter, since we are talking about the run-time value (and address if by-reference), not static semantic properties which might distinguish actual from formal. **************************************************************** From: Robert Dewar Sent: Thursday, May 6, 2010 9:51 AM >>> I don't think "immutably" is quite right. >>> What matters is whether the type is limited "deep down." Immutably >>> is more stringent than that, since it is a visible indication that >>> the type is going to "stay" limited, whereas a simple "limited >>> private" type is not considered immutably limited even if the full >>> type is a task type. I agree this is dynamic semantics, and when we >>> say things like "limited" we mean "really limited." "Immutably >>> limited" is more of a static semantics-level term. Immutably limited is stronger than what you have in mind right? But perhaps it is the simplest rule to follow here? no? **************************************************************** From: Tucker Taft Sent: Thursday, May 6, 2010 9:26 AM The exception for limited types is because there is a presumption that their "state" is not fully captured by their "value" (whatever "value" means). Certainly trying to decide whether a task object has the same "value" as at an earlier call would be difficult. **************************************************************** From: Robert Dewar Sent: Thursday, May 6, 2010 9:32 AM > The exception for limited types is because there is a presumption that > their "state" is not fully captured by their "value" (whatever "value" > means). > Certainly trying to decide whether a task object has the same "value" > as at an earlier call would be difficult. I would say that since you can't prove it has the same value, you just don't eliminate the call in that case, in other words I would replace this with a note if it is to be mentioned at all that said Note: since there is a presumption that the state of an immutably limited object may not be fully captured by their value, generally calls that involve such a value will not be eliminated, since it would be impossible for the compiler to know whether the values were the same in two calls. **************************************************************** From: Tucker Taft Sent: Thursday, May 6, 2010 9:48 AM I don't believe the term "value" is even well defined for (deep-down) limited types. That is the fundamental reason for the exception. Your suggested wording and the use of the term "generally" implies that if the compiler were smart enough it could determine whether the value was the same, but I think that is mistaken for a limited type. Even if the bits are identical, and nothing was done to the object between the two calls, you still can't legitimately consider it to have the same value if it is limited. **************************************************************** From: Robert Dewar Sent: Thursday, May 6, 2010 9:57 AM OK, but lets try to find a simple way of describing this, I hate to have yet a third notion of limitedness :-( **************************************************************** From: Robert Dewar Sent: Thursday, May 6, 2010 9:59 AM A suggestion Ed made in our internal discussion, is that the exception should depend on the limitedness of the type (not immutable limitedness) at the point of the subprogram declaration, on the grounds that the client should know if the exception applies. Interestingly this would have (accidentally) helped out our customer, who had pure routines that did I/O and was upset that calls got eliminated (!) But the calls in question had a limited parameter (by accident I think), but the full declaration was non-limited. **************************************************************** From: Bob Duff Sent: Thursday, May 6, 2010 10:06 AM > I don't think "immutably" is quite right. > What matters is whether the type is limited "deep down." We used to use the term "inherently limited" during the Ada 9X project, but we didn't put it in the RM. I think it means the same as "deep down limited". Also synonymous with "limited and never becomes nonlimited". I thought "immutably limited" was a synonym for what we used to call "inherently limited". This was added to the RM post-2005. Am I wrong? How do these two terms differ? GNAT has a function Is_Inherently_Limited(Node_Id)-->Boolean, which I named before the term "immutably limited" was invented. Today I suggested it should be changed to Is_Immutably_Limited, but now I think you're telling me that's wrong. (Is_Inherently_Limited was called Is_Return_By_Reference previously; I changed it because that's an Ada 95 term.) **************************************************************** From: Tucker Taft Sent: Thursday, May 6, 2010 10:32 AM If you look up immutably limited, it is strictly a static semantics property. It doesn't include record types that are limited only because they include a task, for example, unless they explicitly say "limited record." They don't include limited private non-tagged types, unless they have an access discriminant with a default, etc. I agree something like "inherently limited" is what we want, but "the full type is limited" is close enough, and probably a bit better in some ways, since it reduces how far the user needs to look to decide whether the special case applies. **************************************************************** From: Bob Duff Sent: Thursday, May 6, 2010 10:10 AM > A suggestion Ed made in our internal discussion, is that the exception > should depend on the limitedness of the type (not immutable > limitedness) at the point of the subprogram declaration, on the > grounds that the client should know if the exception applies. Well, maybe, but you could use the same argument about lot of things. For ex., you can't tell whether a type is by-copy or by-reference without peeking at the private part. You can't tell whether "X : Set;" initializes X to the empty set without peeking. (Or trusting comments.) **************************************************************** From: Robert Dewar Sent: Thursday, May 6, 2010 10:14 AM > Well, maybe, but you could use the same argument about lot of things. > For ex., you can't tell whether a type is by-copy or by-reference > without peeking at the private part. You can't tell whether "X : > Set;" initializes X to the empty set without peeking. (Or trusting > comments.) True, so that's not a consideration in this case **************************************************************** From: Tucker Taft Sent: Thursday, May 6, 2010 10:06 AM I think the wording is adequate as is, since it is clearly talking about dynamic semantics, where we are really talking about the deep-down properties. We could throw in "full type is limited" to be clearer. **************************************************************** From: Robert Dewar Sent: Thursday, May 6, 2010 10:13 AM > I think the wording is adequate as is, since it is clearly talking > about dynamic semantics, where we are really talking about the > deep-down properties. We could throw in "full type is limited" to be > clearer. I think "full type is limited" is fine Note that this means the client does not know if the exception applies, but I think that's OK. The client should expect calls to be eliminated if the arguments are the same, we are just pointing out a case where they might not be the same even though they look like they are, but it would be wrong for a client to rely on this in the way our customer is doing for instance. **************************************************************** From: Tucker Taft Sent: Thursday, May 6, 2010 10:15 AM Interesting. It is certainly useful to understand the user problem that led to reviewing this wording. Making it depend on the limitedness at the point of the declaration makes sense, I guess. I agree that it shouldn't vary based on the visibility at the point of call, and this approach satisfies that goal. **************************************************************** From: Bob Duff Sent: Thursday, May 6, 2010 10:32 AM Customer wrote a subprogram that reads from a file, and declared that it's pure. We have no idea why (and probably they don't either -- maybe the person who wrote it is gone). The subprogram has a parameter of a limited private type whose full type is nonlimited. We have no idea whether this was a deliberate attempt to invoke the "don't optimize if limited" rule we are discussing. They were surprised that the second call didn't read the second item from the file, but instead reused the first one. But only at high optimization level. After much going round and round, Robert told them they have to fix their code. **************************************************************** From: Robert Dewar Sent: Thursday, May 6, 2010 11:01 AM The user problem was that they had pure subprograms that did I/O, and were upset that calls got eliminated. Through what I am absolutely *sure* was an accident, the subprograms in question have a parameter of a type that is limited, whose full declaration is not limited. Now GNAT was not paying attention to this restriction at all, so we implemented the restriction, thinking that perhaps it would serendipitously make the (really evidently wrong) code of the customer work by accident. The customer for sure new nothing about this rule! So we implemented it, but in a way that depended on the limitedness of the full type, and that of course did not help the customer. But really the customer code is just wrong here, I don't think it should figure into our discussion. **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, May 6, 2010 12:07 PM > Customer wrote a subprogram that reads from a file, and declared that > it's pure. But IO packages are not pure. They must have interfaced, or gone out of the language at some point? **************************************************************** From: Bob Duff Sent: Thursday, May 6, 2010 12:14 PM Right, I forget the details, but it was some sort of roll-your-own I/O. **************************************************************** From: Robert Dewar Sent: Thursday, May 6, 2010 12:23 PM yes of course! **************************************************************** From: Randy Brukardt Sent: Thursday, May 6, 2010 3:23 PM Presuming they used pragma Import (which seems highy likely), their code is erroneous by B.1(38.1/2) -- as I/O code (which is surely not pure) called from a pure package surely "violates Ada semantics" (in this case the restriction to Pure). So your compiler would be justified in generating any code whatsoever, including calculating PI to 100,000 digits. :-) **************************************************************** From: Robert Dewar Sent: Friday, May 7, 2010 8:11 AM Yes, of course we know this, but that's not useful in dealing with customers, we told them they had three reasonable alternatives Turn off optimization Make the type really limited Stop declaring them pure Clearly the third is best But the point in the context of this discussion is that this was NOT a legitimate use of the exception, so it should be ignored. **************************************************************** From: Gary Dismukes Sent: Friday, May 7, 2010 12:26 PM > I agree something like "inherently limited" > is what we want, but "the full type is limited" > is close enough, and probably a bit better in some ways, since it > reduces how far the user needs to look to decide whether the special > case applies. Rather than just "the full type is limited", I think it would be better to parallel the wording used to define the requirement for build-in-place in 7.6(17.2/3): 17.2/3 If the full type of any part of the object is immutably limited, the anonymous object is built in place. ****************************************************************