Version 1.5 of ai12s/ai12-0258-1.txt
!standard A.18(10/4) 18-04-26 AI12-0258-1/04
!class binding interpretation 18-02-26
!status Amendment 1-2012 18-03-05
!status WG9 Approved 16-06-22
!status ARG Approved 11-0-1 18-03-05
!status work item 18-02-26
!status received 18-01-28
!priority Low
!difficulty Easy
!qualifier Omission
!subject Containers and controlled element types
!summary
Requirements for the handling of controlled parts of container elements are
added.
!question
The Ada Standard does not say anything about how the
initialization/adjust/finalization of container elements with controlled
parts is accomplished. This makes it impossible to reliably use such types
with containers. Should this be specified? (Yes.)
!recommendation
(See Summary.)
!wording
[Editor's note: I've proposed putting this wording into the global part
of the containers package. Arguably, it would be better placed into each
of the sections for the appropriate containers -- but then there would be no
place to put the statement about there being no requirements on "normal"
containers.]
Add after A.18(10):
Implementation Requirements
For an indefinite container (one whose type is defined in an instance of a
child package of Containers whose defining_identifier contains "Indefinite"),
each element of the container shall be created when it is inserted into the
container and finalized when it is deleted from the container (or when the
container object is finalized if the element has not been deleted). For a
bounded container (one whose type is defined in an instance of a child
package of Containers whose defining_identifier starts with "Bounded") that
is not an indefinite container, all of the elements of the capacity of the
container shall be created and default initialized when the container object
is created; the elements shall be finalized when the container object is
finalized. Redundant[For other kinds of containers, when elements are created
and finalized is unspecified.]
AARM Ramification: This allows a user to be able to reason about the behavior
of elements that have controlled parts. In most cases, such elements need to
be stored in an indefinite container.
AARM Implementation Note: If the containers are implemented in Ada, this
implies that elements for an indefinite container are allocated individually,
and that a bounded container contains an array of elements or other data
structure that is initialized for the entire capacity of the container when
it is created. There is no such restriction on the implementation of the
"normal" containers; these can be handled in any way convenient to the
implementation -- in particular, node reuse is allowed.
Replace AARM A.18(5.r/2) with:
If a container's element type is controlled, the point at which the element is
finalized will depend on the implementation of the container. For certain
kinds of containers, we require finalization behavior based on the canonical
implementation of the container (see the Implementation Requirements below).
For the "normal" containers, we do not specify precisely where this will
happen (it will happen no later than the finalization of the container, of
course) in order to give implementations flexibility to cache, block, split,
or reuse the nodes of the container.
Delete AARM A.18(5.s/2).
!discussion
The original intent was that the semantics of controlled elements would follow
from the underlying Ada implementation - thus, no guarantees were given in
order to allow maximum flexibility for the implementation of the containers.
(See the Language Design Principles in AARM A.18(5.r-5.t/2) for a discussion.)
However, without some sort of guarantees, the containers are mostly useless
for containing elements of controlled types.
Consider a type that uses controlled operations to implement a smart pointer.
If such a type with a component of the smart pointer type is put into a
container, the elements could exist for a very long time, preventing the
designated objects from being reclaimed -- essentially voiding the value of
using a smart pointer in the first place.
Therefore, we provide some rules on what is expected for controlled types
when used in the containers. These suggest that a user who has controlled
parts in their element types must use an indefinite container. (Users with
memory management concerns can use a Bounded_Indefinite_Holder [see
AI12-0254-1] inside a bounded container to get the correct semantics).
!corrigendum A.18(10/2)
Insert after the paragraph:
- finalization of the collection of the access type has started if and
only if the finalization of the instance has started.
the new paragraph:
For an indefinite container (one whose type is defined in an instance of a
child package of Containers whose defining_identifier contains "Indefinite"),
each element of the container shall be created when it is inserted into the
container and finalized when it is deleted from the container (or when the
container object is finalized if the element has not been deleted). For a
bounded container (one whose type is defined in an instance of a child
package of Containers whose defining_identifier starts with "Bounded") that
is not an indefinite container, all of the elements of the capacity of the
container shall be created and default initialized when the container object
is created; the elements shall be finalized when the container object is
finalized. For other kinds of containers, when elements are created
and finalized is unspecified.
!ASIS
No ASIS effect.
!ACATS test
ACATS C-Tests can be created to check this for Indefinite and Bounded
containers (the other containers are unspecified). Probably check the
holders (including the new one) and a couple of others - exhaustive tests
would be exhausting :-), and wouldn't add a lot of value.
!appendix
From: Tucker Taft
Sent: Sunday, January 28, 2018 8:11 PM
We had a bug report from a customer complaining that for a Vector where the
element type was controlled, they were seeing default initializations happen
when the Vector was declared, rather than when Insert was called. Similarly,
they were not seeing finalization until the Vector as a whole went away,
rather than when individual elements were deleted. See the response I gave.
There isn't really anything in the RM to back up what I said, other than
indications of the intended implementation approach.
Comments?
Begin forwarded message:
From: "Tucker Taft @ adacore" <taft@adacore.com>
Subject: Re: [R123-016] #xxx Bounded containers declaring objects even when logically empty
Date: January 28, 2018 at 6:33:56 PM EST
To: ...
Cc: report@adacore.com
Hi ...,
I was asked to take a look at your example, and give my "language-lawyerly"
comments on your bug report.
From a language point of view, I would expect that deleting an element from a
container with *indefinite elements* would finalize the element, and creating
a new element would adjust it (no default initialization is defined for
indefinite types, in general).
However, for a container of definite elements, particularly for vectors and
bounded variants, the objects can be stored in an internal array, which is
initially created and later extended to some extent asynchronously with the
individual add/remove operations. The container as a whole undergoes default
initialization, and ultimately finalization, but there is no guarantee that
individual elements are separately default initialized and finalized.
I would agree that the Ada Reference Manual should clarify the semantics here,
however the implementation advice clearly recommends the use of an underlying
array, and the distinction between Capacity and Length indicates how the
physical length and the logical length need not agree.
I would suggest you use Indefinite_Vectors if you would like to postpone
default initialization until an element is added, and have finalization
performed when an element is removed.
Sincerely,
-Tucker Taft
****************************************************************
From: Randy Brukardt
Sent: Sunday, January 28, 2018 9:25 PM
I know this scenario was discussed at the time, and I'm pretty sure we decided
on the intended semantics. I suspect that we felt that it "obviously" followed
from the equivalences.
In the case of the bounded containers, which allow no allocation/deallocation,
it's hard to imagine any other semantics (if implemented in Ada). An array of
elements of "Capacity" is created when the container object is created, and is
destroyed when the container object is destroyed. That's the only place where
initialization/finalization would occur.
He probably needs to use the Bounded_Indefinite_xxx containers, assuming we
decide to add those.
Probably, we need to say something in the Standard, but the problem here is
that for the "normal" containers, it's hard to imagine what we could say (the
implementation can create and destroy nodes when it wants -- I would be
opposed to requiring that to happen at any particular point or in a specific
manner). We could *require* indefinite containers to do Init/Fin per element,
and bounded containers to do Init/Fin only when the container is
created/destroyed -- but not sure it is worth it.
****************************************************************
From: Tucker Taft
Sent: Monday, January 29, 2018 7:42 AM
I think we should add something about controlled components, if only to say it
is implementation defined.
****************************************************************
Questions? Ask the ACAA Technical Agent