!standard 3.2.4(4/3) 13-12-13 AI12-0071-1/05 !standard 3.2.4(6/3) !standard 3.2.4(30/3) !standard 3.2.4(31/3) !standard 3.2.4(32/3) !standard 3.2.4(33/3) !standard 3.2.4(35/3) !standard 3.5.5(7.1/3) !standard 3.5.5(7.2/3) !standard 3.5.5(7.3/3) !standard 3.8.1(10.1/3) !standard 3.8.1(15/3) !standard 4.5.2(29/3) !standard 4.5.2(30/3) !standard 4.6(51/3) !standard 4.9.1(10/3) !standard 5.4(7/3) !standard 5.5(9/3) !standard 13.9.2(3/3) !standard 13.9.2(12) !class binding interpretation 13-05-31 !status Corrigendum 2014 13-12-13 !status WG9 Approved 14-06-27 !status ARG Approved 11-0-0 13-11-16 !status work item 13-05-31 !status received 13-02-12 !priority High !difficulty Medium !qualifier Omission !subject Order of evaluation when multiple predicates apply !summary If multiple predicates apply to a subtype when a check is required, then after checking the constraints and exclusions, the predicates are checked in an order that ensures that a predicate is not evaluated until any prior predicates evaluate to True. The order is unspecified for predicates that are defined or inherited as part of a single declaration. The phrase "satisfies the predicates of a subtype" means that all relevant predicates evaluate to True. !question 3.2.4(6/3) says: The predicate of a subtype consists of all predicate specifications that apply, and-ed together; ... and then we just talk about the evaluation of the predicate. Should we define the order in which this evaluation is performed? (Yes.) !recommendation (See !summary.) !wording Modify 3.2.4(4/3): For a (first) subtype defined by a [derived type declaration] {type_declaration} [Editor's note: We need "type_declaration" so that task and protected type declarations (which can have progenitors) are covered.] [Author's Note (Baird): I believe there is no problem here with anonymous subtypes, even though an anonymous task/protected type can have progenitors that have predicates.] Delete 3.2.4(6/3) (see dynamic semantics), which says the following: The predicate of a subtype consists of all predicate specifications that apply, and-ed together; if no predicate specifications apply, the predicate is True [(in particular, the predicate of a base subtype is True)]. Modify AARM 3.2.4(14.b/3) "cam" => "can". Replace the dynamic semantics section 3.2.4(30/3-33/3) with: If any of the above Legality Rules is violated in an instance of a generic unit, Program_Error is raised at the point of the violation. To determine whether a value *satisfies the predicates* of a subtype S, the following tests are performed in the following order, until one of the tests fails, in which case the predicates are not satisfied and no further tests are performed, or all of the tests succeed, in which case the predicates are satisfied: * the value is first tested to determine whether it satisfies any constraints or any null exclusion of S; * then: + if S is a first subtype, the value is tested to determine whether it satisfies the predicates of the parent and progenitor subtypes (if any) of S (in an arbitrary order); + if S is defined by a subtype_indication, the value is tested to determine whether it satisfies the predicates of the subtype denoted by the subtype_mark of the subtype_indication; * finally, if S is defined by a declaration to which one or more predicate specifications apply, the predicates are evaluated (in an arbitrary order) to test that all of them yield True for the given value. AARM Discussion: It is important to stop on the first of the above steps that fails, as later steps might presume that the earlier steps had succeeded. If predicate checks are enabled for a given subtype, then: * On every subtype conversion, a check is performed that the operand satisfies the predicates of the target subtype. This includes all parameter passing, except for certain parameters passed by reference, which are covered by the following rule: After normal completion and leaving of a subprogram, for each in out or out parameter that is passed by reference, a check is performed that the value of the parameter satisfies the predicates of the subtype of the actual. For an object created by an object_declaration with no explicit initialization expression, or by an uninitialized allocator, if any subcomponents have default_expressions, a check is performed that the value of the created object satisfies the predicates of the nominal subtype. * If any of the predicate checks fail, Assertion_Error is raised, unless the subtype whose directly-specified predicate aspect evaluated to False also has a directly-specified Predicate_Failure aspect. In that case, the specified Predicate_Failure expression is evaluated; if the evaluation of the Predicate_Failure expression propagates an exception occurrence, then this occurrence is propagated for the failure of the predicate check; otherwise, Assertion_Error is raised, with an associated message string defined by the value of the Predicate_Failure expression. In the absence of such a Predicate_Failure aspect, an implementation-defined message string is associated with the Assertion_Error exception. [Note: The above wording assumes AI12-0054-2 is part of the Standard.] Add a NOTE after 3.2.4(35/3): No predicates apply to the base subtype of a scalar type; every value of a scalar type T is considered to satisfy the predicates of T'Base. Modify 3.5.5(7.1/3-7.3/3) as follows: For every static discrete subtype S for which there exists at least one value belonging to S that satisfies [any] {the} predicate{s} of S, the following attributes are defined: S'First_Valid S'First_Valid denotes the smallest value that belongs to S and satisfies the predicate{s} of S. The value of this attribute is of the type of S. S'Last_Valid S'Last_Valid denotes the largest value that belongs to S and satisfies the predicate{s} of S. The value of this attribute is of the type of S. Modify 3.8.1(10.1/3) as follows: * A discrete_choice that is a subtype_indication covers all values (possibly none) that belong to the subtype and that satisfy the static predicate{s} of the subtype (see 3.2.4). Modify 3.8.1(15/3) as follows: * If the discriminant is of a static constrained scalar subtype then, except within an instance of a generic unit, each non-others discrete_choice shall cover only values in that subtype that satisfy its predicate{s}, and each value of that subtype that satisfies its predicate{s} shall be covered by some discrete_choice (either explicitly or by others); Modify 4.5.2(29/3-30-3) as follows: The membership_choice is a subtype_mark, the tested type is scalar, the value of the simple_expression belongs to the range of the named subtype, and the {value satisfies the} predicate{s} of the named subtype [evaluates to True]. The membership_choice is a subtype_mark, the tested type is not scalar, the value of the simple_expression satisfies any constraints of the named subtype, the {value satisfies the} predicate{s} of the named subtype [evaluates to True], and: Modify 4.6(51/3) as follows: After conversion of the value to the target type, if the target subtype is constrained, a check is performed that the value satisfies this constraint. If the target subtype excludes null, then a check is made that the value is not null. If predicate checks are enabled for the target subtype (see 3.2.4), a check is performed that the {value satisfies the} predicate{s} of the target subtype [is satisfied for the value]. Modify 4.9.1(10/3): * both subtypes are static, every value that satisfies the predicate{s} of S1 also satisfies the predicate{s} of S2, and it is not the case that both types each have at least one applicable predicate specification, predicate checks are enabled (see 11.4.2) for S2, and predicate checks are not enabled for S1. Modify 5.4(7/3) as follows: * If the selecting_expression is a name (including a type_conversion, qualified_expression, or function_call) having a static and constrained nominal subtype, then each non-others discrete_choice shall cover only values in that subtype that satisfy its predicate{s} (see 3.2.4), and each value of that subtype that satisfies its predicate{s} shall be covered by some discrete_choice (either explicitly or by others). Modify 5.5(9/3) as follows: ... Otherwise, the sequence_of_statements is executed once for each value of the discrete subtype defined by the discrete_subtype_ definition that satisfies the predicate{s} of the subtype (or until the loop is left as a consequence of a transfer of control). ... In 13.9.2(3/3), X'Valid Yields True if and only if the object denoted by X is normal and has a valid representation, and {then, if the preceding conditions hold, the value of X also satisfies} the predicate{s} of the nominal subtype of X [evaluates to True]. In 13.9.2(12), replace: X'Valid is not considered to be a read of X; hence, it is not an error to check the validity of invalid data. with: Determining whether X is normal and has a valid representation as part of the evaluation of X'Valid is not considered to include an evaluation of X; hence, it is not an error to check the validity of an object that is invalid or abnormal. [Determining whether X satisfies the predicates of its nominal subtype may include an evaluation of X, but only after it has been determined that X has a valid representation.] If X is volatile, the evaluation of X'Valid is considered a read of X. Modify K.2(73.1/3-73.2/3), as follows: S'First_Valid For every static discrete subtype S for which there exists at least one value belonging to S that satisfies [any] {the} predicate{s} of S: S'First_Valid denotes the smallest value that belongs to S and satisfies the predicate{s} of S. The value of this attribute is of the type of S. See 3.5.5. Modify K.2(107.1/3-107.2/3), as follows: S'Last_Valid For every static discrete subtype S for which there exists at least one value belonging to S that satisfies [any] {the} predicate{s} of S: S'Last_Valid denotes the largest value that belongs to S and satisfies the predicate{s} of S. The value of this attribute is of the type of S. See 3.5.5. Modify K.2(262-263), as follows: X'Valid For a prefix X that denotes a scalar object (after any implicit dereference): Yields True if and only if the object denoted by X is normal and has a valid representation, and {then, if the preceding conditions hold, the value of X also satisfies} the predicate{s} of the nominal subtype of X [evaluates to True]. The value of this attribute is of the predefined type Boolean. See 13.9.2. !discussion The obvious solution is to explicitly state that the order is unspecified. That's how other assertions work. However, there are several other factors occurring here that don't occur for other assertions. Most important is the realization that predicates are created by refinement (all predicates are evaluated, each possibly raising their own exception as proposed in AI12-0054-2). Preconditions, by contrast are defined by replacement -- Pre can only be replaced, not modified. (Admittedly, class-wide preconditions are different in this sense -- they might in fact have a similar problem.) In particular, the expectation is that the constraints/exclusions/predicates of a "parent" subtype hold in the child subtype. If we have: subtype Small is Integer range 1..100; subtype Tiny is Small range 1..10; We expect Tiny to be a subrange of Small (and we'd get Constraint_Error if it isn't). We want a similar dynamic to hold for predicates. After all, if we didn't want the predicate to hold, we just would make the second subtype a subtype of the base type directly. This is critical if the exception raised by the failure of a particular predicate is specified (as in AI12-0054-2). If the parts of the declarations are evaluated in a totally arbitrary order, the "wrong" exception could be raised. AI12-0054-2 recommends using multiple subtypes to have different exceptions raised for different failures (see the examples below). If the order is unspecified, then the exact exception raised would not be well-defined, which would make it harder to portably convert existing interfaces into contract assertions. Moreover, in extreme cases, it could cause an exception to be raised when no exception should be raised at all. Consider: subtype Nonnull_Item_Ptr is not null Item_Ptr; subtype Nonempty_Item_Ptr is Nonnull_Item_Ptr with Dynamic_Predicate => Nonempty_Item_Ptr.Count > 0; if Ptr in Nonempty_Item_Ptr then -- (1) The membership at (1) should not raise an exception. However, if the predicate was evaluated before the null exclusion, and Ptr was null, the evaluation of the predicate as part of the membership would raise Constraint_Error. Similar effects are possible using just predicates, or using constraints and predicates. Another example is (using the routines defined in Text_IO): subtype Open_File_Type is File_Type with Dynamic_Predicate => Is_Open (Open_File_Type), Predicate_Failure => raise Status_Error; subtype Read_File_Type is Open_File_Type with Dynamic_Predicate => Mode (Read_File_Type) = In_File; Predicate_Failure => raise Mode_Error with "Can't read file: " & Name (Read_File_Type); if My_File in Read_File_Type then -- (2) If the predicate of Read_File_Type is evaluated first, then (2) would raise Status_Error if My_File is not open, whereas the expected result is for the membership to return False. Thus we define that the constraints and exclusions are evaluated first when checking whether a value satisfies a subtype, the predicates are then checked in an order that ensures that a predicate is not evaluated until any prior predicates evaluate to True. This has no effect on the order of checks when multiple checks are needed (as in a subprogram call with multiple parameters); that is still unspecified. !corrigendum 3.2.4(4/3) @drepl @xbullet @dby @xbullet, the predicates of the parent subtype and the progenitor subtypes apply.> !corrigendum 3.2.4(6/3) @ddel The @i of a subtype consists of all predicate specifications that apply, and-ed together; if no predicate specifications apply, the predicate is True (in particular, the predicate of a base subtype is True). !corrigendum 3.2.4(30/3) @dinsb If predicate checks are enabled for a given subtype, then: @dinss If any of the above Legality Rules is violated in an instance of a generic unit, Program_Error is raised at the point of the violation. To determine whether a value @i of a subtype @i, the following tests are performed in the following order, until one of the tests fails, in which case the predicates are not satisfied and no further tests are performed, or all of the tests succeed, in which case the predicates are satisfied: @xbullet;> @xbullet @xinbull is a first subtype, the value is tested to determine whether it satisfies the predicates of the parent and progenitor subtypes (if any) of @i (in an arbitrary order);> @xinbull is defined by a @fa, the value is tested to determine whether it satisfies the predicates of the subtype denoted by the @fa of the @fa;> @xbullet is defined by a declaration to which one or more predicate specifications apply, the predicates are evaluated (in an arbitrary order) to test that all of them yield True for the given value.> !corrigendum 3.2.4(31/3) @drepl @xindent or @b parameter that is passed by reference, the predicate of the subtype of the actual is evaluated, and a check is performed that the predicate is True. For an object created by an @fa with no explicit initialization @fa, or by an uninitialized @fa, if any subcomponents have @fas, the predicate of the nominal subtype of the created object is evaluated, and a check is performed that the predicate is True. Assertions.Assertion_Error is raised if any of these checks fail.> @dby @xindent or @b parameter that is passed by reference, a check is performed that the value of the parameter satisfies the predicates of the subtype of the actual. For an object created by an @fa with no explicit initialization @fa, or by an uninitialized @fa, if any subcomponents have @fas, a check is performed that the value of the created object satisfies the predicates of the nominal subtype.> @xindent is evaluated; if the evaluation of the Predicate_Failure @fa propagates an exception occurrence, then this occurrence is propagated for the failure of the predicate check; otherwise, Assertion_Error is raised, with an associated message string defined by the value of the Predicate_Failure @fa. In the absence of such a Predicate_Failure aspect, an implementation-defined message string is associated with the Assertion_Error exception.> !corrigendum 3.2.4(32/3) @ddel A value @i a predicate if the predicate is True for that value. !corrigendum 3.2.4(33/3) @ddel If any of the above Legality Rules is violated in an instance of a generic unit, Program_Error is raised at the point of the violation. !corrigendum 3.2.4(35/3) @dinsa @xindent<@s9<6 A Static_Predicate, like a constraint, always remains True for all objects of the subtype, except in the case of uninitialized variables and other invalid values. A Dynamic_Predicate, on the other hand, is checked as specified above, but can become False at other times. For example, the predicate of a record subtype is not checked when a subcomponent is modified.>> @dinst @xindent<@s9<7 No predicates apply to the base subtype of a scalar type; every value of a scalar type @i is considered to satisfy the predicates of @i'Base.>> !corrigendum 3.5.5(7.1/3) @drepl For every static discrete subtype S for which there exists at least one value belonging to S that satisfies any predicate of S, the following attributes are defined: @dby For every static discrete subtype S for which there exists at least one value belonging to S that satisfies the predicates of S, the following attributes are defined: !corrigendum 3.5.5(7.2/3) @drepl @xhang<@xterm S'First_Valid denotes the smallest value that belongs to S and satisfies the predicate of S. The value of this attribute is of the type of S.> @dby @xhang<@xterm S'First_Valid denotes the smallest value that belongs to S and satisfies the predicates of S. The value of this attribute is of the type of S.> !corrigendum 3.5.5(7.3/3) @drepl @xhang<@xterm S'Last_Valid denotes the largest value that belongs to S and satisfies the predicate of S. The value of this attribute is of the type of S.> @dby @xhang<@xterm S'Last_Valid denotes the largest value that belongs to S and satisfies the predicates of S. The value of this attribute is of the type of S.> !corrigendum 3.8.1(10.1/3) @drepl @xbullet that is a @fa covers all values (possibly none) that belong to the subtype and that satisfy the static predicate of the subtype (see 3.2.4).> @dby @xbullet that is a @fa covers all values (possibly none) that belong to the subtype and that satisfy the static predicates of the subtype (see 3.2.4).> !corrigendum 3.8.1(15/3) @drepl @xbullet @fa shall cover only values in that subtype that satisfy its predicate, and each value of that subtype that satisfies its predicate shall be covered by some @fa (either explicitly or by @b);> @dby @xbullet @fa shall cover only values in that subtype that satisfy its predicates, and each value of that subtype that satisfies its predicates shall be covered by some @fa (either explicitly or by @b);> !corrigendum 4.5.2(29/3) @drepl @xbullet is a @fa, the tested type is scalar, the value of the @fa belongs to the range of the named subtype, and the predicate of the named subtype evaluates to True.> @dby @xbullet is a @fa, the tested type is scalar, the value of the @fa belongs to the range of the named subtype, and the value satisfies the predicates of the named subtype.> !corrigendum 4.5.2(30/3) @drepl @xbullet is a @fa, the tested type is not scalar, the value of the @fa satisfies any constraints of the named subtype, the predicate of the named subtype evaluates to True, and:> @dby @xbullet is a @fa, the tested type is not scalar, the value of the @fa satisfies any constraints of the named subtype, the value satisfies the predicates of the named subtype, and:> !corrigendum 4.6(51/3) @drepl After conversion of the value to the target type, if the target subtype is constrained, a check is performed that the value satisfies this constraint. If the target subtype excludes null, then a check is made that the value is not null. If predicate checks are enabled for the target subtype (see 3.2.4), a check is performed that the predicate of the target subtype is satisfied for the value. @dby After conversion of the value to the target type, if the target subtype is constrained, a check is performed that the value satisfies this constraint. If the target subtype excludes null, then a check is made that the value is not null. If predicate checks are enabled for the target subtype (see 3.2.4), a check is performed that the value satisfies the predicates of the target subtype. !corrigendum 4.9.1(10/3) @drepl @xinbull also satisfies the predicate of @i, and it is not the case that both types each have at least one applicable predicate specification, predicate checks are enabled (see 11.4.2) for @i, and predicate checks are not enabled for @i.> @dby @xinbull also satisfies the predicates of @i, and it is not the case that both types each have at least one applicable predicate specification, predicate checks are enabled (see 11.4.2) for @i, and predicate checks are not enabled for @i.> !corrigendum 5.4(7/3) @drepl @Xbullet@fa is a @fa (including a @fa, @fa, or @fa) having a static and constrained nominal subtype, then each non-@b @fa shall cover only values in that subtype that satisfy its predicate (see 3.2.4), and each value of that subtype that satisfies its predicate shall be covered by some @fa (either explicitly or by @b).> @dby @Xbullet@fa is a @fa (including a @fa, @fa, or @fa) having a static and constrained nominal subtype, then each non-@b @fa shall cover only values in that subtype that satisfy its predicates (see 3.2.4), and each value of that subtype that satisfies its predicates shall be covered by some @fa (either explicitly or by @b).> !corrigendum 5.5(9/3) @drepl For the execution of a @fa with the @fa being @b @fa, the @fa is first elaborated. This elaboration creates the loop parameter and elaborates the @fa. If the @fa defines a subtype with a null range, the execution of the @fa is complete. Otherwise, the @fa is executed once for each value of the discrete subtype defined by the @fa that satisfies the predicate of the subtype (or until the loop is left as a consequence of a transfer of control). Prior to each such iteration, the corresponding value of the discrete subtype is assigned to the loop parameter. These values are assigned in increasing order unless the reserved word @b is present, in which case the values are assigned in decreasing order. @dby For the execution of a @fa with the @fa being @b @fa, the @fa is first elaborated. This elaboration creates the loop parameter and elaborates the @fa. If the @fa defines a subtype with a null range, the execution of the @fa is complete. Otherwise, the @fa is executed once for each value of the discrete subtype defined by the @fa that satisfies the predicates of the subtype (or until the loop is left as a consequence of a transfer of control). Prior to each such iteration, the corresponding value of the discrete subtype is assigned to the loop parameter. These values are assigned in increasing order unless the reserved word @b is present, in which case the values are assigned in decreasing order. !corrigendum 13.9.2(3/3) @drepl @xhang<@xterm 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 evaluates to True. The value of this attribute is of the predefined type Boolean.> @dby @xhang<@xterm Yields True if and only if the object denoted by X is normal, has a valid representation, and then, if the preceding conditions hold, the value of X also satisfies the predicates of the nominal subtype of X. The value of this attribute is of the predefined type Boolean.> !corrigendum 13.9.2(12) @drepl @xindent<@s9<23 X'Valid is not considered to be a read of X; hence, it is not an error to check the validity of invalid data.>> @dby @xindent<@s9<23 Determining whether X is normal and has a valid representation as part of the evaluation of X'Valid is not considered to include an evaluation of X; hence, it is not an error to check the validity of an object that is invalid or abnormal. Determining whether X satisfies the predicates of its nominal subtype may include an evaluation of X, but only after it has been determined that X has a valid representation.>> @xindent<@s9> !ACATS Test An ACATS C-Test should be created using some of the examples given in the mail (especially using the Predicate_Failure aspect). !ASIS No ASIS effect. !appendix From: Steve Baird Sent: Tuesday, February 12, 2013 5:05 PM We currently have The predicate of a subtype consists of all predicate specifications that apply, and-ed together; ... and then we just talk about the evaluation of the predicate. I think we should say something about the order in which this evaluation is performed (at the very least, explicitly state that it is unspecified). If we are going to have exceptions flying out of predicates, we may want this to be specified. ---- We have the same problem with type invariants, although this might not be obvious. For type_invariants, we have If a given call requires more than one evaluation of an invariant expression, either for multiple objects of a single type or for multiple types with invariants, the evaluations are performed in an arbitrary order, and if one of them evaluates to False, it is not specified whether the others are evaluated. but this doesn't address the issue because of the ", either for multiple objects of a single type or for multiple types with invariants" wording. One solution (not necessarily the best one) would be to just delete that wording. With that change, the order would be explicitly unspecified. ---- For Pre and Post, it is explicitly stated that conditions are evaluated in any order. **************************************************************** From: Randy Brukardt Sent: Tuesday, February 12, 2013 6:16 PM > We currently have > The predicate of a subtype consists of all predicate specifications > that apply, and-ed together; ... > > and then we just talk about the evaluation of the predicate. > > I think we should say something about the order in which this > evaluation is performed (at the very least, explicitly state that it > is unspecified). > > If we are going to have exceptions flying out of predicates, we may > want this to be specified. It's not just the predicates, but also the order of the predicates, constraints, and null exclusions. Consider: subtype Nonnull_Item_Ptr is not null Item_Ptr; subtype Nonempty_Item_Ptr is Nonnull_Item_Ptr with Dynamic_Predicate => Nonempty_Item_Ptr.Count > 0; With these declarations, we want the null exclusion to be evaluated before the predicate. Otherwise, the predicate would raise Constraint_Error for the null value, and that would happen even when Nonempty_Item_Ptr is used within a membership. One could create a similar example with a range constraint and a predicate using array indexing. Of course, both a null exclusion and range constraint can be written as a predicate, and we would want the semantics to be mostly unchanged: subtype Nonnull_Item_Ptr is Item_Ptr with Dynamic_Predicate => Nonnull_Item_Ptr /= null; subtype Nonempty_Item_Ptr is Nonnull_Item_Ptr with Dynamic_Predicate => Nonempty_Item_Ptr.Count > 0; And finally, if either of these specified an exception to be raised, then the order might matter even more. We could say that the order of evaluation is unspecified, but that would essentially require repeating parts of previous predicates in new ones (duplicating the logic). And it would make composition of subtypes with predicates much harder. Something like (using the Predicate_Failure aspect previously discussed, but the exact form of that doesn't matter to this example): subtype Open_File_Type is File_Type with Dynamic_Predicate => Is_Open (Open_File_Type), Predicate_Failure => raise Status_Error; subtype Read_File_Type is Open_File_Type with Dynamic_Predicate => Mode (Read_File_Type) = In_File; Predicate_Failure => raise Mode_Error with "Can't read file: " & Name (Read_File_Type); seems reasonable (we need both of these subtypes in various routines in Text_IO), but Read_File_Type doesn't work right in memberships if the second predicate gets evaluated first (the call to Mode would raise Status_Error in such a case). **************************************************************** From: Tucker Taft Sent: Wednesday, February 13, 2013 11:02 AM All of these should be "unspecified order" in my view. Any attempt at establishing an order would be more effort than it is worth, as far as I am concerned. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 13, 2013 12:28 PM Hmm, did you look at the examples I posted? You're saying that you don't think that users will try to "refine" predicates, that only replacements will work reliably. And you're also saying that predicates are limited to raising a single exception practically (as you could never tell which one would be tested first). And you're eliminating most of the value of AI12-0054-1, because memeberships could propagate exceptions if the predicates and constraints and null exclusions are evaluated in the "wrong" order. The combination of all of these considerations makes me think that predicates are different than preconditions in this regard. And that makes sense, because preconditions are mostly defined by replacement (Pre can only be replaced, not modified), while predicates are defined by refinement (all predicates are evaluated). If you think there is something wrong with writing subtypes like: subtype Nonnull_Item_Ptr is Item_Ptr with Dynamic_Predicate => Nonnull_Item_Ptr /= null; subtype Nonempty_Item_Ptr is Nonnull_Item_Ptr with Dynamic_Predicate => Nonempty_Item_Ptr.Count > 0; or subtype Open_File_Type is File_Type with Dynamic_Predicate => Is_Open (Open_File_Type), Predicate_Failure => raise Status_Error; subtype Read_File_Type is Open_File_Type with Dynamic_Predicate => Mode (Read_File_Type) = In_File; Predicate_Failure => raise Mode_Error with "Can't read file: " & Name (Read_File_Type); you need to explain how you would do these instead. Or why you think these are wrong. Or something... **************************************************************** From: Tucker Taft Sent: Wednesday, February 13, 2013 12:38 PM Your are right that I did not look at these examples in detail. Looking at them now, I would agree that you must check the "inner" predicates before the "outer" ones. But for the case where multiple independent predicates apply, the order should be unspecified, even if they raise different exceptions. **************************************************************** From: Robert Dewar Sent: Wednesday, February 13, 2013 1:25 PM >> All of these should be "unspecified order" in my view. >> Any attempt at establishing an order would be more effort than it is >> worth, as far as I am concerned. I strongly agree with Tuck on this, any attempt to establish an order would be a big mistake, and any use of predicates depending on an order is to be strongly discouraged IMO. > Hmm, did you look at the examples I posted? You're saying that you > don't think that users will try to "refine" predicates, that only > replacements will work reliably. And you're also saying that > predicates are limited to raising a single exception practically (as > you could never tell which one would be tested first). No that seems nonsense, if we have a predicate that says raises Is_Odd if odd number is given riases Too_Big if value is greater than 100 I think it's just fine if you get one or other of the exceptions (unspecified) > And you're eliminating most of the value of > AI12-0054-1, because memeberships could propagate exceptions if the > predicates and constraints and null exclusions are evaluated in the "wrong" > order. I think that claim is just bogus, and the idea that it eliminates MOST of the value is more than that, it is complete nonsense, since most uses will be very simple. For instance, 0054 as it stands would be just fine for the math library stuff **************************************************************** From: Robert Dewar Sent: Wednesday, February 13, 2013 1:28 PM >> you need to explain how you would do these instead. Or why you think >> these are wrong. Or something... > > Your are right that I did not look at these examples in detail. > Looking at them now, I would agree that you must check the "inner" > predicates before the "outer" ones. But for the case where multiple > independent predicates apply, the order should be unspecified, even if > they raise different exceptions. Well I can live with that limited ordering rule, though I still don't like it. But I don't want to go any further. **************************************************************** From: Jean-Pierre Rosen Sent: Wednesday, February 13, 2013 2:22 PM >> If you think there is something wrong with writing subtypes like: >> >> subtype Nonnull_Item_Ptr is Item_Ptr >> with Dynamic_Predicate => Nonnull_Item_Ptr /= null; >> subtype Nonempty_Item_Ptr is Nonnull_Item_Ptr >> with Dynamic_Predicate => Nonempty_Item_Ptr.Count > 0; > > I find it horrible to depend on the ordering here, if you need to > protect the .Count reference with a non-null test this should be done > in a propert conditional with clear ordering. Hmm... If I write: subtype Small is integer range 1..100; subtype Tiny is Small range 1..10; I expect Tiny to be a subrange of Small (and I get constraint_Error if it isn't). So, in Nonempty_Item_Ptr, I would expect the predicate for Nonull_Item_Ptr to hold. Otherwise, I would have made it a subtype of Item_Ptr. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 13, 2013 3:06 PM > Well I can live with that limited ordering rule, though I still don't > like it. But I don't want to go any further. No need to go further. The important point is that when someone writes a new subtype in terms of an existing one, they're going to expect the existing one holds true. It would be confusing if that's not true. Combining the predicates with "and then" in that case eliminates the possibility. In other cases, where there is not textual expectation of a correlation, I don't see any need for requiring anything, and agree that we should not. Certainly, we don't want to start requiring an order of checks on parameters: I don't see any need for that. Predicates are a special case because they can be constructed by refinement as well as independently. **************************************************************** From: Bob Duff Sent: Wednesday, February 13, 2013 3:22 PM > Hmm... If I write: > > subtype Small is integer range 1..100; subtype Tiny is Small range > 1..10; > > I expect Tiny to be a subrange of Small (and I get constraint_Error if > it isn't). So, in Nonempty_Item_Ptr, I would expect the predicate for > Nonull_Item_Ptr to hold. Otherwise, I would have made it a subtype of > Item_Ptr. I agree with J-P. In fact, I was about to send a similar example. ;-) **************************************************************** From: Robert Dewar Sent: Wednesday, February 13, 2013 4:57 PM > Hmm... If I write: > > subtype Small is integer range 1..100; subtype Tiny is Small range > 1..10; > > I expect Tiny to be a subrange of Small (and I get constraint_Error if > it isn't). So, in Nonempty_Item_Ptr, I would expect the predicate for > Nonull_Item_Ptr to hold. Otherwise, I would have made it a subtype of > Item_Ptr. Yes, of course all predicates will hold, you are missing the poit, which is that if we evaluate the predicares in the "wrong" order then you will get a constraint error instead of an assertion error if Nonnull_Item_Ptr is null. **************************************************************** From: Robert Dewar Sent: Wednesday, February 13, 2013 4:58 PM > No need to go further. The important point is that when someone writes > a new subtype in terms of an existing one, they're going to expect the > existing one holds true. It would be confusing if that's not true. Combining the > predicates with "and then" in that case eliminates the possibility. OK that makes sense to me, I agree (and have changed my mind about this being ugly style :-)) > In other cases, where there is not textual expectation of a > correlation, I don't see any need for requiring anything, and agree that we should not. > Certainly, we don't want to start requiring an order of checks on > parameters: I don't see any need for that. Predicates are a special > case because they can be constructed by refinement as well as independently. and I agree with this as well **************************************************************** From: Randy Brukardt Sent: Wednesday, February 13, 2013 5:06 PM ... > Yes, of course all predicates will hold, you are missing the poit, > which is that if we evaluate the predicares in the "wrong" order then > you will get a constraint error instead of an assertion error if > Nonnull_Item_Ptr is null. And more importantly, that you'll get a Constraint_Error rather than False from a membership test if the predicates are evaluated in the wrong order. Which is what AI12-0054-1 is trying to avoid; it doesn't help to leave this issue unspecified. **************************************************************** From: Steve Baird Sent: Saturday, June 15, 2013 5:34 PM At first I thought there was nothing to do here. Existing proposed wording looked fine, until I realized that "subtype order" didn't handle progenitors. So now we've got something that seems a bit heavy, but I think it does spell out the order of predicate evaluation in the way we want it to be specified. --- All changes are in 3.2.4 (subtype predicates). Typo: 14.b/3 "cam" => "can". In 6/3 "The predicate of a subtype consists of all predicate specifications that apply, and-ed together {as described below};" Add at the beginning of the dynamic semantics section: Testing whether a given value satisfies the predicate of a given subtype consists of a series of other tests, performed in the order described below. If any one of these tests fails, then the value does not satisfy the predicate of the subtype and no further evaluation is performed. If all the these tests pass, then the value satisfies the predicate of the subtype. First, a test is performed that the value satisfies any constraints or null exclusions of the given subtype. This test need not be performed in the case of a predicate test for one subtype that is part (as described below) of a predicate test for another subtype, [If S2 is a subtype of S1 and we have already established that the constraints and null exclusions of S2 are satisfied, testing those of S1 would be redundant.] Next: - For a (first) subtype defined by a derived type declaration, test (in an unspecified order) whether the value satisfies the predicate of the parent subtype and of each of the progenitor subtypes. - For a subtype defined by a subtype indication, test whether the value satisfies the predicate of the subtype denoted by the subtype_mark. Finally, if the subtype is defined by a declaration to which at least one predicate specification applies, test (in an unspecified order [if both a static and a dynamic predicate are specified]) whether evaluation of each of the specified predicates yields True for the given value. [In other words, constraints and null exclusions are checked first, then inherited predicates (recursively applying the same ordering rules) and finally directly specified predicates. In the case of declaring a subtype S2 of a subtype S1 where, for example, the predicate of S2 passes the current instance to a function whose formal parameter subtype is S1, this ordering means that the function will not be invoked until after the given value's membership in S1 has been established.] **************************************************************** From: Steve Baird Sent: Saturday, June 15, 2013 5:54 PM I forgot interaction with AI-54/2 assuming we pass that one. > Finally, if the subtype is defined by a declaration to which > at least one predicate specification applies, test (in an > unspecified order [if both a static and a dynamic predicate > are specified]) whether evaluation of each of the specified > predicates yields True for the given value. If False is returned, and if this test occurs in the context of a check (as opposed to as part of a membership test), and if the Predicate_Failure aspect has been specified for the declaration, then the Predicate_Failure aspect is evaluated and the resulting String is passed as the second argument in a call to Exceptions.Raise_Exception, with Assertions.Assertion_Error'Identity as the first argument. **************************************************************** From: Steve Baird Sent: Sunday, June 16, 2013 3:46 AM Revised version of the above: If False is returned, and if this test occurs (dynamically) as part of a check [(as opposed to as part of a membership test)], then Exceptions.Raise_Exception is called with Assertions.Assertion_Error'Identity as the Exception_Id argument. If the Predicate_Failure aspect has been specified by the specified for the declaration, then the String argument is the result of evaluating the Predicate_Failure expression; otherwise value of the String argument is implementation-defined. [Redundant: Evaluation of the Predicate_Failure expression may propagate an exception; for example, the Predicate_Failure expression may be a raise_expression. If an exception is propagated evaluating the string argument of the call to Raise_Exception, then (as usual) control is transferred as for the raise of any exception and the aforementioned call to Raise_Exception is not executed.] Ok to mention raise_expressions in this way? In 3.2.4(31/3), slap square brackets around Assertions.Assertion_Error is raised if any of these checks fail. because this is now redundant. **************************************************************** From: Tucker Taft Sent: Thursday, November 14, 2013 11:32 PM Here is a re-write of AI12-0071. Steve proposed some wording, but I ended up pretty much rewriting it completely. I also adopted the phraseology of "value satisfies the predicates of subtype S". Note the use of the plural "predicates" and the term "satisfies" (which Steve also proposed) rather than talking about a predicate "evaluating to True." [Editor's notes: This is version /03 of the AI. Steve had written the following in his incomplete draft: The chapter 13 wording below is post-Berlin stuff we have not discussed previously. I was talking about validity/volatility interactions with Randy and he pointed out that this AI would be a good place to address those issues. End Editor's notes] **************************************************************** From: Tucker Taft Sent: Friday, November 15, 2013 10:23 PM Here is the full updated AI. It turns out that progenitor *subtype* was already defined (in 3.9.4). [Editor's notes: This is version /04 of the AI.] **************************************************************** From: Randy Brukardt Sent: Friday, December 13, 2013 5:27 PM The approved wording for AI12-0071-1 says in part: To determine whether a value *satisfies the predicates* of a subtype S, the following tests are performed in the following order, until one of the tests fails, in which case the predicates are not satisfied and no further tests are performed, or all of the tests succeed, in which case the predicates are satisfied: * the value is first tested to determine whether it satisfies any constraints or any null exclusion of S; * then: + if S is the first subtype of a derived type, the value is tested to determine whether it satisfies the predicates of the parent subtype and of any of the progenitor subtypes of S (in an arbitrary order); + if S is defined by a subtype_indication, the value is tested to determine whether it satisfies the predicates of the subtype denoted by the subtype_mark of the subtype_indication; * finally, if S is defined by a declaration to which one or more predicate specifications apply, the predicates are evaluated (in an arbitrary order) to test that all of them yield True for the given value. --- What about the case of a task or protected type with progenitors? Consider a task type (the issues for a protected type are identical). A task type is not a derived type (which is declared by a derived_type_declaration, see 3.4; a task_type_declaration is surely not one of those). So the first of the middle bullets does not apply to it. And it certainly is not defined by a subtype_indication. So what causes the predicates of the progenitors to be evaluated?? (Nothing. :-) It appears that Tucker made the same mistake that Bob made in the original wording, Bob's mistake having been corrected by the change to 3.2.4(4/3) earlier in AI12-0071-1. Anyway, how should this be fixed? Generalizing the first bullet seems appealing, but the wording about the parent subtype gives pause (since there is no parent subtype for a task type). We could stick in more "any"s, but that starts making the wording impenetrable: + if S is the first subtype of a type, the value is tested to determine whether it satisfies the predicates of any parent subtype and of any of the progenitor subtypes of S (in an arbitrary order); Maybe just adding another bullet after the first would be better (leaving the derived type bullet alone): + if S is the first subtype of a task or protected type, the value is tested to determine whether it satisfies the predicates of any of the progenitor subtypes of S (in an arbitrary order); Either way, I think we can consider this the result of my editorial review (rather than sending the AI back for reconsideration), since we're not making any important semantic change. Thoughts or better suggestions?? Randy. P.S. The question has been raised as to whether predicates on interface subtypes could be useful. In an example like: type Int is interface; function Is_OK (I : Int) return Boolean is abstract; subtype OK_Int is Int with Dynamic_Predicate => Is_OK(OK_Int); The predicate is always going to be illegal, as it is statically bound and the function has to be abstract. However, by forcing the call to be dispatching: subtype OK_Int is Int with Dynamic_Predicate => Is_OK(Int'Class(OK_Int)); The predicate is then legal and it should be obvious how it could be useful in some cases. (Consider a File_Type interface, for instance, and function Is_Open.) Since these seems to at least possibly be useful, there doesn't seem to be a good reason to ban them; and in the absence of that, we have to make them work right. **************************************************************** From: Tucker Taft Sent: Saturday, December 14, 2013 9:13 AM > The approved wording for AI12-0071-1 says in part: > > ... > Thoughts or better suggestions?? I hate to special-case tasks and protected types. How about: > + if S is [the]{a} first subtype [of a derived type], the value is tested > to determine whether it satisfies the predicates of the parent > [subtype] and [of any of the] progenitor subtypes {(if any)} > of S (in an arbitrary order); > > Randy. > > P.S. The question has been raised as to whether predicates on > interface subtypes could be useful. In an example like: I don't see sufficient benefit here, given the somewhat awkward nature of this beast. ****************************************************************