!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). ****************************************************************