!standard 13.11.4(0) 12-06-27 AI05-0298-1/02 !standard 13.11.6(0) !standard A.4.9(5/2) !standard A.4.9(2/2) !standard A.4.9(7/2) !standard A.4.9(10/2) !standard A.4.9(11.2/3) !standard A.4.9(11.5/3) !standard A.4.9(11.7/3) !standard A.4.9(11.10/3) !standard A.4.10(2/3) !standard A.4.10(5/3) !standard A.4.10(7/3) !standard A.4.10(10/3) !standard A.4.10(13/3) !standard A.4.10(16/3) !standard A.4.10(18/3) !standard A.4.10(21/3) !standard A.10.8(26) !class presentation 12-05-16 !status Amendment 2012 11-05-16 !status ARG Approved 9-0-1 12-06-15 !status work item 12-05-16 !status received 12-04-06 !priority Low !difficulty Easy !qualifier Omission !subject Last-minute presentation issues in the Standard !summary This AI corrects minor errors in the Standard. 1) Use fully qualified names in Pure and Preelaborate pragmas in A.4.9 and A.4.10. 2) Remove pragma Pure on library-level renames in A.4.9 and A.4.10. 3) Correct the example in A.10.8(25-27). 4) Correct the example in 13.11.6. [Editor's note: There are also corrections to AARM 3.10.2(22.rr), A.18.2(254.a/2), A.18.3(156.a/2), A.18.4(82.a/2), A.18.7(103.a/2), A.18.10(230.a/3), and A.18.18(72.a/3) which are not detailed here, but details can be found in the !appendix.] !question 1) In A.4.9 and A.4.10, there are a number of library subprogram declarations that are followed by Pure or Preelaborate pragmas, e.g.: with Ada.Containers; function Ada.Strings.Hash (Key : String) return Containers.Hash_Type; pragma Pure(Hash); The Pure pragma is incorrect, however; it should be pragma Pure(Ada.Strings.Hash); since this is a pragma that occurs at the place of a compilation unit, and 10.1.6(5) says that the root libary_item (Ada in this case) is directly visible, implying that the subprogram itself and other ancestors are not. (This only applies to the language-defined subprograms, not to packages.) Fix this? (Yes.) 2) There are four cases where the Standard defines a subprogram that is a child of Ada.Strings.Fixed that renames the corresponding subprogram in Ada.Strings: Hash, Hash_Case_Insensitive, Equal_Case_Insensitive, Less_Case_Insensitive. In all of those cases, the declarer is followed by a Pure pragma in the standard. However, a pragma Pure is not allowed on a library-level renames (the category is the same as the renamed unit). Fix this? (Yes.) 3) The example A.10.8(26-27) includes an instance for Small_Int, uses the values -126 and 126, and has a comment that says that 'Width is 4. But Small_Int, is defined in 3.2.2(15/2) and 3.5.4(35) as -10 .. 10, for which 'Width is 3 and the values -126 and 126 would raise Constraint_Error. Should the example be corrected? (Yes.) 4) The example in 13.11.6 does not compile. The errors that prevent that should be fixed. !recommendation (See summary.) !wording 1) In A.4.9(2/2), A.4.9(11.2/3), A.4.9(11.7/3), A.4.9(11.10/3), A.4.10(2/3), A.4.10(7/3), A.4.10(10/3), A.4.10(13/3), A.4.10(18/3), and A.4.10(21/3), put the fully qualified name of the subprogram into the pragma Pure or Preelaborate. [Editor's note: The paragraph numbers were wrong in A.4.9; these have been corrected and the correct numbers used above.] 2) In A.4.9(5/2), A.4.9(11.5/3), A.4.10(5/3), and A.4.10(16/3) remove the pragma Pure. 3) Replace A.10.8(26) with: subtype Byte_Int is Integer range -127 .. 127; package Int_IO is new Integer_IO(Byte_Int); use Int_IO; -- default format used at instantiation, -- Default_Width = 4, Default_Base = 10 4) In 13.11.4(13/3), the Pool parameter should have mode "in out". In 13.11.4(16/3), modify "return {Storage_Elements.}Storage_Count". In 13.11.6(11/3), replace: Storage : Storage_Array (1 .. Pool_Size); Next_Allocation : Storage_Count := 1; by: Storage : Storage_Array (0 .. Pool_Size-1); Next_Allocation : Storage_Count := 0; [Note: This is necessary so alignment works properly.] In 13.11.6(12/3), delete "aliased". In 13.11.6(16/3) and (25/3), the Pool parameter should have mode "in out". Add before 13.11.6(21/3): "use type Subpool_Handle;" In each of 13.11.6(21/3), 13.11.6(23/3), and 13.11.6(24/3), in the call of Set_Pool_of_Subpool, remove "'Unchecked_Access" from the Pool parameter. In 13.11.6(34/3), modify: {Pool.Markers(Pool.Current_Pool)}[Result].Start := Pool.Next_Allocation; In 13.11.6(24/3), modify: Pool.Next_Allocation := Pool.Markers(Pool.Current_Pool){.Start}; !discussion 1) This bug clearly is covered by the Dewar rule (the Standard never says anything obviously silly); in this case, it's clear that the language-defined subprograms and associated pragmas are intended to be legal (why would the Standard want to define illegal pragmas for language-defined subprograms?) 2) The renames are Pure whether or not the (illegal) pragmas are given, so removing these pragmas does not change the meaning of the language. Note that unit categories is one of many properties that are not changed (and thus cannot be specified) for a renaming. 3) This is an example, so the change is non-normative (and correct is better than wrong). 4) The majority of the changes are in the example 13.11.6, so those changes are non-normative (and correct is better than wrong). There are two changes in 13.11.4; in 13.11.4(16/3), the return type name is written differently than in the parent unit Storage_Pools. This difference is unnecessary and potentially confusing (although legal); in any case there is no significant difference caused by this change. The other change is to the mode of the Pool parameter to Default_Subpool_for_Pool (13.11.4(13/3)). This parameter needs to be "in out" in order for the example to work as intended. We could have restructured the example significantly in order to avoid the error, but that complicates the implementation of the pool for little benefit. The primary use of this routine is in implicit calls from allocators, and for those the mode does not matter: as a pool is a tagged type, the parameter is always passed by reference, and the pool associated with an access type has to be a variable (by 13.11(15)), so any mode is legal. Moreover, that rule makes constant Storage_Pool objects useless for their primary purpose (as they cannot be specified for an access type), so they should be rare. Thus we changed the mode of Default_Subpool_for_Pool to "in out". !corrigendum 13.11.4(0) @dinsc This is just a placeholder; the real change is found in the conflict file. !corrigendum 13.11.6(0) @dinsc This is just a placeholder; the real change is found in the conflict file. !corrigendum A.4.9(2/2) @drepl @xcode<@b Ada.Containers; @b Ada.Strings.Hash (Key : String) @b Containers.Hash_Type; @b Pure(Hash);> @dby @xcode<@b Ada.Containers; @b Ada.Strings.Hash (Key : String) @b Containers.Hash_Type; @b Pure(Ada.Strings.Hash);> !corrigendum A.4.9(5/2) @drepl @xcode<@b Ada.Containers, Ada.Strings.Hash; @b Ada.Strings.Fixed.Hash (Key : String) @b Containers.Hash_Type @b Ada.Strings.Hash; @b Pure(Hash);> @dby @xcode<@b Ada.Containers, Ada.Strings.Hash; @b Ada.Strings.Fixed.Hash (Key : String) @b Containers.Hash_Type @b Ada.Strings.Hash;> !corrigendum A.4.9(7/2) @drepl @xcode<@b Ada.Containers; @b @b Bounded @b @b Ada.Strings.Bounded.Generic_Bounded_Length (<@>); @b Ada.Strings.Bounded.Hash (Key : Bounded.Bounded_String) @b Containers.Hash_Type; @b Preelaborate(Hash);> @dby @xcode<@b Ada.Containers; @b @b Bounded @b @b Ada.Strings.Bounded.Generic_Bounded_Length (<@>); @b Ada.Strings.Bounded.Hash (Key : Bounded.Bounded_String) @b Containers.Hash_Type; @b Preelaborate(Ada.Strings.Bounded.Hash);> !corrigendum A.4.9(10/2) @drepl @xcode<@b Ada.Containers; @b Ada.Strings.Unbounded.Hash (Key : Unbounded_String) @b Containers.Hash_Type; @b Preelaborate(Hash);> @dby @xcode<@b Ada.Containers; @b Ada.Strings.Unbounded.Hash (Key : Unbounded_String) @b Containers.Hash_Type; @b Preelaborate(Ada.Strings.Unbounded.Hash);> !corrigendum A.4.9(11/2) @drepl @xindent @dby @xindent The library function Strings.Hash_Case_Insensitive has the following declaration: @xcode<@b Ada.Containers; @b Ada.Strings.Hash_Case_Insensitive (Key : String) @b Containers.Hash_Type; @b Pure(Ada.Strings.Hash_Case_Insensitive);> @xindent The library function Strings.Fixed.Hash_Case_Insensitive has the following declaration: @xcode<@b Ada.Containers, Ada.Strings.Hash_Case_Insensitive; @b Ada.Strings.Fixed.Hash_Case_Insensitive (Key : String) @b Containers.Hash_Type @b Ada.Strings.Hash_Case_Insensitive;> The generic library function Strings.Bounded.Hash_Case_Insensitive has the following declaration: @xcode<@b Ada.Containers; @b @b Bounded @b @b Ada.Strings.Bounded.Generic_Bounded_Length (<@>); @b Ada.Strings.Bounded.Hash_Case_Insensitive (Key : Bounded.Bounded_String) @b Containers.Hash_Type; @b Preelaborate(Ada.Strings.Bounded.Hash_Case_Insensitive);> @xindent The library function Strings.Unbounded.Hash_Case_Insensitive has the following declaration: @xcode<@b Ada.Containers; @b Ada.Strings.Unbounded.Hash_Case_Insensitive (Key : Unbounded_String) @b Containers.Hash_Type; @b Preelaborate(Ada.Strings.Unbounded.Hash_Case_Insensitive);> @xindent !corrigendum A.4.10(0) @dinsc This is just a placeholder; the real change is found in the conflict file. !corrigendum A.10.8(26) @drepl @b Int_IO @b Integer_IO(Small_Int); @b Int_IO; -- @i<@ft> -- @i<@ft>> @dby @xcode<@b Byte_Int @b Integer @b -127 .. 127; @b Int_IO @b Integer_IO(Byte_Int); @b Int_IO; -- @i<@ft> -- @i<@ft>> !ACATS test None needed. !appendix From: Adam Beneschan Sent: Friday, April 6, 2012 1:12 PM !topic Incorrect Pure/Preelaborate pragmas on library subprograms !reference A.4.9, A.4.10 !from Adam Beneschan xx-xx-xx !discussion In A.4.9 and A.4.10, there are a number of library subprogram declarations that are followed by Pure or Preelaborate pragmas, e.g.: with Ada.Containers; function Ada.Strings.Hash (Key : String) return Containers.Hash_Type; pragma Pure(Hash); The Pure pragma is incorrect, however; it should be pragma Pure(Ada.Strings.Hash); since this is a pragma that occurs at the place of a compilation unit, and 10.1.6(5) says that the root libary_item (Ada in this case) is directly visible, implying that the subprogram itself and other ancestors are not. (This only applies to the language-defined subprograms, not to packages.) **************************************************************** From: Adam Beneschan Sent: Friday, April 6, 2012 1:26 PM !topic Incorrect Pure on library-level renaming subprograms !reference A.4.9(5/2), A.4.9(14.2/3), A.4.10(5/3), A.4.10(16/3) !from Adam Beneschan 12-04-06 !discussion There are four cases where the Standard defines a subprogram that is a child of Ada.Strings.Fixed that renames the corresponding subprogram in Ada.Strings: Hash, Hash_Case_Insensitive, Equal_Case_Insensitive, Less_Case_Insensitive. In all of those cases, the declarer is followed by a Pure pragma in the standard, e.g.: with Ada.Containers, Ada.Strings.Hash; function Ada.Strings.Fixed.Hash (Key : String) return Containers.Hash_Type renames Ada.Strings.Hash; pragma Pure(Hash); -- should be Pure(Ada.Strings.Hash) according -- to my previous note I'm not sure it's legal to have a Pure pragma there. Pure is a program unit pragma, and 10.1.5(4) lists this as one of the places a program unit pragma may appear: At the place of a compilation_unit, in which case the pragma shall immediately follow in the same compilation (except for other pragmas) a library_unit_declaration that is a subprogram_declaration, generic_subprogram_declaration, or generic_instantiation, ... subprogram_renaming_declaration is not one of the syntactic categories listed as something the pragma may follow; and the way the syntax is written, a subprogram_renaming_declaration is not a subprogram_declaration. (It may be that subprogram_renaming_declaration should be added to the list in 10.1.5(4), and maybe also package_renaming_declaration and generic_renaming_declaration. But if not, the incorrect Pure pragmas should be removed from the Standard.) **************************************************************** From: Randy Brukardt Sent: Friday, April 6, 2012 4:41 PM > !topic Incorrect Pure/Preelaborate pragmas on library subprograms > !reference A.4.9, A.4.10 !from Adam Beneschan xx-xx-xx Cool, it's "xx" month. I thought it was April! :-) > !discussion > > In A.4.9 and A.4.10, there are a number of library subprogram > declarations that are followed by Pure or Preelaborate pragmas, e.g.: > > with Ada.Containers; > function Ada.Strings.Hash (Key : String) return Containers.Hash_Type; > pragma Pure(Hash); > > The Pure pragma is incorrect, however; it should be > > pragma Pure(Ada.Strings.Hash); This of course is the problem that comes from using pragmas for this. This would be much better written as an aspect: function Ada.Strings.Hash (Key : String) return Containers.Hash_Type with Pure => True; [or just "with Pure"] and exactly how to describe the unit is not an issue (since you don't have to do it). But that's not the default in the Standard because "with Pure" was thought to be ugly by some people. Grumble. Anyway, we'll put this into an early Ada 2012 AI. **************************************************************** From: Yuta Tomino Sent: Friday, April 27, 2012 2:49 PM !topic Storage Subpool Example has some errors !reference Ada 2012 RM 13.11.6 (3/3)(21/3)(23/3)(24/3)(27/3) !from Yuta Tomino 2012-04-27 !keywords !discussion 13.11.6 Storage Subpool Example has some errors. There are the points that I had to fix for compile, in below: (3/3) Insert use type because operators of Subpool_Handle are required. {use type System.Storage_Pools.Subpools.Subpool_Handle;} (21/3) Remove 'Unchecked_Access because 2nd parameter of Set_Pool_of_Subpool is not access but in out, defined in 13.11.4 (10/3) Subpools.Set_Pool_of_Subpool (Pool.Markers(1)'Unchecked_Access, Pool['Unchecked_Access]); end Initialize; (23/3) Insert downcast to MR_Subpool because "Start" component is added in the derived type. And, in next line, same as (21/3) return Result : constant not null Subpool_Handle := Pool.Markers(Pool.Current_Pool)'Unchecked_Access do {MR_Subpool (}Result{.all)}.Start := Pool.Next_Allocation; Subpools.Set_Pool_of_Subpool (Result, Pool['Unchecked_Access]); end return; end Create_Subpool; (24/3) Insert {.Start}. And, same as (21/3) if Pool.Current_Pool /= 1 then Pool.Next_Allocation := Pool.Markers(Pool.Current_Pool){.Start}; Pool.Current_Pool := Pool.Current_Pool - 1; -- Move to the previous subpool else -- Reinitialize the default subpool: Subpools.Set_Pool_of_Subpool (Pool.Markers(1)'Unchecked_Access, Pool['Unchecked_Access]); end if; end Deallocate_Subpool; (27/3) Add +1 for right alignment because Next_Allocation is started from 1. -- Correct the alignment if necessary: Pool.Next_Allocation := Pool.Next_Allocation + (({1} - Pool.Next_Allocation) mod Alignment); For reference, it's an output by gcc-4.7: mr_pool.adb:9:15: expected type "System.Storage_Pools.Subpools.Root_Storage_Pool_With_Subpools'Class" mr_pool.adb:9:15: found type access to "Mark_Release_Pool_Type" defined at line 9 mr_pool.adb:24:16: no selector "Start" for type "System.Storage_Pools.Subpools.Root_Subpool'Class" mr_pool.adb:25:52: expected type "System.Storage_Pools.Subpools.Root_Storage_Pool_With_Subpools'Class" mr_pool.adb:25:52: found type access to "Mark_Release_Pool_Type" defined at line 25 mr_pool.adb:33:18: operator for type "System.Storage_Pools.Subpools.Subpool_Handle" is not directly visible mr_pool.adb:33:18: add with_clause and use_clause for "MR_Pool" mr_pool.adb:37:38: expected type "System.Storage_Elements.Storage_Offset" mr_pool.adb:37:38: found type "MR_Subpool" defined at mr_pool.ads:28 mr_pool.adb:43:18: expected type "System.Storage_Pools.Subpools.Root_Storage_Pool_With_Subpools'Class" mr_pool.adb:43:18: found type access to "Mark_Release_Pool_Type" defined at line 43 mr_pool.adb:50:14: access-to-variable designates constant mr_pool.adb:60:18: operator for type "System.Storage_Pools.Subpools.Subpool_Handle" is not directly visible mr_pool.adb:60:18: add with_clause and use_clause for "MR_Pool" **************************************************************** From: Pascal Pignard Sent: Monday, May 7, 2012 3:29 PM !topic Missing limited in example in 3.10.2? !reference Ada 2012 draft 17 3.10.2 !from Pascal Pignard 12-05-07 !keywords limited !discussion Here is the draft 17 text: 3.10.2 Operations of Access Types ... 22.rr package P is type Int_Ptr is access all Integer; type Rec(D: access Integer) is limited private; private type Rec_Ptr is access all Rec; function F(X: Rec_Ptr) return Boolean; function G(X: access Rec) return Boolean; type Rec(D: access Integer) is -- missing limited? record C1: Int_Ptr := Int_Ptr(D); -- Illegal! C2: Rec_Ptr := Rec'Access; -- Illegal! C3: Boolean := F(Rec'Access); -- Illegal! C4: Boolean := G(Rec'Access); end record; end P; Seems that "limited" should be added in private part for type "Rec"? GNAT complains about: "current instance must be a limited type". **************************************************************** From: Randy Brukardt Sent: Monday, May 7, 2012 3:48 PM ... > Here is the draft 17 text: > 3.10.2 Operations of Access Types > ... > 22.rr package P is > type Int_Ptr is access all Integer; > type Rec(D: access Integer) is limited private; > private > type Rec_Ptr is access all Rec; > function F(X: Rec_Ptr) return Boolean; > function G(X: access Rec) return Boolean; > type Rec(D: access Integer) is > -- missing limited? No. > Seems that "limited" should be added in private part for type "Rec"? > GNAT complains about: "current instance must be a limited type". This text is unchanged since the Ada 95 RM. Even so, I think you would be right for Ada 95, but the rule in question was changed for Ada 2005 (3.7(10/3); the Ada 2012 terminology change is irrelevant for this purpose). Only access discriminants with defaults have to be on a limited type; without defaults (as here) are allowed on any type. So, if you ran GNAT in Ada 2005 or Ada 2012 mode, then GNAT is wrong; otherwise, it is irrelevant, since the rules were changed. **************************************************************** From: Adam Beneschan Sent: Monday, May 7, 2012 4:55 PM I think the problem is the use of Rec'Access in the default expression for C4, not the access discriminant. If I remove all four fields C1 through C4 (and replace them with "null"), GNAT doesn't complain; but adding back C4 does lead to the error Pascal described. ICC's compiler has the same behavior, except with a more explanatory error message. (The issue is that 3.10(9) says that the current instance of an immutably limited type is defined to be aliased, but the current instance of other records is not, and therefore 'Access isn't allowed in that case.) **************************************************************** From: Steve Baird Sent: Monday, May 7, 2012 5:14 PM Agreed. Note that this was not the rule in Ada95 when the example was written. **************************************************************** From: Adam Beneschan Sent: Monday, May 7, 2012 5:42 PM My Ada 95 book (the original one, from December 1994) says it was, except that there was no "immutably" limited concept. But 3.10(9) still said "the current instance of a limited type ... [is] defined to be aliased", and the rules have always said that a view has to be aliased to apply 'Access. So the declaration of C4 would have been illegal even back then, even if the type didn't have an access discriminant (which was itself illegal on a record type that didn't use the word "limited"). **************************************************************** From: Steve Baird Sent: Monday, May 7, 2012 5:48 PM > So the declaration of C4 would have been illegal even back then, even > if the type didn't have an access discriminant You're right. I was somehow thinking that the type in the example had a limited component, which would make it (in Ada2012 terminology) limited but not immutably limited. **************************************************************** From: Tucker Taft Sent: Monday, May 7, 2012 4:14 PM > This text is unchanged since the Ada 95 RM. Even so, I think you would > be right for Ada 95, but the rule in question was changed for Ada 2005 > (3.7(10/3); the Ada 2012 terminology change is irrelevant for this purpose). > Only access discriminants with defaults have to be on a limited type; > without defaults (as here) are allowed on any type. Access discriminants are allowed, but self-referential 'Access requires the enclosing type be limited, I believe. **************************************************************** From: Randy Brukardt Sent: Monday, May 7, 2012 5:56 PM > I think the problem is the use of Rec'Access in the default expression > for C4, not the access discriminant. If I remove all four fields C1 > through C4 (and replace them with "null"), GNAT doesn't complain; but > adding back C4 does lead to the error Pascal described. ICC's > compiler has the same behavior, except with a more explanatory > error message. (The issue is that 3.10(9) says that the > current instance of an immutably limited type is defined to be > aliased, but the current instance of other records is not, and > therefore 'Access isn't allowed in that case.) Thanks, I think. I was thrown off by the fact that this code was written for Ada 95, but it appears to be illegal Ada 95 (an access discriminant is never allowed on a non-limited type in Ada 95, and this type is not limited). Plus he didn't say what was rejected with that message, so I never looked at the uses of 'Access, I just assumed the message was bogus. So it looks like the type should be "limited record", and it should have been that for any version of Ada (even Ada 95). **************************************************************** From: Tucker Taft Sent: Monday, May 7, 2012 8:02 PM >> (The issue is that 3.10(9) says that >> the current instance of an immutably limited type is defined to be >> aliased, but the current instance of other records is not, and >> therefore 'Access isn't allowed in that case.) > > Agreed. > Note that this was not the rule in Ada95 when the example was written. Are you sure? I think Ada95 only allowed 'Access when inside a limited type as well. **************************************************************** From: Randy Brukardt Sent: Monday, May 7, 2012 8:21 PM Steve is thinking in his typical Bairdian way: Ada 95 required "limited", but Ada 2005 requires "immutably limited". Of course, this example is still wrong for Ada 95, because it isn't limited at all, but if a limited component was added (say of "File_Type"), then it would have been legal. (But we know this rule was murky at best, which is why we changed it.) **************************************************************** From: Pascal Pignard Sent: Tuesday, May 8, 2012 3:32 PM If needed, here is the full GNAT report: (GNAT GPL 2011 on Mac OS 10.6) bash-3.2$ gnatmake -gnatlf essai_p2.ads gcc -c -gnatlf essai_p2.ads GNAT GPL 2011 (20110419) Copyright 1992-2011, Free Software Foundation, Inc. Compiling: essai_p2.ads (source file time stamp: 2012-05-08 12:49:37) 1. package Essai_P2 is 2. type Int_Ptr is access all Integer; 3. type Rec(D: access Integer) is limited private; 4. private 5. type Rec_Ptr is access all Rec; 6. function F(X: Rec_Ptr) return Boolean; 7. function G(X: access Rec) return Boolean; 8. type Rec(D: access Integer) is 9. record 10. --C1: Int_Ptr := Int_Ptr(D); -- Illegal! 11. --C2: Rec_Ptr := Rec'Access; -- Illegal! 12. --C3: Boolean := F(Rec'Access); -- Illegal! 13. C4: Boolean := G(Rec'Access); | > > > current instance must be a limited type 14. end record; 15. end ; 15 lines: 1 error gnatmake: "essai_p2.ads" compilation error At line 13, Rec is pointed by error pointer. Changing GNAT option to Ada 95, 2005 or 2012 gives the same. **************************************************************** From: Adam Beneschan Sent: Tuesday, May 8, 2012 12:39 PM !topic Implementation note on Ada.Containers.Vectors is no longer true !reference AARM A.18.2(254.a/2) !from Adam Beneschan 12-05-08 !discussion The AARM says this, in the description of Ada.Containers.Vectors: 254/2 The execution of an assignment_statement for a vector shall have the effect of copying the elements from the source vector object to the target vector object. 254.a/2 Implementation Note: An assignment of a Vector is a "deep" copy; that is the elements are copied as well as the data structures. We say "effect of" in order to allow the implementation to avoid copying elements immediately if it wishes. For instance, an implementation that avoided copying until one of the containers is modified would be allowed. However, I believe that the last statement is no longer true, now that Reference and Constant_Reference have been added. Consider: package Rec_Vectors is new Ada.Containers.Vectors (Positive, Rec); V1 : aliased Rec_Vectors.Vector; V2 : aliased Rec_Vectors.Vector; Ref1 : Rec_Vectors.Constant_Reference_Type; Ref2 : Rec_Vectors.Constant_Reference_Type; ... code to set elements up in V1 V2 := V1; Ref1 := Rec_Vectors.Constant_Reference (V1, 1); Ref2 := Rec_Vectors.Constant_Reference (V2, 1); If the implementation avoided copying Rec objects until one of the containers is modified, as suggested by 254.a, then after the two Constant_Reference calls, Ref1.Element and Ref2.Element would have the same value. Then, if element 1 is later modified in one of the vectors, the Rec object of one of the elements would have to be relocated, which means that at that point, either Ref1 or Ref2 is wrong. (An implementation that went ahead and copied Rec objects as soon as one of them were *referenced*, even by Constant_Reference, would probably be OK. But I'd think that would defeat the purpose of avoiding the copy. Maybe there's a way to work things out so that a Rec object is copied when an Adjust on the Constant_Reference_Type is performed... But the last sentence of 254.a still wouldn't be quite correct.) **************************************************************** From: Randy Brukardt Sent: Wednesday, May 16, 2012 11:25 PM ... > The AARM says this, in the description of Ada.Containers.Vectors: > > 254/2 The execution of an assignment_statement for a vector shall have > the effect of copying the elements from the source vector object to > the target vector object. > > 254.a/2 Implementation Note: An assignment of a Vector is a "deep" copy; > that is the elements are copied as well as the data structures. We > say "effect of" in order to allow the implementation to avoid > copying elements immediately if it wishes. For instance, an > implementation that avoided copying until one of the containers is > modified would be allowed. > > However, I believe that the last statement is no longer true, now that > Reference and Constant_Reference have been added. Reference and Constant_Reference should change nothing, since they are semantically equivalent to Update_Element and Query_Element, respectively. I can believe that the statement was *never* really true, though. > Consider: > > package Rec_Vectors is new Ada.Containers.Vectors (Positive, Rec); > > V1 : aliased Rec_Vectors.Vector; > V2 : aliased Rec_Vectors.Vector; > > Ref1 : Rec_Vectors.Constant_Reference_Type; > Ref2 : Rec_Vectors.Constant_Reference_Type; > > ... code to set elements up in V1 > > V2 := V1; > > Ref1 := Rec_Vectors.Constant_Reference (V1, 1); > Ref2 := Rec_Vectors.Constant_Reference (V2, 1); > > If the implementation avoided copying Rec objects until one of the > containers is modified, as suggested by 254.a, then after the two > Constant_Reference calls, Ref1.Element and Ref2.Element would have the > same value. Then, if element 1 is later modified in one of the > vectors, the Rec object of one of the elements would have to be > relocated, which means that at that point, either Ref1 or Ref2 is > wrong. (An implementation that went ahead and copied Rec objects as > soon as one of them were *referenced*, even by Constant_Reference, > would probably be OK. But I'd think that would defeat the purpose of > avoiding the copy. Maybe there's a way to work things out so that a > Rec object is copied when an Adjust on the Constant_Reference_Type is > performed... But the last sentence of 254.a still wouldn't be quite > correct.) As I said, you can write an example of this problem using Query_Element, if Rec is passed by reference (which it must be if it is tagged, and which is probably is if it is composite): -- Assume Rec has a component C1 whose type is Natural. procedure Inc_C1 (Element : in out Rec) is begin Element.C1 := Element.C1 + 1; end Inc_C1; procedure Deal_with_V1 (Element : in Rec) is Val : Natural := Element.C1; begin Update_Element (V2, Inc_C1'access); if Val /= Element.C1 then raise Program_Error; end if; end Deal_with_V1; procedure Deal_with_V2 (Element : in Rec) is Val : Natural := Element.C1; begin Query_Element (V1, Deal_with_V1'access); if Val+1 /= Element.C1 then raise Program_Error; end if; end Deal_with_V1; V2 := V1; Query_Element (V2, Deal_with_V2'access); -- !! If the copying of elements is delayed until V2 is modified, then the one of the parameters to Deal_with_V1 or Deal_with_V2 is going to end pointing to the wrong element (since they start out the same, but one of them must be copied when Update_Element is called). Of course, neither of these examples is very likely, but it's certainly true that a language-defined container must be prepared for this problem. The main point is that it is not new; (almost) nothing changed when Ada 2012 added Constant_Reference and Reference. This is the exact situation that the tampering checks are intended to prevent; but they allow this case to increase usability (and because we assumed the effect of a deep copy on assignment). (Note, however, that whether Deal_with_V2 works as expected here depends on the parameters passing mechanism, which depends on the element type and is implementation-defined in some cases. So it is not a good idea to depend on this unless the element type is always going to be a tagged type; or in the reverse [assuming the parameter never changes], if the element type is always scalar.) A container implementation with delayed copying would have to copy any element as soon as Query_Element or Constant_Reference is used on it. That would reduce the value of the implementation technique (but it still could be worthwhile, so long as function Element is used to read elements frequently). It also could avoid the copying for Query_Element if it knew that the element type was guaranteed to be passed by copy; this is the only difference caused by Ada 2012. (Note that this is the reason that this technique works for Ada.Strings.Unbounded: the element type is discrete.) It probably was a mistake to suggest this possible implementation, but the intent is unchanged: to specify as little as possible so that implementers can use innovative implementations. ****************************************************************