!standard 4.5.10(9/5) 22-01-19 AI22-0011-1/05 !standard 4.5.10(10/5) !standard 4.5.10(12/5) !standard 4.5.10(13/5) !standard 4.5.10(15/5) !standard 4.5.10(16/5) !standard 4.5.10(17/5) !standard 4.5.10(18/5) !standard 4.5.10(19/5) !standard 4.5.10(20/5) !standard 4.5.10(24/5) !standard 4.5.10(25/5) !standard 4.5.10(27/5) !standard 4.5.10(29/5) !standard 4.5.10(34/5) !class binding interpretation 21-11-11 !status Corrigendum 1-2022 22-01-19 !status WG9 Approved 22-10-18 !status ARG Approved 11-0-0 21-11-18 !status work item 21-11-11 !status received 21-04-28 !priority Low !difficulty Easy !qualifier Omission !subject Reduction expression issues !summary Various minor issues with reduction expressions. !issue A few minor issues with reduction expressions. 1) It should be made clear that in the case of an indefinite accumulator subtype, the tag, bounds, or discriminant values of the accumulator object may be determined by its initial value in the same way as for any other object declaration. 2) It should be made clear that neither the accumulator type nor the value type of a reduction expression may be an anonymous access type. 3) It should be made clear that in the zero-iteration case, the initial value is still assigned to the accumulator (which involves a conversion to the accumulator subtype, which may fail). RM 4.5.10(29/5) incorrectly states otherwise. 4) Accum_Type is a subtype, despite its name. The same applies to Value_Type. We work around this by saying Accum_Type subtype all over the place. It would be better to call these Accum_Subtype and Value_Subtype in the first place. 5) The result of a reduction expression has the nominal subtype of Accum_Type. However, the initial value may have some other subtype (the only requirement is that it have the type of Accum_Type). Similarly, the expression of the iterated_element_association may not belong to Accum_Type. We need wording to convert these to the appropriate subtype. (Note that the parameter and/or result of the reducer subprogram does have the correct subtype and does not need a conversion.) Should these be fixed? (Yes.) !recommendation (See Summary.) !wording Modify 4.5.10(9/5): In the remainder of this subclause, we will refer to nonlimited subtypes [*Value_Type*]{*Value_Subtype*} and [*Accum_Type*]{*Accum_Subtype*} of a reduction_attribute_reference. These subtypes and interpretations of the names and expressions of a reduction_attribute_reference are determined by the following rules: Modify 4.5.10(10/5): * [*Accum_Type*]{*Accum_Subtype*} is a subtype of the expected type of the reduction_attribute_reference. Replace 4.5.10(12/5) with: function Reducer(Accumulator : Accum_Subtype; Value : Value_Subtype) return Accum_Subtype; Replace 4.5.10(13/5) with: procedure Reducer(Accumulator : in out Accum_Subtype; Value : in Value_Subtype); Modify 4.5.10(15/5): * The expected type of an initial_value_expression of a reduction_specification is [that of subtype *Accum_Type*]{the type of *Accum_Subtype*}. Modify 4.5.10(16/5): * The expected type of the expression of the iterated_element_association of a value_sequence is [that of subtype *Value_Type*]{the type of *Value_Subtype*}. Modify 4.5.10(17/5): If the reduction_attribute_reference has a value_sequence with the reserved word parallel, [the subtypes *Accum_Type* and *Value_Type*]{*Accum_Subtype* and *Value_Subtype*} shall statically match. Modify 4.5.10(18/5): If the identifier of a reduction_attribute_designator is Parallel_Reduce, [the subtypes *Accum_Type* and *Value_Type*]{*Accum_Subtype* and *Value_Subtype*} shall statically match. Modify AARM 4.5.10(18.a/5): For a reduction_attribute_reference with a value_sequence that does not have the reserved word parallel or has a prefix and the identifier of the reduction_attribute_designator is Reduce, [the subtypes *Accum_Type* and *Value_Type*]{*Accum_Subtype* and *Value_Subtype*} can be different because only one logical thread of control is presumed so there is no need to combine multiple results. Add after 4.5.10(18/5) [at the end of the Legality Rules section]. Neither the type of @i nor @i shall be an anonymous access type. Replace 4.5.10(19/5) with: A reduction_attribute_reference denotes a value, with its nominal subtype being *Accum_Subtype* Redundant[(the subtype of the first parameter of the subprogram denoted by the reducer_name)]. [Editor's note: We didn't need to change this one, but it is odd and confusing to have a name for something and not use it in the rules. I left the old part as a parenthesized remark as it can be clarifying.] Modify 4.5.10(20/5): For the evaluation of a value_sequence, the iterated_element_association, the chunk_specification, and the aspect_specification, if any, are elaborated in an arbitrary order. Next an iteration is performed, and for each value conditionally produced by the iteration (see 5.5 and 5.5.2), the associated expression is evaluated with the loop parameter having this value, which produces a result that is converted to [*Value_Type*]{*Value_Subtype*} and is used to define the next value in the sequence. Modify 4.5.10(24/5): The evaluation of a use of this attribute begins by evaluating the parts of the reduction_attribute_designator (the reducer_name Reducer and the initial_value_expression Initial_Value), in an arbitrary order. It then {converts the the value of the initial_value_expression (the initial value) to *Accum_Subtype* and then }initializes the accumulator {with the result}[of the reduction expression to the value of the initial_value_expression (the initial value)]. The value_sequence V is then evaluated. Add an AARM note after 4.5.10(24/5): AARM Ramification: This initialization follows the object initialization rules of an explicit object declaration. In particular, if the accumulator subtype is indefinite, then the tag, array bounds, or discriminants of the accumulator object are determined by its initial value as needed. Modify 4.5.10(25/5): If the value_sequence does not have the reserved word parallel, each value of the value_sequence is {converted to *Value_Subtype* and then} passed, in order, as the second (Value) parameter to a call on Reducer, with the first (Accumulator) parameter being the prior value of the accumulator, saving the result as the new value of the accumulator. The reduction expression yields the final value of the accumulator. Modify 4.5.10(27/5): Each logical thread of control creates a local accumulator for processing its subsequence. The accumulator for a subsequence is initialized to the {result of converting the} first value of the subsequence {to *Accum_Subtype*}, and calls on Reducer start with the second value of the subsequence (if any). The result for the subsequence is the final value of its local accumulator. Modify 4.5.10(29/5): If the evaluation of the value_sequence yields an empty sequence of values, the reduction expression yields the initial value{ of the accumulator}. Modify 4.5.10(34/5): For a parallel reduction expression, it is a bounded error if the reducer subprogram is not associative. That is, for any arbitrary values of {*Value_Subtype*}[subtype *Value_Type*] A, B, C and a reducer function R, the result of R (A, R (B, C)) should produce a result equal to R (R (A, B), C)); it is a bounded error if R does not. The possible consequences are Program_Error, or a result that does not match the equivalent sequential reduction expression due to the order of calls on the reducer subprogram being unspecified in the overall reduction. Analogous rules apply in the case of a reduction procedure. !discussion Adding an AARM note for #1 is harmless at worst. It seems worthwhile to clarify this point. #2 eliminates useless-but-hard-to-implement corner cases. If the the type of the accumulator subtype can be an anonymous access type, then what happens if the intial value expression is an allocator? When is that allocated object finalized? Is the accumulator object a standalone object of an anonymous access type? Similar questions arise if the type of the value subtype can be an anonymous access type and the value sequence contains allocators. It might be argued that no new legality rule is needed to disallow anonymous access types because this is already a (somewhat obscure) consequence of the existing rules, at least in the case of a reducer function (as opposed to a reducer procedure). It seems better to state this explicitly. The change for #4 clarifies most of the wording. Talking about the "Accum_Type subtype" is annoying, while the "type of Accum_Subtype" is a usual thing to talk about. So we make this change. The change for #3 seems like a clear improvement; otherwise a reduction expression might yield a result that is not within its nominal subtype. But for that to be the case (#5), we need to convert the initial value and any expressions to the Accum_Subtype, so that any constraints or predicates are respected. We chose to keep the accumulator(s) always having the appropriate subtype. This is similar to what would happen if we had a declaration for the accumulator: Accumulator : Accum_Subtype; And in any case we have to check the value before passing it to a reducer, so there is no real benefit in waiting. !corrigendum 4.5.10(9/5) @drepl In the remainder of this subclause, we will refer to nonlimited subtypes @i and @i of a @fa. These subtypes and interpretations of the @fas and @fas of a @fa are determined by the following rules: @dby In the remainder of this subclause, we will refer to nonlimited subtypes @i and @i of a @fa. These subtypes and interpretations of the @fas and @fas of a @fa are determined by the following rules: !corrigendum 4.5.10(10/5) @drepl @xbullet<@i is a subtype of the expected type of the @fa.> @dby @xbullet<@i is a subtype of the expected type of the @fa.> !corrigendum 4.5.10(12/5) @drepl @xcode< @b Reducer(Accumulator : @i; Value : @i) @b @i;> @dby @xcode< @b Reducer(Accumulator : @i; Value : @i) @b @i;> !corrigendum 4.5.10(13/5) @drepl @xcode< @b Reducer(Accumulator : @b @i; Value : @b @i);> @dby @xcode< @b Reducer(Accumulator : @b @i; Value : @b @i);> !corrigendum 4.5.10(15/5) @drepl @xbullet@fa of a @fa is that of subtype @i.> @dby @xbullet@fa of a @fa is the type of @i.> !corrigendum 4.5.10(16/5) @drepl @xbullet of the @fa of a @fa is that of subtype @i.> @dby @xbullet of the @fa of a @fa is the type of @i.> !corrigendum 4.5.10(17/5) @drepl If the @fa has a @fa with the reserved word @b, the subtypes @i and @i shall statically match. @dby If the @fa has a @fa with the reserved word @b, @i and @i shall statically match. !corrigendum 4.5.10(18/5) @drepl If the @fa of a @fa is Parallel_Reduce, the subtypes @i and @i shall statically match. @dby If the @fa of a @fa is Parallel_Reduce, @i and @i shall statically match. Neither the type of @i nor @i shall be an anonymous access type. !corrigendum 4.5.10(19/5) @drepl A @fa denotes a value, with its nominal subtype being the subtype of the first parameter of the subprogram denoted by the @i@fa. @dby A @fa denotes a value, with its nominal subtype being @i (the subtype of the first parameter of the subprogram denoted by the @i@fa). !corrigendum 4.5.10(20/5) @drepl For the evaluation of a @fa, the @fa, the @fa, and the @fa, if any, are elaborated in an arbitrary order. Next an iteration is performed, and for each value conditionally produced by the iteration (see 5.5 and 5.5.2), the associated @fa is evaluated with the loop parameter having this value, which produces a result that is converted to Value_Type and is used to define the next value in the sequence. @dby For the evaluation of a @fa, the @fa, the @fa, and the @fa, if any, are elaborated in an arbitrary order. Next an iteration is performed, and for each value conditionally produced by the iteration (see 5.5 and 5.5.2), the associated @fa is evaluated with the loop parameter having this value, which produces a result that is converted to Value_Subtype and is used to define the next value in the sequence. !corrigendum 4.5.10(24/5) @drepl @xindent (the @i@fa Reducer and the @i@fa Initial_Value), in an arbitrary order. It then initializes the @i of the reduction expression to the value of the @i@fa (the @i). The @fa V is then evaluated.> @dby @xindent (the @i@fa Reducer and the @i@fa Initial_Value), in an arbitrary order. It then converts the the value of the @i@fa (the @i) to @i and then initializes the @i with the result. The @fa V is then evaluated.> !corrigendum 4.5.10(25/5) @drepl @xindent does not have the reserved word @b, each value of the @fa is passed, in order, as the second (Value) parameter to a call on Reducer, with the first (Accumulator) parameter being the prior value of the accumulator, saving the result as the new value of the accumulator. The reduction expression yields the final value of the accumulator.> @dby @xindent does not have the reserved word @b, each value of the @fa is converted to @i and then passed, in order, as the second (Value) parameter to a call on Reducer, with the first (Accumulator) parameter being the prior value of the accumulator, saving the result as the new value of the accumulator. The reduction expression yields the final value of the accumulator.> !corrigendum 4.5.10(27/5) @drepl @xindent @dby @xindent, and calls on Reducer start with the second value of the subsequence (if any). The result for the subsequence is the final value of its local accumulator.> !corrigendum 4.5.10(29/5) @drepl @xindent yields an empty sequence of values, the reduction expression yields the initial value.> @dby @xindent yields an empty sequence of values, the reduction expression yields the initial value of the accumulator.> !corrigendum 4.5.10(34/5) @drepl For a parallel reduction expression, it is a bounded error if the reducer subprogram is not associative. That is, for any arbitrary values of subtype @i @i, @i, @i and a reducer function @i, the result of @i (@i, @i (@i, @i)) should produce a result equal to @i (@i (@i, @i), @i)); it is a bounded error if @i does not. The possible consequences are Program_Error, or a result that does not match the equivalent sequential reduction expression due to the order of calls on the reducer subprogram being unspecified in the overall reduction. Analogous rules apply in the case of a reduction procedure. @dby For a parallel reduction expression, it is a bounded error if the reducer subprogram is not associative. That is, for any arbitrary values of @i @i, @i, @i and a reducer function @i, the result of @i (@i, @i (@i, @i)) should produce a result equal to @i (@i (@i, @i), @i)); it is a bounded error if @i does not. The possible consequences are Program_Error, or a result that does not match the equivalent sequential reduction expression due to the order of calls on the reducer subprogram being unspecified in the overall reduction. Analogous rules apply in the case of a reduction procedure. !ACATS test An ACATS B-Test should be written to check that no anonymous access types are allowed. ACATS C-Test(s) should be written to check that the conversions are made (and fail when needed). !appendix From: Steve Baird WG 9 Review issue #139 - May 14, 2021 Looking more closely at value_sequences involves looking at iterated_element_associations. And that leads to wondering about whether the accessibility level is well defined for the loop parameter of such an iteration. Consider this expression: [for Loop_Param of ... => Loop_Param.Aliased_Component'Access]'Reduce (Function_With_Anonymous_Access_Parameter, Init_Value) When Function_With_Anonymous_Access_Parameter is called (at runtime), an accessibility level will need to be passed in. What rules define that level? Perhaps the apparently-missing accessibility level definition follows from some equivalence rule, but I don't see it. If there is an issue here, then (at least) two solutions come to mind. One approach is to make sure this case is well-defined in 3.10.2. Alternatively, we could require that the Value_Type and the Accum_Type of a reduction expression shall each not be an anonymous access type. Is there a similar issue with quantified expressions? Perhaps not (although it seems suspicious that 5.5.2(8.a/5) talks about "the loop statement", as opposed to other looping constructs). [Remainder of (off-topic) note is elided here, see AI22-0025-1 for it - Editor.] **************************************************************** From: Randy Brukardt WG 9 Review issue #139 - May 29, 2021 This can't possibly be something that is going to matter to 99.99% of reduction expressions, so resolving this sort of corner case can wait for a future Ada. (Preferably about Ada 2222. :-) Marked as deferred. **************************************************************** !topic Roman numbers reduction expression example !reference Ada 202X Darft 29 RM 4.2.1(18/5ff), 4.5.10 !from Christoph Grein 2021-05-27 !discussion I'm, or better GNAT CE 2020 and 2021 are having problems with the example cited above. GNAT complains about the initial value 0: [for I in R'Range => (if I < R'Last and then R(I) < R(I + 1) then -1 else 1) * R(I)] 'Reduce("+", 0) roman.adb:40:25: warning: value not in range of type "Roman_Number" defined at line 24 [enabled by default] roman.adb:40:25: warning: "Constraint_Error" will be raised at run time [enabled by default] I've reported this in 2020, but since I'm not a supported user, didn't get an answer. My analysis: Accum_Type and Value_Type are the same here, the Reducer is function "+" (Left, Right: Roman_Number'Base) return Roman_Number'Base; Now according to the RM, Accum_Type is a subtype of the expected type, here the expected type is obviously the *type of Roman_Number*, let's call it T (in recursive letter like it's done in the RM when talking about a type). Now Roman_Number trivially *is* a subtype of T , but the RM does not explicitly say which subtype. Starting from the reducer subprogram, the subtype to choose must obviously be Roman_Number'Base or else the initial value 0 and also the value -1 in the value sequence are not in range. The RM is rather obscure in this subclause. So either the example is wrong or GNAT chooses the wrong subtype. **************************************************************** From: Tucker Taft Sent: Thursday, May 27, 2021 2:31 PM I agree that the GNAT warning is incorrect in this case. It is a somewhat unusual case in that the initial value never becomes the final value of the reduction. But in general, the warning does seem overly strict, since there are probably a number of situations where the initial value never becomes the final value, and it is certainly wrong to say "Constraint_Error *will* be raised". The nominal subtype of a reduction expression is the Accum_Type, and that comes from the first parameter to "+", which as you point out is Roman_Number'Base, and 0 is certainly a legal value of that subtype. I suggest you suppress the warnings for now. **************************************************************** From: Christoph Grein Sent: Friday, May 28, 2021 2:42 AM It does raise Constraint_Error. There is another problem in GNAT:     X: Roman_Number := "III" * "IV" * "XII"; -- 144 (that is, CXLIV) roman.adb:51:30: error: there is no applicable operator "*" for a string type Replacing this by    X: Roman_Number := to_Roman_number("III") * "IV" * "XII"; it compiles. So, Tuck, you say GNAT is wrong? I'll rereport it to report@adacore.com with reference to you. **************************************************************** From: Tucker Taft Sent: Friday, May 28, 2021 7:12 AM > It does raise Constraint_Error. Interesting. It sounds like it is checking against the wrong subtype, and the warning comes from that, so I guess suppressing the warning won't solve the problem. It is presumably using the first subtype of the type rather than the subtype of the first parameter of the Reducer subprogram as the subtype for the accumulator. >There is another problem in GNAT: > > X: Roman_Number := "III" * "IV" * "XII"; -- 144 (that is, CXLIV) >roman.adb:51:30: error: there is no applicable operator "*" for a string >type > >Replacing this by > X: Roman_Number := to_Roman_number("III") * "IV" * "XII"; >it compiles. It looks like overload resolution is not considering fully the possibility of user-defined string literals. >So, Tuck, you say GNAT is wrong? I'll rereport it to report@adacore.com >with reference to you. OK. Since this is an unsupported account, I presume it might not be fixed immediately, but thank you for documenting the problems! That will certainly make it more likely a fix can be found sooner. **************************************************************** From: Egil Harald Hoevik Sent: Friday, May 28, 2021 7:40 AM > It does raise Constraint_Error. explicitly stating Roman_Number'Base(0) seems to be a workaround > There is another problem in GNAT: > > X: Roman_Number := "III" * "IV" * "XII"; -- 144 (that is, CXLIV) > roman.adb:51:30: error: there is no applicable operator "*" for a string > type > > Replacing this by > X: Roman_Number := to_Roman_number("III") * "IV" * "XII"; > it compiles. That triggered a GNAT bug-box here with the newest GNAT Pro wavefront (22.0w 20210424) however, both X: Roman_Number := to_Roman_number("III") * to_Roman_number("IV") * to_Roman_number("XII"); and X: Roman_Number := Roman_Number' ("III") * Roman_Number' ("IV") * Roman_Number'("XII"); works, though **************************************************************** From: Steve Baird [Privately] Sent: Friday, October 22, 2021 5:25 PM Let's assume we are talking about a reduction expression with neither "parallel" nor "Parallel_Reduce" anywhere in sight. The type of the Initializer expression and the type of the first parameter of the Reducer_Subprogram are required to be the same. And if the reducer subprogram is a function, then the function result type also has to be the same type. The RM uses the name Accum_Type to denote this type. I suppose it goes without saying that Accum_Type cannot be an anonymous access type. And it is stated explicitly that it cannot be limited. But it can be class-wide, right? [Brief digression: Do we need to state that Value_Type cannot be an anonymous access type?] And the aforementioned 2-3 subtypes of Accum_Type can all differ with respect to subtype-specific properties, such as constraints or predicates. They are not required to statically match. Consider, for example, the case where the subtype of the first parameter of the Reducer_Subprogram is indefinite (e.g., Standard.String or Some_Discriminated_Type'Class). In that case, presumably the accumulator object inherits the constraints and/or tag of the initial value; does that need to be stated explicitly, or does that follow implicitly from 4.5.10(24): " It then initializes the accumulator of the reduction expression to the value of the initial_value_expression (the initial value)." ? This is inconsistent with the 4.5.10(19) rule that the nominal subtype of a reduction expression is "the subtype of the first parameter of the subprogram denoted by the reducer_name". To see the inconsistency, consider the zero-iteration case. In this case, the value of the reduction expression is that of the initial value. In that case, there is no justification for assuming that the value of the reduction expression will satisfy the constraints/predicates of its nominal subtype. This can lead to problems, for example with a case statement that cases on such a zero-iteration reduction expression and only provides choices for the values of the nominal subtype of that reduction expression. Suppose we require that the other 1-2 subtypes shall be statically compatible with the one that was chosen as the nominal subtype? Alternatively, we could change the dynamic semantics to include an explicit nominal-subtype membership check in the zero-iteration case. The latter approach is a dynamic semantics change; those are sometimes considered the worst sort of incompatibility, but it doesn't seem so bad in this case. Opinions? **************************************************************** From: Tucker Taft [Privately] Sent: Friday, October 22, 2021 5:56 PM ... >The RM uses the name Accum_Type to denote this type. No, it uses Accum_Type to designate the *subtype* and several times says "subtype Accum_Type". I think this might be a fundamental issue in the way you interpreted this section. You really need to think of Accum_Type as a particular *subtype*. > I suppose it goes without saying that Accum_Type cannot be an anonymous > access type. And it is stated explicitly that it cannot be limited. But it > can be class-wide, right? > > [Brief digression: Do we need to state that Value_Type cannot be an anonymous > access type?] Not sure why you say this goes without saying. I haven't thought about all the implications, but I don't see any rule disallowing Accum_Type from being an anonymous access type, and if that causes heartburn, we need to say it explicitly. > And the aforementioned 2-3 subtypes of Accum_Type can all differ with > respect to subtype-specific properties, such as constraints or predicates. > They are not required to statically match. I don't agree. Subtype conformance is required, so that to me says that we do expect the subtypes to match. And if that is not clear to you, we should make it clear. There seems no reason to allow the subtypes to differ, and as mentioned above, Accum_Type is itself a particular subtype. > Consider, for example, the case where the subtype of the first parameter of > the Reducer_Subprogram is indefinite (e.g., Standard.String or > Some_Discriminated_Type'Class). In that case, presumably the accumulator > object inherits the constraints and/or tag of the initial value; does that > need to be stated explicitly, or does that follow implicitly from 4.5.10(24): > " It then initializes the accumulator of the reduction expression to the > value of the initial_value_expression (the initial value)." >? I agree that that would seem to establish the *runtime* subtype properties of the accumulator, if Accum_Type is an indefinite subtype. I suppose it wouldn't hurt to make that explicit, at least in the AARM. > This is inconsistent with the 4.5.10(19) rule that the nominal subtype of a > reduction expression is > "the subtype of the first parameter of the subprogram denoted by the > reducer_name". Not sure why you say that. Isn't this the same as a function whose result subtype is indefinite? The nominal subtype of the result of the function call is still that of the result subtype. It isn't affected by the possibility that the body is simply "return value;". > To see the inconsistency, consider the zero-iteration case. In this case, > the value of the reduction expression is that of the initial value. In that > case, there is no justification for assuming that the value of the reduction > expression will satisfy the constraints/predicates of its nominal subtype. I don't agree, since the assignment of the initial value to the accumulator happens even in the case of the zero-iteration case, so if Accum_Type is *not* an indefinite subtype, then clearly the initial value must satisfy its constraints. I think the problem is really the last rule which says: If the evaluation of the value_sequence yields an empty sequence of values, the reduction expression yields the initial value. This probably should be augmented with something like "converted to Accum_Type" or to say it "yields the initial value assigned to the accumulator." > This can lead to problems, for example with a case statement that cases on > such a zero-iteration reduction expression and only provides choices for the > values of the nominal subtype of that reduction expression. > > Suppose we require that the other 1-2 subtypes shall be statically > compatible with the one that was chosen as the nominal subtype? I am unclear on what you mean by the other 1-2 subtypes. In my view, there is only the Accum_Type, which despite its name is a well-defined *subtype* which is the nominal subtype of the whole expression. Accum_Type must represent a particular subtype because in the parallel cases we require it to statically match the Value_Type, in 4.5.10(17/5). > Alternatively, we could change the dynamic semantics to include an explicit > nominal-subtype membership check in the zero-iteration case. The latter > approach is a dynamic semantics change; those are sometimes considered the > worst sort of incompatibility, but it doesn't seem so bad in this case. I definitely think the initial value needs to satisfy the constraints of the Accum_Type, and the Accum_Type is the nominal subtype of the result. The only confusing part in my view is 4.5.10(15/5) which should probably be clarified to say we return the initial value of the accumulator, which is of the Accum_Type. > Opinions? See above. **************************************************************** From: Steve Baird [Privately] Sent: Friday, October 22, 2021 6:49 PM Thanks for the quick response. Ok, I was confused - Accum_Type is a subtype, not a type. So a lot of my message was noise. === I say Accum_Type cannot be an anonymous access type (at least in the function case) because in a case like function Reducer(Accumulator : access Integer; Value : Value_Type) return access Integer; the two access types are not the same type, so there is no way that it can be subtype conformant with any profile of the form function Reducer(Accumulator : Accum_Type; Value : Value_Type) return Accum_Type; But I guess this argument doesn't hold up if we are talking about the procedure case. So maybe this doesn't go without saying. In general, we don't want to open the door for something that couldn't have been written explicitly using explicit loops, object declarations, etc. via a straightforward equivalence. Saying "we have this type/subtype that is taken from a subprogram specification and we are going to use it in an object declaration for an accumulator" is fine for a named entity, but not for an anonymous one. And do we really want to have to deal with the case where the initializer expression is an allocator of an anonymous access type whose designated type has whatever heartburn-inducing properties you care to imagine? Or a call to a function with an anonymous access result type having a similar designated type? Ditto for anonymous value types. Do we really want to spend time on static/dynamic accessibility issues regarding actual parameters of implicit calls when the value type for a Reducer subprogram is an anonymous access type? Perhaps the case is weaker here, since I think the equivalent explicit expansion doesn't involve generating any names that denote an existing anonymous type. Nonetheless, a value sequence whose value type is anonymous seems to me like a new (and therefore undesirable) use of anonymous access types. === I agree that an AARM note would be sufficient to clarify that the accumulator can inherit tag/constraint stuff from the initial value if the accumulator subtype is indefinite. === Regarding the problem with nominal subtypes and the zero-iteration case, I agree that your solution > This probably should be augmented with something like "converted to > Accum_Type" or to say it "yields the initial value assigned to the > accumulator." addresses the problem. **************************************************************** From: Steve Baird [Privately] Sent: Friday, October 22, 2021 7:01 PM > This probably should be augmented with something like "converted to > Accum_Type" or to say it "yields the initial value assigned to the > accumulator." That also would have the advantage of removing any ambiguity about what happens when the initial assignment to the accumulator involves array bound sliding. **************************************************************** From: Tucker Taft [Privately] Sent: Friday, October 22, 2021 8:58 PM ... > But I guess this argument doesn't hold up if we are talking about the > procedure case. So maybe this doesn't go without saying. An anonymous access type parameter cannot be of mode "in out" so the procedure case wouldn't allow for that. I certainly don't see any particular value to allowing "access T" but we should probably explicitly disallow it. Saying that two anonymous access subtypes are of different types is not particularly meaningful. What is relevant is whether they are statically matching subtypes. > In general, we don't want to open the door for something that couldn't have > been written explicitly using explicit loops, object declarations, etc. via > a straightforward equivalence. Saying "we have this type/subtype that is > taken from a subprogram specification and we are going to use it in an object > declaration for an accumulator" is fine for a named entity, but not for an > anonymous one. I am not sure I am following you here. Are you saying indefinite subtypes should not be allowed? The accumulator is initialized by the initial value, so I don't see a problem there. > And do we really want to have to deal with the case where the initializer > expression is an allocator of an anonymous access type whose designated type > has whatever heartburn-inducing properties you care to imagine? > Or a call to a function with an anonymous access result type having a similar > designated type? I am convinced, but I think we just need to explicitly disallow aonymous access types, not try to argue that they are illegal for some obscure reason. > Ditto for anonymous value types. > > Do we really want to spend time on static/dynamic accessibility > issues regarding actual parameters of implicit calls when the value type for > a Reducer subprogram is an anonymous access type? > Perhaps the case is weaker here, since I think the equivalent explicit > expansion doesn't involve generating any names that denote an existing > anonymous type. Nonetheless, a value sequence whose value type is anonymous > seems to me like a new (and therefore undesirable) use of anonymous access types. I agree the case is weaker, but simplest is to disallow anonymous access types for both the Accum_Type and the Value_Type. === > Regarding the problem with nominal subtypes and the zero-iteration case, I > agree that your solution >> This probably should be augmented with something like "converted to >> Accum_Type" or to say it "yields the initial value assigned to the >> accumulator." > addresses the problem. OK. **************************************************************** From: Tucker Taft [Privately] Sent: Friday, October 22, 2021 8:59 PM > That also would have the advantage of removing any ambiguity about what > happens when the initial assignment to the accumulator involves array > bound sliding. Good point. **************************************************************** From: Randy Brukardt [Privately] Sent: Friday, October 22, 2021 9:06 PM > Ok, I was confused - Accum_Type is a subtype, not a type. So a lot of my > message was noise. I wondered if you were really confused, since resolution is usually about types. But 4.5.10(11/5) does indeed require *subtype* conformance of the reducer subprograms - in particular this means that for the function case, the parameter and result have to be the same subtype. However, that doesn't apply to the initial value nor the expression of the iterated_element_association; those use expected type. Some sort of conversion needs to be applied to both of those, else they won't match the nominal subtype. === ... > But I guess this argument doesn't hold up if we are talking about the > procedure case. So maybe this doesn't go without saying. Correct. The expected type resolution for other things will properly handle anonymous types. > In general, we don't want to open the door for something that couldn't have > been written explicitly using explicit loops, object declarations, etc. via > a straightforward equivalence. Saying "we have this type/subtype that is > taken from a subprogram specification and we are going to use it in an object > declaration for an accumulator" is fine for a named entity, but not for an > anonymous one. I'm not sure I agree with this, at least if anonymous access is really the first-class sort of thing that it is supposed to be. You certainly can write an equivalent loop (we "fixed" iterators to allow anonymous access types precisely for this reason). I can believe that the wording for reduction expressions won't handle it properly, but that's arguably the fault of the wording and not the language semantics. OTOH, I'd just as soon anonymous access didn't exist at all, so I certainly don't care that much. > And do we really want to have to deal with the case where the initializer > expression is an allocator of an anonymous access type whose designated type > has whatever heartburn-inducing properties you care to imagine? Or a call to > a function with an anonymous access result type having a similar designated type? I don't want to deal with those in ANY context. :-) > Ditto for anonymous value types. Do we really want to spend time on > static/dynamic accessibility issues regarding actual parameters of implicit > calls when the value type for a Reducer subprogram is an anonymous access > type? Perhaps the case is weaker here, since I think the equivalent explicit > expansion doesn't involve generating any names that denote an existing > anonymous type. Nonetheless, a value sequence whose value type is anonymous > seems to me like a new (and therefore undesirable) use of anonymous access types. Shrug. Doesn't seem worse than any other loop. === > Regarding the problem with nominal subtypes and the zero-iteration case, I > agree that your solution >> This probably should be augmented with something like "converted to >> Accum_Type" or to say it "yields the initial value assigned to the >> accumulator." > addresses the problem. Correct, a conversion is necessary (else the nominal subtype wouldn't be respected). It seems that might be necessary for the expression of a value sequence, if that can get returned somehow (one presumes that if it gets passed to a reducer subprogram, parameter passing will do the appropriate conversion -- perhaps all that is needed is an AARM note that a check is needed on passing to the reducer since the value may not belong to the Value_(sub)Type. **************************************************************** From: Stephen Baird [Privately] Sent: Friday, October 22, 2021 10:53 PM > Are you saying indefinite subtypes should not be allowed? No, I'm only agreeing that (one way or another) we want to disallow anonymous access types. > ... simplest is to disallow anonymous access types for both the Accum_Type > and the Value_Type. Agreed. **************************************************************** From: Randy Brukardt [Privately] Sent: Friday, November 12, 2021 7:47 PM A couple of thoughts on some ancient mail (mostly so this appears in the !appendix of the AI): ... >I say Accum_Type cannot be an anonymous access type (at least in the >function case) because in a case like > function Reducer(Accumulator : access Integer; > Value : Value_Type) return access Integer; the two >access types are not the same type, so there is no way that it can be >subtype conformant with any profile of the form > function Reducer(Accumulator : Accum_Type; > Value : Value_Type) return Accum_Type; Subtype conformance of anonymous access types is structural; if it wasn't, you could never have a spec for a subprogram with an anonymous access parameter, because it would never conform to the body (as you say, those are separate types). Tucker said something about this, but it was garbled and I failed to understand what he was driving at. ... > In general, we don't want to open the door for something that couldn't > have been written explicitly using explicit loops, object > declarations, etc. via a straightforward equivalence. Saying "we have > this type/subtype that is taken from a subprogram specification and we > are going to use it in an object declaration for an accumulator" is > fine for a named entity, but not for an anonymous one. I don't see anything in the current wording that truly reflects the model that we've used the Accum_(sub)Type as the subtype of an object declaration. (Aside: Accum_Type is a terrible name for a subtype!! One has to stick "subtype" in front of it every time you care about the subtype properties). The wording simply says that it "initializes the accumulator", nothing is said about the subtype of the accumulator object or even if there is such an object. Moreover, it just says that it is initialized to the initial value (with more words than that). There's nothing about any subtype in sight, or check, or anything. Accum_Type is never tied to the accumulator object at all; it's used in various other ways, but not there. Admittedly, the accumulator object has to have that type (but not necessarily subtype) in order for the Dynamic Semantics to mean anything at all, but we shouldn't be assuming that. I brought up this point after you guys were mostly done discussing this topic, and nobody paid any attention to me. :-) There are no subtype checks mentioned anywhere in the RM text. The proposed AARM note makes sense, but the normative model needs to be clearer as well. I took a stab at adding some of that topic to the AI as I updated it, but I may have missed by quite a bit. ... > Do we really want to spend time on static/dynamic accessibility issues > regarding actual parameters of implicit calls when the value type for > a Reducer subprogram is an anonymous access type? This reminded me that aliased parameters also bring up such considerations. We banned them from user-defined literal functions for this reason. Do we need to do that here as well? Answering my own question: No, because "subtype conformance" requires "aliased" to match, and the given profiles do not have aliased parameters. So a subprogram with an aliased parameter can never be subtype conformant with those profiles. When we use normal resolution only, we don't get that for free. One could probably have used a similar argument to note that anonymous access types aren't allowed here, although its more nebulous. Accum_Type as described in this profile is clearly a named subtype (as that's all that allowed in a profile), and one cannot name anonymous "access T". ("subtype S is access T;" is not a legal declaration). As such, you are comparing a named subtype to an anonymous access, and that cannot match. Still, it's probably best to just say that is illegal, and then we don't have to come up with arguments as to why it is not allowed. **************************************************************** Editor's note: We have a couple of Editorial Review fixes to the wording as approved by ARG meeting #62M. First, the wording of 4.3.5(27/5) adds an additional comma, which makes parsing the sentence hard (and possibly ambiguous). We rearrange the words to avoid introducing a comma. From: The accumulator for a subsequence is initialized to the first value of the subsequence, { converted to Accum_Subtype,} and to: The accumulator for a subsequence is initialized to the {result of converting the} first value of the subsequence {to Accum_Subtype}, and Second, 4.3.5(25/5) converts a value to Accum_Subtype and passes it to the second (value) parameter of the Reducer. The problem here is that second parameter has Value_Subtype, not Accum_Subtype. For a reducer without the parallel keyword, these don't even need to be the same type, so the existing wording makes no sense. Replacing Accum_Subtype with Value_Subtype fixes that. ****************************************************************