Version 1.3 of ai12s/ai12-0416-1.txt
!standard 5.5(9/5) 21-01-14 AI12-0416-1/05
!standard 6.1.2(17/5)
!standard 6.4(7)
!standard 6.4(9)
!standard 6.4(10.1/2)
!standard A.18(2/5)
!standard A.18(5/3)
!class Amendment 20-12-08
!status Amendment 1-2012 20-12-08
!status work item 20-12-08
!status received 20-12-08
!priority Low
!difficulty Easy
!subject Fixups from Draft 26 review - part 2
!summary
(1) We clarify the use of the term "container" in the introduction of
the Containers child packages.
(2) Define the term "smallest first".
(3) Add a sentence explaining when a parallel construct with a
loop_parameter_specification is complete.
(4) The equivalence of a call on a prefixed view with a normal call is
also true statically, so that the same Legality Rules are enforced on
the prefix as would be enforced on a normal call.
(5) Assertion expressions that apply to a call are included in the Global
aspect even when the expressions are not enabled.
(6) The rule about named associations following any positional associations
applies only to a single actual_parameter_part.
!problem
(1) The term "container type" is used in A.18(12/5). But the only definition
is associated with aggregates (see 4.3.5) and that isn't really appropriate
here.
(2) Is_Sorted "Returns True if the elements are sorted smallest first as
determined by the generic formal "<" operator; otherwise, Is_Sorted returns
False." Smallest first implies that the "<" has to return true for the
elements, but that doesn't seem to account for equal elements.
(3) Most parallel constructs have a definition of when the construct is
complete. For instance, 5.5.2(10.2/5) ends with " In the absence of a transfer
of control, the associated parallel construct of a parallel generalized
iterator is complete when all of its logical threads of control are complete".
There is similar wording in 5.6.1(3/5) for parallel blocks. But there is no
such wording for parallel loops that iterates over a discrete range, formally
one with a loop_parameter_specification.
(4) 6.4(10.1/2) [a Dynamic Semantics paragraph] says that a prefixed view
is equivalent to a call on the underlying subprogram. Equivalences defined
in Dynamic Semantics only apply to runtime (not Legality). There are a number
of Legality Rules that apply to calls, and in particular to parameters of
calls, which need to be enforced on prefixed views (and in particular the
prefix of a prefixed view). Two Legality Rules in 4.1.3 cover two
important cases, but there don't appear to be any language that would, for
instance, apply the anti-order dependence rules of 6.4.1 to the prefix of
a prefixed view as if it is a parameter.
(5) 6.1.2(17/5) includes "including any assertion expressions that might be
evaluated as part of the call, including preconditions, postconditions,
predicates, and type invariants." Does this include assertion expressions
that are not enabled? One can read it either way.
(6) 6.4(7) includes "Any positional associations shall precede any named
associations." This could be read to include nested calls. But we want
a call like:
Func1 (Param1 => True, Param_2 => Func2 (123))
to be legal.
!proposal
(1) Rewrite the basic introductory paragraph to clarify the meaning of
"container" by itself and mention that we can talk about more specific
things as well.
(2) Define "smallest first".
(3) Add a definition of when a parallel construct with a
loop_parameter_specification is complete.
(4) Move 6.4(10.1/2) to Static Semantics (after 6.4(9)).
(5) Add "(even if not enabled)" to 6.1.2(17/5) and make other improvements
to the wording.
(6) Prefix the rule with: "For the parameter_associations of a single actual_parameter_part
or iterator_actual_parameter_part,".
!wording
[Editor's note: These changes were applied to Draft 28 of the Ada 202x RM,
even though they have not yet been approved, in order that that draft be as
accurate as possible.]
(1) Modify A.18(2/5):
A variety of sequence and associative containers are provided. Each container
{package defines}[includes] a cursor type{ as well as a container type}. A
cursor is a reference to an element within a container. Many operations on
cursors are common to all of the containers. A cursor referencing an element
in a container is considered to be overlapping only with the element container
object itself.
AARM Discussion: We use the term "container" alone when it is clear from
context what kind of entity (package, type, or object) that we are talking
about. Otherwise, we use "container package", "container type", or
"container object". Note that "container type" is defined in 4.3.5 for a
different usage; in all of A.18 we mean "container type" to be one of the
primary types declared in the child packages of package COntainers, such as
Vector, List, or Map.
(2) Modify A.18(5/3):
When a formal function is used to provide an ordering for a container, it is
generally required to define a strict weak ordering. A function "<" defines
a /strict weak ordering/ if it is irreflexive, asymmetric, transitive, and in
addition, if x < y for any values x and y, then for all other values z,
(x < z) or (z < y).{ Elements are in a /smallest first/ order using such an
operator if, for every element y with a predecessor x in the order, (y < x) is
false.}
AARM Reason: Given a "<" operator that provides a strict weak ordering,
knowing that (y < x) is false is enough to know that (x <= y) is true.
For a strict weak ordering, (x = y) when both (x < y) and (y < x) are false.
Therefore, it is not necessary to use the "=" operator or test (x < y).
We only need to discuss adjacent elements since a strict weak ordering is
transitive.
(3) Add to the end of 5.5(9/5):
In the absence of a transfer of control, the associated parallel construct of
a loop_parameter_specification is complete when all of its logical threads of
control are complete.
Add an AARM note:
Discussion: The rules for completing a parallel construct when there is
a transfer of control are given in 5.1.
(4) Move 6.4(10.1/2) after 6.4(9), under the heading "Static Semantics".
(5) Modify 6.1.2(17/5):
The Global aspect for a callable entity defines the global variables that
might be referenced as part of a call on the entity, including any assertion
expressions that [might be evaluated as part of]{apply to} the call{ (even
if not enabled)}, including preconditions, postconditions, predicates, and
type invariants.
(6) Modify 6.4(7):
A parameter_association is named or positional according to whether or not the
formal_parameter_selector_name is specified. {For the parameter_associations
of a single actual_parameter_part or iterator_actual_parameter_part, any}[Any]
positional associations shall
precede any named associations. Named associations are not allowed if the prefix
in a subprogram call is an attribute_reference.
!discussion
(1) The term "container" appears to be used to mean whatever is convinient in
paragraph A.18(2/2). The first sentence sounds like it could mean any of
package/type/object. The second sentence only makes sense for a package
(nothing is "included" in a type or object). The third sentence works for a
type or object, but not a package (can't reference an element in a package,
only within an object or [abstractly] a type). The fourth sentence is like
the first.
We try to clarify the meaning a bit. It probably would be better to have a
more extensive terminology paragraph before this one, but even if we did that,
this one would need to be less vague.
We use the terms "container type" and "container package" in other wording,
and it is best to introduce them early.
(2) If elements A and B are equal, then A < B and B < A are both False.
That means that the correct "smallest first" test isn't simply A < B (if A is
the "first" element), but (A < B) or (not B < A). (This corresponds to less
than or equal.) Because of the requirements of a "strict weak ordering",
if (A < B) is True, then B < A has to be False. So testing (not B < A) is
sufficient.
We define "smallest first" accordingly. We use "predecessor" as that is the
term used in defining the list containers, and it works as an English term
when applied to the Vector containers and to arrays.
(3) While 7.6.1(2/2) makes it clear that a sequential construct is "complete"
when its execution ends, it is much less clear when the execution ends for a
parallel construct made up of several threads, all of which will end at
different times. We felt it necessary to explain this for other parallel
constructs, and it seems necessary to do so for normal parallel loops as well.
Note that we talk about the "associated parallel construct" so that this
wording clearly applies to parallel reductions and any other parallel
constructs using a loop_parameter_specification (as does the wording for
other kinds of iterators). It's not just about parallel loops.
(4) The rules that are needed for a call on a prefixed view include:
* Calling an abstract subprogram with a nondispatching call is illegal;
* An in out or out parameter shall be a variable;
* The anti-order-dependence rules of 6.4.1;
* The accessibility checks on an aliased parameter of a function.
In particular, rules that apply to actual parameters have to apply to the
prefix of a prefixed view. It isn't practical to copy all of those into
4.1.3.
The first rule (and any rule that depends only on the subprogram's properties)
is covered by the sentence in 4.1.3(9.2/3): "The selected_component denotes a
view of this subprogram that omits the first formal parameter." But this does
not cover any rule that applies to the actual parameter of a call (since the
prefix is not defined to be such a parameter).
The equivalence in 6.4(10.1/2) covers runtime rules, but it doesn't apply
at compile-time (as noted in the question).
In order to make that equivalence apply at compile time, we need to change
the equivalence to Static Semantics.
Note that the Legality Rules 4.1.3(13.1/5) and 4.1.3(13.2/2) are NOT
redundant. They apply not just to calls, but also to uses of a prefixed view
that are not a call (in particular, a renaming, use as an actual for
a formal subprogram, or use as the prefix of 'Access [the last is only
possible for access-to-protected-procedures]).
It's likely that there are still some rules that should fail for a prefixed
view that is not a call (especially if prefixed views are extended to untagged
types in a future Ada). These cases seem unlikely enough that it isn't worth
worrying about them now. For instance, it seems that one could "hide" a local
object in a renaming and evade the accessibility check on an aliased parameter
of a function. But for that to happen, one needs a local renaming of a prefixed
view of a function returning a composite type, with the prefix being a local,
AND the function being called in a library-level context (such as being used
to initialize an allocator of a library-level access type). [Editor's note:
No simple fix seems available for this particular issue, which is why we chose
to ignore it at this point.]
(5) We try to avoid having Legality Rules depend on whether an assertion is
enabled. That prevents the act of enabling an assertion from changing the
legality of a program unit.
The "might" in the original wording was intended to convey that, but it is
clearly not enough to make that point. So we explicitly mention that assertion
expressions that are "not enabled" are included.
(6) We need to clarify that we are talking only about a single list of
parameters. Otherwise, the rule could be interpreted textually. We have
fixed the wording of a number of other rules over the years that have
similar nesting problems (for instance, the naming of subtypes in
access-to-subprogram types are not the subtypes associated directly
with conformance of a parameter list). Now that it has been noted, this one
should be fixed, too.
Note that iterator_actual_parameter_part also uses parameter_associations,
so this rule also applies to them and we want that to remain the case.
Generic instantiations has their own rule for this; it doesn't need a fix
since instances cannot be nested.
!corrigendum 5.5(9/4)
Replace the paragraph:
For the execution of a loop_statement with the iteration_scheme
being for loop_parameter_specification,
the loop_parameter_specification is first elaborated. This
elaboration creates the loop parameter and elaborates the
discrete_subtype_definition.
If the discrete_subtype_definition defines a subtype with a null range,
the execution of the loop_statement is complete. Otherwise, the
sequence_of_statements is executed once for each value of the
discrete subtype defined by the discrete_subtype_definition 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 reverse is present, in which case the values
are assigned in decreasing order.
by:
For the execution of a loop_statement that has an iteration_scheme
including a loop_parameter_specification, after elaborating the
chunk_specification and aspect_specification, if any,
the loop_parameter_specification is
elaborated. This elaborates the discrete_subtype_definition,
which defines the subtype of the loop parameter. If the
discrete_subtype_definition defines a subtype
with a null range, the execution of the loop_statement is complete.
Otherwise, the sequence_of_statements is conditionally executed once for
each value of
the discrete subtype defined by the discrete_subtype_definition 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 associated with the given iteration. If the loop is a parallel
loop, each chunk has its own logical thread of control with its own copy
of the loop parameter; otherwise (a sequential loop), a single logical
thread of control performs the loop, and there is a single copy of the
loop parameter. Each logical thread of control handles a distinct
subrange of the values of the subtype of the loop parameter such that
all values are covered with no overlaps. Within each logical thread of
control, the values are assigned to the loop parameter in increasing
order unless the reserved word reverse is present, in which case the
values are assigned in decreasing order. In the absence of a transfer of
control, the associated parallel construct of a loop_parameter_specification
is complete when all of its logical threads of control are complete.
If a chunk_specification with a discrete_subtype_definition is
present, then the logical thread of control associated with a given chunk
has its own copy of the chunk parameter initialized with a distinct value from
the discrete subtype defined by the discrete_subtype_definition. The
values of the chunk parameters are assigned such that they increase with
increasing values of the ranges covered by the corresponding loop
parameters.
Whether or not a chunk_specification is present in a parallel loop,
the total number of iterations of the loop represents an upper bound
on the number of logical threads of control devoted to the loop.
!corrigendum 6.1.2(0)
Insert new clause:
See the conflict file for the changes.
!corrigendum 6.4(7)
Replace the paragraph:
A parameter_association is named or positional according to
whether or not the formal_parameter_selector_name is specified.
Any positional associations shall precede any named associations. Named
associations are not allowed if the prefix in a subprogram call is
an attribute_reference.
by:
A parameter_association is named or positional according to
whether or not the formal_parameter_selector_name is specified.
For the parameter_associations of a single actual_parameter_part
or iterator_actual_parameter_part, any positional associations shall
precede any named associations. Named associations are not allowed if the
prefix in a subprogram call is an attribute_reference.
!corrigendum 6.4(9)
Insert after the paragraph:
A subprogram call shall contain at most one association for each formal
parameter. Each formal parameter without an association shall have a
default_expression (in the profile of the view denoted by the name
or prefix). This rule is an overloading rule (see 8.6).
the new paragraph:
Static Semantics
If the name or prefix of a subprogram call denotes a prefixed view
(see 4.1.3), the subprogram call is equivalent to a call on the underlying
subprogram, with the first actual parameter being provided by the prefix
of the prefixed view (or the Access attribute of this prefix if the first
formal parameter is an access parameter), and the remaining actual parameters
given by the actual_parameter_part, if any.
!corrigendum 6.4(10.1/2)
Delete the paragraph:
If the name or prefix of a subprogram call denotes a prefixed view
(see 4.1.3), the subprogram call is equivalent to a call on the underlying
subprogram, with the first actual parameter being provided by the prefix
of the prefixed view (or the Access attribute of this prefix if the first
formal parameter is an access parameter), and the remaining actual parameters
given by the actual_parameter_part, if any.
!corrigendum A.18(2/2)
Replace the paragraph:
A variety of sequence and associative containers are provided. Each container
includes a cursor type. A cursor is a reference to an element within a
container. Many operations on cursors are common to all of the containers. A
cursor referencing an element in a container is considered to be overlapping
with the container object itself.
by:
A variety of sequence and associative containers are provided. Each container
package defines a cursor type as well as a container type. A cursor is a
reference to an element within a container. Many operations on cursors are
common to all of the containers. A cursor referencing an element in a container
is considered to be overlapping only with the element itself.
!corrigendum A.18(5/3)
Replace the paragraph:
When a formal function is used to provide an ordering for a container,
it is generally required to define a strict weak ordering. A function "<"
defines a strict weak ordering if it is irreflexive,
asymmetric, transitive, and in addition, if x < y for
any values x and y, then for all other values z,
(x < z) or (z < y).
by:
When a formal function is used to provide an ordering for a container,
it is generally required to define a strict weak ordering. A function "<"
defines a strict weak ordering if it is irreflexive,
asymmetric, transitive, and in addition, if x < y for
any values x and y, then for all other values z,
(x < z) or (z < y). Elements are in a smallest first order
using such an operator if, for every element y with a predecessor x in
the order, (y < x) is false.
!ASIS
No ASIS effect.
!ACATS test
No ACATS Tests needed.
!appendix
From the RM Review of Jean-Pierre Rosen, October 2020
A.18(12/5)
For an instance I of a container package with a container type C,
the specific type T of the object returned from a function that
returns an object of an iterator interface, as well as the primitive
operations of T, shall be nonblocking. The Global aspect specified
for T and the primitive operations of T shall be (in all, out
synchronized) or a specification that allows access to fewer global
objects.
I find this paragraph strange. What is a "container type"? The type on
which the container is instantiated? Then it would rather be the
contained type... And "C" is not referred to anywhere.
****************************************************************
From: Randy Brukardt
Sent: Tuesday, December 8, 2020 1:37 AM
In Jean-Pierre's review, he comments on the definition of Is_Sorted:
The case of equal elements is not addressed. Shouldn't it be:
Returns True if the elements are sorted smallest first as determined
by the generic formal "<" operator {and the generic formal "="
operator}
The direct answer here is clear: No, we don't use the formal "=" for sorting.
Doing so would greatly complicate the rules for "<" as one would need to worry
about interactions with "=" as well as defining a strict weak ordering.
Moreover, we want the vector sort and the array sort to work the same way, and
there is no "=" passed into the array sort. And we want to support multiple
sorts for each container (or array), just using different "<" operations, the
equality of elements might even be different for each ordering operation.
But there still seems to be a problem here, in that the wording doesn't take
into account the possibility of equal elements. Elements are "equal" if
neither A < B nor B < A are True. "Smallest first" seems to imply that A < B
for all adjacent A and B, but that's not right. The actual requirement has to
be
(A < B) or (not B < A)
I'm not sure of the best way to fix this. The pedantic solution would be to
formally define "smallest first" (that's never done in the RM). We could put
that directly below the "strict weak ordering" paragraph in A.18. That would
be something like:
Elements are in a /smallest first/ order using an operator "<" if for an
element A that precedes an element B, (A < B) or (not B < A) is True.
AARM Reason: For elements that are considered equal for a particular "<"
operator, neither A < B nor B < A are true.
Or we could add a To Be Honest AARM note somewhere that says something similar.
Or we could somehow try to handle equal elements explicitly in the wording.
(I don't have an idea for this.)
I do think we need to say something somewhere, as we certainly mean to allow
sorting equal elements and for Is_Sorted to give a meaningful answer if there
are equal elements. And it's not obvious, as J-P's comment shows.
Thoughts?
****************************************************************
From: Randy Brukardt
Sent: Tuesday, December 8, 2020 9:11 PM
...
> Or we could somehow try to handle equal elements explicitly in the
> wording.
> (I don't have an idea for this.)
Another idea would be to write Is_Sorted as an expression function, in which
case no wording would be needed. The sort operation can then be defined in
terms of Is_Sorted, again eliminating the English wording. But that wouldn't
help the array sorts, which don't have an Is_Sorted operation.
> I do think we need to say something somewhere, as we certainly mean to
> allow sorting equal elements and for Is_Sorted to give a meaningful
> answer if there are equal elements. And it's not obvious, as J-P's
> comment shows.
>
> Thoughts?
Still waiting for thoughts. :-)
I haven't written this up yet, as I don't know the solution to use.
****************************************************************
From the AARM review of Steve Baird, October 2020
In 6.1.2(17/5) we talk about assertion expressions. Interactions with
assertion policy need to be clarified.
****************************************************************
Editor's response to the above (January 2021)
Clearly, we don't want Legality to change when something is enabled or
disabled, so I'd suggest adding "even if they are disabled".
Tucker suggests rewording as:
... defines the global variables that might be referenced as part of a
call on the entity, including any assertion expressions that [might be
evaluated as part of]{apply to} the call{ (even if not enabled)},
including preconditions, postconditions, predicates, and type invariants.
****************************************************************
From the AARM review of Steve Baird, October 2020
I suppose we *could* clarify that the rule 6.4(7):
Any positional associations shall precede any named associations.
applies separately to each actual_parameter_part. For example, the
following function call is legal:
Func1 (Param1 => True, Param_2 => Func2 (123))
even though a positional association "follows" a named association.
****************************************************************
Editor's response to the above (January 2021)
This actually bothers me enough to worry about it. We've generally fixed such
issues when they've been noted, for instance for subtypes in subprogram
declarations. We probably should prefix this rule with "For the
parameter_associations of a single actual_parameter_part or
iterator_actual_parameter_part,".
****************************************************************
Questions? Ask the ACAA Technical Agent