!standard 7.6 (21) 01-09-20 AC95-00012/00 !standard 7.6.1(13) !standard 7.6 (17.1) !class confirmation 01-09-20 !status received no action 01-09-19 !subject Finalization called by aggregate during elaboration !summary !appendix !topic Finalization called by aggregate during elaboration !reference RM95-7.6.1(13),4.3(5),7.6(21) !from Adam Beneschan 09-19-01 !discussion In this example, where T is a controlled type, is it true that the Finalize procedure on T may or may not be called during elaboration of the declaration? declare Obj : T := (Ada.Finalization.Controlled with Field1 => 1, Field2 => 2); begin end; My understanding of the RM is that T's Finalize procedure may be called before the sequence of statements but isn't necessarily called. This is because the aggregate causes an anonymous object to be created (4.3(5)), and 7.6.1(13) says that the aggregate has to be finalized before the sequence of statements, but 7.6(21) gives implementations permission not to create the anonymous object. However, my understanding is contradicted by ACATS test c761011, which contains this test: Normal: -- Case 1 begin declare Obj1 : Ctrl := Create (Ident_Int (1)); Obj2 : constant Ctrl := (Ada.Finalization.Controlled with D => False, Finalized => Ident_Bool (False), C1 => Ident_Int (2)); Obj3 : Ctrl := (Ada.Finalization.Controlled with D => True, Finalized => Ident_Bool (False), C2 => 2.0 * Float (Ident_Int (3))); -- Finalization: User_Error Obj4 : Ctrl := Create (Ident_Int (4)); begin Comment ("Finalization of normal object"); Use_It (Obj1); -- Prevent optimization of Objects. Use_It (Obj2); -- (Critical if AI-147 is adopted.) Use_It (Obj3); Use_It (Obj4); end; Failed ("No exception raised by finalization of normal object"); exception when Program_Error => if not Was_Finalized (Ident_Int (1)) or not Was_Finalized (Ident_Int (2)) or not Was_Finalized (Ident_Int (4)) then Failed ("Missing finalizations - 1"); end if; when E: others => Failed ("Exception " & Exception_Name (E) & " raised - " & Exception_Message (E) & " - 1"); end Normal; This test seems to assume that the declaration of Obj3 will *not* call the Finalize routine, since Finalize on this particular aggregate will raise an exception. The Program_Error handler makes sure Obj1, Obj2, and Obj4 were all finalized, which makes sense if the exception occurs while trying to finalize Obj3 at the end of the block, but doesn't make sense if the exception occurs while trying to finalize an anonymous object when Obj3 is declared. Is my understanding of the RM correct, or have I missed something? **************************************************************** From: Randy Brukardt Sent: Wednesday, September 19, 2001 7:08 PM (example deleted) > Is my understanding of the RM correct, or have I missed something? Sure, you've missed the Corrigendum. ACATS test C761011 was created to test a rule added in Technical Corrigendum 1. Technical Corrigendum 1 was published by ISO on June 1, 2001 (as ISO/IEC 8652:1995/Cor.1:2001), so it is an official part of the ISO Ada standard by any measure. Conformity assessment (the purpose of the ACATS) is done to the ISO standard, so the ACATS tests reflect the rules corrected in the Corrigendum. The rule in question is 7.6(17.1/1). The consolidated AARM (available on the web at www.adaic.org/standards/95aarm/html/AA-7-6.html describes the reasons behind the rule change; the entire Defect Report is also available on line (click on the number, 8652/0022). **************************************************************** From: Adam Beneschan Sent: Thursday, September 20, 2001 4:24 PM Yes, I had indeed missed this new rule; thank you for pointing it out to me. I've been wrestling over the question over whether the wording in 7.6(21) ought to be changed, or a note added to 4.3, to forestall possible confusion, but I'm not sure whether this is necessary. However, I'm now wondering if the new rule 7.6(17.1/1) is adequate in the case of nested aggregates. Consider this modification of the example in 17.1/1: package P is type Dyn_String is private; type Dyn_Substring is private; Null_Substring : constant Dyn_Substring; ... private type Dyn_String is new Ada.Finalization.Controlled with ... procedure Finalize(X : in out Dyn_String); procedure Adjust(X : in out Dyn_String); type Dyn_Substring is record Str : Dyn_String; First_Index, Last_Index : Integer; end record; Null_Substring : constant Dyn_Substring := (Str => (Ada.Finalization.Controlled with ...), First_Index => 1, Last_Index => 0); ... end P; The declaration of Null_Substring involves two aggregates; by 4.3(5), an anonymous object will be (conceptually) created for each. Let's call the anonymous object of type Dyn_Substring Anon1, and the one of type Dyn_String Anon2. Following 4.3(5), to create Anon1, the values of the components are obtained and "assigned into the corresponding components ... of the anonymous object." One of those components is the inner aggregate; according to 7.6(17.1/1), since the inner aggregate has a controlled type, the implementation may not create an anonymous object for it. Thus, Anon2 will not be created, and the aggregate will be built directly in Anon1.Str. However, Dyn_Substring is not a controlled type, and therefore 7.6(17.1/1) does not apply to it. Therefore, it's still legal (but not required) for the compiler to create an anonymous object Anon1. If it does so, the compiler will build Anon1 by assigning to its components (including building the inner aggregate directly in Anon1.Str), and then assign Null_Substring to Anon1 (which involves a call to Adjust on Null_Substring.Str, by 7.6(16)), and then (by 7.6.1(13)) finalize Anon1, which includes (by 7.6.1(9)) finalization of Anon1.Str; thus, Program_Error will still be raised for the same reasons cited in 8652/0022. **************************************************************** From: Randy Brukardt Sent: Thursday, September 20, 2001 10:28 PM > Yes, I had indeed missed this new rule; thank you for pointing it out > to me. I've been wrestling over the question over whether the wording > in 7.6(21) ought to be changed, or a note added to 4.3, to forestall > possible confusion, but I'm not sure whether this is necessary. 7.6(21) is complete garbage; Claw (and most other O-O programs using Adjust) couldn't be written if it was implemented literally. (Indeed, Tucker had done exactly that, and it caused us trouble with ObjectAda for two years.) AI-00147 changes it; unfortunately, we've (the ARG) never been able to agree (and stay agreed) on how to change it. It's on the agenda again for the upcoming meeting; I hope it finally gets finished. > However, Dyn_Substring is not a controlled type, and therefore > 7.6(17.1/1) does not apply to it. Therefore, it's still legal (but > not required) for the compiler to create an anonymous object Anon1. > If it does so, the compiler will build Anon1 by assigning to its > components (including building the inner aggregate directly in > Anon1.Str), and then assign Null_Substring to Anon1 (which involves a > call to Adjust on Null_Substring.Str, by 7.6(16)), and then (by > 7.6.1(13)) finalize Anon1, which includes (by 7.6.1(9)) finalization > of Anon1.Str; thus, Program_Error will still be raised for the same > reasons cited in 8652/0022. I *think* this behavior is intentional. Certainly AI-0083 talks only about extension aggregates. And I know we (the ARG) wanted to bound this requirement, because it is a significant amount of work to get right. The point of the rule is to make it possible to declare deferred constants of controlled types without triggering access-before-elaboration problems. To get it to occur with components (as in your example), it is necessary to both the controlled type and the outer type in the same package. Thus there is an easy work-around to avoid any problems: make sure that controlled types are not used in components in the same package as they are declared. So I don't think there is a problem here. But I could be wrong. :-) **************************************************************** From: Tucker Taft Sent: Friday, September 21, 2001 7:47 AM Well, I certainly expected that all "initialization" contexts would require initialize-in-place for controlled subcomponents. We should look at the wording carefully to see if this loophole exists, and figure out whether it is worth fixing. **************************************************************** From: Randy Brukardt Sent: Friday, September 21, 2001 12:13 PM It certainly exists: 7.6(17.1/1) says "aggregate of a controlled type". It would have to say something like "aggregate with any controlled parts" to cover subcomponents. That "fix" is easy enough. However, this is a significant change to the rule. It would have a substantial impact on implementors, as it would extend the cost to all aggregates, not just to extension aggregates. (The fact that the rule only applies to extension aggregates is mentioned several times in the AI.) Moreover, it would do so to fix a rather unlikely case. And this rule was rather controversial in the first place, I would expect a large extension to be more so. Is there enough interest in changing this rule to open an AI?? ****************************************************************