Version 1.6 of ai22s/ai22-0006-1.txt
!standard 4.3.3(20.3/5) 22-01-14 AI22-0006-1/03
!standard 4.3.3(20.4/5)
!standard 4.3.3(32/5)
!class binding interpretation 21-11-11
!status Corrigendum 1-2022 22-01-14
!status WG9 Approved 22-10-18
!status ARG Approved 12-0-0 21-11-18
!status work item 21-11-11
!status received 21-11-11
!priority Medium
!difficulty Medium
!qualifier Omission
!subject Two-pass iteration for array aggregates
!summary
Counts must match for a "two pass" array aggregate.
!issue
The array aggregates described in the three paragraphs starting at
RM 4.3.3(20.2/5) are generated at run time via two passes. In the first pass,
we generate a count that is an upper bound on the number of elements that the
array aggregate is going to have. It is intended that this can be used in
determining how much space to allocate for the aggregate object.
In the second pass, we fill in the aggregate; the number of element
values generated in the second pass is the actual length (that is, the number
of elements) of the aggregate.
The use of "produced" in 4.3.3 (20.3/5) versus the use of "conditionally
produced" in 4.3.3 (20.4/5) was intended to imply that filters only
participate in the second pass. With this interpretation, it makes
sense that the first count (which is generated without filters) might be
larger than the second count (which is generated with filters).
However, allowing the case where the first count is greater than the second
count introduces problems in some (obscure) build-in-place scenarios. Should
these rules be revisited? (Yes.)
!recommendation
Evaluation of filters is required in the first pass and the two resulting
element counts are checked for equality (as opposed to only checking that
the first count is at least as large as the second). Strictly speaking,
this is an incompatible change, but only with respect to an Ada 2022 feature.
!wording
Replace 4.3.3(20.3/5 - 20.4/5): [Note the preceding lead-in paragraph is
provided for readability, but it is not changed.]
For an array_aggregate that contains only array_component_associations that
are iterated_component_associations with iterator_specifications,
evaluation proceeds in two steps:
Each iterator_specification is elaborated (in an arbitrary order) and
an iteration is performed solely to determine a maximum count for the
number of values produced by the iteration; all of these counts are
combined to determine the overall length of the array, and ultimately
the limits on the bounds of the array (defined below);
A second iteration is performed for each of the iterator_specifications,
in the order given in the aggregate, and for each value conditionally
produced by the iteration (see 5.5 and 5.5.2), the associated expression
is evaluated, its value is converted to the component subtype of the
array type, and used to define the value of the next component of the
array starting at the low bound and proceeding sequentially toward the
high bound. A check is made that the second iteration results in an
array length no greater than the maximum determined by the first
iteration; Constraint_Error is raised if this check fails.
with
For an array_aggregate that contains only array_component_associations that
are iterated_component_associations with iterator_specifications,
evaluation proceeds in two steps:
Each iterator_specification is elaborated (in an arbitrary order) and
an iteration is performed solely to determine a count for the
number of values conditionally produced by the iteration; all of these
counts are combined to determine the overall length of the array, and
ultimately the bounds of the array (defined below);
A second iteration is performed for each of the iterator_specifications,
in the order given in the aggregate, and for each value conditionally
produced by the iteration (see 5.5 and 5.5.2), the associated expression
is evaluated, its value is converted to the component subtype of the
array type, and used to define the value of the next component of the
array starting at the low bound and proceeding sequentially toward the
high bound. As part of this second iteration, a check is made that it
results in the same number of elements as the first iteration;
Constraint_Error is raised if this check fails. This check is performed
before any attempt to access any nonexistent element of the array object.
Modify 4.3.3(32/5) [Implementation Permissions]
When evaluating iterated_component_associations for an array_aggregate that
contains only iterated_component_associations with iterator_specifications,
the first step of evaluating an iterated_component_association can be
omitted if the implementation can determine the number of values
by some other means. {[Redundant: Such "other means" might include
making use of an applicable index constraint or the Length function
of a suitable container type.]}
!discussion
At least one compiler vendor has been confused about what is intended here,
so some clarification is called for.
---
Build-in-place causes issues as one cannot use too much memory in such cases.
Consider the case where the first iteration yields a larger count, so we
allocate more space for the aggregate than it requires.
Now let's say that the array type is limited, the aggregate is returned
as the result of a (BIP) function, the function call is used to initialize
an allocator, and the access type in question has a user-defined storage
pool.
type Vec is array (Some_Index_Type range <>) of Some_Limited_Type;
function F return Vec is
begin
return <aggregate>;
end F;
type Ref is access Vec with Storage_Pool => ... ;
Ptr : Ref := new Vec'(F);
Alternatively, omit the function and just use the aggregate to initialize
the allocator.
There are two issues here:
1) Implementation complexity and overhead may be required to ensure that
the Max_Size_In_Storage_Elements implementation requirement of
13.11(21.5/3) is met when storage is allocated for the aggregate.
2) Suppose that later on we try to deallocate this allocated object via an
instance of Unchecked_Deallocation. In this case 13.11(21.7/3) requires
that the Size_In_Storage_Elements parameter of the Free call has to
equal that of the corresponding Allocate call. So an implementation
would have to store this S_I_S_E value somewhere because it cannot be
calculated from the bounds of the allocated array.
Compiler writers could deal with these issues, but it would be simpler
(and cheaper in time and space) if they didn't have to.
---
Finally, it was unclear to at least some RM readers (thst is, me - Steve
Baird) that the phrase "by some other means" in 4.3.3(32/5) includes the case
where the aggregate has an applicable index constraint. In that case,
it is intended that an implementation can omit the first pass and use
the applicable index constraint instead to determine the value of the
first count. It would be nice to see this point stated more explicitly.
!corrigendum 4.3.3(20.3/5)
Replace the paragraph:
- 1.
- Each iterator_specification is elaborated (in an
arbitrary order) and an iteration is performed solely to determine a maximum
count for the number of values produced by the iteration; all of these counts are
combined to determine the overall length of the array, and
ultimately the limits on the bounds of the array (defined below);
by:
- 1.
- Each iterator_specification is elaborated (in an
arbitrary order) and an iteration is performed solely to determine a count for
the number of values conditionally produced by the iteration; all of these
counts are combined to determine the overall length of the array, and
ultimately the bounds of the array (defined below);
!corrigendum 4.3.3(20.4/5)
Replace the paragraph:
- 2.
- A second iteration is performed for each of the
iterator_specifications, in the order given in the aggregate, and
for each value conditionally produced by the iteration (see 5.5 and 5.5.2),
the associated expression is evaluated, its
value is converted to the component subtype of the array type, and used to
define the value of the next component of the array starting at the low bound
and proceeding sequentially toward the high bound. A check is made
that the second iteration results in an array length no greater than the
maximum determined by the first iteration; Constraint_Error is raised if this
check fails.
by:
- 2.
- A second iteration is performed for each of the
iterator_specifications, in the order given in the aggregate, and
for each value conditionally produced by the iteration (see 5.5 and 5.5.2),
the associated expression is evaluated, its
value is converted to the component subtype of the array type, and used to
define the value of the next component of the array starting at the low bound
and proceeding sequentially toward the high bound. As part of this second
iteration, a check is made that it results in the same number of elements as
the first iteration; Constraint_Error is raised if this check fails. This
check is performed before any attempt to access any nonexistent element of
the array object.
!corrigendum 4.3.3(32)
Replace the paragraph:
When evaluating iterated_component_associations for an
array_aggregate that contains only iterated_component_associations
with iterator_specifications, the first step of evaluating an
iterated_component_association can be omitted if the implementation can
determine the maximum number of values by some other means.
by:
When evaluating iterated_component_associations for an
array_aggregate that contains only iterated_component_associations
with iterator_specifications, the first step of evaluating an
iterated_component_association can be omitted if the implementation can
determine the maximum number of values by some other means.
Such "other means" might include making use of an applicable index constraint
or the Length function of a suitable container type.
!ACATS test
ACATS C-Tests are needed to check that the counts must match, and that
Constraint_Error is raised if they do not.
!appendix
****************************************************************
Questions? Ask the ACAA Technical Agent