!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 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) @dinsa @xbullet @dinst For an indefinite container (one whose type is defined in an instance of a child package of Containers whose @fa 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 @fa 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" 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. ****************************************************************