AI22-0106-1
!standard 4.3.3(17.1/5) 24-08-08 AI22-0106-1/04
!standard 4.3.3(19)
!class binding interpretation 24-04-25
!status Amendment 1-2022 24-07-18
!status WG9 Approved 24-10-10
!status ARG Approved 8-0-0 24-07-18
!status work item 24-04-25
!status received 24-03-04
!submitter Stephen Baird
!priority Low
!difficulty Easy
!qualifier Omission
!subject Multidimension array_aggregates and iterators
Iterated_component_associations with iterator_specifications are not allowed in multidimensional array aggregates.
Index parameters are not allowed in other discrete_choice_lists of the same aggregate.
4.3.3(21-22) says that the evaluation of an array aggregate begins with determining its bounds (all of them) before we start evaluating component expressions. For each dimension whose bounds are not both known statically, we evaluate a range or an iteration or some such construct at the start of the evaluation of the aggregate to determine the bounds for that dimension.
However, that approach is impractical if an index or loop parameter of an outer dimension is used (somehow) in the discrete range or iterator of an inner dimension.
For example:
[for Idx in 1..10 => [for E of Some_Function (Idx) => E+1]]
Note that the iterated_component_specification for the second dimension mentions Idx, which is defined as an interated_component_association for an earlier dimension. Evaluating that iterated_component_specification "early" (at the start of the evaluation of the aggregate and, in particular, before the object Idx exists) makes no sense.
Another problematic example is:
[for Idx in 1..0 => [for E of Some_Object => E+1]]
In this case, how do we determine the bounds for the second dimension? It is never executed, as the first dimension is null.
Even a simple example that doesn’t use any iterator_specifications causes similar issues:
[for Idx in 1..5 => [for Idx2 in 1..Idx => Idx2+1]]
Here the bounds of the subaggregate depends on the outer iteration, and again cannot be evaluated until Idx exists. Moreover, an aggregate like this will always raise Constraint_Error as the bounds of the various subaggregates don’t match as is required. A compiler might be able to figure that out in order to set the bounds without evaluating Idx, but that’s weird and complex (since the expression containing Idx can be arbitrarily complicated).
Something needs fixing here.
(See Summary.)
Modify 4.3.3(17.1/5):
Either all or none of the array_component_associations of an array_component_association_list shall be iterated_component_associations with an iterator_specification.{ An iterated_component_association with an iterator_specification shall not appear as the iterator_specification of an array_component_association of a multidimensional array_aggregate.}
AARM Reason: Many cases of nested iterators cannot be allowed, and many more are useless, as every subaggregate has to have the same bounds. As such, we simplify implementations by not allowing these constructs at all.
Add after 4.3.3(19):
An index parameter of an iterated_component_association of an aggregate shall not appear in the discrete_choice_list of an array_component_association of any subaggregate of that aggregate.
AARM To Be Honest: When we say "an iterated_component_association of an aggregate", we mean of any part of that aggregate, including of any subaggregates, but not of a nested aggregate that might appear in a component expression.
AARM Reason: Such uses of index parameters are useless, as every subaggregate has to have the same bounds. Constraint_Error would be raised for most such uses, and the remainder are trivial.
There probably are reasonable answers to each of the problems posed in the !issue. For instance, for the first example, we could say that the value of Idx for the first pass is some value produced by the choice (it doesn't matter which one); all of the actual iterations have to match that length.
And certainly we could come up with some rule applying to iterators that never execute as in the second example.
Indeed, during discussion of this topic, Tucker Taft tried to create a set of rules for the evaluation of iterators in multidimensional array aggregates. The result had five steps, and probably subtly changed the existing rules for evaluation of array aggregates. Moreover, he wasn't convinced that the rules were correct (more work was needed). This suggests that the level of work needed here is extensive.
But before doing that, we need to take a step back and consider the big picture.
Iterated_component_associations with iterator_specifications were added to array_aggregates because of a desire to have the capabilities of indexed container aggregates and array aggregates to be approximately the same. We didn't want container aggregates to have a major capability not present in array aggregates.
However, container aggregates have only a single dimension. There are no multidimensional container aggregates. Thus, the capability was designed and justified with a single dimension in mind. Indeed, the rules for such iterator associations in array aggregates were created for single-dimensional arrays, and little or no consideration was given to the problems created when the iterators are nested.
Moreover, no one considered whether such constructs are even useful. It's fairly clear that most such constructs are not useful, since the inner dimensions all have to have the same bounds (that is, the iterator has to produce the same number of elements for every execution). That's not very likely for iterators over objects or containers, except for the rather trivial case of all of the iterators being the same (not depending upon the outer level).
We've also not considered which combinations of iterators make sense. Moreover, we've not considered the implementation costs of combinations of iterators. And we have no idea if there is any user demand for using iterators in multidimensional array aggregates (we don't really know if there is much demand for the iterators even in the single dimension case).
As such, it seems abusive to implementers to expect them to support nesting of iterators (other than the discrete_choice_list version, which follows the existing rules for discrete_choices). We therefore choose to ban iterated_component_associations with iterator_specifications from multidimensional array aggregates. If, in the future, uses for such constructs emerge, it would be easy to relax the ban (and the rules needed would be much more obvious as well if good usage examples existed).
This just leaves us with the question of rules for iterated_component_associations with discrete_choice_lists.
It should be obvious that there is no use for an index parameter in the bounds of a subaggregate, as the bounds of a subaggregate has to be the same for all iterations.
We could still allow it, with an implementation using an arbitrary value for the index parameter (one that belongs to the discrete_choice_list), for the purposes of determining bounds. But given that almost all possible uses would raise Constraint_Error, or essentially do nothing, it probably is best to ban it.
For example, an aggregate like:
[for I in 1 .. 10 => [1 ..I => I*I]]
will always raise Constraint_Error as the bounds differ between the different evaluations of the subaggregates.
Aggregates like:
[for I in 1 .. 1 => [1 ..I => I*I]]
and:
[for I in 1 .. 10 => [1 ..10+I*0 => I*I]]
would work, but there is an equivalent aggregate that does not use the index parameter in the subaggregate.
The ban only applies to the inner discrete_choice_list(s), and not to uses in the component expression. For example, the example given in 4.3.3(47/5) remains legal:
G : constant Matrix :=
(for I in 1 .. 4 =>
(for J in 1 .. 4 =>
(if I=J then 1.0 else 0.0)));
Illegal examples are given in the !issue and in the !discussion.
@drepl
Either all or none of the @fa{array_component_association}s of an @fa{array_component_association_list} shall be @fa{iterated_component_association}s with an @fa{iterator_specification}.
@dby
Either all or none of the @fa{array_component_association}s of an @fa{array_component_association_list} shall be @fa{iterated_component_association}s with an @fa{iterator_specification}. An @fa{iterated_component_association} with an @fa{iterator_specification} shall not appear as the @fa{iterator_specification} of an @fa{array_component_association} of a multidimensional @fa{array_aggregate}.
@dinsa
A bottom level subaggregate of a multidimensional @fa{array_aggregate} of a given array type is allowed to be a @fa{string_literal} only if the component type of the array type is a character type; each character of such a @fa{string_literal} shall correspond to a @fa{defining_character_literal} of the component type.
@dinst
An index parameter of an @fa{iterated_component_association} of an @fa{aggregate} shall not appear in the @fa{discrete_choice_list} of an @fa{array_component_association} of any subaggregate of that @fa{aggregate}.
An ACATS B-Test is needed to check that multidimensional array_aggregates do not allow
iterated_component_associations with iterator_specifications. Another ACATS B-Test is needed to check that index parameters are not used in discrete_choice_lists of subaggregates.
This question was originally raised in an internal AdaCore discussion.