AI22-0120-1
!standard 4.3.3(17/5) 24-11-01 AI22-0120-1/02
!class Amendment 24-10-24
!status Amendment 1-2022 24-11-01
!status WG9 Approved 25-06-09
!status ARG Approved 10-1-2 24-10-31
!status work item 24-10-24
!status received 24-02-19
!priority Low
!difficulty Easy
!subject Relax array aggregate restriction on nonstatic choices with others
The rules on named array aggregates are relaxed so that one nonstatic/null-range choice may be used, even if there is also an "others" choice.
It is surprising to some Ada users (see ARG GitHub issue # 105) that a simple array aggregate of the form:
X : Bit_Vector := [A => True, others => False];
is not legal if the value of A is nonstatic. The restriction is that an array aggregate may only have one component association, counting any "others" association as well, if the choice for the association is nonstatic (or a null range).
Should this sort of aggregate be legal? (Yes.)
We recommend that the "others" component association not be counted when limiting the array aggregate to a single nonstatic/null-range choice.
Modify 4.3.3(17/5):
The discrete_choice_list of an array_component_association (including an iterated_component_association) is allowed to have a discrete_choice that is a nonstatic choice_expression or that is a subtype_indication or range that defines a nonstatic or null range, only if it is the single discrete_choice of its discrete_choice_list, and either there is only one array_component_association in the enclosing array_component_association_list{, not counting any component association with an others choice,} or the enclosing aggregate is an array_delta_aggregate[Redundant:, not an array_aggregate].
The restriction might be considered surprising[a][b] to a typical Ada user, as there seems no additional semantic complexity to permit the "others" association in such an aggregate. In that sense the restriction might seem arbitrary.[c][d] The existing run-time check defined in 4.3.3(29/3) ensures that the nonstatic choice will be checked to be within range of the applicable index constraint, when an "others" is present.
Furthermore, one reason to only allow a single nonstatic choice was to simplify checking for lack of overlap between choices, but by definition an "others" choice never overlaps with anything else. A second reason was to simplify determining the bounds of the aggregate, but if there is an others choice, then by definition the bounds are determined by the applicable index constraint.
For Ada 95 we had allowed the others to be nonstatic, while the non-others choice was static, which introduced the need for the check in 4.3.3(29/3). This AI is proposing the natural next step, which removes a restriction that is no longer needed given the existence of this check. On the other hand, allowing two non-others choices, with either or both being nonstatic, would require an additional set of checks, which would add both semantic and implementation complexity. So we are not suggesting going further.
Here are several aggregates that become legal with this proposed change:
|
[A => True,
others =>
False][e] |
@drepl
The @fa{discrete_choice_list} of an @fa{array_component_association} (including an @fa{iterated_component_association}) is allowed to have a @fa{discrete_choice} that is a nonstatic @fa{choice_expression} or that is a @fa{subtype_indication} or @fa{range} that defines a nonstatic or null range, only if it is the single @fa{discrete_choice} of its @fa{discrete_choice_list}, and either there is only one @fa{array_component_association} in the enclosing @fa{array_component_association_list} or the enclosing @fa{aggregate} is an @fa{array_delta_aggregate}, not an @fa{array_aggregate}.
@dby
The @fa{discrete_choice_list} of an @fa{array_component_association} (including an @fa{iterated_component_association}) is allowed to have a @fa{discrete_choice} that is a nonstatic @fa{choice_expression} or that is a @fa{subtype_indication} or @fa{range} that defines a nonstatic or null range, only if it is the single @fa{discrete_choice} of its @fa{discrete_choice_list}, and either there is only one @fa{array_component_association} in the enclosing @fa{array_component_association_list}, not counting any component association with an @b{others} choice, or the enclosing @fa{aggregate} is an @fa{array_delta_aggregate}, not an @fa{array_aggregate}.
Any existing B tests that disallow such aggregates should be fixed, and a new C test that verifies that these aggregates are legal, and do the right thing, should be created.
This issue was raised in ARG GitHub issue # 105.
From: Tucker Taft
Sent: Thursday, October 31, 2024 12:53 PM
With respect to AI22-0120-1 about relaxing the array aggregate rules, in a proposed aggregate such as:
[A => True, others => False]
"A" cannot have predicates if it is a nonstatic subtype, thanks to RM 3.2.4(28/3):
The discrete_choice of a named_array_aggregate shall not denote a nonstatic subtype to which predicate specifications apply.
So Steve's nasty scenario is not permitted.
So it is limited to a single contiguous range, if dynamic. If static, we do allow discontiguous ranges due to a static predicate, but that capability already exists, so it is not associated with this AI.
From: Randy Brukardt
Sent: Friday, November 1, 2024 10:22 PM
> [A => True, others => False]
Related to this same AI (which we approved without changes, so I'm writing this to put it into the !appendix), Tucker had given a transformation of the above aggregate that didn't make much sense:
[for I in A => (if F(I) then True else False)]
The correct transformation that Tuck was thinking of would be:
[for I in others => (if I in A then True else False)]
(Yes, an "normal" iterator can have an others choice.)
More generally,
[A => B, others => C]
could be transformed to:
[for I in others => (if I in A then B else C)]
which should work for any aggregate of this form. (I used "in" so this works if A is a subtype or range. Cool, huh?) Of course, this may not provide very good code quality.
[a]Not to me. This would be an entirely new case for code generation for aggregates; it is unlikely that it could be combined with any existing case. (There can be any number of static choices, but those can and have to be ordered at compile time when mixing with an others choice; and the existing dynamic choice cases cannot have others choices.) As such, allowing this would increase the complexity of supporting aggregates and would be a substantial amount of work to create and fully test. (And if the latter isn't done, it could be a significant source of bugs as this is not a very likely form compared to other forms of array aggregate.)
[b]When I said "surprising" I meant to a typical Ada user, not to an implementor.
[c]If you are going to do this, you might at least try to justify drawing the line in this new place rather than in the old one. (A line needs to be drawn, unrestricted array aggregates are essentially unimplementable.)
We could say that the basic requirement here is that choices never overlap. That's not possible to check statically if there is more than one dynamic choice. But arguably, others is a special case, since it can never overlap with any other choice (rather, the values it covers change to avoid an overlap). So that justifies allowing this particular case and no other additional cases. (And it argues for the current restriction being wrong.)
I prefer being specific rather than talking about "semantic complexity" and "arbitrary".
Why I'm helping you justify a change I'm against, I don't know. ;-)
[d]I borrowed your rationale ... ;-)
[e][for I in A => (if f(I) then True else False)]