!standard 11.3(4/2) 13-01-30 AI12-0054-1/04 !standard 13.9.2(2) !standard 13.9.2(3/3) !class binding interpretation !status Amendment 202x 13-01-30 !status work item 12-12-08 !status received 12-12-07 !priority High !difficulty Medium !qualifier Error !subject A raise_expression does not cause membership failure !summary Consider: subtype S is Integer with Dynamic_Predicate => Is_Gnarly(S) or else raise Program_Error; then a membership test "X in S" does not raise Program_Error just because Is_Gnarly(X) returns False. Similarly, "X'Valid" does not raise Program_Error just because Is_Gnarly(X) returns False. !question AI12-0022-1, "Raise expressions for specifying the exception raised for an assertion" added the ability to control which exception is raised by failure of various assertions. This is an important capability, because it allows to change interfaces to use assertions while preserving compatibility. However, this feature doesn't work properly for predicates, because predicates are evaluated by membership tests, so we would get spurious failures. Should these spurious failures be removed from the language, so that raise_expressions can be used in predicates? (Yes.) !recommendation See !summary. !wording 11.3(4/2) was amended by AI12-0022-1 as follows: ... For the execution of a raise_statement with an exception_name, the named exception is raised. {Similarly, for the evaluation of a raise_expression, the named exception is raised.} [{In both of these cases, if}[If] a string_expression is present, the expression is evaluated and its value is associated with the exception occurrence.] ... Add a new paragraph after that: There is one exception to the above rule: For an individual membership test (see 4.5.2) of the form "X in S", if a raise_expression @Redundant[statically] within the predicate of S is evaluated, then the exception is not raised; instead, the entire predicate immediately evaluates to False. This includes raise_expressions @Redundant[statically] within the default_expression for a formal parameter used in a call @Redundant[statically] within the predicate. AARM Discussion: But it does NOT include raise_expressions within the default_expression for a component; those actually raise the exception. Modify 13.9.2(2-3/3) so it refers to the semantics of "in" rather than directly to the semantics of the predicate, so the newly-modified semantics of "in" automatically work for 'Valid: 2 For a prefix X that denotes a scalar object Redundant[(after any implicit dereference)]{of nominal subtype S}, the following attribute is defined: 3/3 X'Valid Yields True if and only if the object denoted by X is normal, has a valid representation, and the [predicate of the nominal subtype of X]{the membership test "X in S"} evaluates to True. The value of this attribute is of the predefined type Boolean. !discussion Note that "within" in the new wording means statically/textual containment as usual. We're not talking about anything dynamic. But we do include formal parameter defaults. The purpose of a raise_expression in an assertion is to specify which exception is raised on failure. For example: subtype S is Integer with Dynamic_Predicate => Is_Round(S) or else raise Program_Error; is not really saying that Program_Error should be raised. It is saying that Is_Round should be True for all objects of subtype S (and, oh by the way, if it's not, then Program_Error should be raised instead of the usual exception). That works just fine for parameter passing and the like: procedure P(X: S); ... P(X); If Is_Round(X) is False on the call, Program_Error is raised as expected. But if we have: Some_Integer : Integer := ...; ... if Some_Integer in S then ... we don't want to raise Program_Error. We want the 'if' condition to return True or False according to whether Is_Round(X) is True. Therefore, we change the rule so that "Is_Round(S) or else raise Program_Error" is evaluated as "Is_Round(S) or else predicate-is-False" -- but only for membership tests. Note that this special case for raise_expressions in membership tests does not apply to any other ways of raising an exception. For example, if Is_Round(X) propagates an exception, then the membership test propagates that exception occurrence. This unfortunately means you cannot in general make use of abstraction by moving an (entire) existing predicate into a function body (or expression function); to make that transformation you need to leave the "raise" part behind. The following example shows how to raise a particular exception for a particular "reason" the predicate fails. If the evaluation of "X in T" evaluates one of the raise_exceptions, then "X in T" will be True. type T is ... with Predicate => (T >= First_Allowed or else raise Too_Small) and then (T <= Last_Allowed or else raise Too_Big) and then (Is_Good(T) or else raise Badness); Rationale for the default_expression business: A defaulted parameter really should be strongly equivalent to an explicit parameter. Switching from one to the other should not cause subtle changes in run-time semantics. The component case is less clear. We choose to treat those in the opposite way (the exception is raised) because otherwise there would be an implementation difficulty for implementations that choose to wrap default initializations in an initialization procedure (whether or not inlined), because an extra "we're in an 'in'" flag would need to be passed. Intended implementation: If the implementation chooses to generate code for predicates inline at the places where they are used, then it would be appropriate to replace "raise ..." with a jump to "return False from the predicate" whenever the predicate is being evaluated as part of a membership test. If an implementation uses an out-of-line function to evaluate the predicate, it can pass an extra "In_Membership" flag, or it can generate two copies of the function. Either way, the raise isn't replaced by False -- it is replaced by a jump to a place that returns False for the whole predicate. The above type T with mixed "and then" and "or else" shows why. An implementation that wraps the predicate with an exception handler like "when others => False" would be incorrect. An implementation that turns "raise ..." into "False" would also be wrong. !corrigendum 11.3(4/2) @drepl To @i is to raise a new occurrence of that exception, as explained in 11.4. For the execution of a @fa with an @i@fa, the named exception is raised. If a @i@fa is present, the @fa is evaluated and its value is associated with the exception occurrence. For the execution of a re-raise statement, the exception occurrence that caused transfer of control to the innermost enclosing handler is raised again. @dby To @i is to raise a new occurrence of that exception, as explained in 11.4. For the execution of a @fa with an @i@fa, the named exception is raised. Similarly, for the evaluation of a @fa, the named exception is raised. In both of these cases, if a @i@fa is present, the @fa is evaluated and its value is associated with the exception occurrence. For the execution of a re-raise statement, the exception occurrence that caused transfer of control to the innermost enclosing handler is raised again. There is one exception to the above rule: For an individual membership test (see 4.5.2) of the form "X @b S", if a @fa statically within the predicate of S is evaluated, then the exception is not raised; instead, the entire predicate immediately evaluates to False. This includes @fas statically within the @fa for a formal parameter used in a call statically within the predicate. !corrigendum 13.9.2(2) @drepl For a @fa X that denotes a scalar object (after any implicit dereference), the following attribute is defined: @dby For a @fa X that denotes a scalar object (after any implicit dereference) of nominal subtype @i, the following attribute is defined: !corrigendum 13.9.2(3/3) @drepl @xhang<@xtermYields True if and only if the object denoted by X is normal, has a valid representation, and the predicate of the nominal subtype of X evaluates to True. The value of this attribute is of the predefined type Boolean.> @dby @xhang<@xtermYields True if and only if the object denoted by X is normal, has a valid representation, and the membership test "X @b @i" evaluates to True. The value of this attribute is of the predefined type Boolean.> !ACATS Test One or more ACATS C-Tests will be needed to test this semantics (and to ensure that neither of the "wrong" implementations are used). !ASIS No effect (AI12-0022-1 will take care of any issues). !appendix This AI was created out of a problem noted during discussion on AI12-0037-1 during ARG meeting #48 in Boston. **************************************************************** From: Tucker Taft Sent: Saturday, December 8, 2012 12:06 PM I think a raise expression in a predicate should yield False only if it has a boolean expected type. **************************************************************** From: Randy Brukardt Sent: Thursday, January 24, 2013 4:39 PM ... > OK, I've included a new version below. Thanks. One comment: ... > !wording > > 11.3(4/2) was amended by AI12-0022-1 as follows: > > ... For the execution of a raise_statement with an exception_name, the > named exception is raised. {Similarly, for the evaluation of a > raise_expression, the named exception is raised.} [{In both of these > cases, if}[If] a string_expression is present, the expression is > evaluated and its value is associated with the exception occurrence.] > ... > > Add a new paragraph after that: > > There is one exception to the above rule: For an individual membership > test (see 4.5.2) of the form "X in S", if a raise_expression within > the predicate of S is evaluated, then the exception is not raised; > instead, the entire predicate immediately evaluates to False. This > includes raise_expressions within the default_expression for a formal > parameter. Later you informally explain that "within" means textually within (with the inclusion of default parameters of calls). However, every time I read this normative wording, I read it as "anywhere within", and I know that's not what we mean. (After all, this is dynamic semantics, so usually textual considerations don't apply.) I think we need some sort of qualifier on "within" to make it clearer that this is textual, not dynamic. Perhaps something like: There is one exception to the above rule: For an individual membership test (see 4.5.2) of the form "X in S", if a raise_expression immediately within the predicate of S is evaluated, then the exception is not raised; instead, the entire predicate immediately evaluates to False. For the purpose of this exception, raise_expressions within the default_expression for a formal parameter used in a call immediately within the predicate are considered immediately within the predicate. [There's too many words in this last sentence, but you get the idea.] Or we could use "textually" rather than "immediately" here. The rest of the AI looks good. **************************************************************** From: Tucker Taft Sent: Thursday, January 24, 2013 4:57 PM > ... Later you informally explain that "within" means textually within > (with the inclusion of default parameters of calls). However, every > time I read this normative wording, I read it as "anywhere within", > and I know that's not what we mean. (After all, this is dynamic > semantics, so usually textual considerations don't apply.) I think we > need some sort of qualifier on "within" to make it clearer that this is > textual, not dynamic. I would vote for "textually within" or "statically within" (favoring the latter). **************************************************************** From: Bob Duff Sent: Thursday, January 24, 2013 5:07 PM > Later you informally explain that "within" means textually within > (with the inclusion of default parameters of calls). However, every > time I read this normative wording, I read it as "anywhere within", > and I know that's not what we mean. (After all, this is dynamic > semantics, so usually textual considerations don't apply.) I think we > need some sort of qualifier on "within" to make it clearer that this is > textual, not dynamic. Well, I think we often (always?) use "within" in the textual sense in the RM. > Perhaps something like: > > There is one exception to the above rule: For an individual membership > test (see 4.5.2) of the form "X in S", if a raise_expression > immediately within the predicate of S is evaluated, then the exception > is not raised; instead, the entire predicate immediately evaluates to > False. For the purpose of this exception, raise_expressions within the > default_expression for a formal parameter used in a call immediately > within the predicate are considered immediately within the predicate. I think "immediately within" could be confusing, because that's a formal term defined in Chap 8, and it refers to declarative regions. (It's definitely textual.) > [There's too many words in this last sentence, but you get the idea.] Yes, too many words, I think. > Or we could use "textually" rather than "immediately" here. I like that better. I suggest using my paragraph as is, except with the two occurrences of "within" replaced by "Redundant[textually] within". I suggest you commit my changes as is, and then commit another version with "textually" added twice, so people can see the diff's. > The rest of the AI looks good. OK, thanks, maybe we can vote on this soon. **************************************************************** From: Bob Duff Sent: Thursday, January 24, 2013 5:08 PM > I would vote for "textually within" or "statically within" > (favoring the latter). Either one is OK with me, but I think it should be @Redundant. **************************************************************** From: Randy Brukardt Sent: Thursday, January 24, 2013 5:34 PM > > Later you informally explain that "within" means textually within > > (with the inclusion of default parameters of calls). However, every > > time I read this normative wording, I read it as "anywhere within", > > and I know that's not what we mean. (After all, this is dynamic > > semantics, so usually textual considerations don't apply.) I think we > > need some sort of qualifier on "within" to make it clearer > > that this is textual, not dynamic. > > Well, I think we often (always?) use "within" in the textual sense in > the RM. I view it as ambiguous; I'm likely to use "within" as a short-hand for "during the evaluation of" when discussing dynamic semantics. Whether that's made it into the normative wording, I don't know. > > Perhaps something like: > > > > There is one exception to the above rule: For an individual > > membership test (see 4.5.2) of the form "X in S", if a > > raise_expression immediately within the predicate of S is evaluated, > > then the exception is not raised; instead, the entire predicate > > immediately evaluates to False. For the purpose of this exception, > > raise_expressions within the default_expression for a formal > > parameter used in a call immediately within the predicate are considered > > immediately within the predicate. > > I think "immediately within" could be confusing, because that's a > formal term defined in Chap 8, and it refers to declarative regions. > (It's definitely textual.) Yes, that could be true. > > [There's too many words in this last sentence, but you get > the idea.] > > Yes, too many words, I think. > > > Or we could use "textually" rather than "immediately" here. > > I like that better. I suggest using my paragraph as is, except with > the two occurrences of "within" replaced by "Redundant[textually] > within". I don't like the last sentence of your original wording much, because I wondered where the heck a "formal parameter" occurs in a predicate expression (there are only actual parameters in such an expression - someone might think you are talking about the current instance as if it is a parameter, but that's not the intent). Moreover, the statement is ambiguous, because you only mean to include default expressions that are used in calls that are "statically within" or "textually within" or whatever term you use the predicate expression -- it surely doesn't apply to some random default expression that occurs inside of some called function. Finally, if you are formally talking about something that occurs "textually within", by no stretch of the imagination can that apply to a default expression. Thus I wanted to make these points clear (I know I overdid it, I was hoping for a bit of wordsmithing, not outright rejection). The only reason this made any sense to me at all is because I know what rule you are trying to encapsulate -- I'm not sure how Adam (for example) was going to figure that out. The idea I had was to "simply" say that a default_expression is considered statically within the predicate expression, but it didn't work out so simple when I made it crystal clear. I'm sure the appropriate wording is somewhere between our two proposals. > I suggest you commit my changes as is, and then commit another version > with "textually" added twice, so people can see the diff's. OK, although I would prefer a clearer version of the last sentence. > > The rest of the AI looks good. > > OK, thanks, maybe we can vote on this soon. Hope so too. **************************************************************** From: Jeff Cousins Sent: Monday, January 28, 2013 8:08 AM I think Randy's point about clarifying what is meant by "formal parameter" is more important than what (if any) word precedes "within". **************************************************************** From: Bob Duff Sent: Monday, January 28, 2013 10:32 AM > I think Randy's point about clarifying what is meant by "formal > parameter" is more important than what (if any) word precedes "within". Well, I still think my suggestion is best. I can't imagine how "statically within" could be confused to mean dynamically. If folks don't like it, then please propose an alternative. Randy comments: > I don't like the last sentence of your original wording much, because > I wondered where the heck a "formal parameter" occurs in a predicate > expression ... But it doesn't say that. It's clearly (to me ;-)) talking about a default expression FOR a formal parameter. It's the default expression that is "within...". (Of course it's only implicitly there -- that's what defaults do.) I'm ready to vote on the AI, with "statically within". If others are not, we need alternative wording. **************************************************************** From: Jeff Cousins Sent: Monday, January 28, 2013 10:44 AM Extend "This includes raise_expressions within the default_expression for a formal parameter." to "This includes raise_expressions within the default_expression for a formal parameter used in a call within the predicate." **************************************************************** From: Robert Dewar Sent: Monday, January 28, 2013 10:47 AM > Extend "This includes raise_expressions within the default_expression > for a formal parameter." to "This includes raise_expressions within > the default_expression for a formal parameter used in a call within > the predicate." I am fine with any of the wording suggestions here, the intent is obvious (I just finished implementing this feature :-)) **************************************************************** From: Bob Duff Sent: Monday, January 28, 2013 11:18 AM > Extend "This includes raise_expressions within the default_expression > for a formal parameter." to "This includes raise_expressions within > the default_expression for a formal parameter used in a call within > the predicate." Looks good to me. **************************************************************** From: Randy Brukardt Sent: Monday, January 28, 2013 7:29 PM "{statically} within the predicate". Thanks Jeff for making this suggestion as it avoids the need for me to invoke "Editor's discretion" to fix this sentence, as I found the original unacceptable. (That's a power I'd rather avoid using very often.) ****************************************************************