Version 1.1 of ai12s/ai12-0350-1.txt

Unformatted version of ai12s/ai12-0350-1.txt version 1.1
Other versions for file ai12s/ai12-0350-1.txt

!standard A.18.18(22/5)          20-01-09 AI12-0350-1/01
!standard A.18.18(67/5)
!standard A.18.18(73/3)
!standard A.18.32(13/5)
!class Amendment 20-01-09
!status work item 20-01-09
!status received 19-12-24
!priority Low
!difficulty Easy
!subject Swap for Indefinite_Holders
!summary
A Swap operation is added to both Indefinite_Holders and Bounded_Indefinite_Holders.
The Move operation of a Bounded_Indefinite_Holder can copy the element.
!problem
(1) It can be useful to Swap one holder value with another. For the Indefinite_Holders, such an operation can avoid copying the element (avoiding any associated Adjust/Finalize calls); this cannot be done without involving an extra holder.
(2) A.18.18(73/3) is Implementation Advice that Move for an Indefinite_Holder should not copy the element. The rules of A.18.18 apply to Bounded_Indefinite_Holders unless otherwise specified. But Move without copying doesn't make sense for a bounded container (the memory buffer is part of the object and surely isn't moving), so this Advice should be deleted for Bounded_Indefinite_Holders.
!proposal
(See Summary.)
!wording
(1) Add after A.18.18(22/5):
procedure Swap (Left, Right : in out Holder) with Pre => (if Tampering_With_The_Element_Prohibited (Left) then raise Program_Error elsif Tampering_With_The_Element_Prohibited (Right) then raise Program_Error), Post => Is_Empty (Left) = Is_Empty (Right)'Old and then Is_Empty (Right) = Is_Empty (Left)'Old;
Add after A.18.18(67/5):
procedure Swap (Left, Right : in out Holder) with Pre => (if Tampering_With_The_Element_Prohibited (Left) then raise Program_Error elsif Tampering_With_The_Element_Prohibited (Right) then raise Program_Error), Post => Is_Empty (Left) = Is_Empty (Right)'Old and then Is_Empty (Right) = Is_Empty (Left)'Old;
If Left denotes the same object as Right, then the operation has no effect. Otherwise, operation exchanges the elements (if any) contained by Left and Right.
Modify A.18.18(73/3):
Move (and Swap) should not copy {any elements}[the element], and should minimize copying of internal data structures.
(1) Add after A.18.32(13/5):
The Implementation Advice about the Move and Swap operations is deleted for bounded holders; these operations can copy elements as needed.
AARM Reason: The memory of a bounded indefinite holder belongs directly to the container, so it cannot be moved with the element. If the element type contains any internal pointers, moving it without calling Adjust would leave such pointers pointing to the wrong holder object. Thus, a full copy is needed, including any associated finalization and adjustments.
!discussion
!ASIS
No ASIS effect.
!ACATS test
An ACATS C-Test for Swap for each container is needed.
!appendix

!topic Swap operations for (Bounded) Indefinite Holders
!reference Ada 202x RMA.18.18
!from Niklas Holsti 19-12-24
!keywords bounded indefinite holder swap
!discussion

My potential applications for Indefinite_Holders and 
Bounded_Indefinite_Holders often need to move elements between holders 
within protected operations. For example, a single holder can be used as 
a message mailbox between two tasks, or a one-dim array of holders can 
be used as a queue of inter-task messages.

However, some of these indefinite "held" element types can require 
non-trivial initialization and finalization, and sometimes those 
operations involve reference counts or other things that need to be 
updated in another protected operation. This nesting of protected calls 
leads to concerns about the priorities of those protected operations and 
potential violations of the ceiling-priority rule.

For such applications, using the presently proposed operations for 
(Bounded) Indefinite Holders poses problems, because -- as I understand 
it -- moving an element from one holder to another holder (which may be 
initially non-empty) can at present only be done by sequences of 
operations that require copying and assigning of element values, which 
may invoke finalization and initialization and adjustment.

In my applications that could make use of (bounded) indefinite holders 
(and currently use "holders" implemented by variant record types) these 
problems are solved by defining a "swap" operation that exchanges the 
contents of the two holders. Such a swap operation does not, in 
principle, require initialization or finalization of any element value, 
nor do any reference counts need to be updated.

Therefore I suggest that the generic packages Indefinite_Holders and 
Bounded_Indefinite_Holders should be extended to provide a Swap 
procedure defined as follows:

    procedure Swap (This, That : in out Holder)

with the result that anything that is true of This before the Swap 
(Is_Empty or not, value of the Element function) is true of That after 
the Swap, and vice versa for That and This. Moreover, it should be 
required that the elements, if any, held in This and That are neither 
finalized nor initialized nor adjusted in the Swap operation.

A not-so-nice aspect of this proposal is that such a Swap operation 
cannot be implemented in Ada itself, as I understand it. However, in 
earlier discussions about indefinite holders Randy Brukardt opined that 
the ability to implement standard container types in Ada itself is not a 
requirement.

I also have a related question about the present RM text:

There are already Swap procedures for some container types, for example 
Vectors, which swap two elements of the container, with the brief 
explanation that "Swap exchanges the values" of the elements. Nothing 
seems to be said in the RM about initialization or finalization or 
adjustment of the elements. Can we assume that no initializations or 
finalizations or adjustments are involved in Vectors.Swap and the other 
existing Swap operations? If so, why can we assume it?

****************************************************************

From: Tucker Taft
Sent: Thursday, December 26, 2019  2:12 PM

Thanks for the suggestion, and the point about missing documentation on the 
init/adjust/finalization implications of "Swap."  We will look into the wording 
to clarify that issue, and consider adding a "Swap" to indefinite holders.  
Probably the appropriate place to suggest that init/adjust/finalization should 
be omitted is in Implementation Advice, in analogy with the existing wording 
about "Move."

****************************************************************

From: Niklas Holsti
Sent: Thursday, December 26, 2019  2:30 PM

Thanks for your kind response, Tuck. Your plan seems ok to me.

****************************************************************

From: Randy Brukardt
Sent: Thursday, December 26, 2019  2:51 PM

> Thanks for the suggestion, and the point about missing documentation 
> on the init/adjust/finalization implications of "Swap."

It's not "missing", it's supposed to be crystal-clear that Swap (as opposed 
to Swap_Links) does a copy, just like any other container operation.

> We will look into the wording to clarify that issue, and consider 
> adding a "Swap" to indefinite holders.

> Probably
> the appropriate place to suggest that init/adjust/finalization should 
> be omitted is in Implementation Advice, in analogy with the existing 
> wording about "Move."

*If* we wanted to do that (hint: we don't :-), that would make sense. See my 
upcoming response to Niklas' original message for much more.

****************************************************************

From: Randy Brukardt
Sent: Thursday, December 26, 2019  4:02 PM

...
> In my applications that could make use of (bounded) 
> indefinite holders (and currently use "holders" implemented 
> by variant record types) these problems are solved by 
> defining a "swap" operation that exchanges the contents of 
> the two holders. Such a swap operation does not, in 
> principle, require initialization or finalization of any 
> element value, nor do any reference counts need to be updated.

Sorry, this is semantic nonsense. The model of finalization requires that
*every* operation that copies an object does appropriate
adjust/finalization. Otherwise, it would be impossible to build a
bullet-proof abstraction since one would not be able to ensure that the
access values all point appropriately.

Ada does not have a "move" operation; in order to do so, there would need to
be an Adjust-like operation to appropriately fix-up an values as needed when
movement occurs.

Of course, there might exist on a particular implementation some type that
could be moved without trouble, but that is definitely not the normal case.

First of all, the canonical implementation for controlled types involves a
linked-list of controlled objects. (Janus/Ada uses this implementation, Gnat
does not.) In such an implementation, moving the object without adjusting
the pointers that point at the object would leave the list pointing at an
object that is probably getting used for something else (no longer a
controlled object). This would break the linked list, and near immediate
program failure (often a GP fault) follows. (I have plenty of experience
with this from various compiler bugs. ;-)

One could imagine requiring some sort of internal "move" operation to fix up
such pointers. However, it is quite common for controlled abstractions to
link all of the objects of the type together. Claw, for instance, uses this
method to be able to find and invalidate objects when the human user
forcibly closes them.

Again, if the object gets moved without doing the adjust, then the linked
list gets broken, with disasterous results. The only way to avoid that
problem is call the operations that are designed to fix up the links -- that
is, Adjust and Finalize.

Essentially, any operation that doesn't call Initialize/Adjust/Finalize as
defined by the language has to be defined to be erroneous. Any other result
would break many existing controlled types and essentially neuter the
controlled facility enough so that it couldn't be used to solve most of the
problems it currently does solve.

> Therefore I suggest that the generic packages 
> Indefinite_Holders and Bounded_Indefinite_Holders should be 
> extended to provide a Swap procedure defined as follows:
> 
>     procedure Swap (This, That : in out Holder)
> 
> with the result that anything that is true of This before the 
> Swap (Is_Empty or not, value of the Element function) is true 
> of That after the Swap, and vice versa for That and This. 

This is fine.

> Moreover, it should be required that the elements, if any, 
> held in This and That are neither finalized nor initialized 
> nor adjusted in the Swap operation.

This is not fine. As detailed above, any such operation is necessarily
erroneous or the model of controlled types is unusable. Moreover, for
Janus/Ada at least (and any implementation that followed the canonical
chained implementation of controlled types), the program will almost
immediately fault (probably the next time that a finalization operation
occurs). Swap has to have at least the option of copying the elements, and
should never eliminate a copy unless it is allowed to do so by 7.6.
(Specifically, 7.6(22-27).)

> A not-so-nice aspect of this proposal is that such a Swap 
> operation cannot be implemented in Ada itself, as I 
> understand it. However, in earlier discussions about 
> indefinite holders Randy Brukardt opined that the ability to 
> implement standard container types in Ada itself is not a requirement.

I think you misunderstood my point. There's no requirement in Ada that the
containers (or any other language defined units) be implemented in Ada.
However, we try to keep "magic" units to a minimum, because they complicate
the semantics of the language (which also complicates its use). So while
there are things like Unchecked_Conversion and
Generic_Dispatching_Constructor, most packages like Text_IO and the
containers *could* be implemented in Ada.

Ada users have an expectation that when they see a subprogram declaration,
it will operate like any other similar declaration that they could write. We
don't want to get very far away from that ideal, for any language-defined
unit.
 
> I also have a related question about the present RM text:
> 
> There are already Swap procedures for some container types, 
> for example Vectors, which swap two elements of the 
> container, with the brief explanation that "Swap exchanges 
> the values" of the elements. Nothing seems to be said in the 
> RM about initialization or finalization or adjustment of the 
> elements. Can we assume that no initializations or 
> finalizations or adjustments are involved in Vectors.Swap and 
> the other existing Swap operations? If so, why can we assume it?

This is part of the basic semantics of containers. All container operations
copy elements, complete with Initialize/Adjust/Finalize if that is
necessary, unless the operation explicitly states otherwise. This is
described in the introductory material to the containers, specifically the
lengthy Language Design notes in A.18.

Moreover, the basic semantics of Ada certainly apply to the containers
unless there is text to the contrary. If an object is copied, then the
normal Adjust/Finalize semantics will apply to it

Additionally, the AARM notes associated with Swap and Swap_Links in A.18.3
specifically say that Swap "may" copy the element and Swap_Links does not.
Similarly, the AARM notes Swap in A.18.2 says that a copy "is allowed".

Note that AI12-0258-1 says that there is no guarentee of where
Initialize/Adjust/Finalize occur in definite containers. So the vague
wording is appropriate for the definite containers.

Argubly, there should be stricter rules for copying of elements for the
indefinite containers, but we did consider such things in AI12-0258-1 and
decided to only tie down creation and destruction of the elements.
Otherwise, we would need a description of copying for every container
operation that involves an element (which would be a *lot* of text), and
moreover, we would pretty much be requiring a specific implementation for
the containers (preventing anything clever). We definitely have not wanted
to do that (this is why a container that takes a Storage_Pool would not be
very useful).

I note that A.18(11/5) gives conflicting requirements for a
Bounded_Indefinite_Holder (which is both Bounded and Indefinite); the
"Indefinite" requirement should have priority (this has to be true since one
doesn't necessarily even know the exact type of the element). 

Anyway, as noted above, any other solution would totally destroy the model
of controlled types such that they couldn't be used for the vast majority of
purposes that they are used for today. (Or, one would have to add a
"Move_Adjust" operation to controlled types, but then existing types would
not have it, and would have to raise Program_Error unless they defined it,
lest their implementation be completely trashed by a move.)

In a less important issue, I don't understand your usage case. The
Bounded_Indefinite_Containers are intended to be used in environments where
access types aren't allowed. But you are talking about controlled types
containing protected objects, which necessarily are limited unless they
contain access types. And the containers only can be instantiated with
copyable non-limited types, so you can't even put such objects into a
container.

Moreover, since Ada allows unlimited (safe) optimizations of non-limited
controlled types, it is not safe to use a non-limited controlled type as a
lock object. The compiler could completely eliminate the object as being
unused, and that would be a correct implementation of Ada. Such
optimizations are *not* permitted for limited controlled objects, so if the
presence of a Finalization matters in some context, the type has to be a
limited controlled object.

It would be nice if we had a way to create containers for limited objects,
but we haven't managed to find a usable model to date. (Well, at least I
haven't, nor has one been proposed by someone else.) Also note that it is
never safe (or allowed) to "move" a limited object -- the location of a
limited object is considered part of its value. One has to create a new
limited object at a new location and destroy the old one.

All of this is a long way to say that I don't think you are (or could be)
getting what you want in the usage that you are proposing here. It *might*
work for a particular implementation at a particular time, but there is no
reason to assume that will continue, since you are well outside of the
requirements of Ada semantics.

The "correct" way to do this is with a level of indirection, where the
objects are either aliased or allocated as needed. I could imagine trying to
design a container around such a scheme, but it would have to be rather
different than the existing containers (since precise control over the
creation and destruction of objects is needed, the objects have to be
individually allocated, and most importantly, copying is never allowed -- so
operations like Element and Replace_Element can't exist).

The best way to deal with this problem would be to invent a limited
container family that has "no copying" semantics -- Ada pretty much always
allows non-limited objects to be copied when convinient for the
implementation, and especially for the containers. That seems like too big a
job for Ada 202x, but let's see what others say.

****************************************************************

From: Niklas Holsti
Sent: Monday, January 6, 2020  11:30 PM

Thanks for your critique, Randy. I make some comments below. And 
apologies for taking so long to answer (I tried to think of some way to 
get around your valid objections).

> ...
>> In my applications that could make use of (bounded)
>> indefinite holders (and currently use "holders" implemented
>> by variant record types) these problems are solved by
>> defining a "swap" operation that exchanges the contents of
>> the two holders. Such a swap operation does not, in
>> principle, require initialization or finalization of any
>> element value, nor do any reference counts need to be updated.
> 
> Sorry, this is semantic nonsense. The model of finalization requires that
> *every* operation that copies an object does appropriate
> adjust/finalization. Otherwise, it would be impossible to build a
> bullet-proof abstraction since one would not be able to ensure that the
> access values all point appropriately.

Right, I forgot about the possibility of such access values. Perhaps 
because I haven't yet made much use of controlled types.

> Ada does not have a "move" operation; in order to do so, there would need to
> be an Adjust-like operation to appropriately fix-up an values as needed when
> movement occurs.

This absence is perhaps a pity, since "move" and especially "swap" would 
be useful for managing memory based on reference counts or "single 
owner" (linear logic) principles. Oh well.

> First of all, the canonical implementation for controlled types involves a
> linked-list of controlled objects. (Janus/Ada uses this implementation, Gnat
> does not.) In such an implementation, moving the object without adjusting
> the pointers that point at the object would leave the list pointing at an
> object that is probably getting used for something else (no longer a
> controlled object).

Yeah. At least if the moved element has some controlled sub-components, 
I would think.

>> Therefore I suggest that the generic packages
>> Indefinite_Holders and Bounded_Indefinite_Holders should be
>> extended to provide a Swap procedure defined as follows:
>>
>>      procedure Swap (This, That : in out Holder)
>>
>> with the result that anything that is true of This before the
>> Swap (Is_Empty or not, value of the Element function) is true
>> of That after the Swap, and vice versa for That and This.
> 
> This is fine.

Ok. The pressures that led to my original proposal and ended up in 
Bounded_Indefinite_Holders had two parts, relative to the applications 
that caused these pressures:

1) To replace my use of variant record types, and the related manual, 
variant-dependent "case" dispatching, with class-wide types and 
automatic dispatching. It seems Bounded_Indefinite_Holders provides 
that, so far so good.

2) To replace manual reference-count management (calls to "increment" 
and "decrement" operations) with automatic calls for controlled types. 
This now seems impossible, for my applications, in conjunction with 
Bounded_Indefinite_Holders, because the reference-counts have to be 
task-safe, and a Swap operation with a controlled element-type would 
typically require several reference-count updates, each a protected 
call, and I'm afraid that could be too much overhead for my applications 
(e.g. Swap would sometimes be called from interrupt handlers).

Oh well, one out of two isn't bad. Apologies for the noise, as usual.

>> I also have a related question about the present RM text:
>>
>> There are already Swap procedures for some container types,
>> for example Vectors, which swap two elements of the
>> container, with the brief explanation that "Swap exchanges
>> the values" of the elements. Nothing seems to be said in the
>> RM about initialization or finalization or adjustment of the
>> elements. Can we assume that no initializations or
>> finalizations or adjustments are involved in Vectors.Swap and
>> the other existing Swap operations? If so, why can we assume it?
> 
> This is part of the basic semantics of containers. All container operations
> copy elements, complete with Initialize/Adjust/Finalize if that is
> necessary, unless the operation explicitly states otherwise. This is
> described in the introductory material to the containers, specifically the
> lengthy Language Design notes in A.18.

In the Annotated ARM, I see. But I don't see a very explicit statement 
in those Language Design notes in AARM 2012 A.18 nor in the 202X version 
(in fact, the only references to copying have been deleted in 202X). But 
there are more explicit statements for Swap and Swap_Links in the AARM, 
as you say later.

> In a less important issue, I don't understand your usage case. The
> Bounded_Indefinite_Containers are intended to be used in environments where
> access types aren't allowed. But you are talking about controlled types
> containing protected objects, which necessarily are limited unless they
> contain access types.

Sorry if I was unclear. I'm talking about elements in 
Bounded_Indefinite_Holders that are (or contain) references (but not 
necessarily access values -- could be just array indices) to some other 
objects (typically largish, statically allocated communication buffers) 
that are managed by counting the number of such references to each such 
object. The reference counts are updated by protected operations in a 
single protected object that is not part of the "held" elements.

It seems to me that this is typical manually implemented allocation and 
release of storage in a statically allocated set of storage "slots", 
some of which are in use at any given time, and some are not in use (free).

This is like "shared_ptr" in C++, but using an application-defined, 
statically allocated, type-specific "heap" instead of the system heap, 
and implementing it in a task-safe way.

In the original design of the library that is used in these 
applications, these "reference" types were limited, with a "copy" 
operation that maintains the reference counts. However, users of the 
library were bothered by the contagious nature of "limited", so the 
"reference" types are now non-limited, and users are allowed to copy 
"reference" values, which is error-prone. But the non-limited nature is 
necessary if I want to use Bounded_Indefinite_Holders, as you point out.

> Moreover, since Ada allows unlimited (safe) optimizations of non-limited
> controlled types, it is not safe to use a non-limited controlled type as a
> lock object. The compiler could completely eliminate the object as being
> unused, and that would be a correct implementation of Ada.

These "reference" objects (elements in the Bounded_Indefinite_Holders) 
are not used as locks, if I understand your meaning.

For initialization and finalization, the Language Design notes in AARM 
202X A.18 refer to the Implementation Requirements of the same section, 
which say that for indefinite containers, "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)", with the Ramification 
"This allows a user to be able to reason about the behavior of elements 
that have controlled parts".

This seems to promise that using controlled types with an indefinite 
container will work, at least for Initialize and Finalize. But Adjust is 
not mentioned.

Can I assume, for example, that (Bounded_)Indefinite_Holders.Clear will 
finalize the held element, if any?

And that (Bounded_)Indefinite_Holders.Replace_Element will perform the 
same Initialize, Adjust and Finalize calls that an assignment statement 
canonically would do? That is, Finalize the old element (if any), 
overwrite it with the new element, and then Adjust the result (perhaps 
via an anonymous temporary)?

> Such
> optimizations are *not* permitted for limited controlled objects, so if the
> presence of a Finalization matters in some context, the type has to be a
> limited controlled object.

I live (so far) and (perhaps) learn. However, I hope that the A.18 rules 
for indefinite containers, quoted above, solve this problem in this 
scenario.

I seem to remember seeing implementations of reference-counted pointers 
as non-limited controlled types, with Adjust incrementing the reference 
count and Finalize decrementing it. But perhaps those implementations 
were wrong for the reasons you give?

> The "correct" way to do this is with a level of indirection, where the
> objects are either aliased or allocated as needed.

That is what the "reference" types I described, above, attempt to 
achieve. But as the referenced objects are statically allocated -- 
typically, elements of a statically allocated array -- and typically 
referenced by indices rather than access values, they do not have to be 
aliased.

> I could imagine trying to design a container around such a scheme,
> but it would have to be rather different than the existing containers
> (since precise control over the creation and destruction of objects
> is needed, the objects have to be individually allocated, and most
> importantly, copying is never allowed -- so operations like Element
> and Replace_Element can't exist).
> 
> The best way to deal with this problem would be to invent a limited 
> container family that has "no copying" semantics -- Ada pretty much
> always allows non-limited objects to be copied when convinient for
> the implementation, and especially for the containers.
In view of the major problems in my suggestion, I think it can be buried 
without further ado.

While studying this issue, I noticed that the operation 
Indefinite_Holders.Move has Implementation Advice saying "Move should 
not copy the element" (because a pointer can be moved instead). The way 
Bounded_Indefinite_Holders is now defined, in A.18.32, seems to import 
the same advice, although it is not appropriate for the Bounded version, 
as I understand it. Perhaps a note to this effect should be added to 
A.18.32?

Thanks again, and best wishes for the rest of the new year,

****************************************************************

From: Randy Brukardt
Sent: Thursday, January 9, 2020  10:58 PM

...
> Ok. The pressures that led to my original proposal and ended 
> up in Bounded_Indefinite_Holders had two parts, relative to 
> the applications that caused these pressures:
> 
> 1) To replace my use of variant record types, and the related 
> manual, variant-dependent "case" dispatching, with class-wide 
> types and automatic dispatching. It seems 
> Bounded_Indefinite_Holders provides that, so far so good.
> 
> 2) To replace manual reference-count management (calls to "increment" 
> and "decrement" operations) with automatic calls for 
> controlled types. 
> This now seems impossible, for my applications, in 
> conjunction with Bounded_Indefinite_Holders, because the 
> reference-counts have to be task-safe, and a Swap operation 
> with a controlled element-type would typically require 
> several reference-count updates, each a protected call, and 
> I'm afraid that could be too much overhead for my 
> applications (e.g. Swap would sometimes be called from 
> interrupt handlers).

Reference count updates could be done using atomic objects instead (possibly
using the new Atomic_Operations primitives to do atomic updates). That
probably would be cheaper than using a protected object, but whether it
would be enough I don't know.

> Oh well, one out of two isn't bad. Apologies for the noise, as usual.
...
> In the original design of the library that is used in these 
> applications, these "reference" types were limited, with a "copy" 
> operation that maintains the reference counts. However, users 
> of the library were bothered by the contagious nature of 
> "limited", so the "reference" types are now non-limited, and 
> users are allowed to copy "reference" values, which is 
> error-prone. But the non-limited nature is necessary if I 
> want to use Bounded_Indefinite_Holders, as you point out.

Unless of course the references are controlled and the copies update the
counts correctly. But the important thing in such cases is that the language
guarantees that the calls to Adjust and Finalize are paired properly, but
not how many times those happen. Compilers are allowed to eliminate unneeded
copies and the associated operations (build-in-place is one way to do that,
for instance), and also dead objects and operations can be eliminated. None
of that would be a problem for a reference count implementation, but it does
matter when looking at what happened in an actual case.
 
...
> For initialization and finalization, the Language Design 
> notes in AARM 202X A.18 refer to the Implementation 
> Requirements of the same section, which say that for 
> indefinite containers, "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)", with the Ramification "This allows a user to be 
> able to reason about the behavior of elements that have 
> controlled parts".
> 
> This seems to promise that using controlled types with an 
> indefinite container will work, at least for Initialize and 
> Finalize. But Adjust is not mentioned.
> 
> Can I assume, for example, that 
> (Bounded_)Indefinite_Holders.Clear will finalize the held 
> element, if any?
> 
> And that (Bounded_)Indefinite_Holders.Replace_Element will 
> perform the same Initialize, Adjust and Finalize calls that 
> an assignment statement canonically would do? That is, 
> Finalize the old element (if any), overwrite it with the new 
> element, and then Adjust the result (perhaps via an anonymous 
> temporary)?

You can assume that Finalize, and the rest will be called at some point --
the object will not go unfinalized forever. And you certainly can assume
that Finalize will be called no later than the destruction of the holder
object.

But more than that I can't say, but the reason is that that new language is
broken in the specific case of a Bounded_Indefinite_Container. It's of the
form:

    If the container name contains Bounded, then xxx must be true. If the
    container name contains Indefinite, then yyy must be true.

As written, that seems to require that xxx and yyy are both true -- but
they're conflicting requirements.

I think the intent should be that the indefinite rules apply to a
Bounded_Indefinite_Container, but we definitely need to fix the rule to make
that clear.

If that is right, then you can assume that Finalize is called when the
holder is cleared, and that To_Holder initializes the object. If it is
copied, you can always assume that Finalize/copy/Adjust is called, but you
*can't* assume that it is copied (if the container can use build-in-place or
something scheme to avoid that).

...
> While studying this issue, I noticed that the operation 
> Indefinite_Holders.Move has Implementation Advice saying 
> "Move should not copy the element" (because a pointer can be 
> moved instead). The way Bounded_Indefinite_Holders is now 
> defined, in A.18.32, seems to import the same advice, 
> although it is not appropriate for the Bounded version, as I 
> understand it. Perhaps a note to this effect should be added 
> to A.18.32?

Probably need a rule (not just a note) to cancel that Implementation Advice,
since as you note, it's bogus.

****************************************************************

From: Randy Brukardt
Sent: Thursday, January 9, 2020  11:31 PM

... 
> But more than that I can't say, but the reason is that that new 
> language is
> broken in the specific case of a Bounded_Indefinite_Container. It's of 
> the form:
> 
>     If the container name contains Bounded, then xxx must be true. If the
>     container name contains Indefinite, then yyy must be true.
> 
> As written, that seems to require that xxx and yyy are both true -- 
> but they're conflicting requirements.

Actually, the wording does have the needed exception. Not sure why I didn't 
see it the first three or four times I read this wording....

... 
> If that is right, then you can assume that Finalize is called when the 
> holder is cleared, and that To_Holder initializes the object. If it is 
> copied, you can always assume that Finalize/copy/Adjust is called, but 
> you *can't* assume that it is copied (if the container can use
> build-in-place or something scheme to avoid that).

So this is true. Sorry about any confusion (it was all mine).

****************************************************************

Questions? Ask the ACAA Technical Agent