CVS difference for ai05s/ai05-0005-1.txt

Differences between 1.44 and version 1.45
Log of other versions for file ai05s/ai05-0005-1.txt

--- ai05s/ai05-0005-1.txt	2012/02/04 09:07:37	1.44
+++ ai05s/ai05-0005-1.txt	2012/02/19 04:54:03	1.45
@@ -2674,6 +2674,300 @@
 
 ****************************************************************
 
+!topic Number of Deallocate calls required by 13.11(21.6/3)
+!reference RM12 13.11, AI05-0107
+!from Adam Beneschan 11-11-18
+!discussion
+
+In the Implementation Requirements section of 13.11, which describes
+the points where a storage pool's Allocate and Deallocate routines may
+be called, 13.11(21.6/3) says "The number of calls to Deallocate
+needed to implement an instance of Unchecked_Deallocation (see
+13.11.2) for any particular object is the same as the number of
+Allocate calls for that object."  However, I believe that this
+statement is not always true.  Consider:
+
+    package Pack2 is
+
+       type Integer_Array is array (Natural range <>) of Integer;
+
+       type Disc_Rec (Is_Dyn_Array   : Boolean := False;
+                      Dyn_Array_Size : Natural := 0) is 
+          record
+             case Is_Dyn_Array is
+                when False =>
+                   ID : Integer;
+                when True =>
+                   Parameters : Integer_Array (1 .. Dyn_Array_Size);
+             end case;
+          end record;
+
+       type Enclosing_Rec is 
+          record
+             D : Disc_Rec;
+          end record;
+
+       type Rec_Acc is access all Enclosing_Rec;
+       for Rec_Acc'Storage_Pool use Pack1.Custom_Pool;
+       procedure Free is new 
+          Ada.Unchecked_Deallocation (Enclosing_Rec, Rec_Acc);
+
+    end Pack2;    
+
+and then, where A is a Rec_Acc:
+
+      A := new Enclosing_Rec' (D => (Is_Dyn_Array => True,
+                                     Dyn_Array_Size => 10,
+                                     Parameters => (1 .. 10 => -1)));
+      ...
+      ...
+      A.D := (Is_Dyn_Array => False, Dyn_Array_Size => 0, ID => 123);
+      ...
+      ...
+      Free (A);
+
+Assume the implementation implements dynamic record components like
+"Parameters" by means of a pointer.  Thus, when the allocator that
+assigns A is performed, there should be two calls to the the storage
+pool's Allocate routine: one to allocate an Integer_Array with 10
+integers, and another to allocate the Enclosing_Rec.  When A.D is
+reassigned, this should result in a call to Deallocate to deallocate
+the space used by A.D.Parameters.  (This is clearly allowed by
+paragraphs 21.4 and 21.7.)  Then, when Free (A) is called, only one
+call to Deallocate is now needed.
+
+My problem is that this seems to violate the language of
+13.11(21.6/3), which requires that, since the number of Allocate calls
+for the object is two, the number of Deallocate calls to implement the
+Unchecked_Deallocation instance should also be two.  This isn't really
+true, since one of the Deallocate calls happened earlier, during the
+assignment, and not during the Unchecked_Deallocation instance.  I
+think that the intent is probably something like "the total number of
+Deallocate calls on an allocated object, counting the times it happens
+during assignment and in an instance of Unchecked_Allocation, equals
+the total number of Allocate calls".  Maybe it would be enough to add
+a "To Be Honest" note to the AARM?
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, November 18, 2011  1:01 PM
+
+...
+> Assume the implementation implements dynamic record components like 
+> "Parameters" by means of a pointer.  Thus, when the allocator that 
+> assigns A is performed, there should be two calls to the the storage 
+> pool's Allocate routine: one to allocate an Integer_Array with 10 
+> integers, and another to allocate the Enclosing_Rec.  When A.D is 
+> reassigned, this should result in a call to Deallocate to deallocate 
+> the space used by A.D.Parameters.  (This is clearly allowed by 
+> paragraphs 21.4 and 21.7.)  Then, when Free (A) is called, only one 
+> call to Deallocate is now needed.
+> 
+> My problem is that this seems to violate the language of 
+> 13.11(21.6/3), which requires that, since the number of Allocate calls 
+> for the object is two, the number of Deallocate calls to implement the 
+> Unchecked_Deallocation instance should also be two.  This isn't really 
+> true, since one of the Deallocate calls happened earlier, during the 
+> assignment, and not during the Unchecked_Deallocation instance.  I 
+> think that the intent is probably something like "the total number of 
+> Deallocate calls on an allocated object, counting the times it happens 
+> during assignment and in an instance of Unchecked_Allocation, equals 
+> the total number of Allocate calls".  Maybe it would be enough to add 
+> a "To Be Honest" note to the AARM?
+
+You're right, of course, and it surely was my intent to allow an implementation
+like this (this is essentially how Janus/Ada works). (I say "my" here, because I
+pushed through these wording changes to ensure that the Janus/Ada-style
+implementation was clearly allowed by the Standard.)
+
+But this is an unusual case: you need a discriminant-dependent array component
+in a variant of a mutable record type (and an assignment into it changing the
+variant) in order to cause it to happen. So I don't think it is worth hair-ing up
+the wording further to allow it explicitly. Probably a To-Be-Honest would be
+sufficient.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, November 18, 2011  1:26 PM
+
+>...Probably a
+> To-Be-Honest would be sufficient.
+
+I agree.
+
+Or we could just decide to trust implementations to do something
+reasonable:
+
+    The number of calls to Allocate and Deallocate needed to implement
+    allocators and Unchecked_Deallocation is unspecified.
+
+The current wording isn't really specifying anything important.
+
+****************************************************************
+
+From: Stephen Leake
+Sent: Friday, November 18, 2011  2:00 PM
+
+> Assume the implementation implements dynamic record components like 
+> "Parameters" by means of a pointer.
+
+Then A is one object, and A.D[.all] is a different object.
+
+> Thus, when the allocator that assigns A is performed, there should be 
+> two calls to the the storage pool's Allocate routine: one to allocate 
+> an Integer_Array with 10 integers, and another to allocate the 
+> Enclosing_Rec.
+
+That's two calls, for two objects.
+
+> When A.D is reassigned, this should result in a call to Deallocate to 
+> deallocate the space used by A.D.Parameters.
+
+That's one call to Deallocate for A.D[.all]
+
+> (This is clearly allowed by paragraphs 21.4 and 21.7.) Then, when Free
+> (A) is called, only one call to Deallocate is now needed.
+
+Which is consistent; there was only one call to Allocate for A.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, November 18, 2011  4:23 PM
+
+> > Assume the implementation implements dynamic record components like 
+> > "Parameters" by means of a pointer.
+> 
+> Then A is one object, and A.D[.all] is a different object.
+
+Totally irrelevant, because the language is defined in terms of allocator calls;
+"object" (which is not very well-defined anyway) is not involved.
+13.11(16/3) says that for "an allocator of a type T", the storage is allocated by
+calling Allocate. The definition is in terms of types. T is "Enclosing_Rec" in Adam's
+example.
+ 
+> > Thus, when the allocator that assigns A is performed, there should 
+> > be two calls to the the storage pool's Allocate routine: one to 
+> > allocate an Integer_Array with 10 integers, and another to allocate 
+> > the Enclosing_Rec.
+> 
+> That's two calls, for two objects.
+
+OK, but still irrelevant, because all of the rules are defined in terms of types, not
+objects. (In addition, Ada definitely does not define these things as separate objects
+ - it would be weird to have two objects that are directly parts of the same type.)
+
+It also should be clear that we are not talking about the entirety of A.D here; there
+is a portion that is allocated separately but the rest of it is allocated as part of
+the enclosing object. So it would be difficult to define your model formally.
+
+> > When A.D is reassigned, this should result in a call to Deallocate 
+> > to deallocate the space used by A.D.Parameters.
+> 
+> That's one call to Deallocate for A.D[.all]
+
+Which is a part of Enclosing_Rec.
+
+> > (This is clearly allowed by paragraphs 21.4 and 21.7.) Then, when 
+> > Free
+> > (A) is called, only one call to Deallocate is now needed.
+> 
+> Which is consistent; there was only one call to Allocate for A.
+
+Which again is irrelevant, the rules are defined in terms of the "allocator for type T"
+(that is Enclosing_Rec).
+
+One could imagine redefining the rules for objects and allocators as you suggest, but
+that would be a massive undertaking which would have far-reaching consequences. It's
+not worth it for a bug of this tiny magnitude.
+
+In Adam's example, Janus/Ada would do exactly what he describes:
+
+     A := new Enclosing_Rec' (D => (Is_Dyn_Array => True,
+                                     Dyn_Array_Size => 10,
+                                     Parameters => (1 .. 10 => -1)));
+
+-- Call Allocate for Enclosing_Rec, including the fixed size part of Disc_Rec -
+   roughly 16 bytes in this example (include the array descriptor for Parameters).
+-- Call Allocate for the dynamic part (the data area of component
+   A.D.Parameters) - 20 bytes in this example.
+
+      A.D := (Is_Dyn_Array => False, Dyn_Array_Size => 0, ID => 123);
+
+-- Call Deallocate for the dynamic part of the old target object (the data area of
+   component A.D.Parameters) - 20 bytes.
+-- {Call Allocate for the dyanmic part of the new target object (but there is none
+   in this example, so this doesn't happen)}
+
+      Free (A);
+
+-- {Call Deallocate for the dyanmic part of A (but again there is none in this example,
+   so this doesn't happen)}
+-- Call Deallocate for Enclosing_Rec, including the fixed size part of Disc_Rec.
+
+This is one possible model that the wording is intending to allow.
+
+Note that Janus/Ada uses this model for all objects; the only differences with
+stack-declared objects is that the storage pools involved are implementation-defined
+(so you can't observe the sequence of calls); and the fixed part is allocated directly
+on the stack by the compiler (so there are no calls at all). (In very simple cases, the
+compiler optimizes away the empty calls to handle the obviously non-existent dynamic part(s),
+of course.)
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, November 18, 2011  4:40 PM
+
+> Or we could just decide to trust implementations to do something
+> reasonable:
+> 
+>     The number of calls to Allocate and Deallocate needed to implement
+>     allocators and Unchecked_Deallocation is unspecified.
+> 
+> The current wording isn't really specifying anything important.
+
+I somewhat disagree. The current wording is intended to give a guarantee to a pool writer
+that they only have to handle Deallocate calls that have parameters that match successful
+Allocate calls. That could simplify implementation for some pools.
+
+In particular, if a pool only allows Allocate calls with sizes that are a multiple of 100
+(to take an unlikely example), raising Storage_Error for other sizes, it should not need
+to worry about getting a Deallocate call with a size that is not a multiple of 100. If it
+gets a Deallocate with a size of 75, it could (and probably should) simply punt and raise
+Program_Error, because such a call would represent an implementation bug and a violation
+of 13.11(21.6/3).
+
+If we simply say "unspecified", then any Deallocate call would be possible, even if any
+similar Allocate call would have been failed.
+
+13.11(21.7/3) prevents getting different parameters for Deallocate than you get for Allocate.
+13.11(21.6/3) prevents you from getting extra calls, or omitting some calls. (Which is
+exactly what the AARM says.) The intent is that you get an exact matching Deallocate call
+(from somewhere) that you get for Allocate.
+
+One could hope that implementations don't screw this up, but I'd rather have explicit wording
+(so pool writers can depend upon the behavior).
+
+---
+
+Aside: We could fix this wording by simply prefixing it: Say "If there are no intervening
+assignments to an object O, then the number of calls to Deallocate needed to implement an
+instance of Unchecked_Deallocation (see 13.11.2) for O is the same as the number of Allocate
+calls for that object."
+
+In that case, the number of calls would be unspecified for objects that are assigned to after
+they are allocated, but probably that is good enough (it would be hard for a compiler to know
+whether an object was assigned to, so it is unlikely to do something nasty).
+
+But I still think a TBH is good enough here; the only time this would not be true is in fairly
+obscure cases, and it is unlikely that any pool writer (much less ordinary user) would notice
+the difference.
+
+****************************************************************
+
 From: John Barnes
 Sent: Thursday, December 22, 2011  8:47 AM
 
@@ -2822,8 +3116,85 @@
 D.2.2(6.a/2), D.2.2(21.a/2), H.4(1.a), H.4(27.a).
 
 ****************************************************************
+
+From: Brad Moore
+Sent: Friday, January 27, 2012  11:39 AM
+
+[Part of a much larger NB review. - Editor]
+
+13.1 (29.o/3 ) Added wording so that inheritance depends on whether 
+               operational items are visible rather than whether they 
+			   occur before the declaration (we don't want to look into 
+			   private parts). {Also} {limited}[Limited] operational 
+			   inheritance to untagged types to avoid anomalies with 
+			   private extensions (this is not incompatible, no existing 
+			   operational attribute used this capability). Also added 
+			   wording to clearly define that subprogram inheritance 
+			   works like derivation of subprograms.
+
+****************************************************************
+
+A summary of mail from February 7-9, 2012:
+
+RM 6.5(21.e/3) says (in wording that comes from AI05-0234):
+   Note that the flag should only be queried in the case where any access
+   discriminants which the result object might have subtypes with "bad"
+   accessibility levels (as determined by the rules of 3.10.2 for
+   determining the accessibility level of the type of an access
+   discriminant in the expression or return_subtype_indication of a
+   return statement).
+
+Randy Brukardt wrote:
+I've received a comment from Canada that this does not parse, and I agree.
+
+Tucker Taft wrote:
+As indicated above, I believe that the problematic sentence 
+should have two copies of "might have" in a row, separated by a comma.
+Not a very common occurrence, but I think that makes the 
+sentence meaningful, if not glorious English.
+
+Gary Dismukes writes:
+Seems like it could be worded more smoothly, maybe:
+ 
+  "... in the case where any access discriminants of the result object
+   might have subtypes ..."
+
+(Surely "any" is sufficient to convey the first "might have"?)
+
+Randy Brukardt wrote:
+Thanks Gary, I did not relish responding to a group that repeatedly 
+complained about confusing word usage that everything is fine if we 
+just duplicated "might have". I kinda thought that that version was 
+barely an improvement over the original.
+
+(A bit later...)
+
+Unfortunately, I can't quite get from that wording to the T'Class 
+examples that follow it in the Standard. T'Class doesn't have any 
+access discriminants, but objects of it might, and thus *might* be 
+subject to the check.
+
+Maybe it would be better to turn it around:
+
+  Note that the flag should only be queried in the case where the result 
+  object might have access discriminants that might have subtypes with "bad"
+  accessibility levels (as determined by the rules of 3.10.2 for 
+  determining the accessibility level of the type of an access 
+  discriminant in the expression or return_subtype_indication of a return statement).
+ 
+We've got two "might"s here, but that's the point: the result object "might"
+have discriminants (we don't know this when generating the code), and 
+the discriminants "might" have a bad accessibility level. If both are 
+true, we need a check, otherwise we don't.
+
+Do I understand this right, and is this better, or did I miss something?
+
+Gary Dismukes wrote:
+You understand it right, and I think your rewording is fine (and better).
+
+****************************************************************
 
-Editor's note (January 27, 2012): All of the items above this
+Editor's note (February 13, 2012): All of the items above this
 marker have been included in the working version of the AARM.
 
 ****************************************************************

Questions? Ask the ACAA Technical Agent