!standard 4.9(24/5) 20-09-10 AI12-0393-1/02 !class binding interpretation 20-09-03 !status Amendment 1-2012 20-09-10 !status ARG Approved 14-0-0 20-09-09 !status work item 20-09-03 !status received 20-07-06 !priority Low !difficulty Easy !qualifier Omission !subject No invalid static constants !summary A constant is not static if its initialization fails due to a constraint or predicate check failure. !question What happens if the initialization of a constant with a static expression fails because the value violates a constraint or predicate of the object. Is the object still static? (No.) !recommendation We addressed part of this question in AI12-0108-1, to the extent of deciding on No-Action, but agreeing the issue is a pathology. This issue has recurred in the context of a static predicate, and it seems worth re-opening, and coming to the conclusion that such a constant is not static. However, we plan to retain this as a pathology, since there was an ACATS test (ACATS test B490001) which required allowing the declaration of such a constant object, which forced implementers to make a choice about this question. !wording Modify 4.9(24/5): A static constant is a constant view declared by a full constant declaration or an object_renaming_declaration with a static nominal subtype, having a value defined by a static scalar expression or by a static string expression {, and which satisfies any constraint or predicate that applies to the nominal subtype}. !discussion The main issue justifying re-opening this issue is that in various places in the RM we presume that a static constant satisfies its constraints and predicates, and can safely be used in places that require static expressions. Once you break that presumption, you might have to review many parts of the RM to see if you end up with contradictory or meaningless statements. Hence, it seems simplest to say that a constant can be static only if satisfies its constraints or its predicates. To aid implementors, we are promising not to test this, as the RM was ambiguous before, and there was an ACATS test that required allowing such a declaration and thus required making a choice as to whether or not the object was static. A compiler survey of Ada 95 compiler reported in AI12-0108-1 noted that implementers were almost evenly split on the question of whether this object is static, as one might expect when the RM gives no answer to the question. Moreover, as the ACATS test has been in the test suite since 1996, this split has not produced significant portability problems in practice. Additionally, this fix would be incompatible for existing implementations that treated the constant as static (making previously legal code illegal, even if it never is executed), potentially impacting their customers. Such code is very possible in conditionally compiled code; enforcement of legality rules on static expressions enforced on such code is a known issue with Ada. Finally, in most cases, the original "static" object that did not satisfy a constraint or predicate would raise an exception at run time, so code using it would never be executed, and thus how it could be used is not relevant. For all of these reasons, requiring implementers to expend effort on fixing this seems counter-productive. !corrigendum 4.9(24/5) @drepl A @i is a constant view declared by a full constant declaration or an @fa with a static nominal subtype, having a value defined by a static scalar expression or by a static string expression. @dby A @i is a constant view declared by a full constant declaration or an @fa with a static nominal subtype, having a value defined by a static scalar expression or by a static string expression, and which satisfies any constraint or predicate that applies to the nominal subtype. !ASIS No ASIS effect. !ACATS test No ACATS test should be created for this case. !appendix From: Steve Baird Sent: Monday, July 6, 2020 6:56 PM In AI12-0108 there is discussion of a problem illustrated by the following example: package Range_Check is subtype S is Integer range 1 .. 9; X : constant S := 10; end Range_Check; This was an unusual AI in which the ARG confirmed the legality of the above example but explicitly declined to decide whether X is a static constant. So the legality of with Range_Check; package Range_Check_Client is Named_Number : constant := Range_Check.X; end Range_Check_Client; was left up in the air. There were good reasons for this which can be found in the AI, but this is still not a very satisfactory situation. Now a related question involving static predicates has now come up and it may be time to revisit this question. Consider this example: package Predicate_Check is pragma Assertion_Policy (...); subtype S is Integer with Static_Predicate => S in 1 .. 10; X : constant S := 11; end Predicate_Check; with Predicate_Check; package Predicate_Check_Client is Named_Number : constant := Predicate_Check.X; end Predicate_Check_Client; It is a similar situation, with the interesting difference that the first package will elaborate successfully if the specified Assertion_Policy is Ignore (and assuming that the first package declaration is legal). [One point that everyone agrees on is that for each of these two packages, we want the unit's legality to be unaffected by changing the argument of the Assertion_Policy pragma from Ignore to Check or vice versa.] After discussing this with Randy, Tuck, and others, the following proposal emerged: 1) Explicitly clarify (in a way that is not done in AI12-0108) that declaring a constant of a static subtype whose value is a static expression that does not belong to that subtype is legal, but does not declare a static constant. In the four example units given above, this means that the two declarations named X are legal and the two named number declarations are illegal. 2) Affirm that this situation is viewed as a pathology and that, in particular, no tests will be written which would fail if an implementation chose (incorrectly) to treat such a constant declaration as a static constant. 3) Clarify that this resolution of the issue applies in the same way to both range checks and predicate checks, and that enabling or disabling a predicate (e.g., via an Assertion_Policy pragma) has no effect on a unit's legality. Opinions? **************************************************************** From: Tucker Taft Sent: Monday, July 6, 2020 8:31 PM After our various discussions, this emerged as the only approach that seemed to work without undue complexities. **************************************************************** From: Arnaud Charlet Sent: Monday, July 6, 2020 11:23 PM Can someone clarify what are the alternatives and what are the undue complexities involved? **************************************************************** From: Tucker Taft Sent: Tuesday, July 7, 2020 7:30 AM The main issue I would say is that in various places in the RM we presume that a static constant satisfies its constraints and predicates, and can safely be used in places that require static expressions. Once you break that presumption, you might have to review many parts of the RM to see if you end up with contradictory or meaningless statements. Hence, it seems simplest to say that a constant can be static only if satisfies its constraints or its predicates. To aid implementors, we are promising not to test this, as we believe it is a bit of a corner case, and the RM was ambiguous before. **************************************************************** From: Arnaud Charlet Sent: Tuesday, July 7, 2020 8:25 AM OK thanks for the details. FWIW this seems to match GNAT's behavior so that's good: 1. package Static is 2. 3. subtype S is Integer range 1 .. 9; 4. X : constant S := 10; | >>> warning: value not in range of type "S" defined at line 3 >>> warning: "Constraint_Error" will be raised at run time 5. 6. Named_Number : constant := X; | >>> non-static expression used in number declaration >>> expression raises exception, cannot be static (RM 4.9(34)) 7. 8. end Static; 1. package Predicate_Check is 2. pragma Assertion_Policy (...); 3. subtype S is Integer with Static_Predicate => S in 1 .. 10; 4. X : constant S := 11; | >>> warning: static expression fails static predicate check on "S" >>> warning: expression is no longer considered static 5. 6. Named_Number : constant := X; | >>> non-static expression used in number declaration >>> "X" is not a static constant (RM 4.9(5)) 7. 8. end Predicate_Check; **************************************************************** From: Tucker Taft Sent: Tuesday, July 7, 2020 11:26 AM Good to hear. **************************************************************** From: Randy Brukardt Sent: Tuesday, July 7, 2020 12:48 PM > OK thanks for the details. > > FWIW this seems to match GNAT's behavior so that's good: Keep in mind that this is an Ada 95 issue at its heart, so we have to consider the behavior of all Ada 95 compilers, not just GNAT. That various wildly, based in the survey done for AI12-0108-1. (I'd suggest reading that AI for more details.) Moreover, there is an ACATS test requiring part of this behavior. Legality Rules based on values are always dubious: they cause problems in conditionally compiled code as well as portability problems. Sometimes that cannot be helped, but given that static values really don't have ranges anyway, range checks are particularly dubious for static expressions. The most compatible choice would have been to follow AdaMagic's lead and leave the object static, but that would have required extra work for a number of compilers, including GNAT. Therefore, we decided to treat it as a pathology since it didn't seem important which choice was made (in almost all cases, the original "static" object would raise an exception at runtime, so how it would get used further is not relevant and such code would never be executed anyway). It seems reasonable to continue this practical solution, but Steve's example seems to make it necessary for the language proper to define the correct behavior (for the reasons Tucker noted). So we're proposing fixing the wording but leaving the AI as a pathology to tell the ACATS people not to test the ramifications. **************************************************************** Editor's note (9/10/20): I was asked to check the ACATS test that is related to this test. The named test (C490001) does not appear to have anything to do with static expressions. That test was named in AI12-0108-1 and was not checked when this AI was created. After investigation, it appears that the declarations SubInt1 : constant My_Int_Sub := -128; -- OK. -- But raises Constraint_Error at run-time. SubInt3 : constant My_Int_Sub := 115; -- OK. -- But raises Constraint_Error at run-time. on lines 321 through 325 of test B490001 is what is referred to in this AI. Note that these declarations are never used later in the test so whether or not they are static is not determined by the test. Thus it does not require a particular choice to the question, but rather it requires implementers to decide the question on their own. A compiler survey of Ada 95 compilers reported in AI12-0108-1 showed that implementers split almost evenly on the answer to the question. The AI text was updated to reflect this. ****************************************************************