CVS difference for 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