!standard 07.06 (17) 00-07-13 AI95-00083/07 !standard 07.06 (21) !class binding interpretation 95-07-27 !status Corrigendum 2000 99-07-28 !status WG9 approved 95-06-14 !status work item (letter ballot was 10-1-0) 96-06-05 !status ARG approved (subject to letter ballot) 7-0-3 95-11-01 !status received 95-07-27 !qualifier Clarification !subject Aggregates of a controlled type !summary When an (extension) aggregate of a controlled type is assigned other than by an assignment or return statement, the aggregate is built "in place". No anonymous object is created and Adjust is not called on the target object. !question If an object of a controlled type is declared in the same package as the type, and initialized with an aggregate, is Program_Error raised? (No.) !recommendation When an aggregate of a controlled type is created and immediately assigned into an object other than in an assignment or return statement (that is in an initial expression, subaggregate, formal parameter, generic in parameter, or allocator), the implementation must not create a separate anonymous object for the aggregate; it must create the value of the aggregate directly in the target object. Thus, there is no assignment from the anonymous object to the target object, so the Finalize and Adjust that would be done for that assignment are not done. !wording (See corrigendum.) !discussion Consider the following controlled type: type Dyn_String is private; Null_String : constant Dyn_String; ... private type Dyn_String is new Ada.Finalization.Controlled with record ... end record; procedure Finalize(X : in out Dyn_String); procedure Adjust(X : in out Dyn_String); Null_String : constant Dyn_String := (Ada.Finalization.Controlled with ...); Clearly, at the time when the full constant declaration for Null_String is elaborated, the bodies for Finalize and Adjust have not yet been elaborated. 7.6(21) gives the permission to build the aggregate directly in the target object, thereby eliminating the need for the assignment (and the associated calls on Adjust/Finalize): For an aggregate or function call whose value is assigned into a target object, the implementation need not create a separate anonymous object if it can safely create the value of the aggregate or function call directly in the target object. However, it seems important to *require* this behavior, so that this kind of programming is portable (that is, it will work portably without raising Program_Error due to an access-before-elaboration from calling Adjust or Finalize before their bodies are elaborated). In other words, the first sentence of 7.6(21) should be an Implementation Requirement in the case where a new object is being created. Note that no Adjust ever takes place on an aggregate as a whole, since there is no assignment to the aggregate as a whole (4.3(5) and AARM 4.3(5.b)). AARM 7.6(21.a) talks about this case, and says that "only one value adjustment is necessary". This is misleading. It should say that only one adjustment of each controlled *subcomponent* (if any) is necessary in this case. *No* adjustments of the object as a whole are necessary (and as suggested above, such adjustments should be disallowed). Note that this interpretation applies to all object creations, not just to object_declarations. Thus, continuing the above example, if we have: type Dyn_String_Ptr is access all Dyn_String; Null_String_Ptr: Dyn_String_Ptr := new Dyn_String'(Ada.Finalization.Controlled with ...); The aggregate must be built directly in the newly-created heap object. Similarly, if we have function Is_Null (Value : in Dyn_String) return Boolean; then the aggregate actual parameter in the call <> if Is_Null ((Ada.Finalization.Controlled with ...)) then is built directly in a temporary object, and Adjust is not called on the object as a whole. We exempt assignment and return statements from this requirement as there is no compelling reason to burden implementations with this requirement in those cases. Note that all aggregates of a controlled type are extension aggregates: Controlled and Limited_Controlled are private, so it is not possible to create a normal record aggregate for such a type. !corrigendum 7.06(17) @dinsa For an @fa, after the @fa and @fa have been evaluated, and any conversion (including constraint checking) has been done, an anonymous object is created, and the value is assigned into it; that is, the assignment operation is applied. (Assignment includes value adjustment.) The target of the @fa is then finalized. The value of the anonymous object is then assigned into the target of the @fa. Finally, the anonymous object is finalized. As explained below, the implementation may eliminate the intermediate anonymous object, so this description subsumes the one given in 5.2, @lquote@lquoteAssignment Statements@rquote@rquote. @dinst @i<@s8>@hr For an aggregate of a controlled type whose value is assigned, other than by an @fa or a @fa, the implementation shall not create a separate anonymous object for the aggregate. The aggregate value shall be constructed directly in the target of the assignment operation and Adjust is not called on the target object. !ACATS test ACATS test C761010 was constructed to test this rule. The annex H ACATS test CXH3002 also requires these semantics. !appendix !section 7.6(21) !subject Aggregates of a controlled type !reference RM95-7.6(21) !reference AARM-7.6(21.a);6.0 !from Tucker Taft 95-07-25 !reference as: 95-5234.a Tucker Taft 95-7-25>> !discussion Consider the following controlled type: type Dyn_String is private; Null_String : constant Dyn_String; ... private type Dyn_String is new Ada.Finalization.Controlled with record ... end record; procedure Finalize(X : in out Dyn_String); procedure Adjust(X : in out Dyn_String); Null_String : constant Dyn_String := (Ada.Finalization.Controlled with ...); Clearly, at the time when the full constant declaration for Null_String is elaborated, the bodies for Finalize and Adjust have not yet been elaborated. RM 7.6(21) gives the permission to build the aggregate directly in the target object, thereby eliminating the need for the assignment (and the associated calls on Adjust/Finalize). However, it seems important to *require* this behavior, so that this kind of programming is portable (that is, it will work portably without raising Program_Error due to an access-before-elaboration from calling Adjust or Finalize before their bodies are elaborated). In other words, at least parts of 7.6(21) should be moved to an Implementation Requirement (or the Dynamic Semantics). In particular, when a constant is initialized from an aggregate of a controlled type, the aggregate should be built in place, and no Adjust or Finalize on the constant object as a whole should take place. Note that no Adjust ever takes place on an aggregate as a whole, since there is no assignment to the aggregate as a whole (AARM 4.3(5,5b)). Note that AARM 7.6(21a) talks about this case, and says that "only one value adjustment is necessary". This is misleading. It should say that only one adjustment of each controlled *subcomponent* (if any) is necessary in this case. *No* adjustments of the object as a whole are necessary (and as suggested above, such adjustments should be disallowed). -Tuck **************************************************************** !section 7.6(21) !subject Aggregates of a controlled type !section 7.6 !reference AI95-00083/02 !reference RM95-7.6(21) !from Gary Dismukes 97-07-11 !reference 1997-15794.a Gary Dismukes 1997-11-8>> !discussion AI-83 requires that a controlled object initialized by an aggregate be built in place, without the use of the canonical temporary object. It seems that this requirement should also exist for cases involving controlled components with default initialization given by an aggregate, but it's not clear from the wording of the AI whether it's intended to apply to such cases. For example, building on the example in the AI: type Dyn_String is private; Null_String : constant Dyn_String; ... private type Dyn_String is new Ada.Finalization.Controlled with record ... end record; procedure Finalize(X : in out Dyn_String); procedure Adjust(X : in out Dyn_String); type Rec_with_String is record Str : Dyn_String := (Ada.Finalization.Controlled with ...); end record; RS : Rec_with_String; Is it intended that the component RS.Str be initialized in place, without the use of a separate temporary object to hold the value of the aggregate? The fact that the summary says "explicitly initialized" makes it unclear to me whether the AI is intended to apply to this case (although the end of the !recommendation does state that the interpretation applies to all object creations). The question is whether the component's default initialization is properly considered to be an "explicit initialization" for purposes of this AI. **************************************************************** From: Randy Brukardt Sent: October 7, 1999 At the recent ARG meeting, discussion centered around insuring that all places where an extension aggregate can be used are explicitly covered by the wording for this AI. (This discussion subsumes AI-00197, which now will be deleted and merged into this one). The meeting enumerated the places where an extension aggregate can be used: Assignment, return statement, generic IN parameter, formal parameter, allocator, initial expression, and subaggregate. The meeting then discussed for each place whether or not this rule should apply. Eventually, it was decided it should apply to all cases except assignment and return statements. While return statements probably could be implemented without a temporary for the aggregate, there did not seem to be any compelling reason to burden implementations with such a requirement. New wording was crafted to insure that all of the cases are covered. Note that all aggregates of a controlled type are extension aggregates: Controlled itself is private, so it is not possible to create a normal aggregate for such a type. ------ The WG9 concerns with this AI were also discussed at the recent ARG meeting. The following example was given: type T is new TP with ... X : T := T'(TP with ...); If TP is controlled, then TP initialize gets called, but the finalize is T. After discussion, it was determined that is in fact the expected behavior in Ada 95, and indeed this AI doesn't change that fact. There are three ways to create a controlled object in Ada 95: via Initialize (a default-initialized object); Adjust (an explicitly initialized object); and via an aggregate (for which neither Initialize nor Adjust is called). It was pointed out that it is important that an overridding Finalize (for instance) call the Finalize routine of its parent. If T's Finalize calls TP Finalize in the above example, there probably aren't any problems. Tucker volunteered to write an LSN document describing the Ada 95 controlled type model, including the various surprises which are possible. **************************************************************** !topic Controlled objects are created without any initialization nor adjust. !reference RM95-7.6(10) !reference RM95-7.6(21) !reference AI95-0083 !from Bj=F6rn K=E4llberg 99-05-01 !keywords Initialize, Adjust, Controlled, Aggregates !discussion In 7.6 (1-2) it is stated, that every object is initialized and finalized, and by the use of controlled types the user can gain additional control. However, since the binding interpretation AI95-0083, this is not true. It is possible to create controlled objects, that are neither initialized nor adjusted. This is a serious break of the abstraction and makes it very hard or impossible to use finalization for a number of purposes. One example is the example in the book Ada95 by John Barnes, page 302, where an attempt is made to keep a reference count. Consider a declaration where the object is assigned an initial value. Package A is Type C_t is new controlled with ... end A; with A; use A; package B is O1 : C_t; O2 : C_t := O1; O3 : C_T := (C_t with .....) Above O1 is initialized. O2 is not initialized by 7.6(10) but is instead adjusted as part of the assignment operation. O3 is not initialized, but by the AI83 it is not adjusted either. The rationale behind AI95-0083 was to avoid program_error due to access of the subprogram ?Adjust? before elaboration. However, the same argument applies also to Initialize, so before AI83 it was never possibly to declare controlled objects in the same package specification as the type, if there are overriding subprograms for Initialize or Adjust. Now it is still impossible to declare objects, except if they are initialized with an aggregate expression. Apart from making Initialize less useful, it is also a break against the uniformity goal of Ada95. A possible solution would be to mandate that the objects subject to AI83 should be initialized instead. A similar problem arises regarding aggregates. 7.6(17) states that in an assignment statement an anonymous object is created, into which the values are assigned. The target is finalized, this anonymous value is assigned into the target, and the anonymous object is initialized. Also in this case seems at least two very popular compilers use the AI83, and not adjust (nor initialize) the anonymous object. Thus, also the anonymous object is created without any initialization nor adjust. In order to construct reliable abstractions in the presence of controlled types some predicates like those in 7.6(1) are needed. However, as written, the interpretation is not quite clear. Is the intent that the following statements shall be true for every controlled object? (YES!) (Initialize, Adjust, Finalize means, that the corresponding primitive operations of the controlled object are called) - Every controlled object is Initialized or Adjusted when created - Every object is Finalized before being destroyed - In the absence of an abort, Initialize or Adjust is always paired with a corresponding Finalize. In the presence of an abort, the corresponding Finalize operation may be called several times. **************************************************************** From: Robert Dewar Sent: Tuesday, December 07, 1999 10:01 PM I really dislike AI95-0083 for just the reasons given in the K=E4llberg note **************************************************************** From: Tucker Taft Sent: Wednesday, December 08, 1999 2:08 PM Bjorn Kaelberg wrote: > ... > Is the intent that the following statements shall be true for every > controlled object? (YES!) No. Aggregates (and objects initialized with an aggregate) undergo neither Initialize nor Adjust. Since you can only write an aggregate when you have full visibility on the type, there is no abstraction-breaking going on here, since you could arbitrarily change any visible component anyway in that case. By making the type private (or a private extension) you ensure that no aggregates will be written outside of the package region. > ...(Initialize, Adjust, Finalize means, that the > corresponding primitive operations of the controlled object are called) > > - Every controlled object is Initialized or Adjusted when created > - Every object is Finalized before being destroyed > - In the absence of an abort, Initialize or Adjust is always paired with > a corresponding Finalize. In the presence of an abort, the corresponding > Finalize operation may be called several times. Finalize is paired with an Initialize, an Adjust, or an aggregate evaluation. Initialize corresponds to initialization by default, Adjust corresponds to initialization by copy (from an object with its own independent finalize), and aggregate evaluation corresponds to component-by-component initialization. > /Bj=F6rn K=E4llberg Robert Dewar wrote: > I really dislike AI95-0083 for just the reasons given in the K=E4llberg note I don't understand this complaint. Are you suggesting that aggregates should *not* be finalized, or that either Adjust or Initialize should be called on an aggregate before it is finalized? Or perhaps are you simply complaining that we shouldn't "require" the optimization that enables the definition of deferred constants in the same package with a controlled type? I really don't see any viable alternatives at this point, even if some of us dislike AI-83 for aesthetic reasons. There is existing code that uses aggregates to define controlled values, and these assume that Adjust is *not* performed on the aggregate before it gets used or finalized. Personally, I find it intuitive that when I construct a value using an aggregate, I construct it exactly as I want it, not in some way that will require an Adjust before it gets used further or finalized. **************************************************************** From: John Barnes Sent: Sunday, March 26, 2000 2:20 AM I really want to see this initialization of aggregates discussed sometime. I really hate it. It is AI-197 and 83. Tucker was supposed to be giving us a lecture on the topic but unfortunately he will not be with us. But I note that the situation has Robert's disapproval as well. >Robert Dewar wrote: > >> I really dislike AI95-0083 for just the reasons given in the >> Kallberg note Maybe we have to put up with something (because of "null" constants in certain predefined packages) but we don't have to put up with exactly what we have at the moment. I had hoped to write something last week but we had an unexpected elderly relative in and out of hospital and a funeral. Well it was the hospitalization which was unexpected. So was the funeral. I will try to write something later today or tomorrow. **************************************************************** From: Tucker Taft Sent: Sunday, March 26, 2000 7:22 AM Here is my "lecture" on initialization-by-aggregate: > >Finalize is paired with an Initialize, an Adjust, or an aggregate > >evaluation. Initialize corresponds to initialization by default, > >Adjust corresponds to initialization by copy (from an object with its > >own independent finalize), and aggregate evaluation corresponds to > >component-by-component initialization. I really don't know what more I could say. If those who don't "like" these AIs could be more specific, I might be able to expand my lecture. I can believe there was a confused belief that finalize was paired with only Initialize or Adjust, but that is wrong. There are three ways to initialize a controlled object, by default, by copy, and by component-by-component initialization. The third is available only when within the scope of the full type definition, as appropriate (*all* controlled types should be private, or else the "control" is pointless). Let me know if a longer lecture would be of use, and what area I should elaborate further. **************************************************************** From: Robert Dewar Sent: Sunday, March 26, 2000 7:42 AM Well I must say I have lost the thread here. Can you answer the following questions 1. Does the original wording in the RM reflect your lecture? If not how not and why not? 2. Does the AI change things wrt your lecture? **************************************************************** From: Tucker Taft Sent: Sunday, March 26, 2000 9:23 AM > 1. Does the original wording in the RM reflect your lecture? If not how > not and why not? Yes. However, the original wording (e.g. of 7.6(1)) does not clearly spell out that there are three ways to initialize a controlled object, by default, by assignment, and component-by-component. You must glean it from reading 4.3(5) which indicates that an object is created as part of evaluating an aggregate. > 2. Does the AI change things wrt your lecture? No. My lecture still holds. What the AI says is that when a controlled object is initialized by assignment from an aggregate, rather than: 1) creating an anonymous object by evaluating the aggregate, and 2) assigning that anonymous object into the object being initialized (with the attendant adjustment) 3) finalizing the anonymous object. This must be shortened to simply: 1) initializing the object directly from the component values specified in the aggregate. Essentially, the "object" in which the aggregate is evaluated is the user specified object, rather than a separate anonymous object. The properties of this user-specified object are exactly like the "normal" anonymous objects created as part of evaluating an aggregate. In particular, they undergo no adjustment after evaluation of the aggregate, but they do need to be finalized when their lifetime ends. The reason for the AI is that we do not want Adjust or Finalize called in connection with using an anonymous object as a temporary holder for the aggregate value, since neither operation has necessarily been elaborated yet. To reemphasize the critical point, this AI does *not* create a new class of objects that get finalized without first having Adjust or Initialize called. That class of objects already existed -- the anonymous objects created as part of evaluating an aggregate. That class of objects will continue to exist independent of how we end up wording this AI. We most definitiely do not want to change the way aggregate objects are created and finalized. The only issue is whether the anonymous object should be created, copied, and then finalized for the cases when an aggregate is used to initialize some user-specified object. This AI says no -- such aggregates should be evaluated directly into their target object, to avoid the problematic "early" calls on adjust and finalize. The overall "balance" equation should remain that for a given controlled type: Number of aggregate evaluations + Number of calls on Initialize + Number of calls on Adjust = Number of calls on Finalize This AI does not destroy this balance. It simply reduces the value by one on each side of the equation. **************************************************************** From: Robert Dewar Sent: Sunday, March 26, 2000 11:05 AM OK, this sounds reasonable to me, I withdraw my objection ... I am still a bit concerned by making this a required change, I don't see how the RM justifies this requirement. I see it as desirable, but how can we require this change to be made? **************************************************************** From: John Barnes Sent: Sunday, March 26, 2000 3:16 PM Folks Here is a hastily written note on AI-83 as promised. I hope it makes some sort of sense. See you on Wednesday. -------- AI-83 and counting objects The background to Bjorn Kallberg's note on AI-83 and aggregates of controlled types is that he had to give a course on Ada 95 to some Ada 83 programmers. One topic was of course controlled types. Looking for an exercise for them to do he came across an example in my book, (page 302 of 2nd edition, page 286 of 1st edition). This example declares a controlled type and keeps track of how many objects of the type are in existence and also keeps an identity number in each object. Here is the relevant lump of book (sorry, it's a bit long) ... begin extract As a simple example, suppose we wish to declare a type and keep track of how many objects (values) of the type are in existence and also record the identity number of each object in the object itself. We could declare with Ada.Finalization; use Ada.Finalization; package Tracked_Things is type Thing is new Controlled with record Identity_Number: Integer; ... -- other data; end record; procedure Initialize(Object: in out Thing); procedure Adjust(Object: in out Thing); procedure Finalize(Object: in out Thing); end Tracked_Things; package body Tracked_Things is The_Count: Integer := 0; Next_One: Integer := 1; procedure Initialize(Object: in out Thing) is begin The_Count := The_Count + 1; Object.Identity_Number := Next_One; Next_One := Next_One + 1; end Initialize; procedure Adjust(Object: in out Thing) renames Initialize; procedure Finalize(Object: in out Thing) is begin The_Count := The_Count - 1; end Finalize; end Tracked_Things; In this example we have considered each value of a thing to be a new one and so Adjust is the same as Initialize and we can conveniently use a renaming declaration to provide the body as was mentioned in Section 12.6. An alternative approach might be to consider new things to be created only when an object is first declared (or allocated). This variation is left as an exercise. The observant reader will note that the identity number is visible to users of the package and thus liable to abuse. We can overcome this by using a child package in which we extend the type. This enables us to provide different views of a type and effectively allows us to create a type with some components visible to the user and some components hidden. Consider package Tracked_Things is type Identity_Controlled is abstract tagged private; private type Identity_Controlled is abstract new Controlled with record Identity_Number: Integer; end record; procedure Initialize ... -- etc. end Tracked Things; package Tracked_Things.User_View is type Thing is new Identity_Controlled with record ... -- visible data end record; end Tracked_Things.User_View; In this arrangement we first declare a private type Identity_Controlled just containing the component Identity_Number (this component being hidden from the user) and then in the child package we further extend the type with the visible data which we wish the user to see. Note carefully that the type Identity_Controlled is abstract so objects of this type cannot be declared and moreover the user cannot even see that it is actually a controlled type. We also declare Initialize, Adjust and Finalize in the private part and so they are also hidden from the user. ... end extract Bjorn asked his students to write such a package and declare some objects. For some students it worked and for others it all went wrong. It went wrong of course for those who were lazy and declared their objects in the same package as the type and thus had Initialize called before its body had been elaborated. AI-83 was designed so far as I am aware primarily to allow constants to be declared in the package defining a type provided they were initialized with an aggregate. This seemed necessary if users were to declare packages in the same style for example as the type Character_Set in the package Ada.Strings.Maps where the declaration of the type is accompanied by that of a constant of the type named Null_Set. Personally I feel that the Ada style here is a bit dubious; it seems bad practice to mix up declarations of a type with those of objects of the type since in a sense the type is not properly declared until the end of the package. I would prefer to provide null values by declaring a function returning a null value. This would avoid the problems. (Maybe Ada is too free in its ability to do what you want with a package (especially with tagged types) and the building block approach lets us down by enabling us to build inappropriate structures - maybe we should have had class packages ...!) Now given that we have to allow the wretched user to declare a constant in the package defining the type then it seems we have to allow these wretched in situ aggregates in that package. However, there seems no reason to me to require this optimization for objects initialized with aggregates in other packages. It seems to me to be quite reasonable to wish to monitor the behaviour of a program by counting the number of times an object of a type has been created and indeed to know how many are in existence. Whether the type is private or not is irrelevant since the count is not in the object but hidden in the defining package where it is available to the author of the package via various subprograms . It seems that not only does the aggregate optimization permit an object to be declared without it being counted but moreover it will still get Finalized and so the count of objects in existence could go negative which seems very surprising. However, if we restricted AI-83 to just the package defining the type then provided the declarer of the package didn't declare any objects, it would be fine because all objects declared elsewhere would be counted properly. Well that's my view of the story, I hope it aligns with Bjorn's. Incidentally when I wrote the example I think I hadn't realized that if one declares an initialized object then Initialize is not called anyway but only Adjust. This means that one can't really count how many objects have been made but just how many values have been made (maybe trying to distinguish between values and objects is foolish and I should remove the exercise that asks the reader to do that from the book). However, it does seem reasonable to want to be able to count how many are in existence at any time and to be able to rely on the count going to zero at the end of the relevant scope. It's clearly an awkward topic. **************************************************************** From: Tucker Taft Sent: Sunday, March 26, 2000 7:20 PM > ... > It seems that not only does the aggregate optimization > permit an object to be declared without it being counted but > moreover it will still get Finalized and so the count of > objects in existence could go negative which seems very > surprising. As I tried to point out, with or without this optimization, the proper equation is: Number of aggregate evaluations + Number of Inits + Number of Adjusts = Number of Finalizes. If you don't count aggregates, but you do subtract one on each finalization, you will get a negative count. This optimization will not change that fact. > However, if we restricted AI-83 to just the package defining > the type then provided the declarer of the package didn't > declare any objects, it would be fine because all objects > declared elsewhere would be counted properly. The whole notion of non-private controlled types seems like an obscure example, and not worth making compilers more complicated. Having different *run time* semantics when inside or outside a package seems like a dangerous precedent, and creates questions about inline expansion, macro-based generic instantiation, child packages, etc. All in all not a pretty situation. Furthermore, as pointed out above, you still need to count the "wretched" aggregates, whether or not you perform this optimization, since they do require a balancing finalization. > ... However, it does seem reasonable to want to > be able to count how many are in existence at any time and > to be able to rely on the count going to zero at the end of > the relevant scope. And you will get the right answer if and only if you count aggregate evaluations as well. > ... It's clearly an awkward topic. I think the example you have is a bit "awkward." As you have discovered, it really doesn't match the "normal" usage model for nonlimited controlled types. Nonlimited controlled types are really about having conceptual "values" that may involve levels of indirection. If you shifted your example over to using limited controlled types, it would probably make more sense, and you would only have to worry about Initialize and Finalize -- no Adjusts or aggregates creating new values. **************************************************************** From: Robert Dewar Sent: Sunday, March 26, 2000 9:16 PM <> I find this a bit of an extraordinary statement, non-private controlled types are a valuable feature in my view. **************************************************************** From: Tucker Taft Sent: Sunday, March 26, 2000 7:25 PM Robert Dewar wrote: > > OK, this sounds reasonable to me, I withdraw my objection ... I am still > a bit concerned by making this a required change, I don't see how the > RM justifies this requirement. I see it as desirable, but how can we > require this change to be made? This is clearly a "binding interpretation." The reason to require it is to make the usage portable. If we wanted to make things more complicated, we could say that this "optimization" is only necessary prior to elaboration of Adjust and Finalization, and everything they might call during their execution. But that seems nasty to define. If we have to make initialization-by-aggregate special somewhere, we might as well give it a uniform semantics everywhere, so it can never be the cause of an unexpected Program_Error due to an implicit call on Adjust or Finalize. **************************************************************** From: Robert Dewar Sent: Sunday, March 26, 2000 9:17 PM I find this FAR too ambitious for a binding interpretation. Sure it is desirable to have all compilers do the same thing, but I don't find the justification for this AI in the RM. I would vote against it as a binding interpretation, I don't see it can be supported. **************************************************************** From: Tucker Taft Sent: Sunday, March 26, 2000 9:44 PM > I find this FAR too ambitious for a binding interpretation. Sure it is > desirable to have all compilers do the same thing, but I don't find the > justification for this AI in the RM. I would vote against it as a binding > interpretation, I don't see it can be supported. I thought there was general agreement that there was a language bug in this area, so a binding interpretation was appropriate to fix the bug. The complaint with the AI was that finalizations were not always balanced by adjusts and initializes. It turns out that this AI does not have any effect on that complaint, so that particular complaint cannot be fixed by fiddling with this AI. If we want to debate whether the original problem should be fixed, that's OK, but let's not get the two discussions confused. Also, if there are useful examples of non-private controlled types, it would be helpful to see them, so we could evaluate alternative solutions keeping them in mind. **************************************************************** From: Pascal Leroy Sent: Monday, March 27, 2000 2:11 AM > I find this FAR too ambitious for a binding interpretation. Sure it is > desirable to have all compilers do the same thing, but I don't find the > justification for this AI in the RM. I would vote against it as a binding > interpretation, I don't see it can be supported. I am confused now. As I recall, this topic was discussed circa 1995, and I don't remember seeing such a staunch opposition at the time. Could you explain what new piece of information has caused you to change your mind? I don't think it is merely "desirable" for compilers to all do the same thing in this instance, I think it's absolutely critical, because "null" constants of a private type are such a common idiom, and numerous private types are controlled (because they contain indirections). Now maybe the resolution is to _mandate_ that Program_Error be raised in this instance. I could live with that (I believe that deferred constants should not be in the language in the first place, so I am not going to fight for them). But I couldn't live with the notion that the behavior is implementation-defined. **************************************************************** From: Randy Brukardt Sent: Monday, March 27, 2000 11:12 AM > And since there is no way I can count the aggregate evaluations since > they occur silently, the whole quest is lost. Well that's a shame. I really don't think so; I agree with Tuck here. If your type is private, the only place that aggregates can occur is inside of your package. So there is no particular problem to insure that they do the right thing; and your users can't break that behavior. Moreover, there is a distinct advantage to having them *not* call Initialize -- it provides a way to construct a value without having to worry about Initialize coming along and screwing it up. That can be handy. If you type is not private, it is not remotely bulletproof -- all that it takes to mess up your count is for someone to change it explicitly. So what is the concern? At most this gives you one more way to mess things up. > It looks more and more as if types have to be limited in order to rely > upon them and so user-defined assignment was a forlorn goal. > > RIP := Baloney. Claw is mostly constructed out of types that have user-defined assignment with reference counting, and we have no problem with aggregates. We've never had a user-reported bug having to do with this finalization (except those related to compiler errors and the over-aggressive optimization covered by AI-147). We don't use aggregates in the bodies (although we could if we were careful with the reference counts). It is not a problem in practice. It is more interesting to deal with Adjust in a task-safe manner. > Do extension aggregates have any bearing on this matter? All aggregates of controlled types are extension aggregates by definition. Controlled is private, so a non-extension aggregate is impossible. **************************************************************** From: Robert Dewar Sent: Monday, March 27, 2000 7:33 AM <> There is an international standard for Ada. Vendors implement this standard, and there is a validation suite for this standard. If the language is modified by the ARG in a manner that is not reasonably justified by the standard, then we have a situation that needs examination. Remember what we are talking about here is just one thing denial or approval of validation Nothing else is important. In other words, the issue is not the label on the AI, it is whether a test appears for this feature. I think we are on very shaky ground if we start including mandatory tests for features not reasonably derivable from the intent of the RM. I just don't see this feature that the AI prescribes as being reasonably derivable from the intent of the RM. Yes, it is a hole. Yes, the language should have addressed this, but it didn't. It is one thing for the ARG to be in the business of improving the language by filling in the hole, it is quite another for the ARA to be in the business of denying validation to a vendor who implements the language as defined in the international standard. Traditionally, the import of the "binding interpretation" designation was precisely that it results in a mandatory test. I don't see that here. There are two situations 1. All vendors move to implement this AI anyway, in which case it does not matter two hoots what designation it has. 2. Not all vendors implement this AI. Now I think in practice the market place will put pressure on to implement it, but if the market place does not generate such pressure, I think it is dubious for the ARG to substitute legislation to replace market demand. The result of this can easily be for a vendor to say: "we no longer bother with validation, since it is no longer being run by the AJPO, and so has no official status in the US. Furthermore the replacement validation has run amok, and is requiring compilers to do things that are not required by the standard." I would far prefer this to be a non-binding interpretation, and then have a set of tests that tests for adherence to these NBI's reporting the results. Speaking for ACT, we will be happy to work hard on passing all these tests (we like to pass 100% of all the tests as you know :-) and will be happy to advertise that our compiler passes 100% of the NBI tests, showing that our technology is advancing and keeping track of things. If a competitor fails to implement all the ABI tests, we will be happy to point this out, but would feel it inappropriate to deny validation on this basis. I hope that makes my position clearer. **************************************************************** From: Pascal Leroy Sent: Monday, March 27, 2000 8:11 AM > I think we are on very shaky ground if we start including mandatory tests > for features not reasonably derivable from the intent of the RM. > > I just don't see this feature that the AI prescribes as being reasonably > derivable from the intent of the RM. Yes, it is a hole. Yes, the language > should have addressed this, but it didn't. Arguments from intent are always rather bogus but... The RM states that calling an unelaborated subprogram raises Program_Error, and in the case that we are discussing, Adjust is clearly not elaborated. Now there is this permission to optimize away some calls to Adjust, and this is what makes the behavior implementation-defined. It seems to me that the intent of this permission was to make it possible to generate better code, not to allow the declaration of null objects of controlled types. Therefore, I would argue that the intent of the RM is rather clear: this case should raise Program_Error. In fact, I believe that the AI is not satisfactory as written: it doesn't completely fix the problem. We implemented it a few years ago, and I recently received a complaint from a customer who was trying to write a null object for a type that was _not_ controlled, but had controlled subcomponents. This case has exactly the same problem as the one covered by the AI (i.e., P_E is raised by a call to an unelaborated Adjust), but it is _not_ covered by the AI. So if you have a controlled type, and for one reason or another need to wrap it into a record, your application suddenly fails at elaboration. > 2. Not all vendors implement this AI. Now I think in practice the market place > will put pressure on to implement it, but if the market place does not generate > such pressure, I think it is dubious for the ARG to substitute legislation > to replace market demand. I am not exactly happy with the notion that the semantics of Ada should be governed by market pressures. The problem we are discussing is not an obscure cornercase of the language, it's very visible, in the sense that users are likely to run into (and in fact they do). I'd much prefer an ARG resolution, even if that resolution is that it has to raised P_E. **************************************************************** From: Robert Dewar Sent: Monday, March 27, 2000 9:03 AM <> But the question is, do you think we should deny validation to a vendor who conforms to the RM in this area! **************************************************************** From: Randy Brukardt Sent: Monday, March 27, 2000 11:22 AM > < be governed by market pressures. The problem we are discussing is not > an obscure cornercase of the language, it's very visible, in the sense > that users are likely to run into (and in fact they do). I'd much > prefer an ARG resolution, even if that resolution is that it has to > raised P_E.> >> > > But the question is, do you think we should deny validation to a vendor > who conforms to the RM in this area! Well, for what it is worth, there is an Annex H test which requires implementation of AI-83. So at least compilers which have validated Annex H have indeed been held to this requirement. I've even rejected petitions on the test based on approved AI-83; I think Dan Lehman did so as well. So we've been requiring this behavior for validation for quite a while. Randy ACAA Technical Agent **************************************************************** From: Randy Brukardt [Randy@RRSoftware.Com] Sent: Monday, March 27, 2000 11:28 AM > In fact, I believe that the AI is not satisfactory as > written: it doesn't completely fix the problem. We > implemented it a few years ago, and I recently received a > complaint from a customer who was trying to write a null > object for a type that was _not_ controlled, but had > controlled subcomponents. This case has exactly the same > problem as the one covered by the AI (i.e., P_E is raised by > a call to an unelaborated Adjust), but it is _not_ covered by > the AI. So if you have a controlled type, and for one reason > or another need to wrap it into a record, your application > suddenly fails at elaboration. I thought that was what was covered by AI-197 (now folded into AI-83) and it ought to be covered by the Corrigendum wording for AI-83. Are you sure it isn't? Are you looking at the version I created last November, and not some old, obsolete version?? **************************************************************** From: Robert A Duff Sent: Monday, March 27, 2000 12:06 PM Robert, > Remember what we are talking about here is just one thing > > denial or approval of validatoin > > Nothing else is important. It seems to me that *users* of Ada are more important than validation issues, and uniformity in the case of controlled aggregates is of benefit to users. I understand your point, but I think there is plenty of precedent for making this sort of small change to fix what is clearly a bug in the language design. I don't see it as an indication that the ARG is running amok. I would like to see a (required-to-pass) ACATS test for this feature. The "market" alone has not shown itself to be capable of forcing this kind of uniformity. **************************************************************** From: John Barnes Sent: Monday, March 27, 2000 1:15 PM >> And since there is no way I can count the aggregate evaluations since >> they occur silently, the whole quest is lost. Well that's a shame. >If you type is not private, it is not remotely bulletproof -- all that it >takes to mess up your count is for someone to change it explicitly. So what >is the concern? At most this gives you one more way to mess things up. Nonsense, the count is not in the object, it is in the package defining the type and thus not visible to users. Privateness is a red herring. **************************************************************** From: Pascal Leroy Sent: Monday, March 27, 2000 2:26 PM John, I have read this paragraph five times, and I still don't understand it. Could you illustrate what you mean with a code example? **************************************************************** From: Tucker Taft Sent: Monday, March 27, 2000 2:04 PM In John's example, he only has one count, which is an overall count of objects/values of the type. In such a case, it is certainly true that the count can be hidden even though the controlled type is public. I also agree with John that this shows that controlled types are not particularly good for this kind of application. However, I also will say that this is not the application for which they were designed. They were designed to support treating objects with possible levels of indirection as a single conceptual object, so assignment did the appropriate amount of copying or reference counting of underlying levels of indirection, and storage was automatically reclaimed when the object went away. This application works fine, and is probably the kind of application that should be used in examples of non-limited controlled types. **************************************************************** From: Robert Dewar Sent: Monday, March 27, 2000 2:22 PM That seems far too limited a view of why they were designed. perhaps that was Tuck's main purpose, but they have many other purposes (e.g. we use them internally for protected types, to make sure that various operating system resources such as locks, are released). **************************************************************** From: Robert A Duff Sent: Monday, March 27, 2000 2:55 PM That's what *limited* controlled types are for. I think Tuck was talking only about non-limited controlled types. By the way, remember the history? Non-limited controlled types very nearly didn't make it into the language, which was not true of the limited kind. **************************************************************** From: Tucker Taft Sent: Monday, March 27, 2000 3:25 PM Responding to Robert Dewar: I am surprised that you would use *non-limited* controlled types for this purpose. I certainly understand the resource management uses of *limited* controlled types, but non-limited controlled types seem most naturally designed for treating objects with levels of indirection as "first class citizens." **************************************************************** From: Robert Dewar Sent: Monday, March 27, 2000 4:34 PM OK, point taken, yes, indeed these are of course limited types ... **************************************************************** From: Robert Dewar Sent: Monday, March 27, 2000 1:46 PM <> Sorry, I misread this, can you quote the test, since this claim is definitely interesting. **************************************************************** From: Robert Dewar Sent: Monday, March 27, 2000 1:45 PM But validation is never withheld for failing an annex test, so this is not nearly so critical. I must say I don't know what AI-83 is offhand, so I don't know whether this is fully comparable. I agree with Pascal that if we want to force uniformity, requiring Program_Error is more appropriate. **************************************************************** From: Robert Dewar Sent: Monday, March 27, 2000 1:44 PM <> OK, then the disagreement is clear enough. I object to such a test and I think the ARG is overstepping its bounds by requiring such a test. **************************************************************** From: Randy Brukardt Sent: Monday, March 27, 2000 5:28 PM Robert asked: > Sorry, I misread this, can you quote the test, since this claim is > definitely interesting. The test in question is CXH3002. I was incorrect, however, about Dan Lehman having ruled on it (I actually went and looked it up). I *did* rule on it (informally); the implementor certainly accepted the explanation and was not particularly concerned about it. (There never was a formal petition, and it was not run past the FRT). **************************************************************** From: Ada Rapporteur Group of WG9; managing the Ada issues [ARG@ACM.ORG] on behalf of Randy Brukardt [Randy@RRSOFTWARE.COM] Sent: Monday, March 27, 2000 8:27 PM To: ARG@ACM.ORG Subject: Re: Fw: [Ada-Comment] Controlled objects are created without anyinitialization nor adjust. > can you list the relevant section of CXH3002. According to the petition I have, the types and subprograms are defined on lines 118-125, and the object is declared at line 204. It's not a very realistic example, but it appears whomever wrote it expected AI-83 to be in force, as there is a comment next to the object: -- attempt to call Initialize here would P_E! Stupid place to test a core language rule, if that was the intent. **************************************************************** From: Pascal Leroy Sent: Tuesday, March 28, 2000 1:51 AM > I thought that was what was covered by AI-197 (now folded into AI-83) and it > ought to be covered by the Corrigendum wording for AI-83. Are you sure it > isn't? Are you looking at the version I created last November, and not some > old, obsolete version?? You're right, I was stupidly looking at an old version, the one that has been approved by WG9 in 1995, and which we implemented. The latest wording correctly covers the case of a controlled subaggregate. **************************************************************** From: Tucker Taft Sent: Monday, April 03, 2000 9:30 AM Erhard Ploedereder wrote: > ...I think it is absolutely counter-productive > to say that, for now, we should mandate P_E to be raised here, when the > official language fix is just around the corner. Independent of the bigger issues, I would certainly find it silly for us to start mandating Program_Error for the most simple of these cases. Furthermore, my impression as an implementor, is once you support the simple case of "Obj : constant blah := (aggregate)" handling the more general case is a relatively small step. In fact, it could make things more complicated for the compiler to disallow Program_Error for the simple case, and then require it for the more complex cases. I could see in a transitional period disallowing Program_Error in the simple case, and making it implementation-defined for the more complex cases, with the goal of shifting to disallowing Program_Error in all cases eventually. In other words, the initial ACATS test would check only the simple cases for lack of P_E. Eventually, ACATS tests for the more complex cases would be created as well. **************************************************************** From: Robert Dewar Sent: Wednesday, April 05, 2000 1:08 PM Let's get back to the substantive issue here, which is what the subject is about. Should this be an NBI or not. My argument that it should not be binding is that it seems clearly to be a change int he language. I have not seen a good argument that this interpretation was intended or can be read into the RM. On the contrary it seems pretty clear that the RM demands program error here. I am not at all convinced that a proper analysis of implementation difficulty has been carried out. The only time we can MANDATE what are clear changes are when we know there is no implementation burden, e.g. making Bit_Order static. **************************************************************** From: Erhard Ploedereder Sent: Wednesday, April 05, 2000 5:32 PM The RM doesn't demand P_E. P_E is the canonical semantics, but 7.6(21) explicitly gives the permission to do as the AI is now mandating. Raising P_E implies for the user that the declaration of a non-limited controlled types with user-defined Initialize and Adjust and an object creation of that type must not be in the same package spec (or list of basic decl. items). It's a pretty nasty portability problem if one implementation allows this for initialized objects and the other always raises P_E for this. The change by the AI is upward-compatible for the user (except for extremely pathological examples). And, it will be in the Technical Corrigendum. All this argues for implementers to make the change ASAP. Traditionally, "non-binding" was applied to situations, where - it was an upward-incompatible change, - it directly contradicted the RM, or - it was a severe implementation burden. The first does not apply; the second (arguably) does not really apply, given 7.6(21) and the derivative nature of the elaboration error that arises; no strong argument was made so far for the third case. So I don't think that a NBI rating is warranted. Whether the ACATS includes a rated test for it at day zero or after some suitable phasing-in period, is a different question. Needless to say, I'm heavily against the day zero approach, which is unreasonable. **************************************************************** From: Robert Dewar Sent: Wednesday, April 05, 2000 5:47 PM <> I hereby make that case, we have not done the analysis in GNAT to know how difficult this is. It is by no means trivial, we did investigate this some time ago, and decided that the difficulty did not justify the technical gain at the time, and that there was no significant customer demand for this feature. I object to changes in the language which force vendors to deploy resources in a manner that is not consistent with best serving our customers. I prefer that *we* be the judge of how best to serve our customers when it comes to improvements in our technology, not the ARG! What the NBI designation does here is to say, "sorry, we were wrong, this implementation permission should have been a requirement, and in the next version of the language it will be a requirement." The trouble with the ARG is that it has a VERY narrow range of vision. When it comes to finding a bug in the language and fixing it, that very narrow range of vision is fine. When it comes to deciding what enhancements to mandate to the language, this narrow vision is very damaging, because it leads the ARG to decide that a particular feature is desirable in isolation. But such isolated decisions never make sense. Every improvement you make damages the Ada tecnologies out there by siphoning off effort from possibly more important features, and the ARG is in no position to make this tradeoff. The language definition is quite clear, I see no convincing argument in the AI, or elsewhere to change the language here. Yes, I see an argument that the language would be more useful if this change is made, but that same argument can be made for hundreds of other possible changes! **************************************************************** From: Tucker Taft Sent: Wednesday, April 05, 2000 6:09 PM Robert Dewar wrote: > ... > The only time we can MANDATE what are clear changes are when we know > there is no implementation burden, e.g. making Bit_Order static. I think we should mandate changes when we think there is a clear bug in the language. To me this is a bug. People bump into this frequently when they try to do very "normal" things with controlled types (e.g. define a Null_Unbounded_String). We simply goofed when we wrote the RM. This isn't an enhancement in my view, but rather an important bug fix. In general, the burden of proof of implementation problems has to be on the implementor, not on the ARG. Clearly several compilers have already implemented some version of this. It is hopeless for the ARG to do implementation burden analysis on compilers they didn't write. If there is an ARG member who knows of a serious implementation burden, then by all means speak up. I haven't heard anything like that yet. **************************************************************** From: Robert Dewar Sent: Wednesday, April 05, 2000 7:22 PM We are doing an analysis right now of the implementation burden. I must say that I don't think of this as a bug in the language. Yes, it was a bit of bad design, but if we are allowed to fix bad design decisions, I have MUCH more important candidates, for example, the horrible non-upward compatible design of fixed-point, which continues to cause us grief, and the goof on the definition of Size which ends up being THE major non compatibility between Ada 83 and Ada 95. These are FAR more serious mistakes in the RM, but I am NOT suggesting fixing them (well I think we need to fix them, but I would not dream of fixing them by changing the language, rather than extending it). ****************************************************************