!standard 13.03 (25) 05-04-03 AI95-00051-02/03 !standard 13.03 (28) !standard 13.03 (30) !standard 13.03 (31) !standard 13.03 (32) !standard 13.03 (42) !standard 13.03 (43) !standard 13.03 (50) !standard 13.03 (56) !standard 13.03 (53) !class binding interpretation 95-06-25 !status Amendment 200Y 05-02-13 !status WG9 Approved (Letter ballot, 05-01) !status ARG Approved 7-0-3 05-02-13 !status work item 04-12-08 !status received 04-12-08 !reference AI95-00291 !priority High !difficulty Medium !subject Size and Alignment clauses !summary This AI describes the recommended level of support for Size and Alignment clauses for subtypes and for objects. This AI combines the previous AI-109 (treating subtypes and objects in separate AIs turned out to be a bad idea), but removes discussion of aliased objects (now treated in AI-291). The following Recommended Level of Support is added: 1. The implementation should support a Size or Alignment clause for an object if it specifies the same Size or Alignment that the implementation would have chosen by default (that is, a "confirming" representation item, as defined in AI-291). 2. For a signed integer type, the implementation should support a Size/Alignment clause which specifies a value no larger than the largest Size/Alignment value that would be chosen by the implementation in the absence of a attribute_definition_clause for any signed integer type. Corresponding advice applies for modular integer types, fixed point types, enumeration types, record types, and array types. 3. Only confirming Size/Alignment clauses need be supported for floating point types, access types, protected types, and task types. 4. Alignments other than a power-of-two need not be supported. 5. Confirming representation items have no semantic effect (this means eliminating rules of the form "If attribute Xyz is specified, then" ...) !question What is the recommended level of support for Size and Alignment clauses? For example, the recommended level of support for the Size attribute of an object (in RM-13.3(42,43)) implies that the following should be accepted: X: Integer; for X'Size use 1024; Is this the intent? (No). !recommendation (See summary.) !wording In 13.3(25) replace: "If the Alignment of a subtype is specified, then the Alignment of an object is at least as strict, unless the object's Alignment is also specified". with "The Alignment of an object is at least as strict as the Alignment of its subtype, unless the object's Alignment is specified". Replace 13.3(28) with "Program execution is erroneous if an object that is not allocated under control of the implementation is not aligned according to its Alignment". Replace 13.3(30-32) with: - An implementation should support an Alignment clause for a discrete type, fixed point type, record type, or array type, specifying an Alignment value that is zero or a power of two, subject to the following: - An implementation need not support an Alignment clause for a signed integer type specifying an Alignment greater than the largest Alignment value that is ever chosen by default by the implementation for any signed integer type. A corresponding limitation may be imposed for modular integer types, fixed point types, enumeration types, record types, and array types. - An implementation need not support a nonconfirming Alignment clause which could enable the creation of an object of an elementary type which cannot be easily loaded and stored by available machine instructions. Replace 13.3(42-3) with The size of an array object should not include its bounds. The recommended level of support for the Size attribute of objects is the same as for subtypes (see below). In 13.3(50) replace "If the Size of a subtype is specified, and allows for independent ..." with "If the Size of a subtype allows for independent ..." . Insert after 13.3(56) (as part of the bulleted list) - An implementation should support a Size clause for a discrete type, fixed point type, record type, or array type, subject to the following: - An implementation need not support a Size clause for a signed integer type specifying a Size greater than that of the largest signed integer type supported by the implementation in the absence of a size clause (that is, when the size is chosen by default). A corresponding limitation may be imposed for modular integer types, fixed point types, enumeration types, record types, and array types. AARM NOTE: Note that the "corresponding limitation" for a record or array type implies that an implementation may impose some reasonable maximum size for records and arrays (e.g. 2**32 bits), which is an upper bound ("capacity" limit) on the size, whether chosen by default or by being specified by the user. The largest size supported for records need not be the same as the largest size supported for arrays. Replace 13.9(14) with Since the Size of an array object generally does not include its bounds, the bounds should not be part of the converted data. !discussion The current recommended level of support, interpreted strictly, includes support for absurd constructs. This is the motivation for this AI. This AI depends on AI-291's definition of "confirming", as well as AI-291's coverage of various cases involving aliased objects and by-reference types. We "recommend" support for nonconfirming Size and Alignment clauses for a discrete and fixed-point type. A minor fix is to move the advice about the size of array objects from 13.9 to 13.3, where it actually will be seen by people reading about Size. !example This example shows some ramifications of the Recommended Level of Support. For each representation item, we note whether it is covered by the Recommended Level of Support [Should be supported], whether it might be covered by the Recommended Level of Support (depending on the largest values allowed for integers) [Might be supported], or is not covered by the Recommended Level of Support [Need not be supported]. type Unspecified is range System.Min_Int .. System.Max_Int; -- no Size or Alignment clause type T1 is range 1 .. 10; for T1'Size use Unspecified'Size; -- Should be supported for T1'Alignment use Unspecified'Alignment; -- Should be supported type T2 is range 1 .. 10; for T2'Size use 2 * Unspecified'Size; -- Might be supported type T3 is range 1 .. 10; for T3'Size use 2 * Unspecified'Alignment; -- Might be supported type T4 is new Integer; for T4'Alignment use 3; -- Need not be supported !corrigendum 13.3(25) @drepl @xindent; the expression of such a clause shall be static, and its value nonnegative. If the Alignment of a subtype is specified, then the Alignment of an object of the subtype is at least as strict, unless the object's Alignment is also specified. The Alignment of an object created by an allocator is that of the designated subtype.> @dby @xindent; the expression of such a clause shall be static, and its value nonnegative. The Alignment of an object is at least as strict as the Alignment of its subtype, unless the object's Alignment is specified. The Alignment of an object created by an @fa is that of the designated subtype.> !corrigendum 13.3(28) @drepl If the Alignment is specified for an object that is not allocated under control of the implementation, execution is erroneous if the object is not aligned according to the Alignment. @dby Program execution is erroneous if an object that is not allocated under control of the implementation is not aligned according to its Alignment. !corrigendum 13.3(30) @drepl @xbullet @dby @xbullet !corrigendum 13.3(31) @drepl @xbullet @dby @xbullet !corrigendum 13.3(32) @drepl @xbullet @dby @xbullet !corrigendum 13.3(42) @drepl The recommended level of support for the Size attribute of objects is: @dby The size of an array object should not include its bounds. The recommended level of support for the Size attribute of objects is the same as for subtypes (see below). !corrigendum 13.3(43) @ddel @xbullet< A Size clause should be supported for an object if the specified Size is at least as large as its subtype's Size, and corresponds to a size in storage elements that is a multiple of the object's Alignment (if the Alignment is nonzero).> !corrigendum 13.3(50) @drepl If the Size of a subtype is specified, and allows for efficient independent addressability (see 9.10) on the target architecture, then the Size of the following objects of the subtype should equal the Size of the subtype: @dby If the Size of a subtype allows for efficient independent addressability (see 9.10) on the target architecture, then the Size of the following objects of the subtype should equal the Size of the subtype: !corrigendum 13.3(56) @dinsa @xbullet @dinss @xbullet @xbullet !corrigendum 13.9(14) @drepl The Size of an array object should not include its bounds; hence, the bounds should not be part of the converted data. @dby Since the Size of an array object generally does not include its bounds, the bounds should not be part of the converted data. !ACATS Test There are existing "Recommended Level of Support" tests which cover most of this advice. It probably would be worth checking for missing coverage. !appendix !section 13.3(42) !subject Object'Size !reference RM95-13.3(42,43) !from Keith Thompson 95-05-10 !reference as: 95-5141.a Keith Thompson 95-5-10>> !discussion The recommended level of support for the Size attribute of an object implies that the following should be accepted: X: Integer; for X'Size use 1024; Is this the intent? If so, what should be done with the extra 922 bits (if Integer'Size = 32)? If X is passed as a parameter, must all 1024 bits be copied? (Presumably not, since the callee won't be expecting them.) A possible implementation is to treat only the first 32 of the 1024 bits as an Integer object, but then the other 922 bits are effectively not part of X. It seems more sensible to reject the size clause. I see little point in allowing a 'Size for an integer object to exceed the largest size of any integer type. Suggestion: for elementary types, the recommended level of support for the Size attribute should not call for sizes larger than the largest supported size for the class (discrete, float, access, etc.). **************************************************************** !section 13.3(42) !subject Size clauses for objects specifying large sizes !reference AI95-00051 !reference 13.3(42) !from Tucker Taft 95-10-30 !reference 95-5373.g Tucker Taft 95-10-30>> !discussion This AI talks about base subtypes, which don't exist for non-scalar types. In general, when you start talking about representation items, I find it is better to consider "integral" scalar types (integer, enum, and fixed), floating point types, access types, array types, and non-array composite types separately, rather than grouping everything together. If the right answer happens to be the same for each of these distinct groups, great. But don't be afraid to admit that different rules are needed for different kinds of types. As far as this AI, I think its recommendations are reasonable for "integral" scalar subtypes. For floating point and access subtypes, I don't think the implementation needs to accept anything but a "confirming" Size clause. For composite types, the Size clause on an object presumably just specifies some amount of padding on allocation, but has no effect on any other operations on the type, and hence is pretty trivial to support. Even "block assignment" of a "padded" composite object should not be affected, since assignment is really defined more in terms of component-wise assignment, and clearly there are no additional components. In particular, any gaps in the middle of a composite type don't need to be assigned, compared, or whatever, as part of a predefined operation on a composite object. So clearly padding at the end can be ignored for these operations as well. Hence, for composite types, I would suggest that any size that is a multiple of the storage unit, and is at least as big as the subtype 'Size, be accepted. It need have no effect other than to add padding at the end of the object. **************************************************************** !section 13.3(55) !subject Missing AI: When is the Size "specified" for a subtype? !reference RM95-13.3(55) !from Tucker Taft 95-10-30 !reference 95-5373.b Tucker Taft 95-10-30>> !discussion In 13.3(55), it defines a recommended meaning for Size, when it is not "specified" for a subtype. Throughout the language, two statically matching subtypes are considered completely equivalent. Hence, if a subtype statically matches the first subtype, and the size has been specified for the first subtype, then Size should be considered "specified" for the statically matching subtype. Or to put it another way, all statically matching subtypes should have the same value for 'Size. Any other interpretation of the phrase "if not specified" in 13.3(55) would create numerous problems. In particular, if one said that specifying a size for a first subtype "broke" static matching between the first subtype and other subtypes whose constraints statically match those of the first subtype, then putting on a "confirming" rep-clause could seriously alter the correctness of a program. Alternatively, if one said that only specifying a non-confirming rep-clause would "break" static matching, then compilers that differed over what was the default Size for a subtype could have dramatically different effects with respect to static subtype matching. (Remember that compilers that do not support the S.P. Annex need not obey 13.3(55).) Note that the "Recommended Level of Support" part of the summary becomes mandatory in the Systems Programming Annex, whereas the "Implementation Advice" does not. ASIDE: Note that there has been some interest in reviving the ability to specify sizes on "secondary" subtypes. The above suggested interpretation for "specified" should not be interpreted to affect this possibility either way. If a secondary subtype were to have a Size specified, then we would have to determine how such rep-clauses would interact with static matching. However, even if such rep-clauses were allowed, we believe that when they are *not* present, any subtypes which statically match the first subtype must have the same 'Size as the first subtype. END OF ASIDE. **************************************************************** !section 13.1(14) !subject Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !from Gary Dismukes 95-11-01 !reference 95-5379.a Gary Dismukes 95-11-2>> !discussion (This comment is being submitted on behalf of Robert Dewar.) Some comments on the SIZE problem and Issue ------------------------------------------- First of all, let's look at the current rules in the absence of size clauses: type Count is Integer range 1 .. 65; subtype Number_Of_Companies is Count range 1 .. 64; subtype Number_Of_Hotels is Count range 1 .. 65; type Count_Ptr is access all Count; type Companies_Ptr is access all Number_Of_Companies; type Hotels_Ptr is access all Number_Of_Hotels We have, according to paragraph 13.3(55): Count'Size = 7 Number_Of_Companies'Size = 6; Number_Of_Hotels'Size = 7; This is required. In Ada 83, many (most? nearly all?) compilers would have given Integer'Size to all three types, but Ada 95 requires that the sizes be minimal. This causes a number of subtle incompatibilities but we already (unwisely in my view, I will not hide this) decided to introduce this behavior. Moreover, type Count_Ptr is convertible to type Hotels_Ptr, but not to type Companies_Ptr. This again seems very strange, but that's the way the language is, and for this to work, we need to worry about: X : aliased Count_Ptr; Y : aliased Hotels_Ptr; where X'Access and Y'Access must be interconvertible. This means that X and Y must be stored using the same representation. In the absence of Size clauses, Ada 95 achieves this requirement by the default size rules in 13.3(55), and in this case 13.1(14) clearly is correct: "If two subtypes statically match, then their subtype-specific aspects (Size and Alignment) are the same" Together with the assumption (perhaps it is a requirement, but I can't see the reference right now and anyway it is not really relevant to this argument which it is) that the two objects will be stored the same way if the sizes are the same. What Happens When we Add Size Clauses ------------------------------------- 13.3(55) allows a size to be specified for the first subtype. If this is a confirming size, then all is well, but consider in the above example what happens when we write: type Count is Integer range 1 .. 65; subtype Number_Of_Companies is Count range 1 .. 64; subtype Number_Of_Hotels is Count range 1 .. 65; for Count'Size use 16; type Count_Ptr is access all Count; type Companies_Ptr is access all Number_Of_Companies; type Hotels_Ptr is access all Number_Of_Hotels A straightforward reading of 13.3(55) would clearly suggest that the size clause affects only the size of Count, and that we would have: Count'Size = 16; Number_Of_Companies'Size = 6; Number_Of_Hotels'Size = 7; But now we are in trouble, the subtypes Count and Number_Of_Hotels still appear to staticaly match, but 13.1(14) is violated. So something is wrong. There are two basic approaches one can take to solving this problem: Tuck's Approach Hereinafter referred to as TA TA reads 13.1(14) as a requirement rather than an observation, even though it does not have a shall in it. So the effect of 13.1(14) in TA is that if the other rules on static subtypes are met, then the Size and Alignment SHALL be the same. TA reads into the last sentence of 13.3(55) the fact that if you specify the size for the first named subtype, then you also are implicitly specifying the size for any statically matching subtypes (because of the requirement of 13.1(14), and that therefore the default ("if not specified") size rule does not apply. Using TA, in the above example, with the size clause, the value of Number_Of_Hotels'Size would become 16. Dewar's approach Hereinafter referred to as DA Does not treat 13.1(14) as a requirement, but rather a statement about what is true about static subtypes. In other words, if this is not true, then the subtypes do not statically match. Roughly the view is that if you state a rule such as: "Identical twins always have the same sex" and then you look at a boy and a girl, you know they are not identical twins, no matter what rules have been stated elsewhere. DA then takes the more natural reading of 13.3(55), and assumes that the size clause applies only to the first named subtype. Tuck mentions a third approach as a strawman in his comments on this subject, but (a) I don't understand it, and (b) as far as I can tell no one is arguing for it, so I won't bother with it. Now, some comments on the two approaches: 1. Both TA and DA allow confirming rep clauses to be added for the first subtype without modifying the behavior in any way. This is important, since apparently one of Tuck's main concerns is that confirming rep clauses not have any semantic effect. (I agree that confirming rep clauses should not have any effect, but in the case of Size, this principle is so badly compromised by the inability to give any rep clauses for subtypes at all, not even confirming rep clauses, that I can't get too excited about it, but in any case, DA allows confirming rep clauses that are neutral). 2. DA means that a non-confirming size clause can affect the convertability of pointers to different subtypes. This is presumably the overwhelming concern that leads to the preference of TA. However, I must say I am underwhelmed. First of all it is a bit bizarre that the legality of these conversions depends on the equality of values that are likely to be logically related in the program (for example in the above case, if you open a new company, you suddenly get added convertability from Count_Ptr to Companies_Ptr. That's unlikely of course to cause trouble in an existing program. If you remove a hotel, then you lose convertability. This could cause trouble, but in fact is very unlikely to do so, since the conversion from Count_Ptr to Hotels_Ptr is a bizarre one (and the conversion from Hotels_Ptr to Companies_Ptr, which is now allowed is even more bizarre). Furthermore, it seems quite natural to me that if you have two types: access X access Y that they are convertible only if the representations of X and Y are the same, and if you specifically modify the representation of X and do not modify the representation of Y, then they are no longer convertible. 3. TA means that a size clause affects the size of a previously declared subtype. This seems peculiar, and I can see it causing implementation problems. For non subtype specific attributes, there is no problem, since the representation is an attribute of the base type. DA does not at all like this retrospective effect. TA does not care much, since one pass semantics aren't considered too important, and anyway that's not quite fair, because you can think of sizes as only being established at the freeze point. 4. DA thinks that it is strange that if you specify the size of the first subtype, then it has an effect on a certain subset of subtypes, those which happen to statically match, and that saying that this is the case will complicate 13.3(55). For example, TA could be made clear by adding to this paragraph the following sentence: Such a specification affects the first named subtype, and any other subtypes of the same base type that statically match. Probably a note needs to be added: This affects both subtypes declared previously to the size clause, and any subsequent subtypes declared in either the same declarative region or elsewhere. because this is quite surprising (that the size of a subtype in another unit would be affected). The actual processing for subtypes in other units is now something like: If the subtype statically matches the first named subtype, then take the size of the first named subtype, otherwise take the default size from paragraph 13.3(55). By contrast, the effect of DA on the RM would be as follows: Remove 13.1(14) completely Add to the first sentence of 4.9.1(2), giving "A subtype statically matches another subtype of the same type if they have statically matching constraints, and if they have the same subtype specific aspects (Size and Alignment)". What we have here is two conflicting requirements in the RM. I think in such cases it is a waste of time to make arguments saying essentially that you can conclude that one is wrong by intent because it must be wrong, given the other requirement. This argument can be applied either way round and is unhelpful. I mention this, because TA uses this argument, arguing that 13.1(14) implies the desired constructive reading of 13.3(55). DA could make the same argument the other way round, but it is besides the point. What we need is a resolution that is least suprising, and simplest. Ulterior Motive Disclosed ------------------------- DA will now disclose an ulterior motive. Up until the last moment, Ada 95 allowed 'Size to be specified for subtypes. GNAT implemented this capability, and it was definitely useful. It was removed from the RM late on without WG9 discussion. Why? Because of these static matching rules. The attempt was to ensure that 13.1(14) is in fact true, and that specifying sizes for subtypes could not disrupt it. But that did not get done cleanly as we see. To me, the removal of a really useful feature, just so that the very marginal feature of being able to convert pointers to logically unrelated subtypes would work, is a very poor trade off. It is particularly important to be able to specify the size of subtypes given the (in my view injudicious) introduction of the rules in Ada 95 that require separate sizes for subtypes (Ada 83 allowed it but did not require it, few compilers took advantage of it, the only one I know of that did systematically what is now required is the old Intermetrics compiler). This means that if we have type X is range 0 .. 65535; for X'Size use 16; subtype Y is X range 1 .. N; -- N is a constant that happens today to be 255 then Y'Size is 8, and that in the record: type Rec is record XV : X; YV : Y; end record; XV is likely to occupy 16 bits, and YV to occupy 8 bits. This could well be incompatible with existing programs, and note that it would require most vendors to change their size rules, and behavior of their systems (with the sole exception of the Intermetrics compiler, which is indeed compatible with the old Intermetrics compiler). This was not *too* worrisome before the last minute change, since at least you could use a size clause (in this case "for Y'size use X'Size) that would override the default, and duplicate old behavior. Furthermore, the introduction of the restriction means that it is not possible to specify confirming size clauses. So you have a situation in which compared to Ada 83, the sizes of subtypes are required to vary in an unexpected manner, and there is no way to either confirm or change this behavior. DA has the very interesting property that if it is adopted, then there is no reason not to allow compilers to specify the size of subtypes, undoing the ill effects of the last minute, incompletely executed, change. A comment here is that MANY readers of Ada 95 assume that subtype specific attributes can be specified for subtypes. That seems a reasonable assumption and used to be a correct one, but is now surprisingly false. We have got a number of bug reports from people complaining that GNAT does not allow such specifications. We can of course explain that GNAT is right, but the principle of least suprise is definitely violated! Incidentally, a major use for specifying subtype sizes is in building packed records. You just specify the size of each subtype, and then say pragma Pack, and everything packs together nicely without needing to write a (possibly non-portable) representation clause. Furthermore the representation clause here is clearly undesirable over-specification. You don't want to specify the exact layout, but you do want to control the nature of the packing. Yes there are other ways to do this, e.g. by using derived types, but they are messy by comparison. The other major reason for specifying subtype sizes is in porting legacy code, where you want to confirm size choices made by a previous Ada 83 compiler. These requirements are strong enough that in GNAT we definitely intend to implement a feature for this purpose. If TA is adopted, GNAT will introduce an attribute Subtype_Size that can be applied to arbitrary subtypes, and which will compromise static matching in some cases. This attribute is probably technically an extension because of this compromising effect, so we will put it under the non-pedantic (GNU'ese for extensions allowed) mode. If DA is adopted, GNAT will simply allow 'Size for subtypes. A note for Bob Duff ------------------- Yes, yes, all this applies to Alignment too. I can't get excited about alignments for subtypes. At least there is no analog to 13.3(55) requiring an injudicious choice of default alignments. To be Honest ------------ There are a couple more glitches here First, with respect to dynamic subtypes. We have to worry about dynamic subtypes, since they can statically match. For dynamic subtypes, if you want to be sure that access types to them are inter-convertible, then you have to read 13.1(14) as a requirement that the sizes of statically matching dynamic subtypes match. Note that there are no rules on the default sizes for dynamic subtypes. TA of course handles this fine, by changing the "are" in 13.1(14) to "shall". DA can't get very excited about this problem, and doesn't really care if convertability of such access types is impl dependent, but, if pressed, would be inclined to require that all dynamic subtypes have the size of the base type. This avoids differences between implementations, and is perfectly acceptable in practice. Second, with respect to alignments, there is no clear statement that requires choice of alignments. TA would say that 13.1(14) places the requirement that if two subtypes statically match then the alignments should match. Again DA can't get excited, but if pressed would be inclined to say that the alignments of two subtypes of the same size should be identical. Original Intent --------------- Arguments from original intent are somewhat bogus, since the RM does not contain its history. One could say that the longer term intent was that Size should be able to be specified for subtypes, and that the language "subtype specific" still implies this -- a bogus argument for DA. One could also say that the removal of this capability obviously means that 13.1(14) was considered sacrosanct -- a bogus argument for TA. Conclusion ---------- There is a real disagreement here. Needs discussion! **************************************************************** !section 13.1(14) !subject 'SIZE problem violates Ada95 "no surprises" issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 95-5379.a Gary Dismukes 95-11-2 !from David Emery 95-11-02 !reference 95-5382.a David_Emery_at_1-BCCDFA@CCGATE.HAC.COM 95-11-3>> !discussion RBKD's example: type X is range 0 .. 65535; for X'Size use 16; subtype Y is X range 1 .. N; -- N is a constant that happens today to be 255 then Y'Size is 8, and that in the record: type Rec is record XV : X; YV : Y; end record; led me to observe that the following: package Rec_IO is new Direct_IO (Rec); would cause real problems when a database is maintained using Rec_IO bt different invocations of the program, with different values of N. The expectation (guaranteed by Ada83 because Y is a subtype and not a type, I beleive), is that objects of type Rec are the same size. This is a particular issue for Direct_IO, where the data layouts can depend very strongly on the size of the object. Ada95 clearly violates its "no surprises" guideline by having the size of a subtype change in this way. I find it basically counterintuitive (and unacceptable) to have to do a lot of representation clauses for an 'invariant' that should be machine independent, i.e. that objects of the type Rec are the same size, regardless of the value of the constraint N. In particular, if this causes Direct_IO to fail because of varying values of N, then we lose type 'equivalence', exchanging it for subtype equivalence. (What I mean here is that 2 values of the same type can be used, subject only to subtype constraint checks.) dave **************************************************************** !section 13.1(14) !subject Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 95-5379.a Gary Dismukes 95-11-2 !from Bob Duff !reference 96-5462.a Robert A Duff 96-4-11>> !discussion Robert, you sent in this comment just before the last ARG meeting. It was discussed at the meeting, with no resolution of the issues. There are some confusing things. Could you please send a clarification? > (This comment is being submitted on behalf of Robert Dewar.) > > Some comments on the SIZE problem and Issue > ------------------------------------------- > > First of all, let's look at the current rules in the absence of size > clauses: > > type Count is Integer range 1 .. 65; That's not legal syntax. You must mean one of these: type Count is range 1 .. 65; subtype Count is Integer range 1 .. 65; type Count is new Integer range 1 .. 65; The ARG discussion got confused about this. > subtype Number_Of_Companies is Count range 1 .. 64; > subtype Number_Of_Hotels is Count range 1 .. 65; There was some feeling at the ARG meeting that this is not a compelling example, because you're using the same type (two different subtypes) for two totally unrelated things. If this causes surprises, tough luck -- you should have used two different types in the first place. Now, I realize there are (at least) two schools of thought -- some people like to use lots of different types, whereas some people like to make everything a subtype of the same type. Anyway, you weren't there to defend yourself, so you might want to do so now. > type Count_Ptr is access all Count; > type Companies_Ptr is access all Number_Of_Companies; > type Hotels_Ptr is access all Number_Of_Hotels > > We have, according to paragraph 13.3(55): > > Count'Size = 7 > Number_Of_Companies'Size = 6; > Number_Of_Hotels'Size = 7; > > This is required. In Ada 83, many (most? nearly all?) compilers would > have given Integer'Size to all three types, but Ada 95 requires that > the sizes be minimal. This causes a number of subtle incompatibilities > but we already (unwisely in my view, I will not hide this) decided to > introduce this behavior. > > Moreover, type Count_Ptr is convertible to type Hotels_Ptr, but > not to type Companies_Ptr. > > This again seems very strange, but that's the way the language is, > and for this to work, we need to worry about: > > X : aliased Count_Ptr; > Y : aliased Hotels_Ptr; > > where X'Access and Y'Access must be interconvertible. This means that > X and Y must be stored using the same representation. > > In the absence of Size clauses, Ada 95 achieves this requirement by > the default size rules in 13.3(55), and in this case 13.1(14) clearly > is correct: > > "If two subtypes statically match, then their subtype-specific > aspects (Size and Alignment) are the same" > > Together with the assumption (perhaps it is a requirement, but I > can't see the reference right now and anyway it is not really > relevant to this argument which it is) that the two objects will > be stored the same way if the sizes are the same. I don't think there's an explicit requirement, but it seems that this is the only viable run-time model -- aliased objects have to be stored the same way if their type is the same, and their subtypes statically match. The compiler can play all kinds of games with non-aliased objects. > What Happens When we Add Size Clauses > ------------------------------------- > > 13.3(55) allows a size to be specified for the first subtype. If this > is a confirming size, then all is well, but consider in the above > example what happens when we write: > > type Count is Integer range 1 .. 65; Same bug as above. > subtype Number_Of_Companies is Count range 1 .. 64; > subtype Number_Of_Hotels is Count range 1 .. 65; > > for Count'Size use 16; > > type Count_Ptr is access all Count; > type Companies_Ptr is access all Number_Of_Companies; > type Hotels_Ptr is access all Number_Of_Hotels > > A straightforward reading of 13.3(55) would clearly suggest that the > size clause affects only the size of Count, and that we would have: > > Count'Size = 16; > Number_Of_Companies'Size = 6; > Number_Of_Hotels'Size = 7; > > But now we are in trouble, the subtypes Count and Number_Of_Hotels > still appear to staticaly match, but 13.1(14) is violated. So > something is wrong. > > There are two basic approaches one can take to solving this problem: > > Tuck's Approach > > Hereinafter referred to as TA > > TA reads 13.1(14) as a requirement rather than an observation, > even though it does not have a shall in it. So the effect of 13.1(14) > in TA is that if the other rules on static subtypes are met, then > the Size and Alignment SHALL be the same. There are lots of requirements that have no "shall". Legality Rules use "shall", but Static Semantics just state "facts" about the language -- in this case, the sizes "are" the same. The facts are of course requirements on the implementation -- if they're not true in a given implementation, then that's not a correct implementation of Ada. My point is: Don't base your argument on whether "shall" was used. I do admit we have a problem, here. > TA reads into the last sentence of 13.3(55) the fact that if you > specify the size for the first named subtype, then you also are > implicitly specifying the size for any statically matching > subtypes (because of the requirement of 13.1(14), and that > therefore the default ("if not specified") size rule does not > apply. > > Using TA, in the above example, with the size clause, the > value of Number_Of_Hotels'Size would become 16. > > Dewar's approach > > Hereinafter referred to as DA > > Does not treat 13.1(14) as a requirement, but rather a statement > about what is true about static subtypes. In other words, if this > is not true, then the subtypes do not statically match. Roughly > the view is that if you state a rule such as: > > "Identical twins always have the same sex" > > and then you look at a boy and a girl, you know they are not identical > twins, no matter what rules have been stated elsewhere. No, that's not right. If the RM said that, we would have written "An identical twins is a pair of siblings that have the same sex [...and a bunch of other stuff]". Or, "NOTE: Note that identical twins always have the same sex." (presuming it follows from the definition). I wrote 13.1(14), and it's written in the same style as all the other Static Semantics, and it means that we are *requiring* this to be true in all implementations. > DA then takes the more natural reading of 13.3(55), and assumes that > the size clause applies only to the first named subtype. > > Tuck mentions a third approach as a strawman in his comments on this > subject, but (a) I don't understand it, and (b) as far as I can tell > no one is arguing for it, so I won't bother with it. > > Now, some comments on the two approaches: > > 1. Both TA and DA allow confirming rep clauses to be added for the first > subtype without modifying the behavior in any way. This is important, > since apparently one of Tuck's main concerns is that confirming rep > clauses not have any semantic effect. > > (I agree that confirming rep clauses should not have any effect, but in > the case of Size, this principle is so badly compromised by the inability > to give any rep clauses for subtypes at all, not even confirming rep > clauses, that I can't get too excited about it, but in any case, DA > allows confirming rep clauses that are neutral). > > 2. DA means that a non-confirming size clause can affect the convertability > of pointers to different subtypes. This is presumably the overwhelming > concern that leads to the preference of TA. > > However, I must say I am underwhelmed. First of all it is a bit bizarre > that the legality of these conversions depends on the equality of values > that are likely to be logically related in the program (for example in > the above case, if you open a new company, you suddenly get added > convertability from Count_Ptr to Companies_Ptr. That's unlikely of > course to cause trouble in an existing program. If you remove a hotel, > then you lose convertability. This could cause trouble, but in fact is > very unlikely to do so, since the conversion from Count_Ptr to > Hotels_Ptr is a bizarre one (and the conversion from Hotels_Ptr to > Companies_Ptr, which is now allowed is even more bizarre). This is where the two schools of thought I mentioned make a difference -- many folks think that you would make Number_Of_Hotels and Number_Of_Companies two different types, so convertibility changes would not happen. > Furthermore, it seems quite natural to me that if you have two types: > > access X > access Y > > that they are convertible only if the representations of X and Y are > the same, and if you specifically modify the representation of X and > do not modify the representation of Y, then they are no longer > convertible. > > 3. TA means that a size clause affects the size of a previously declared > subtype. This seems peculiar, and I can see it causing implementation > problems. For non subtype specific attributes, there is no problem, > since the representation is an attribute of the base type. > > DA does not at all like this retrospective effect. > > TA does not care much, since one pass semantics aren't considered too > important, and anyway that's not quite fair, because you can think of > sizes as only being established at the freeze point. Note that this "retrospective" effect happens only within a single package spec or declarative part. Now wait a minute, is this really right? Don't freezing rules require you to give the size clause before any other *constrained* subtypes? > 4. DA thinks that it is strange that if you specify the size of the first > subtype, then it has an effect on a certain subset of subtypes, those > which happen to statically match, and that saying that this is the case > will complicate 13.3(55). For example, TA could be made clear by adding > to this paragraph the following sentence: > > Such a specification affects the first named subtype, and any other > subtypes of the same base type that statically match. > > Probably a note needs to be added: > > This affects both subtypes declared previously to the size clause, > and any subsequent subtypes declared in either the same declarative > region or elsewhere. > > because this is quite surprising (that the size of a subtype in another > unit would be affected). The actual processing for subtypes in other > units is now something like: > > If the subtype statically matches the first named subtype, then take > the size of the first named subtype, otherwise take the default size > from paragraph 13.3(55). > > By contrast, the effect of DA on the RM would be as follows: > > Remove 13.1(14) completely > > Add to the first sentence of 4.9.1(2), giving > > "A subtype statically matches another subtype of the same type if they > have statically matching constraints, and if they have the same > subtype specific aspects (Size and Alignment)". > > What we have here is two conflicting requirements in the RM. I think in such > cases it is a waste of time to make arguments saying essentially that you > can conclude that one is wrong by intent because it must be wrong, given > the other requirement. This argument can be applied either way round and > is unhelpful. I mention this, because TA uses this argument, arguing that > 13.1(14) implies the desired constructive reading of 13.3(55). DA could > make the same argument the other way round, but it is besides the point. > > What we need is a resolution that is least suprising, and simplest. > > Ulterior Motive Disclosed > ------------------------- > > DA will now disclose an ulterior motive. > > Up until the last moment, Ada 95 allowed 'Size to be specified for subtypes. > GNAT implemented this capability, and it was definitely useful. It was > removed from the RM late on without WG9 discussion. > > Why? > > Because of these static matching rules. The attempt was to ensure that > 13.1(14) is in fact true, and that specifying sizes for subtypes could > not disrupt it. > > But that did not get done cleanly as we see. This history is correct. > To me, the removal of a really useful feature, just so that the very > marginal feature of being able to convert pointers to logically unrelated > subtypes would work, is a very poor trade off. > > It is particularly important to be able to specify the size of subtypes > given the (in my view injudicious) introduction of the rules in Ada 95 > that require separate sizes for subtypes (Ada 83 allowed it but did not > require it, few compilers took advantage of it, the only one I know of > that did systematically what is now required is the old Intermetrics > compiler). > > This means that if we have > > type X is range 0 .. 65535; > for X'Size use 16; > > subtype Y is X range 1 .. N; > -- N is a constant that happens today to be 255 I presume that N is a *static* constant. Dave Emery sent a note that assumed N is non-static, which I think confuses the issue. > then Y'Size is 8, and that in the record: > > type Rec is record > XV : X; > YV : Y; > end record; > > XV is likely to occupy 16 bits, and YV to occupy 8 bits. This could well > be incompatible with existing programs, and note that it would require > most vendors to change their size rules, and behavior of their systems > (with the sole exception of the Intermetrics compiler, which is indeed > compatible with the old Intermetrics compiler). > > This was not *too* worrisome before the last minute change, since at least > you could use a size clause (in this case "for Y'size use X'Size) that > would override the default, and duplicate old behavior. > > Furthermore, the introduction of the restriction means that it is not > possible to specify confirming size clauses. So you have a situation in > which compared to Ada 83, the sizes of subtypes are required to vary in > an unexpected manner, and there is no way to either confirm or change > this behavior. However, note that Ada 95 allows Size clauses on *objects* and Component_Size clauses for array components, which somewhat alleviates the problem. Also, if you really want a Size clause on a subtype, you can make it a derived type instead. Not ideal, since extra type conversion will be needed, but it does work OK. > DA has the very interesting property that if it is adopted, then there is > no reason not to allow compilers to specify the size of subtypes, undoing > the ill effects of the last minute, incompletely executed, change. > > A comment here is that MANY readers of Ada 95 assume that subtype specific > attributes can be specified for subtypes. That seems a reasonable assumption > and used to be a correct one, but is now surprisingly false. We have got a > number of bug reports from people complaining that GNAT does not allow such > specifications. We can of course explain that GNAT is right, but the > principle of least suprise is definitely violated! > > Incidentally, a major use for specifying subtype sizes is in building > packed records. You just specify the size of each subtype, and then say > pragma Pack, and everything packs together nicely without needing to > write a (possibly non-portable) representation clause. Furthermore > the representation clause here is clearly undesirable over-specification. > You don't want to specify the exact layout, but you do want to control > the nature of the packing. Yes there are other ways to do this, e.g. > by using derived types, but they are messy by comparison. > > The other major reason for specifying subtype sizes is in porting legacy > code, where you want to confirm size choices made by a previous Ada 83 > compiler. > > These requirements are strong enough that in GNAT we definitely intend to > implement a feature for this purpose. > > If TA is adopted, GNAT will introduce an attribute Subtype_Size that can > be applied to arbitrary subtypes, and which will compromise static matching > in some cases. This attribute is probably technically an extension because > of this compromising effect, so we will put it under the non-pedantic (GNU'ese > for extensions allowed) mode. No, I don't think you need to play the "-pedantic" game. Implementation-defined attributes can do whatever you like. The only barrier is the wisdom and good taste of the compiler writer. ;-) > If DA is adopted, GNAT will simply allow 'Size for subtypes. No, *that's* not allowed. Unless, of course, the ARG explicitly rules that it *is* allowed. > A note for Bob Duff > ------------------- > > Yes, yes, all this applies to Alignment too. I can't get excited about > alignments for subtypes. At least there is no analog to 13.3(55) requiring > an injudicious choice of default alignments. And of course for Alignment there's no issue of compatibility with existing Ada 83 compilers. We can decide what the right thing for Size is, and then simply rule that Alignment works the same way, which I think is necessary. > To be Honest > ------------ > > There are a couple more glitches here > > First, with respect to dynamic subtypes. We have to worry about dynamic > subtypes, since they can statically match. For dynamic subtypes, if you > want to be sure that access types to them are inter-convertible, then > you have to read 13.1(14) as a requirement that the sizes of statically > matching dynamic subtypes match. Note that there are no rules on the > default sizes for dynamic subtypes. TA of course handles this fine, by > changing the "are" in 13.1(14) to "shall". No, that's not the correct wording for TA. As I said before, the Static Semantics *never* say "shall", they always say "are", and there's no reason for this case to be any different. But that's a wording issue. > DA can't get very excited about this problem, and doesn't really care > if convertability of such access types is impl dependent, but, if pressed, > would be inclined to require that all dynamic subtypes have the size > of the base type. This avoids differences between implementations, and > is perfectly acceptable in practice. Well, dynamic subtypes are probably fairly rare. Nonetheless, I think it is desirable to *allow* a compiler to deduce a smaller size than the base SUBtype, for dynamic subtypes. > Second, with respect to alignments, there is no clear statement that requires > choice of alignments. TA would say that 13.1(14) places the requirement that > if two subtypes statically match then the alignments should match. Again > DA can't get excited, but if pressed would be inclined to say that the > alignments of two subtypes of the same size should be identical. I'm not sure what you mean by not getting excited, but clearly, if your compiler doesn't obey this, then type conversions of access types simply won't work. > Original Intent > --------------- > > Arguments from original intent are somewhat bogus, since the RM does not > contain its history. > > One could say that the longer term intent was that Size should be able > to be specified for subtypes, and that the language "subtype specific" > still implies this -- a bogus argument for DA. > > One could also say that the removal of this capability obviously means > that 13.1(14) was considered sacrosanct -- a bogus argument for TA. I agree that arguments about "intent" are often bogus. The language designer doesn't always have an accurate memory of intent. Of course, in many cases, there is evidence in the AARM about the intent. In *this* case, the intent was self contradictory -- the language designers intended to support Size clauses on non-first subtypes, and also intended that static matching for static subtypes work as it does, and also intended that low-level mucking with rep clauses should not affect the high-level pristine semantics of static matching. Clearly, one cannot achieve all of that "intent", so something's got to give. At the last minute, we chose to ditch size clauses on non-first subtypes. > Conclusion > ---------- > > There is a real disagreement here. Needs discussion! Indeed. - Bob **************************************************************** !section 13.1(14) !subject Size attribute !reference RM95-13.1(14) !reference RM95-13.3(55) !from Bob Duff !reference 96-5463.a Robert A Duff 96-4-11>> !discussion Here are some general comments on the Size issue. First of all, in all these discussions, PLEASE make sure it's very clear whether you're talking about a query of the Size attribute, or a Size clause. Numerous times, discussions on this issue have become mired in confusion, because I said something about the query, when Robert thought I was talking about the clause, or vice versa. Also, please be sure to distinguish the Size of a subtype from the Size of an object, and the corresponding clauses from each other. The semantics are rather different -- the Size of a subtypes is rather flaky, whereas the Size of an object is pretty well defined. One principle that most people seem to like is that a "confirming rep clause" should not change things. That is, if you have a program where T'Size = 32 by default, and you add a rep clause, "for T'Size use 32;", then the semantics of the new program should be identical. For most rep clauses, in most situations, we have achieved this principle. However, there are some cases where the principle is compromised. In particular 13.3(50-52) says: 50 If the Size of a subtype is specified, and allows for efficient independent addressability (see 9.10) on the target architecture, then the Size of the following objects of the subtype should equal the Size of the subtype: 51 Aliased objects (including components). 52 Unaliased components, unless the Size of the component is determined by a component_clause or Component_Size clause. The phrase "If the Size of a subtype is specified" clearly violates the principle that confirming rep clauses shouldn't change things. The principle is still a good principle -- we probably ought to at least try to minimize the cases where it is compromised. Note well para 51. The Size of a stand-alone object matters to a programmer if the programmer takes the Address of that object, and passes that address off to some other language. The address ought to be pointing at a sequence of bytes of the expected size. The reason para 51 says "aliased" is that 'Address is only well-defined in the case of aliased objects, by 13.3(16): 16 X'Address should produce a useful result if X is an object that is aliased or of a by-reference type, or is an entity whose Address has been specified. Thus, if you say: X: Integer; -- not aliased ...X'Address ... the compiler might put X in a register, and return a null address for X'Address (or even raise an exception). In thinking about Size, note that there can be subtypes whose range is wider than the first subtype: type T is range 0..15; -- T'Size = 4 subtype S is T'Base range -15..15; -- S'Size = 5 The above subtype S is guaranteed to be supported on all implementations. My point is that a rule requiring the size of all subtypes to be the same as the first subtype just won't work. For example, even if you don't agree that T'Size should be 4 by default, one could legally add "for T'Size use 4;" to the above -- certainly that was legal in Ada 83, and it had better still be legal. But that Size clause clearly cannot mean T'Base'Size = 4, nor that S'Size = 4. Robert and I seem to disagree on the desired semantics of packed records. This makes a difference to the Size clause issue, so I think the ARG should explicitly consider what is desirable for packed records. (I should note that one Ada 83 compiler doesn't support packed records at all. Robert is correct that it would be nice to be compatible with that compiler, but if it conflicts with the way packed records have to work, it's not suprising to me that this compatibility is impossible to achieve). Here's my view of packed records. Assume a 32-bit machine with 8-bit bytes. type My_Boolean is new Boolean; -- My_Boolean'Size = 1. type My_Other_Boolean is (No, Yes); -- My_Other_Boolean'Size = 1. subtype One_Bit_Int is Integer range 0..1; -- One_Bit_Int'Size = 1. type Inner_Record is record X: My_Boolean; Y: My_Other_Boolean; Z: One_Bit_Int; end record; pragma Pack(Inner_Record); type Twenty_Nine_Bits is range 0..2**29-1; -- Twenty_Nine_Bits'Size = 29. type Outer_Record is record Inner: Inner_Record; W: Twenty_Nine_Bits; end record; pragma Pack(Outer_Record); To me, it seems highly desirable that the Size values shown above should be chosen by default, and that Outer_Record should fit in a single 32-bit word, and that Outer_Record'Size should be 32. My understanding is that 13.2(8) requires this: 8 For a packed record type, the components should be packed as tightly as possible subject to the Sizes of the component subtypes, and subject to any record_representation_clause that applies to the type; the implementation may, but need not, reorder components or cross aligned word boundaries to improve the packing. A component whose Size is greater than the word size may be allocated an integral number of words. I believe Robert's view (please correct me if I'm wrong) is that the programmer should have to write some Size clauses, in order to achieve the above tight packing. For example, the user would have to write: for One_Bit_Int'Size use 1; for Twenty_Nine_Bits'Size use 29; I'm not sure if Robert's view also requires: for My_Boolean'Size use 1; for My_Other_Boolean'Size use 1; in addition. If so, that's pretty strange, because Boolean'Size = 1 (even though Boolean has no representation clause in Standard). If not, that's even stranger, since I would expect integers and enumerations to behave the same way in this regard. In any case, I don't see why the user should have to write Size clauses to get reasonable packing. That defeats the purpose of pragma Pack. I don't care to specify all kinds of details -- I just want the compiler to choose a reasonably-packed representation. (If I *did* care about details, I would use a record_rep_clause instead.) Certainly, in the Pascal compilers I've used, packed records are packed according to the sub-ranges declared, without any need for Size clauses (which is lucky, since Pascal doesn't have Size clauses). Now, one could imagine that the record is packed like I want it, but the 'Size values are not as I indicated above. I don't see how this can work. The very meaning of 'Size is that there are no objects smaller than that (barring other overriding rep clauses). Also, we don't want to say that packing is different depending on whether the Size was specified or chosen by default. I think Robert's view is that requiring the Size clauses is desirable, because then the desired packing won't silently change if you change the bounds of Twenty_Nine_Bits, for example -- you'll either get the same package, or you'll get an error message on the Size clause. Obviously, I don't agree that this is desirable. - Bob **************************************************************** !section 13.1(14) !subject Size attribute !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5463.a Robert A Duff 96-4-11 !from Keith Thompson 96-04-12 !reference 96-5466.a Keith Thompson 96-4-12>> !discussion Bob Duff writes: > One principle that most people seem to like is that a "confirming rep > clause" should not change things. That is, if you have a program where > T'Size = 32 by default, and you add a rep clause, "for T'Size use 32;", > then the semantics of the new program should be identical. > > For most rep clauses, in most situations, we have achieved this > principle. However, there are some cases where the principle is > compromised. In particular 13.3(50-52) says: > > 50 If the Size of a subtype is specified, and allows for efficient > independent addressability (see 9.10) on the target architecture, then the > Size of the following objects of the subtype should equal the Size of the > subtype: > > 51 Aliased objects (including components). > > 52 Unaliased components, unless the Size of the component is > determined by a component_clause or Component_Size clause. > > The phrase "If the Size of a subtype is specified" clearly violates the > principle that confirming rep clauses shouldn't change things. The > principle is still a good principle -- we probably ought to at least try > to minimize the cases where it is compromised. In most cases, this doesn't necessarily violate the principle; at worst, it's an incomplete statement. Surely if the Size of a subtype, *whether explicitly specified or not*, allows for efficient independent addressability, aliased objects and unaliased components should have the same Size as the subtype. For example, if T'Size is 32 by default, aliased objects and unaliased components should have a 'Size of 32 whether or not there's a confirming size clause "for T'Size use 32". The only case I can think of where there might be a conflict is for a subtype that isn't a first subtype, like this: subtype Byte_Integer is Integer range -128 .. 127; -- Integer'Size = (say) 32, Byte_Integer'Size = 8 Obj: aliased Byte_Integer; -- Obj'Size = 32? In this case, it's reasonable for Obj'Size to be 32. This doesn't conflict with 13.3(50-52), but it does conflict with the interpretation that it should apply whether or not the size is specified. I hope I've at least narrowed down the conflicting cases. **************************************************************** !section 13.1(14) !subject Size attribute !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5463.a Robert A Duff 96-4-11 !from Bob Duff !reference 96-5467.a Robert A Duff 96-4-12>> !discussion Keith Thompson says: > In most cases, this [13.3(50-52)] doesn't necessarily violate the > principle; at worst, it's an incomplete statement. Technically true. However, consider the 80386. You can load 8-bit and 32-bit quantities most efficiently, 16-bit quantities slightly less efficiently. 3-bit quantities are out of the question, since they would require extra locking code, but 16-bit quantities are efficient enough to be called "efficient" -- at least that was *my* intent. So, if you write: type T1 is range 0..2**16-1; -- T1'Size = 16 X1: T1; -- X1'Size = 32 type T2 is range 0..2**16-1; -- T2'Size = 16 for T2'Size use 16; X2: T2; -- X2'Size = 16 This is certainly an allowed implementation, and it seems to me a desirable implementation. It violates the principle, since T1'Size and T2'Size are both 16, but the meaning of their Size is different, since one is "16 by default" and the other is "specified as 16". Here's another "principle", which I'm not sure anybody else agrees with, but it makes sense to me: If you don't give any rep clauses, then the compiler ought to do what's most efficient. If you care about representation more than (the compiler's notion of) efficiency, then give a rep clause. "Predictable representations" is not a goal, if there are no rep clauses. - Bob **************************************************************** !section 13.1(14) !subject Size attribute !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5463.a Robert A Duff 96-4-11 !from Bob Duff !reference 96-5468.a Robert A Duff 96-4-12>> !discussion Oops. In the message I just sent, I forgot to put "aliased". Sorry. Here's a correction: Keith Thompson says: > In most cases, this [13.3(50-52)] doesn't necessarily violate the > principle; at worst, it's an incomplete statement. Technically true. However, consider the 80386. You can load 8-bit and 32-bit quantities most efficiently, 16-bit quantities slightly less efficiently. 3-bit quantities are out of the question, since they would require extra locking code, but 16-bit quantities are efficient enough to be called "efficient" -- at least that was *my* intent. So, if you write: type T1 is range 0..2**16-1; -- T1'Size = 16 X1: aliased T1; -- X1'Size = 32 type T2 is range 0..2**16-1; -- T2'Size = 16 for T2'Size use 16; X2: aliased T2; -- X2'Size = 16 This is certainly an allowed implementation, and it seems to me a desirable implementation. It violates the principle, since T1'Size and T2'Size are both 16, but the meaning of their Size is different, since one is "16 by default" and the other is "specified as 16". Here's another "principle", which I'm not sure anybody else agrees with, but it makes sense to me: If you don't give any rep clauses, then the compiler ought to do what's most efficient. If you care about representation more than (the compiler's notion of) efficiency, then give a rep clause. "Predictable representations" is not a goal, if there are no rep clauses. - Bob **************************************************************** !section 13.1(14) !subject Re: Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !from Robert Dewar !reference 96-5486.a Robert A Duff 96-4-13>> !discussion (Apparently, Robert has less patience than I do, with the silly headers required by the mail server. And I have very little patience indeed -- I had to submit some of my recent comments 3 times. Anyway, I'll be a nice guy, and re-submit this for Robert. -- Bob Duff) Here's Robert's comment: let me narrow down my size comments to a set of requirements (which are possibly conflicting) o it should be possible to write confirming rep clauses for all representation choices made by the compiler o the sizes chosen by an Ada 95 compiler should match those of an Ada 83 compiler o (most important). If the second rule is not met, it should be possible to write rep clauses that cuase the choice to match that of the Ada 83 compiler. I never liked what was going on with size in RM 95 as you now, but did not mind too much as long as 'size could be specified for a subtype as was the case till very late on. It is taking that away that has left RM 95 completely broken with respect to the critical third requirement above. It is merely annoying that the size rules are peculiar, and reflect not the best approximation of industry pratice in Ada 83, but rathr the behavior of one particular vendors compiler that was in fact not a significant presence in the market. It is much MORE than annoying that there is no easy way to override this choice. At the Paris meeting, we considered it essential that a size clause for a first subtype be imposed on all subsequent subtypes, because otherwise confirming rep clauses would be impossible. Ada 95 broke that (why I don't know), but I could live with it because I could specify size for a subtype. At this stage I would like to see: o THat compilers be allowed to specify 'size for other than first subtypes, but not required to do so. o That we simply add to the rules for statically matching subtypes a rule that says that if the sizes are explicitly set and different then the subtypes do not statically match. I don't care too much if you keep the bizarre rule about sizes happening to be different if the subtype happens to statically match the first subtype, as long as I can override it. In fact I don't too much care WHAT is done, since it is only a matter of aesthetics, not functionality. If the ARG does not solve this question, e.g. in the manner suggested above, then GNAT will simply implement 'Subtype_Size and solve the problem itself. This is not a theoretical issue, we are currently not following the RM rules, because we know they will break customer code in a manner that is not easy to provide work arounds for (note in particular that there is no way to easily specify the size of a record component). P.S. I would in fact be inclined to keep the current RM rules about minimum size, consider the following: type x is (a,b,c); in Verdix x'size is 8 or somesuch, BUT Verdix allowed pragma Pack to go ahead and pack to 2 bits anyway. This is inconsistent with the Paris meeting AI's and with the RM, but in practice, the packing is more important than the size, so the RM rules are most practical. **************************************************************** !section 13.1(14) !subject Re: Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5486.a Robert Dewar !from Bob Duff !reference 96-5487.a Robert A Duff 96-4-13>> !discussion > let me narrow down my size comments to a set of requirements > > > (which are possibly conflicting) > > o it should be possible to write confirming rep clauses for all > representation choices made by the compiler Good principle, usually, but we know we've violated it in *some* cases. There are two issues: 1. There are a few (minor) cases in which specifying a certain aspect of representation to a certain value means something different than having an implementation choose that same value by default. This is bad, and we've minimized it to just a few cases. 2. There are many cases in which no rep clause is legal, and therefore no confirming rep clause is legal. For example, rep clauses usually require static-ish things, even though dynamic things can be queried. There are really two possible priniciples -- confirming rep clauses, when legal, should not change semantics, and confirming rep clauses should always be legal, and should not change semantics. The second of these was never even close to true for Ada 83, so perhaps it should be modified: confirming rep clauses should be legal for the cases that come up in practise when interfacing, and should not change semantics. > o the sizes chosen by an Ada 95 compiler should match those of an Ada 83 > compiler > > o (most important). If the second rule is not met, it should be possible > to write rep clauses that cuase the choice to match that of the > Ada 83 compiler. > > I never liked what was going on with size in RM 95 as you now, but did not > mind too much as long as 'size could be specified for a subtype as was > the case till very late on. > > It is taking that away that has left RM 95 completely broken with respect > to the critical third requirement above. > > It is merely annoying that the size rules are peculiar, and reflect not > the best approximation of industry pratice in Ada 83, but rathr the > behavior of one particular vendors compiler that was in fact not a > significant presence in the market. It was the goal of the Ada 95 rules to nail some things down that were vague in Ada 83, so that rep clauses could be more portable across (Ada 95) implementations. In doing that, it may well be that we've broken compatibility with existing Ada 83 implementations. We could solve this problem by going back to the Ada 83 rule, which I paraphrase here: "The Size attribute means whatever the implementation wants it to, and it's relationship with Unchecked_Conversion, 'Address, and pragma Pack is up to the implementation." I'm being somewhat facetious here -- Robert's point is that most implementations agreed with each other, and RM95 chooses an interpretation that was only chosen by some obscure Ada 83 implementation. I'd like to hear some input from compiler vendors here -- what did your Ada 83 implementations do, and what do think your Ada 95 implementations ought to do? > It is much MORE than annoying that there is no easy way to override this > choice. You can override the choice on a per-object and per-component basis, and that has a pretty well-defined meaning. I admit that it's annoying that you can't do so on a subtype basis. The MRT removed that feature for technical reasons. We could decide to add it back in, I suppose, but (1) it's a pretty big extension to be adding under the heading "interpretations", and (2) we need to find a technically correct way to do it. If we choose to do so, I think we *can* find a technically correct way, independent of how the contradiction between 13.1(14) and 13.3(55) is broken. > At the Paris meeting, we considered it essential that a size clause for > a first subtype be imposed on all subsequent subtypes, because otherwise > confirming rep clauses would be impossible. Unfortunately, Ada 95 allows: type T is range 0..1; for T'Size use 1; subtype S is range -1..1; Clearly, we cannot require that S'Size be 1. I suppose we could require it for subtypes whose range happens to be small enough. No, that won't work, either, because the range might not be known at compile time. I guess the rule would have to say something about staticness. Or, we could make it all implementation-defined, and let implementations worry about how to be compatible with (various) Ada 83 compilers. > Ada 95 broke that (why I don't know), but I could live with it because > I could specify size for a subtype. > > At this stage I would like to see: > > o THat compilers be allowed to specify 'size for other than first > subtypes, but not required to do so. Sounds like an extension to me, and not important enough for the ARG to bless. But I could live with it. If we do it, should we not make some sense of the rules? Or do you expect implementations to deal with it? I fear that compilers will simply generate incorrect code for conversions of access types (see 13.1(14.a-14.i). I would prefer to either require it or disallow it, rather than allow it. If we require it, of course, we have to make some sense of it. > o That we simply add to the rules for statically matching subtypes > a rule that says that if the sizes are explicitly set and different > then the subtypes do not statically match. This can work. But note that it violates the first principle above, that confirming rep clauses should work. > I don't care too much if you keep the bizarre rule about sizes happening > to be different if the subtype happens to statically match the first subtype, > as long as I can override it. > > In fact I don't too much care WHAT is done, since it is only a matter of > aesthetics, not functionality. If the ARG does not solve this question, > e.g. in the manner suggested above, then GNAT will simply implement > 'Subtype_Size and solve the problem itself. > > This is not a theoretical issue, we are currently not following the RM > rules, because we know they will break customer code in a manner that > is not easy to provide work arounds for (note in particular that there > is no way to easily specify the size of a record component). Unfortunate, indeed. > P.S. I would in fact be inclined to keep the current RM rules about > minimum size, consider the following: > > type x is (a,b,c); > > in Verdix x'size is 8 or somesuch, BUT Verdix allowed pragma Pack to > go ahead and pack to 2 bits anyway. This is inconsistent with the Paris > meeting AI's and with the RM, but in practice, the packing is more > important than the size, so the RM rules are most practical. OK, good, we agree on *some* things. ;-) Now, don't you think "type X is range 0..2;" is equivalent to the above, at the machine level, and should work the same way with respect to 'Size? - Bob **************************************************************** !section 13.1(14) !subject Re: Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5486.a Robert Dewar !reference 96-5487.a Robert A Duff 96-4-13 !from Bob Duff !reference 96-5494.a Robert A Duff 96-4-16>> !discussion Another correction. Robert Dewar wrote: > > At the Paris meeting, we considered it essential that a size clause for > > a first subtype be imposed on all subsequent subtypes, because otherwise > > confirming rep clauses would be impossible. And I replied: > Unfortunately, Ada 95 allows: > > type T is range 0..1; > for T'Size use 1; > subtype S is range -1..1; I meant "subtype S is T'Base range -1..1;". My point was that S has a ^^^^^^ wider range than the first subtype, so: > Clearly, we cannot require that S'Size be 1. > > I suppose we could require it for subtypes whose range happens to be > small enough. > > No, that won't work, either, because the range might not be known at > compile time. I guess the rule would have to say something about > staticness. Or, we could make it all implementation-defined, and let > implementations worry about how to be compatible with (various) Ada 83 > compilers. Sorry for the confusion. - Bob **************************************************************** !section 13.1(14) !subject Re: Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5486.a Robert Dewar !reference 96-5487.a Robert A Duff 96-4-13 !from Bob Duff !reference 96-5495.a Robert A Duff 96-4-16>> !discussion Robert and I have discussed the Size issue in private e-mail lately. (It's a hobby we have, which has taken up most of our spare time in the last few years. ;-) ) Anyway, Robert recommends the following (I'm paraphrasing what he wrote): Keep the RM rules (13.3(55)) as they are with respect to default sizes. Add a rule that the minimum size default rule does not apply to subtypes that happen to statically match the first subtype. Implementation Permission: *Allow* implementations to support Size clauses for secondary subtypes. Say that two subtypes do not statically match if their Sizes differ (this can be interesting only if one or other of them is a subtype whose Size has been set using the above permission). I can live with Robert's recommendations. I won't go *quite* so far as to say I *agree* with them -- I don't strongly disagree. ;-) How's that for wishy-washy? Robert notes that if the above Implementation Permission is not granted by the ARG, then GNAT will implement essentially the same thing, but call it by a different name ('Subtype_Size). This is important, because when porting real Ada 83 code to GNAT, such a feature has been needed. Robert says: P.S. I do not care two hoots about Alignment, I think having Alignment as a subtype specific attribute is silly, and GNAT ignores the possibility of different subtypes having different alignments as much as it can! Bob says: I agree -- it's not important. For uniformity, we could extend the Impl Permission to Alignment, too. No big deal. The only important thing, is that if Alignments of subtypes can be different, we can't have static matching of those subtypes. Alignments are largely implementation dependent anyway, so this is, I suppose, the implementer's problem. - Bob **************************************************************** !section 13.1(14) !subject Re: Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5486.a Robert Dewar !reference 96-5487.a Robert A Duff 96-4-13 !reference 96-5495.a Robert A Duff 96-4-16 !from Bob Duff 96-4-17 !reference 96-5498.a Pascal Leroy 96-4-17>> !discussion I suppose we'll have an interesting discussion at the next ARG meeting, assuming RBKD is there. I, for one, strongly disagree with the suggestions below, and agree with (the current wording of) AI95-00109 > Anyway, Robert recommends the following (I'm paraphrasing what he wrote): > > Keep the RM rules (13.3(55)) as they are with respect to default > sizes. Add a rule that the minimum size default rule does not apply > to subtypes that happen to statically match the first subtype. > > Implementation Permission: *Allow* implementations to support Size > clauses for secondary subtypes. I don't see what is gained by this. Certainly not portability, since we don't _require_ support for such clauses. The only reason for this permission is that it makes it more likely that, if vendors want to support this capability, they'll use the same mechanism. But then we have to assume that vendors are not completely stupid: if GNAT for instance supports a 'Subtype_Size attribute, and there appears to be good reasons for other vendors to support the same capability then it would be sensible for them to use the attribute 'Subtype_Size. There are many uniformity issues like that, and we don't have to incorporate them in the language: we can just assume that reasonable choices will be made. I suspect that if we put this permission in the language, we'll spend the next five years explaining how all the rest of the RM works in the presence of a size clause for subtypes. > Say that two subtypes do not statically match if their Sizes differ > (this can be interesting only if one or other of them is a subtype whose > Size has been set using the above permission). Now that violates the separation principle (grab your Ada 83 Rationale and read section 15.1 to refresh your memory) and I think it's BAD, and that the language is insufficiently broken here to abandon the separation between the logical properties and the representation. > Robert notes that if the above Implementation Permission is not granted > by the ARG, then GNAT will implement essentially the same thing, but > call it by a different name ('Subtype_Size). This is important, because > when porting real Ada 83 code to GNAT, such a feature has been needed. I would be curious to see an example of such real Ada 83 code that absolutely requires 'Size clauses for subtype. So far, no convincing example has been shown: Robert's Number_Of_Hotels and Number_Of_Companies stuff looked more like an example of how to misuse subtypes. _____________________________________________________________________ Pascal Leroy +33.1.30.12.09.68 pleroy@rational.com +33.1.30.12.09.66 FAX **************************************************************** !section 13.3(55) !subject Size program !reference RM95-13.3(55) !reference 96-5497.a Robert A Duff 96-4-17 !from Bob Duff !reference 96-5499.a Robert A Duff 96-4-19>> !discussion Robert Dewar and Pascal Leroy noted some bugs in the program. Plus I noticed a couple myself. Here's another version, with some bugs fixed. Sorry. :-( Send the results directly to me, and once we've gotten things settled, I'll post to ada-comment. - Bob with System; use System; package Size_Test_Utils is procedure Put_Line(S: String); -- Print a line with line number. procedure Heading(S: String); -- Print a heading with blank lines around it. type Size_In_Bits is range 0..Max_Int; function Img(X: Size_In_Bits) return String; -- Shorthand for Size_In_Bits'Image. procedure Check(Subtype_Name: String; Subtype_Size: Size_In_Bits; Expected_Size: Size_In_Bits); -- Prints out the subtype's Size. If Subtype_Size /= Expected_Size, -- notes that fact. procedure Check(Subtype_Name: String; Subtype_Size: Size_In_Bits); -- Same as previous, but used when there's no particular expected size. -- The following functions are the same as the corresponding -- procedures. They return an irrelevant value. The purpose -- is to be able to call those procedures in a declaration -- context. function Heading(S: String) return Boolean; function Check(Subtype_Name: String; Subtype_Size: Size_In_Bits; Expected_Size: Size_In_Bits) return Boolean; function Check(Subtype_Name: String; Subtype_Size: Size_In_Bits) return Boolean; type Longest_Signed_Integer is range Min_Int..Max_Int; end Size_Test_Utils; with Text_IO; use Text_IO; package body Size_Test_Utils is procedure Put_Line_Number is begin if Col /= 1 then raise Program_Error; end if; Put(Positive_Count'Image(Line)); Set_Col(8); end Put_Line_Number; procedure Put_Line(S: String) is begin Put_Line_Number; Text_Io.Put_Line(S); end Put_Line; procedure Heading(S: String) is begin New_Line; Set_Col(8); Text_Io.Put_Line(S); New_Line; end Heading; function Heading(S: String) return Boolean is begin Heading(S); return True; end Heading; function Img(X: Size_In_Bits) return String is begin return Size_In_Bits'Image(X); end Img; procedure Check(Subtype_Name: String; Subtype_Size: Size_In_Bits; Expected_Size: Size_In_Bits) is begin Put_Line_Number; Put(Subtype_Name & "'Size =" & Img(Subtype_Size)); if Subtype_Size = Expected_Size then Put(" (ok)"); else Put(" (expected" & Img(Expected_Size) & ")"); end if; Text_IO.Put_Line("."); end Check; procedure Check(Subtype_Name: String; Subtype_Size: Size_In_Bits) is begin Put_Line_Number; Put(Subtype_Name & "'Size =" & Img(Subtype_Size)); Text_IO.Put_Line("."); end Check; function Check(Subtype_Name: String; Subtype_Size: Size_In_Bits; Expected_Size: Size_In_Bits) return Boolean is begin Check(Subtype_Name, Subtype_Size, Expected_Size); return True; end Check; function Check(Subtype_Name: String; Subtype_Size: Size_In_Bits) return Boolean is begin Check(Subtype_Name, Subtype_Size); return True; end Check; end Size_Test_Utils; with Size_Test_Utils; use Size_Test_Utils; pragma Elaborate(Size_Test_Utils); package Size_Test_83_Pkg is -- This package prints out the 'Size of various subtypes. -- Ada-95-specific code is avoided here. -- The "..._Test" variables are ignored -- they're just there so we -- can call Check in a declaration context. package Signed_Integers is H1: Boolean := Heading("Signed Integers:"); type Positive_0 is range 0..0; Positive_0_Test: Boolean := Check("Positive_0", Positive_0'Size, 0); Positive_0_Base_Test: Boolean := Check("Positive_0'Base", Positive_0'Base'Size); type Positive_1 is range 0..1; Positive_1_Test: Boolean := Check("Positive_1", Positive_1'Size, 1); Positive_1_Base_Test: Boolean := Check("Positive_1'Base", Positive_1'Base'Size); type Positive_2 is range 0..2; Positive_2_Test: Boolean := Check("Positive_2", Positive_2'Size, 2); Positive_2_Base_Test: Boolean := Check("Positive_2'Base", Positive_2'Base'Size); type Integer_3 is range -2..2; Integer_3_Test: Boolean := Check("Integer_3", Integer_3'Size, 3); Integer_3_Base_Test: Boolean := Check("Integer_3'Base", Integer_3'Base'Size); type Billion is range -1_000_000_000 .. 1_000_000_000; subtype Sub_Pos_0 is Billion range 0..0; Sub_Pos_0_Test: Boolean := Check("Sub_Pos_0", Sub_Pos_0'Size, 0); subtype Sub_Pos_1 is Billion range 0..1; Sub_Pos_1_Test: Boolean := Check("Sub_Pos_1", Sub_Pos_1'Size, 1); subtype Sub_Pos_4 is Billion range 0..15; Sub_Pos_4_Test: Boolean := Check("Sub_Pos_4", Sub_Pos_4'Size, 4); subtype Sub_Int_7 is Billion range -63..63; Sub_Int_7_Test: Boolean := Check("Sub_Int_7", Sub_Int_7'Size, 7); end Signed_Integers; use Signed_Integers; package Enums is H1: Boolean := Heading("Enums:"); type Enum_0 is (Red); Enum_0_Test: Boolean := Check("Enum_0", Enum_0'Size, 0); Enum_0_Base_Test: Boolean := Check("Enum_0'Base", Enum_0'Base'Size, 0); type Enum_1 is (Red, Orange); Enum_1_Test: Boolean := Check("Enum_1", Enum_1'Size, 1); Enum_1_Base_Test: Boolean := Check("Enum_1'Base", Enum_1'Base'Size, 1); type Enum_2 is (Red, Orange, Yellow); Enum_2_Test: Boolean := Check("Enum_2", Enum_2'Size, 2); Enum_2_Base_Test: Boolean := Check("Enum_2'Base", Enum_2'Base'Size, 2); type Enum_4 is ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', Ten, Eleven, Twelve); Enum_4_Test: Boolean := Check("Enum_4", Enum_4'Size, 4); Enum_4_Base_Test: Boolean := Check("Enum_4'Base", Enum_4'Base'Size, 4); subtype Sub_Enum_0 is Enum_4 range '0'..'0'; Sub_Enum_0_Test: Boolean := Check("Sub_Enum_0", Sub_Enum_0'Size, 0); subtype Sub_Enum_1 is Enum_4 range '0'..'1'; Sub_Enum_1_Test: Boolean := Check("Sub_Enum_1", Sub_Enum_1'Size, 1); end Enums; use Enums; package Records is H1: Boolean := Heading("Records:"); type Unpacked_Rec is record A: Positive_2; B: Sub_Enum_1; end record; Unpacked_Rec_Test: Boolean := Check("Unpacked_Rec", Unpacked_Rec'Size); type Rec_3 is new Unpacked_Rec; pragma Pack(Rec_3); Rec_3_Test: Boolean := Check("Rec_3", Rec_3'Size, 3); Rec_3_Obj: Rec_3; A_Test: Boolean := Check("Rec_3_Obj.A", Rec_3_Obj.A'Size, 2); B_Test: Boolean := Check("Rec_3_Obj.B", Rec_3_Obj.B'Size, 1); type Rec_10 is record B1, B2, B3, B4, B5, B6, B7, B8, B9, B10: Boolean; end record; pragma Pack(Rec_10); Rec_10_Test: Boolean := Check("Rec_10", Rec_10'Size, 10); Rec_10_Obj: Rec_10; B1_Test: Boolean := Check("Rec_10_Obj.B1", Rec_10_Obj.B1'Size, 1); type Rec_32 is record X1, X2, X3: Rec_3; X4, X5: Integer_3; Two_Bits: Enum_2; Ten_Bits: Rec_10; end record; pragma Pack(Rec_32); Rec_32_Test: Boolean := Check("Rec_32", Rec_32'Size, 32); Rec_32_Obj: Rec_32; X2_Test: Boolean := Check("Rec_32_Obj.X2", Rec_32_Obj.X2'Size, 3); X5_Test: Boolean := Check("Rec_32_Obj.X5", Rec_32_Obj.X5'Size, 3); end Records; private procedure Require_Body; end Size_Test_83_Pkg; with System; use System; package body Size_Test_83_Pkg is package Predefined_Stuff is end; package body Predefined_Stuff is begin Heading("Predefined Stuff:"); Put_Line("Min_Int = " & Longest_Signed_Integer'Image(Min_Int)); Put_Line("Max_Int = " & Longest_Signed_Integer'Image(Max_Int)); Check("Longest_Signed_Integer", Longest_Signed_Integer'Size); Put_Line("Storage_Unit = " & Longest_Signed_Integer'Image(Storage_Unit)); Check("Address", Address'Size); Put_Line("Boolean'First = " & Boolean'Image(Boolean'First) & "."); Put_Line("Boolean'Last = " & Boolean'Image(Boolean'Last) & "."); Check("Boolean", Boolean'Size, 1); Put_Line("Integer'First = " & Integer'Image(Integer'First) & "."); Put_Line("Integer'Last = " & Integer'Image(Integer'Last) & "."); Check("Integer", Integer'Size); Put_Line("Natural'First = " & Natural'Image(Natural'First) & "."); Put_Line("Natural'Last = " & Natural'Image(Natural'Last) & "."); Check("Natural", Natural'Size); Put_Line("Positive'First = " & Positive'Image(Positive'First) & "."); Put_Line("Positive'Last = " & Positive'Image(Positive'Last) & "."); Check("Positive", Positive'Size); Put_Line("Character'First = " & Character'Image(Character'First) & "."); Put_Line("Character'Last = " & Character'Image(Character'Last) & "."); Check("Character", Character'Size, 8); end Predefined_Stuff; procedure Require_Body is begin null; end Require_Body; end Size_Test_83_Pkg; with Size_Test_Utils; use Size_Test_Utils; with Size_Test_83_Pkg; use Size_Test_83_Pkg; pragma Elaborate(Size_Test_Utils); pragma Elaborate(Size_Test_83_Pkg); package Size_Clause_83_Pkg is -- Same as Size_Test_83_Pkg, except this package concentrates on cases -- where the Size is specified in an attribute_definition_clause. -- Each subtype Foo_Spec corresponds to subtype Foo from -- Size_Test_83_Pkg. The declaration of Foo_Spec and Foo are -- the same, except that Foo_Spec has a Size clause. use Size_Test_83_Pkg.Signed_Integers; use Size_Test_83_Pkg.Enums; use Size_Test_83_Pkg.Records; package Signed_Integers is H1: Boolean := Heading("Signed Integers with Size Clauses:"); type Positive_0_Spec is range 0..0; for Positive_0_Spec'Size use 0; Positive_0_Spec_Test: Boolean := Check("Positive_0_Spec", Positive_0_Spec'Size, 0); Positive_0_Spec_Base_Test: Boolean := Check("Positive_0_Spec'Base", Positive_0_Spec'Base'Size); type Positive_1_Spec is range 0..1; for Positive_1_Spec'Size use 1; Positive_1_Spec_Test: Boolean := Check("Positive_1_Spec", Positive_1_Spec'Size, 1); Positive_1_Spec_Base_Test: Boolean := Check("Positive_1_Spec'Base", Positive_1_Spec'Base'Size); type Positive_2_Spec is range 0..2; for Positive_2_Spec'Size use 2; Positive_2_Spec_Test: Boolean := Check("Positive_2_Spec", Positive_2_Spec'Size, 2); Positive_2_Spec_Base_Test: Boolean := Check("Positive_2_Spec'Base", Positive_2_Spec'Base'Size); type Integer_3_Spec is range -2..2; for Integer_3_Spec'Size use 3; Integer_3_Spec_Test: Boolean := Check("Integer_3_Spec", Integer_3_Spec'Size, 3); Integer_3_Spec_Base_Test: Boolean := Check("Integer_3_Spec'Base", Integer_3_Spec'Base'Size); type Billion_Spec is range -1_000_000_000 .. 1_000_000_000; for Billion_Spec'Size use 31; Billion_Spec_Test: Boolean := Check("Billion_Spec", Billion_Spec'Size, 31); subtype Sub_Pos_0_Spec is Billion_Spec range 0..0; Sub_Pos_0_Spec_Test: Boolean := Check("Sub_Pos_0_Spec", Sub_Pos_0_Spec'Size, 0); subtype Sub_Pos_1_Spec is Billion_Spec range 0..1; Sub_Pos_1_Spec_Test: Boolean := Check("Sub_Pos_1_Spec", Sub_Pos_1_Spec'Size, 1); subtype Sub_Pos_4_Spec is Billion_Spec range 0..15; Sub_Pos_4_Spec_Test: Boolean := Check("Sub_Pos_4_Spec", Sub_Pos_4_Spec'Size, 4); subtype Sub_Int_7_Spec is Billion_Spec range -63..63; Sub_Int_7_Spec_Test: Boolean := Check("Sub_Int_7_Spec", Sub_Int_7_Spec'Size, 7); end Signed_Integers; use Signed_Integers; package Enums is H1: Boolean := Heading("Enums with Size Clauses:"); type Enum_0_Spec is (Red); for Enum_0_Spec'Size use 0; Enum_0_Spec_Test: Boolean := Check("Enum_0_Spec", Enum_0_Spec'Size, 0); Enum_0_Spec_Base_Test: Boolean := Check("Enum_0_Spec'Base", Enum_0_Spec'Base'Size, 0); type Enum_1_Spec is (Red, Orange); for Enum_1_Spec'Size use 1; Enum_1_Spec_Test: Boolean := Check("Enum_1_Spec", Enum_1_Spec'Size, 1); Enum_1_Spec_Base_Test: Boolean := Check("Enum_1_Spec'Base", Enum_1_Spec'Base'Size, 1); type Enum_2_Spec is (Red, Orange, Yellow); for Enum_2_Spec'Size use 2; Enum_2_Spec_Test: Boolean := Check("Enum_2_Spec", Enum_2_Spec'Size, 2); Enum_2_Spec_Base_Test: Boolean := Check("Enum_2_Spec'Base", Enum_2_Spec'Base'Size, 2); type Enum_4_Spec is ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', Ten, Eleven, Twelve); for Enum_4_Spec'Size use 4; Enum_4_Spec_Test: Boolean := Check("Enum_4_Spec", Enum_4_Spec'Size, 4); Enum_4_Spec_Base_Test: Boolean := Check("Enum_4_Spec'Base", Enum_4_Spec'Base'Size, 4); subtype Sub_Enum_0_Spec is Enum_4_Spec range '0'..'0'; Sub_Enum_0_Spec_Test: Boolean := Check("Sub_Enum_0_Spec", Sub_Enum_0_Spec'Size, 0); subtype Sub_Enum_1_Spec is Enum_4_Spec range '0'..'1'; Sub_Enum_1_Spec_Test: Boolean := Check("Sub_Enum_1_Spec", Sub_Enum_1_Spec'Size, 1); type Six_Bit_Enum_4_Spec is ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', Ten, Eleven, Twelve); for Six_Bit_Enum_4_Spec'Size use 6; -- Non-default Size Six_Bit_Enum_4_Spec_Test: Boolean := Check("Six_Bit_Enum_4_Spec", Six_Bit_Enum_4_Spec'Size, 6); Six_Bit_Enum_4_Spec_Base_Test: Boolean := Check("Six_Bit_Enum_4_Spec'Base", Six_Bit_Enum_4_Spec'Base'Size, 6); subtype Sub_Six_Bit_Enum_0_Spec is Six_Bit_Enum_4_Spec range '0'..'0'; Sub_Six_Bit_Enum_0_Spec_Test: Boolean := Check("Sub_Six_Bit_Enum_0_Spec", Sub_Six_Bit_Enum_0_Spec'Size, 0); subtype Sub_Six_Bit_Enum_1_Spec is Six_Bit_Enum_4_Spec range '0'..'1'; Sub_Six_Bit_Enum_1_Spec_Test: Boolean := Check("Sub_Six_Bit_Enum_1_Spec", Sub_Six_Bit_Enum_1_Spec'Size, 1); subtype Sub_Six_Bit_Enum_6_Spec is Six_Bit_Enum_4_Spec range '0'..Twelve; Sub_Six_Bit_Enum_6_Spec_Test: Boolean := Check("Sub_Six_Bit_Enum_6_Spec", Sub_Six_Bit_Enum_6_Spec'Size, 6); end Enums; use Enums; package Records is H1: Boolean := Heading("Records with Size Clauses:"); type Unpacked_Rec_Spec is record A: Positive_2_Spec; B: Sub_Enum_1_Spec; end record; Unpacked_Rec_Spec_Test: Boolean := Check("Unpacked_Rec_Spec", Unpacked_Rec'Size); type Rec_3_Spec is new Unpacked_Rec_Spec; pragma Pack(Rec_3_Spec); for Rec_3_Spec'Size use 3; Rec_3_Spec_Test: Boolean := Check("Rec_3_Spec", Rec_3_Spec'Size, 3); Rec_3_Spec_Obj: Rec_3_Spec; A_Test: Boolean := Check("Rec_3_Spec_Obj.A", Rec_3_Spec_Obj.A'Size, 2); B_Test: Boolean := Check("Rec_3_Spec_Obj.B", Rec_3_Spec_Obj.B'Size, 1); type Rec_10_Spec is record B1, B2, B3, B4, B5, B6, B7, B8, B9, B10: Boolean; end record; pragma Pack(Rec_10_Spec); for Rec_10_Spec'Size use 10; Rec_10_Spec_Test: Boolean := Check("Rec_10_Spec", Rec_10_Spec'Size, 10); Rec_10_Spec_Obj: Rec_10_Spec; B1_Test: Boolean := Check("Rec_10_Spec_Obj.B1", Rec_10_Spec_Obj.B1'Size, 1); type Rec_32_Spec is record X1, X2, X3: Rec_3_Spec; X4, X5: Integer_3_Spec; Two_Bits: Enum_2_Spec; Ten_Bits: Rec_10_Spec; end record; pragma Pack(Rec_32_Spec); for Rec_32_Spec'Size use 32; Rec_32_Spec_Test: Boolean := Check("Rec_32_Spec", Rec_32_Spec'Size, 32); Rec_32_Spec_Obj: Rec_32_Spec; X2_Test: Boolean := Check("Rec_32_Spec_Obj.X2", Rec_32_Spec_Obj.X2'Size, 3); X5_Test: Boolean := Check("Rec_32_Spec_Obj.X5", Rec_32_Spec_Obj.X5'Size, 3); end Records; end Size_Clause_83_Pkg; with Size_Test_Utils; use Size_Test_Utils; with Size_Test_83_Pkg; use Size_Test_83_Pkg; with Size_Clause_83_Pkg; use Size_Clause_83_Pkg; pragma Elaborate(Size_Test_Utils); pragma Elaborate(Size_Test_83_Pkg); pragma Elaborate(Size_Clause_83_Pkg); package Size_Test_95_Pkg is -- Same purpose as Size_Test_83_Pkg and Size_Clause_83_Pkg, -- but we put all the Ada-95-only declarations here. package Signed_Integers is H1: Boolean := Heading("Ada 95 Signed Integers:"); use Size_Test_83_Pkg.Signed_Integers; use Size_Clause_83_Pkg.Signed_Integers; subtype Positive_0_B is Positive_0'Base range -Positive_0'Last..Positive_0'Last; Positive_0_B_Test: Boolean := Check("Positive_0_B", Positive_0_B'Size, 0); subtype Positive_0_BB is Positive_0'Base range 0..Positive_0'Last; Positive_0_BB_Test: Boolean := Check("Positive_0_BB", Positive_0_BB'Size, 0); subtype Positive_0_Spec_B is Positive_0_Spec'Base range -Positive_0_Spec'Last..Positive_0_Spec'Last; Positive_0_Spec_B_Test: Boolean := Check("Positive_0_Spec_B", Positive_0_Spec_B'Size, 0); subtype Positive_0_Spec_BB is Positive_0_Spec'Base range 0..Positive_0_Spec'Last; Positive_0_Spec_BB_Test: Boolean := Check("Positive_0_Spec_BB", Positive_0_Spec_BB'Size, 0); subtype Positive_1_B is Positive_1'Base range -Positive_1'Last..Positive_1'Last; Positive_1_B_Test: Boolean := Check("Positive_1_B", Positive_1_B'Size, 2); subtype Positive_1_BB is Positive_1'Base range 0..Positive_1'Last; Positive_1_BB_Test: Boolean := Check("Positive_1_BB", Positive_1_BB'Size, 1); subtype Positive_1_Spec_B is Positive_1_Spec'Base range -Positive_1_Spec'Last..Positive_1_Spec'Last; Positive_1_Spec_B_Test: Boolean := Check("Positive_1_Spec_B", Positive_1_Spec_B'Size, 2); subtype Positive_1_Spec_BB is Positive_1_Spec'Base range 0..Positive_1_Spec'Last; Positive_1_Spec_BB_Test: Boolean := Check("Positive_1_Spec_BB", Positive_1_Spec_BB'Size, 1); subtype Positive_2_B is Positive_2'Base range -Positive_2'Last..Positive_2'Last; Positive_2_B_Test: Boolean := Check("Positive_2_B", Positive_2_B'Size, 3); subtype Positive_2_BB is Positive_2'Base range 0..Positive_2'Last; Positive_2_BB_Test: Boolean := Check("Positive_2_BB", Positive_2_BB'Size, 2); subtype Positive_2_Spec_B is Positive_2_Spec'Base range -Positive_2_Spec'Last..Positive_2_Spec'Last; Positive_2_Spec_B_Test: Boolean := Check("Positive_2_Spec_B", Positive_2_Spec_B'Size, 3); subtype Positive_2_Spec_BB is Positive_2_Spec'Base range 0..Positive_2_Spec'Last; Positive_2_Spec_BB_Test: Boolean := Check("Positive_2_Spec_BB", Positive_2_Spec_BB'Size, 2); subtype Integer_3_B is Integer_3'Base range -Integer_3'Last..Integer_3'Last; Integer_3_B_Test: Boolean := Check("Integer_3_B", Integer_3_B'Size, 3); subtype Integer_3_BB is Integer_3'Base range 0..Integer_3'Last; Integer_3_BB_Test: Boolean := Check("Integer_3_BB", Integer_3_BB'Size, 2); subtype Integer_3_Spec_B is Integer_3'Base range -Integer_3'Last..Integer_3'Last; Integer_3_Spec_B_Test: Boolean := Check("Integer_3_Spec_B", Integer_3_Spec_B'Size, 3); subtype Integer_3_Spec_BB is Integer_3'Base range 0..Integer_3'Last; Integer_3_Spec_BB_Test: Boolean := Check("Integer_3_Spec_BB", Integer_3_Spec_BB'Size, 2); end Signed_Integers; package Modular_Integers is H1: Boolean := Heading("Ada 95 Modular Integers:"); type Mod_0 is mod 2**0; Mod_0_Test: Boolean := Check("Mod_0", Mod_0'Size, 0); Mod_0_Base_Test: Boolean := Check("Mod_0'Base", Mod_0'Base'Size); type Mod_1 is mod 2**1; Mod_1_Test: Boolean := Check("Mod_1", Mod_1'Size, 1); Mod_1_Base_Test: Boolean := Check("Mod_1'Base", Mod_1'Base'Size); type Mod_2 is mod 2**2; Mod_2_Test: Boolean := Check("Mod_2", Mod_2'Size, 2); Mod_2_Base_Test: Boolean := Check("Mod_2'Base", Mod_2'Base'Size); type Mod_32 is mod 2**32; Mod_32_Test: Boolean := Check("Mod_32", Mod_32'Size, 32); Mod_32_Base_Test: Boolean := Check("Mod_32'Base", Mod_32'Base'Size); type Million is mod 1_000_000; subtype Sub_Mod_0 is Million range 0..0; Sub_Mod_0_Test: Boolean := Check("Sub_Mod_0", Sub_Mod_0'Size, 0); subtype Sub_Mod_1 is Million range 0..1; Sub_Mod_1_Test: Boolean := Check("Sub_Mod_1", Sub_Mod_1'Size, 1); subtype Sub_Mod_4 is Million range 0..15; Sub_Mod_4_Test: Boolean := Check("Sub_Mod_4", Sub_Mod_4'Size, 4); subtype Sub_Int_7 is Million range 0..100; Sub_Int_7_Test: Boolean := Check("Sub_Int_7", Sub_Int_7'Size, 7); end Modular_Integers; private procedure Require_Body; end Size_Test_95_Pkg; with System; use System; with System.Storage_Elements; use System.Storage_Elements; package body Size_Test_95_Pkg is package Predefined_Stuff is end; package body Predefined_Stuff is H1: Boolean := Heading("Ada 95 Predefined Stuff:"); type Longest_Binary_Modular is mod Max_Binary_Modulus; type Longest_Nonbinary_Modular is mod Max_Nonbinary_Modulus; Max_Binary_Modulus_Minus_One: constant := Max_Binary_Modulus - 1; Max_Nonbinary_Modulus_Minus_One: constant := Max_Nonbinary_Modulus - 1; begin Put_Line("Max_Binary_Modulus = " & Longest_Binary_Modular'Image(Max_Binary_Modulus_Minus_One) & " + 1"); Check("Longest_Binary_Modular", Longest_Binary_Modular'Size); Put_Line("Max_Nonbinary_Modulus = " & Longest_Nonbinary_Modular'Image(Max_Nonbinary_Modulus_Minus_One) & " + 1"); Check("Longest_Nonbinary_Modular", Longest_Nonbinary_Modular'Size); Put_Line("Word_Size = " & Longest_Signed_Integer'Image(Word_Size)); Check("Storage_Element", Storage_Element'Size, Storage_Unit); Put_Line("Wide_Character'First = " & Wide_Character'Image(Wide_Character'First) & "."); Put_Line("Wide_Character'Last = " & Wide_Character'Image(Wide_Character'Last) & "."); Check("Wide_Character", Wide_Character'Size, 16); end Predefined_Stuff; procedure Require_Body is begin null; end Require_Body; end Size_Test_95_Pkg; -- This is a main program that calls the Ada 83 parts of the Size test. -- See also Size_Test_95. -- All the work is done during elaboration of the Size_..._Pkg packages. with Text_IO; use Text_IO; with Size_Test_83_Pkg; with Size_Clause_83_Pkg; procedure Size_Test_83 is begin New_Line; Put_Line("[Size_Test_83 done.]"); end Size_Test_83; -- This is a main program that calls the Ada 83 *and* Ada 95 parts -- of the Size test. -- See also Size_Test_83. -- All the work is done during elaboration of the Size_..._Pkg packages. with Text_IO; use Text_IO; with Size_Test_83_Pkg; with Size_Clause_83_Pkg; with Size_Test_95_Pkg; procedure Size_Test_95 is begin New_Line; Put_Line("[Size_Test_95 done.]"); end Size_Test_95; **************************************************************** !section 13.1(14) !subject Re: Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5486.a Robert Dewar !reference 96-5487.a Bob Duff !from Randy Brukardt (RR Software) !reference 96-5500.a rbrukardt@BIX.com 96-4-20>> >It was the goal of the Ada 95 rules to nail some things down that were >vague in Ada 83, so that rep clauses could be more portable across (Ada >95) implementations. In doing that, it may well be that we've broken >compatibility with existing Ada 83 implementations. We could solve this >problem by going back to the Ada 83 rule, which I paraphrase here: "The >Size attribute means whatever the implementation wants it to, and it's >relationship with Unchecked_Conversion, 'Address, and pragma Pack is up >to the implementation." I'm being somewhat facetious here -- Robert's >point is that most implementations agreed with each other, and RM95 >chooses an interpretation that was only chosen by some obscure Ada 83 >implementation. I'd like to hear some input from compiler vendors here >-- what did your Ada 83 implementations do, and what do think your Ada >95 implementations ought to do? Well, I've been intending to get into this discussion since it started last Fall, but never could figure out where to begin. Bob has given me an appropriate starting place. Ada 83 'Size was very vague. At the time we were implementing it, there were no ACVC tests, so we could not tell what the ACVC thought the intent was. Nor had the ARG said anything on the matter. We concluded that 'Size was determined on a type (not subtype) basis, and that it reflected the size of a stand-alone object. (Note that this conclusion is exactly opposite what Ada 95 chose!) We made a one exception to this conclusion: 'Size for a subtype with a radically different representation (such as a constrained array vs. an unconstrained array) would be different -- and that was largely a result of ACVC tests expecting that String(1..10)'Size = 10 * Character'Size (a requirement we found out about only after we had finished implementing 'Size, and one later removed from the tests, I think). It was clear to use that declarations like the following all had the same 'Size, since 'Size was determined on a type basis: Type A Is Range -20000 .. 50000; -- 'Size = 32 = 'Base'Size. Subtype B Is Range 0 .. 20000; -- 'Size is still 32. Subtype C Is Range 0 .. 200; -- 'Size is still 32. However, ACVC tests required that components of subtype C (for example) could be stored in 8 bits. That led us to two more conclusions: 'Size has no effect on what rep. clauses can be used, and the components need to be handled differently than stand-alone objects. (Up to this point, they both were handled the same way). We also thought that 'Size on objects was intended to use discriminant/bounds values to return the actual size. That made some complex code, but of course it was never tested (and probably isn't useful, either). Eventually, we concluded that 'Size had effect on anything, and was just another useless Ada 83 construct. Our final implementation of 'Size looks like: 1) 'Size can be confirmed on all types. 2) 'Size can be set on discrete types only. The 'Size chosen will be used to select a base type representation, and in (unsigned) cases, a possibly different in memory representation. We considered allowing 'Size to be set for some other types. In particular, we considered using a specified size to select the representation of a pointer (similarly to what the Ada 95 compiler for the U2200 does with Convention). We also considered using specified sizes for record types. The latter was not considered important, and the former (long pointers directly supported by the compiler) never was implemented. 3) 'Size is stored in the symbol table to be returned on a user query. 4) 'Size is passed at runtime to generic units (remember we use universal sharing as our generic model), mainly so it can be returned on a query. [This was done because of ACVC tests. We tried to get the ARG and AVO not require this stupidity in this case, but were unsuccessful.] 5) The compiler stores a stand-alone object size (unfortunately called 'Length', that is a length of storage units), which is used for virutally all important purposes. 6) 'Size for objects starts with the stand-alone size, then adds the size of any indirect components, based on their actual sizes. 'Size for composite types uses the maximum size of any indirect components added to the stand-alone size (when it is not specified; note that it cannot be specified for any composite type containing indirect components, since such components are not statically constrained). 7) Pack and rep. clauses use a minimum size calculated on the fly for a subtype (for discrete types). This is essentially the same as the Ada 95 definition of 'Size. Composite types always use 'Size = stand-alone size, and smaller components are not supported. 8) Unchecked_Conversion uses the stand-alone object size for size matching. Components are always converted to the stand-alone size when read, so that is the most appropriate. This probably is why I always was discussing the difference between register and memory-based models for Unchecked_Conversion, which no one else ever seemed to understand. Because of this model, I was always adamantly opposed to 'Size being specifiable on subtypes. With the current thinking about these models, I don't think there is any problem with 'Size specification on discrete subtypes (because 'Size does not affect stand-alone objects). But I still don't anything to do with subtypes having different sizes for composite types. I do agree with Robert that it should be possible for an Ada 95 compiler to layout memory the same as the Ada 83 compiler did. Certainly we want this to be the case for the vast majority of types in the absence of representation clauses. I include Pack in representation clauses; I cannot imagine a situation where our Ada 95 compiler would get the same results for Pack that it did for our Ada 83 compiler -- simply because our Ada 83 compiler never implemented Pack! I guess I never really understood the root issue here. "Obviously", a compiler would store subtypes with the underlying stand-alone object "length"; 'Size has no effect on the representation of a pointed-at-object. "Obviously", most such conversions would work. (As usual, the conversions themselves are uninteresting, but 'Access is defined in terms of conversion, so that the rule does matter.) The problem, of course, from a language definition point of view is that we do want to allow compilers to use different representations for stand-alone objects of different subtypes. Thus, I suppose we get the mess. In any case, I can live with Robert's recommendations. (See the following messages for other issues). BTW, when perusing the ARM, I cannot find any mention of the recommended level of support for specifying 'Size. It says a lot about the meaning of 'Size, but not about what (sub)types allow specification. Have I missed something, or are we all discussing the wrong issue??? Randy Brukardt **************************************************************** !section 13.2(08) !subject By-reference types and packing !reference RM95-13.2(8) !reference RM95-13.2(9) !reference RM95-13.3(72) !reference RM95-13.3(73) !reference 96-5463.a Bob Duff !from Randy Brukardt (RR Software) !reference 96-5501.a rbrukardt@BIX.com 96-4-20>> The referenced message contains examples of packing. This brings up a question in my mind. type Device_Info is record In_Ready: Boolean; Out_Ready: Boolean; end record; pragma Pack(Device_Info); -- Device_Info'Size = 2. type Device_Array is Array (1..4) of Device_Info; for Device_Array'Component_Size use 2; Dev : Device_Array; procedure Operate (A : In Out Device_Info); This example appears to be legal. Consider the call: Operate (Dev(2)); Since the second component of Dev is packed, it will start at bit 2; certainly it will not start at a storage unit boundary, and will not be directly addressible. That means that the item almost certainly will be passed by copy. Either it will be directly passed by copy, or it will be passed by reference via a copy made at the call site. It can be passed by reference only if all parameters of that type are passed using a bit pointer of some sort. Now, consider that a record such as Device_Info can be made into a by-reference type in at least three ways: 1) By explicitly declaring it limited. (This is probably pathological). 2) By applying pragma Atomic to it. 3) By applying pragma Volatile to it. The last two are certainly possible in real programs (which I hopefully illustrated by my choice of names). A by-reference type does not allow pass by copy. Therefore, a bit pointer must be used to pass the type. This is quite expensive, and I believe was not intended. Worse, a side effect of requiring that in even a single unlikely case, is that an implementation using universal generic code sharing will be forced to pass ALL generic private and many generic derived and array types with bit pointers. Have I missed a rule somewhere which states that by reference components are treated similarly to aliased components? Come to think of it, I can't find any rule which says that type Device_Array is Array (1..8) of Aliased Boolean; for Device_Array'Component_Size use 1; is not required, either. (Of course, we can avoid the problem by not supporting the system programming annex, but I doubt that is a good long-term plan...) Randy Brukardt **************************************************************** !section 13.1(14) !subject Re: Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5486.a Robert Dewar !reference 96-5487.a Bob Duff !reference 96-5500.a rbrukardt@BIX.com 96-4-20 !reference 96-5503.a Robert A Duff 96-4-21>> > BTW, when perusing the ARM, I cannot find any mention of the recommended > level of support for specifying 'Size. It says a lot about the meaning of > 'Size, but not about what (sub)types allow specification. Have I missed > something, or are we all discussing the wrong issue??? For subtypes, look at the last sentence of 13.3(55), "If such a subtype is a first subtype, then an implementation should support a specified Size for it that reflects this representation." 13.3(53) is also relevant. For objects, there's 13.3(43): 43 A Size clause should be supported for an object if the specified Size is at least as large as its subtype's Size, and corresponds to a size in storage elements that is a multiple of the object's Alignment (if the Alignment is nonzero). - Bob **************************************************************** !section 13.2(08) !subject By-reference types and packing !reference RM95-13.2(8) !reference RM95-13.2(9) !reference RM95-13.3(72) !reference RM95-13.3(73) !reference 96-5463.a Bob Duff !reference 96-5501.a rbrukardt@BIX.com 96-4-20 !from Bob Duff !reference 96-5504.a Robert A Duff 96-4-21>> > Now, consider that a record such as Device_Info can be made into a by-reference > type in at least three ways: > 1) By explicitly declaring it limited. (This is probably pathological). > 2) By applying pragma Atomic to it. > 3) By applying pragma Volatile to it. > The last two are certainly possible in real programs (which I hopefully > illustrated by my choice of names). It is certainly true that there is no intention of requiring pass-by-reference to be implemented using some sort of bit-field pointers. Clearly, if any of the above are true, then the implementation should allocate things on a storage unit boundary, despite pragma Pack. In fact, some implementations might require 4-byte boundaries, or something, which should be allowed. The same goes for "aliased" things. - Bob **************************************************************** !section 13.1(14) !subject Re: Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5486.a Robert Dewar !reference 96-5487.a Bob Duff !reference 96-5500.a rbrukardt@BIX.com 96-4-20 !reference 96-5503.a Robert A Duff 96-4-21 !from Randy Brukardt 96-4-23 !reference 96-5507.a Ian Goldberg 96-4-23>> !discussion >> BTW, when perusing the ARM, I cannot find any mention of the = recommended >> level of support for specifying 'Size. It says a lot about the = meaning of >> 'Size, but not about what (sub)types allow specification. Have I = missed >> something, or are we all discussing the wrong issue??? >For subtypes, look at the last sentence of 13.3(55), "If such a subtype >is a first subtype, then an implementation should support a specified >Size for it that reflects this representation." 13.3(53) is also >relevant. Therefore, the only required support for specification of 'Size is for = discrete and fixed point types, and only a single size is required. = That means any specification of 'Size on a composite, access, or float = type is at the whim of the implementor, as is specification to = (multiples of) the storage unit size. Thus, most of the examples we have been discussing are relavant only in = terms of permitted support, as opposed to required support. **************************************************************** !section 13.1(14) !subject Re: Dewar's comments on the SIZE problem and Issue !reference RM95-13.1(14) !reference RM95-13.3(55) !reference 96-5486.a Robert Dewar !reference 96-5487.a Bob Duff !reference 96-5500.a rbrukardt@BIX.com 96-4-20 !reference 96-5503.a Robert A Duff 96-4-21 !reference 96-5507.a Ian Goldberg 96-4-23 !from Bob Duff !reference 96-5526.a Robert A Duff 96-4-29>> !discussion > Thus, most of the examples we have been discussing are relavant only in = > terms of permitted support, as opposed to required support. Yes. The question is: should more support be required and/or recommended? Note that 13.2(7-9) could, depending on how you read it, be considered to place further requirements on implementations, in addition to 13.3(55). - Bob P.S. Your mail software seems garble things, e.g. it puts strange "=" signs all over, which is kind of annoying. Especially in the output of the example program, where it changes "=" to "=3D". **************************************************************** !section 13.03(42) !subject Comments to AIs regarding 'Size !reference AI95-00051 !reference AI95-00109 !from Ken Garlington 96-05-13 !keywords Size !reference 96-5552.a Ken Garlington 96-5-13>> !discussion AI95-00051: It seems to me that the restriction on 'Size for fixed and floating point values is excessive, and that the justification could easily apply to any value (e.g., a compiler/architecture could choose a "special" representation for integers, and stay within the language). Suggest replacing the current proposal for fixed/floating point with the following: - For fixed point, up to the Size of the largest fixed point type supported by the implementation. - For floating point, up to the Size of the largest floating point type supported by the implementation. For example, if an architecture had two "special" representations for floating point (e.g., normal and extended floating point on a MIL-STD-1750), a sufficiently large Size for a normal floating point would cause the compiler to generate an extended floating point representation. If there was only one representation for floating point, then Size would have no effect. This also seems consistent with the "minimal size" rule, in that a special representation for a number would force the allocation of a certain number of bits regardless of Size. The suggested alternative seems more consistent with the other rules, while preserving what I think was the intent of the current proposal. (By the way, I assume that someone has made sure these interpretations regarding objects have also been carried forward to types where applicable...) AI95-00109/01: I am strongly in favor of the Robert Dewar position described in the commentary, including his "Ulterior Motive". **************************************************************** !section 13.3(42) !subject Comments to AIs regarding 'Size !reference AI95-00051 !reference AI95-00109 !reference 96-5552.a Ken Garlington 96-5-13 !from Robert Dewar 96-05-18 !keywords Size !reference 96-5564.a Robert Dewar 96-5-18>> !discussion Ken Garlington suggests "For example, if an architecture had two "special" representations for floating point (e.g., normal and extended floating point on a MIL-STD-1750), a sufficiently large Size for a normal floating point would cause the compiler to generate an extended floating point representation. If there was only one representation for floating point, then Size would have no effect. This also seems consistent with the "minimal size" rule, in that a special representation for a number would force the allocation of a certain number of bits regardless of Size." This seems very wrong. Remember that Size is a subtype specific attribute which means that you can get implicit conversions. Consider: type x is float digits 5; -- 32 bits by default function Sqrt (m : x) return x; type y is new x; for y'size use 64; according to Ken's suggestion, y would be represented in double precision, but when you called Sqrt you would get implicit narrowing to 32 bits. This seems very undesirable. The whole point is that Size should not make any significant changes to the internal representation. The idea of having Size affect the precision is not at all in the spirit of the RM requirements, and would be actively undesirable (though not non-conformant, just undesirable). I think it would be helpful if the AI makes clear that this is undesirable and explains why. Similarly Size on a fixed-point type can affect the padding, but should NOT affect the Small value. **************************************************************** !section 13.3(42) !subject Comments to AIs regarding 'Size !reference AI95-00051 !reference AI95-00109 !reference 96-5552.a Ken Garlington 96-5-13 !keywords Size !reference 96-5564.a Robert Dewar 96-5-18 !from Randy Brukardt !reference 96-5569.a Ian Goldberg 96-5-22>> !discussion (From Randy Brukardt, although the header my say something different.) >Ken Garlington suggests > > "For example, if an architecture had two "special" representations for > floating point (e.g., normal and extended floating point on a > MIL-STD-1750), a sufficiently large Size for a normal floating point > would cause the compiler to generate an extended floating point > representation. If there was only one representation for floating point, > then Size would have no effect. This also seems consistent with the > "minimal size" rule, in that a special representation for a number would > force the allocation of a certain number of bits regardless of Size." > >This seems very wrong. Remember that Size is a subtype specific attribute >which means that you can get implicit conversions. Consider: > > type x is float digits 5; -- 32 bits by default > > function Sqrt (m : x) return x; > > type y is new x; > for y'size use 64; > >according to Ken's suggestion, y would be represented in double precision, >but when you called Sqrt you would get implicit narrowing to 32 bits. This >seems very undesirable. > >The whole point is that Size should not make any significant changes to >the internal representation. The idea of having Size affect the precision >is not at all in the spirit of the RM requirements, and would be actively >undesirable (though not non-conformant, just undesirable). I think it would >be helpful if the AI makes clear that this is undesirable and explains why. Well, I completely disagree about the 'undesirable' part. Our (read my) intent for our compiler was always that it would do this. I don't think we ever did it for floating point types, but we do allow it for all other types. (Yes, our compiler [used to] allow explicitly changing the 'Small value for fixed point types; I think we took it out when some ACVC test pointed out it was illegal. I know our compiler has code to do the needed conversions when parameter passing.) While changing representations on derived floating types might be unnecessary, it is very useful to allow this capability on first named subtypes. Otherwise, there is no (semi) portable way to select a particular representation (for interfacing, for example): Type My_Flt is Digits 5; For My_Flt'Size Use 64; The only alternative ways is to select more precision than the program needs, or to add some sort of junk range which may not have anything to do with the program requirements. As with any programming, there is a certain amount of user-beware needed. If someone specifies something unlikely that loses precision, that's their problem and just too bad. (I don't find derived subprograms on a floating point type particularly likely, and the combination is even less likely.) We had to do something similar to be able to convert between the two representations of pointers on the Unisys 2200. Type My_Ada_Ptr Is Access ; Pragma Convention (My_Ada_Ptr, Ada); Type My_C_Ptr Is New My_Ada_Ptr; Pragma Convention (My_C_Ptr, C); An alternative way to specify that would have been: Type My_Ada_Ptr Is Access ; For My_Ada_Ptr'Size Use 36; Type My_C_Ptr Is New My_Ada_Ptr; For My_C_Ptr'Size Use 72; (I didn't do that because it was a lot more work to implement properly.) **************************************************************** !section 13.3(42) !subject Comments to AIs regarding 'Size !reference AI95-00051 !reference AI95-00109 !reference 96-5552.a Ken Garlington 96-5-13 !keywords Size !reference 96-5564.a Robert Dewar 96-5-18 !from Robert Dewar !reference 96-5570.a Robert Dewar 96-5-22>> !discussion Randy said, replying to me >>The whole point is that Size should not make any significant changes to >>the internal representation. The idea of having Size affect the precision >>is not at all in the spirit of the RM requirements, and would be actively >>undesirable (though not non-conformant, just undesirable). I think it would >>be helpful if the AI makes clear that this is undesirable and explains why. >Well, I completely disagree about the 'undesirable' part. > >Our (read my) intent for our compiler was always that it would do this. >I don't think we ever did it for floating point types, but we do allow it for all other types. (Yes, our compiler [used to] allow explicitly changing >the 'Small value for fixed point types; I think we took it out when some >ACVC test pointed out it was illegal. I know our compiler has code to >do the needed conversions when parameter passing.) As best I understand it, this is a comment from Randy on how he would like the language to be, but it seems clear that this is not what is intended. The advice that Size not alter the internal representation of an item is clearly applicable in this case. Also the strong difference in rules between Size and Small indicates that the notion that Size could affect Small is not at all what is intended. Consider the following type x is digits 5; procedure Noop (a : in out x) is begin null; end; type y is new x; for y'size use 64; It seems quite wrong that a call to Noop with an argument of type y would modify the value of the argument (by stripping extra precision). This is precisely why a 'Small attribute clause would not have been allowed for y in the corresponding fixed-point case, and it seems clear that Size is only expected to affect padding etc where the conversions do not have significant change-of-representation semantics. The whole idea of change of representation in Ada is that it should be explicit, never implicit. Randy, are you simply saying you don't think the language should have been designed with this restriction, or are you seriously suggesting that it is appopriate that 'Size clauses cause a change of representation resulting in implicit (non-trivial) change of representation conversions. **************************************************************** !section 13.3(42) !subject Comments to AIs regarding 'Size !reference AI95-00051 !reference AI95-00109 !reference 96-5552.a Ken Garlington 96-5-13 !keywords Size !reference 96-5564.a Robert Dewar 96-5-18 !reference 96-5570.a Robert Dewar 96-5-22 !from Randy Brukardt !reference 96-5571.a Ian Goldberg 96-5-23>> !discussion Robert gives the following example in replying to me (Randy Brukardt): >Consider the following > > type x is digits 5; > > procedure Noop (a : in out x) is > begin > null; > end; > > type y is new x; > for y'size use 64; > >It seems quite wrong that a call to Noop with an argument of type y would >modify the value of the argument (by stripping extra precision). This is >precisely why a 'Small attribute clause would not have been allowed for y >in the corresponding fixed-point case, and it seems clear that Size is >only expected to affect padding etc where the conversions do not have >significant change-of-representation semantics. The whole idea of change >of representation in Ada is that it should be explicit, never implicit. I of course agree that change of representation should be explicit, never implicit. But the problem here is (taking some license) that programmer here is planning on using the EXPLICIT conversion to or from Y(...) to handle the conversion. They just forgot that the Noop routine would be derived. Normal Ada programmers probably ought to avoid derived routines (for untagged types) altogether, because the rules are just too complex for the average programmer to understand. It took us months to get this stuff right in our Ada 83 compiler, and I doubt we still have it right in our Ada 95 compiler. Anyway, explicit representation change via derived types has a long history in Ada, including an entire section devoted to the idea. It seems very odd to deny a programmer (and implementors) the capability to specify a representation for floating point and fixed point types, but to allow it for all other types. (It is true that the numeric conversion rules are liberal enough that it is not necessary to do this, which is probably why it was omitted in the first place. But it is very natural, given that it must be done for access, array, and record types.) In any case, the most useful usage of 'Size to force a representation is on the first-named subtype of an original type declaration. That is a capability which is not present anywhere else in the language, and one that is needed from time to time. I think the original commentor had that case in mind, and that is mainly what I care about here. The problem is that it is difficult to separate the cases. Some other way to specify a hardware floating-point representation would also fill the need, of course. Pragma Float_Rep does not seem too satisfying, however, since such a usage could not be portable to another compiler. Ada 83 generally restricted clauses on derived types to those without primitive operations. Such a restriction on representation changing clauses of any kind could be recommended, and eliminate Robert's concern, without throwing the baby out with the bathwater. (Of course, the fact that 'Size is a "subtype" attribute makes reasoning about it much more complex. Once the capability to specify 'Size for subtypes was removed, there was no reason to retain "subtype" attributes [other than that they slightly simplify the description of 'Component_Size and record component clauses]. Unfortunately, such a change is too much for the ARG to undertake.) **************************************************************** !section 13.3(42) !subject Comments to AIs regarding 'Size !reference AI95-00051 !reference AI95-00109 !reference 96-5552.a Ken Garlington 96-5-13 !keywords Size !reference 96-5564.a Robert Dewar 96-5-18 !reference 96-5570.a Robert Dewar 96-5-22 !from Robert Dewar !reference 96-5572.a Robert Dewar 96-5-23>> !discussion Randy said (Of course, the fact that 'Size is a "subtype" attribute makes reasoning about it much more complex. Once the capability to specify 'Size for subtypes was removed, there was no reason to retain "subtype" attributes [other than that they slightly simplify the description of 'Component_Size and record component clauses]. Unfortunately, such a change is too much for the ARG to undertake.) I don't find the reasoning complex. It is an invention of Ada 83, not Ada 95 that draws a strong distinction between size and other rep clauses such as record and enumeration representation clauses. The reason is the same in Ada 83 and Ada 95, namely that it is assumed that different sizes do NOT correspond to a significant change in repressenation. To make Size a type specific attribute in Ada 95 would introduce terrible upwards incompatibilities (the program I gave with Noop is legal Ada 83, and legal Ada 95, but would of course be illegal Ada 95 if we followed Randy's suggestion and made Size a type-related attribute). The fact of the matter is that Randy is complaining about a design decision made in Ada 83, which is unchanged in Ada 95. His recommendation of not using derived types for non-tagged types strikes me as absurd, since this is a much used feature in both Ada 83 and Ada 95 programs, in particular to effect change of representation. It sounds like the RR compiler at first missed the very important distinction between Size and other rep clauses, but this is a crucial distinction in Ada 83 as well as in Ada 95. It is quite in order to complain about this design decision, but that does not translate into action at this stage. I happen to think that the Ada 83 decision (and the corresponding identical Ada 95 semantics) is quite reasonable, but in any case it is clearly the rule of the language as it stands, and I do not see any cogent argument for suggesting a change at this stage. **************************************************************** !section 13.3(42) !subject Comments to AIs regarding 'Size !reference AI95-00051 !reference AI95-00109 !reference 96-5552.a Ken Garlington 96-5-13 !keywords Size !reference 96-5564.a Robert Dewar 96-5-18 !reference 96-5570.a Robert Dewar 96-5-22 !reference 96-5571.a Ian Goldberg 96-5-23 !from Erhard Ploedereder !reference 96-5573.a Erhard Ploedereder 96-5-24>> > Anyway, explicit representation change via derived types has a long > history in Ada, including an entire section devoted to the idea. It seems > very odd to deny a programmer (and implementors) the capability to specify a > representation for floating point and fixed point types, but to allow it for > all other types. (It is true that the numeric conversion rules are liberal > enough that it is not necessary to do this, which is probably why it was > omitted in the first place. But it is very natural, given that it must be > done for access, array, and record types.) This argument conveniently ignores that this RM Section (13.6) in Ada9X talks only about records (and arrays) and requires the absence of inherited subprograms for derived records with rep.clauses. On might infer from the omission that such representation change for real types was considered undesirable. Note also RM 13.1(10) which imposes the "no-inherited subprograms" rule for all type-related rep.clauses (anything other than Size and Alignment) on derived types. It would be most curious if for T'Small use ... were illegal, while for T'Size use were legal with an effect that changes 'Small. **************************************************************** !section 13.3(40) !subject Size of Mutable Variables !reference RM95-13.3(40) !from Bob Duff !reference 96-5496.a Robert A Duff 96-4-16>> !discussion What is the meaning of the Size attribute for an unconstrained variable that is of a mutable type? Consider: subtype Small_Natural is Natural range 0..100; type Mutable(Len: Small_Natural := 0) is record Chars: String(1..Len); end record; X: Mutable; Suppose the implementation allocates the maximum possible size for X (perhaps 104 bytes). The "current size" of the value of X (i.e. the "constrained size", with Len constrained to 0) is perhaps just 4 bytes (for Len -- the Chars field is empty). Should X'Size be the current size (perhaps 4*8) or the maximum size (perhaps 104*8)? The RM does not answer this question. After some discussion with Tucker and Robert Dewar, I believe the answer should be the max size. Here's why: Suppose we add a Size clause to the above: for X'Size use 104*8; -- Specify the Size of the *object*. Clearly, this will not be legal if 104*8 is less than the max possible size. By 13.1(17), a query of X'Size *must* now return 104*8. 13.1(17.a) gives an example of this. So, in the case of a specified Size (on the object), the query *cannot* return the current size. To preserve the principle that confirming rep clauses shouldn't change things, we must use the same rule even when there's *not* a Size clause. Now, suppose we have a constrained object: subtype Mutable_3 is Mutable(Len => 3); Y: Mutable_3; Z: constant Mutable := (Len => 3, Chars => "abc"); Here, the compiler will not allocate the max size. Y'Size and Z'Size should clearly be the *constrained* Size -- the Size given that Len = 3. Now, suppose we have a parameter: procedure Proc(Param1: Mutable; -- Constant; therefore constrained. Param2: in out Mutable3; -- Constrained. Param3: in out Mutable) is begin ... end Proc; Proc(X, X, X); -- Unconstrained actual parameters. Proc(Y, Y, Y); -- Constrained actual parameters. Inside Proc, Param1'Size and Param2'Size should be the constrained size, based on the value of the actual parameter's discriminant. The alternative would be to return the max size if the actual parameter denotes an unconstrained object. But that's not really reasonable from an implementation point of view -- inside Proc, we don't know whether the actual denotes an unconstrained object. For Param3, however, we *do* know whether the actual is constrained or unconstrained -- it is necessary to pass extra dope to implement the 'Constrained attribute. Therefore, Param3'Size should be the max size, or the constrained size, depending on the constrainedness of the actual view. (Note: I said "view" there because the actual could be another formal -- we should care about the constrainedness of the actual *view*, not the constrainedness of the actual *object*.) Note: all aliased objects are constrained, so the parameter issue does not come up for access values. That is, Ptr.all'Size will clearly always return the constrained size. My conclusion from all this is that you have to view a declared object as being a fixed-size container. Thus, 'Size should return the max size, if that's what was allocated. Now, what about implementations that don't allocate the max? That is, implementations that use the deallocate/reallocate approach, when assigning to a mutable record? Presumably, such an implementation would return the constrained size, even when the object is unconstrained. One might argue that we should make the rules the same for both kinds of implementation. However, in the example with the rep clause: for X'Size use 104*8; -- Specify the Size of the *object*. it would seem that the RM forbids, or at least frowns upon, the deallocate/reallocate implementation in this case. In any case, the RM requires X'Size to be 104*8. Another point is that you can have aliased components of a record. So you can have pointers into the middle. This implies that the deallocate/reallocate approach is questionable anyway, in the general case. (In Ada 83, the same problem occurs when you rename a component. One Ada 83 compiler, in fact, had exactly this bug -- renaming was implemented as a pointer to the component, and a size-changing assignment to the outer record caused the pointer to dangle. Here, I'm not talking about a discriminant-dependent component, whose renaming is illegal -- just a regular component declared before the variant part. You could get around this for renaming, by implementing renaming differently -- store a pointer to the beginning of the outermost object, and an offset. But that implementation is not reasonable for access-to-components, because unlike renamings, access values can be assigned, so you would have a rather severe distributed overhead. It seems to me we worried about this case during the 9X design, and we asked the DR's what to do. They said to make the rules about access-to-component the same as those for renaming-of-component, which essentially forbids the deallocate/reallocate implementation.) If an implementation does choose the deallocate/reallocate implementation (presumably just in those cases where it works), then it should be free to return the constrained size for an unconstrained object. It seems to me that the answer to the above questions should, at most, be Implementation Advice, saying that Size "should" be such-and-such in these cases. I don't think a hard requirement is called for in this case (even for the SP Annex). - Bob **************************************************************** !section 13.3(40) !subject Size of Mutable Variables !reference RM95-13.3(40) !reference 96-5496.a Bob Duff 96-4-16 !from Randy Brukardt (RR Software) !reference 96-5502.a rbrukardt@BIX.com 96-4-20>> !discussion Most of the referenced message is confused, to say the least, about the ways that various implementation models work. >What is the meaning of the Size attribute for an unconstrained variable >that is of a mutable type? Consider: > > subtype Small_Natural is Natural range 0..100; > type Mutable(Len: Small_Natural := 0) is > record > Chars: String(1..Len); > end record; > X: Mutable; > >Suppose the implementation allocates the maximum possible size for X >(perhaps 104 bytes). The "current size" of the value of X (i.e. the >"constrained size", with Len constrained to 0) is perhaps just 4 bytes >(for Len -- the Chars field is empty). Should X'Size be the current >size (perhaps 4*8) or the maximum size (perhaps 104*8)? The RM does not >answer this question. Neither does Ada 83. We assumed (for some reason which is no longer obvious), we believed it is the current size. >After some discussion with Tucker and Robert Dewar, I believe the answer >should be the max size. Here's why: Suppose we add a Size clause to the >above: > > for X'Size use 104*8; -- Specify the Size of the *object*. > >Clearly, this will not be legal if 104*8 is less than the max possible >size. By 13.1(17), a query of X'Size *must* now return 104*8. >13.1(17.a) gives an example of this. So, in the case of a specified >Size (on the object), the query *cannot* return the current size. That makes sense. >To preserve the principle that confirming rep clauses shouldn't change >things, we must use the same rule even when there's *not* a Size clause. OK. >Now, suppose we have a constrained object: > > subtype Mutable_3 is Mutable(Len => 3); > Y: Mutable_3; > Z: constant Mutable := (Len => 3, Chars => "abc"); > >Here, the compiler will not allocate the max size. Y'Size and Z'Size >should clearly be the *constrained* Size -- the Size given that Len = 3. Well, that depends on the compiler. I cannot imagine a situation where our compiler would allocate record subtypes differently than the base type, even if we adopted the 'max. size' implementation approach in some cases. > .... > >My conclusion from all this is that you have to view a declared object >as being a fixed-size container. Thus, 'Size should return the max >size, if that's what was allocated. My first reaction to this statement is that it is first sensible statement I've seen about 'Size in the entire 15 years I've been working with Ada. OK, there must have been one other at some point. :-) This IS the model that our compiler has ALWAYS used. For all objects. For all components. For EVERYTHING. >Now, what about implementations that don't allocate the max? That is, >implementations that use the deallocate/reallocate approach, when >assigning to a mutable record? Presumably, such an implementation would >return the constrained size, even when the object is unconstrained. No, it would return the size of the container. In Ada 83, it appeared to me that returning the constrained size was intended. However, Ada 95 says (13.3(56)) that the size of the container is returned, not including the indirectly accessed space. I would find it very strange that subtypes would work one way, and objects would work the other. And it wouldn't make any sense, either. For our current implementation, that would be 12*8. >One might argue that we should make the rules the same for both kinds of >implementation. I think that the proposed solution IS the same for both kinds of implementation. >However, in the example with the rep clause: > > for X'Size use 104*8; -- Specify the Size of the *object*. > >it would seem that the RM forbids, or at least frowns upon, the >deallocate/reallocate implementation in this case. In any case, the RM >requires X'Size to be 104*8. I don't see any forbidding of a deallocate/reallocate implementation here. I agree that X'Size is 104*8. We will happly give your your 104 bytes, but of course we would only use the first 12. The other 92 would just be ignored. >Another point is that you can have aliased components of a record. So >you can have pointers into the middle. This implies that the >deallocate/reallocate approach is questionable anyway, in the general >case. (In Ada 83, the same problem occurs when you rename a component. >One Ada 83 compiler, in fact, had exactly this bug -- renaming was >implemented as a pointer to the component, and a size-changing >assignment to the outer record caused the pointer to dangle. Here, I'm >not talking about a discriminant-dependent component, whose renaming is >illegal -- just a regular component declared before the variant part. >You could get around this for renaming, by implementing renaming >differently -- store a pointer to the beginning of the outermost object, >and an offset. But that implementation is not reasonable for >access-to-components, because unlike renamings, access values can be >assigned, so you would have a rather severe distributed overhead. It >seems to me we worried about this case during the 9X design, and we >asked the DR's what to do. They said to make the rules about >access-to-component the same as those for renaming-of-component, which >essentially forbids the deallocate/reallocate implementation.) I have no clue as to what you are talking about here. We had this discussion during the development of Ada 95. I can assure you that if the rules would have forbidden the deallocate/reallocate implementation, I would have screamed bloody murder! No, the aliased component rules do not cause any problems for deallocate/reallocate model. Obviously that compiler you are thinking of had a severe bug that had nothing whatsoever to do with the implementation model. Consider: Declare type Mutable_Again(Len: Small_Natural := 0) is record Chars: Aliased String(1..Len); Counter : Aliased Integer := 0; end record; X: Mutable_Again; X3: Mutable_Again(3); Type P_Int Is Access All Integer; P : P_Int; Begin P := X.Counter'Access; X := (3, "ABC", 10); Put (P.All); -- Will print 10. End; Our implementation assigns ALL object addresses and component offsets statically. In this case, for the Pentium machine, we would have: X.Len'Offset = 0; X.Chars'Offset = 2; X.Counter'Offset = 12; When we assign the pointer, we will store X'Address + 12. When X is mutated, Chars'Data will be deallocated, and a new one allocated. That will have no effect on the offset of Counter, so that X'Address + 12 still points at the component Counter, and there is no problem. Of course, X.Chars'Access would change during this assignment, but it of course is discriminant dependent, so the 'Access is illegal. X3.Chars'Access can be taken, but since the type is not mutable, the item Chars'Data will never be deallocated/reallocated, so again there is no problem. >If an implementation does choose the deallocate/reallocate >implementation (presumably just in those cases where it works), then it >should be free to return the constrained size for an unconstrained >object. As above, it always works. At least ours does. Secondly, I don't think we want it to return the constrained size. I think we want it to return the subtype size here, that is Mutable'Size. >It seems to me that the answer to the above questions should, at most, >be Implementation Advice, saying that Size "should" be such-and-such in >these cases. I don't think a hard requirement is called for in this >case (even for the SP Annex). OK by me. But consider the above first. >- Bob Randy. **************************************************************** !section 13.3(40) !subject Size of Mutable Variables !reference RM95-13.3(40) !reference 96-5496.a Bob Duff 96-4-16 !reference 96-5502.a rbrukardt@BIX.com 96-4-20 !from Bob Duff !reference 96-5505.a Robert A Duff 96-4-21>> !discussion > Most of the referenced message is confused, to say the least, about the > ways that various implementation models work. > > subtype Mutable_3 is Mutable(Len => 3); > > Y: Mutable_3; > > Z: constant Mutable := (Len => 3, Chars => "abc"); > > > >Here, the compiler will not allocate the max size. Y'Size and Z'Size > >should clearly be the *constrained* Size -- the Size given that Len = 3. > > Well, that depends on the compiler. I cannot imagine a situation where our > compiler would allocate record subtypes differently than the base type, even > if we adopted the 'max. size' implementation approach in some cases. IMHO such a compiler is pretty badly broken. It seems essential to me that you allocate constrained objects using the constrained size. I suppose the RM doesn't say that, but neither does it say you can't allocate 2**32 bits for an integer variable. > My first reaction to this statement is that it is first sensible statement > I've seen about 'Size in the entire 15 years I've been working with Ada. :-) > I have no clue as to what you are talking about here. Sorry, I didn't define what I meant by the deallocate/reallocate approach. There are actually (at least) two deallocate/reallocate approaches. The one *I* was talking about, is where the whole object is deallocated and then reallocated on assignment. The compiler I was talking about had a bug that is *directly* *caused* by this implementation approach. You, on the other hand, are talking about deallocating/reallocating just the discriminant-dependent component. Yes, I agree that this approach works fine in the presence of aliased objects and renaming. (It's a rather unusual approach, though, is it not? I was under the impression that *most* compilers always allocate a single contiguous block of storage for each object.) >....We had this discussion during the development of Ada 95. I can > assure you that if the rules would have forbidden the > deallocate/reallocate implementation, I would have screamed bloody > murder! Didn't you do just that, numerous times? ;-) ;-) Yes, *your* deallocate/reallocate approach is not broken by the Ada 95 rules. Not to worry. >... No, the aliased component rules do not cause any problems > for deallocate/reallocate model. Obviously that compiler you are thinking of > had a severe bug that had nothing whatsoever to do with the implementation > model. > As above, it always works. At least ours does. Secondly, I don't think we > want it to return the constrained size. I think we want it to return the > subtype size here, that is Mutable'Size. Actually, I don't think it matters at all what 'Size returns -- if the object is not a single contiguous region of storage, then the 'Size attribute seems useless, anyway. - Bob **************************************************************** !section 13.3(40) !subject Size of Mutable Variables !reference RM95-13.3(40) !reference 96-5496.a Bob Duff 96-4-16 !reference 96-5502.a rbrukardt@BIX.com 96-4-20 !reference 96-5505.a Robert A Duff 96-4-21 !from Randy Brukardt !reference 96-5506.a Ian Goldberg 96-4-23>> !discussion >> Well, that depends on the compiler. I cannot imagine a situation = where our >> compiler would allocate record subtypes differently than the base = type, even >> if we adopted the 'max. size' implementation approach in some cases. >IMHO such a compiler is pretty badly broken. It seems essential to me >that you allocate constrained objects using the constrained size. I >suppose the RM doesn't say that, but neither does it say you can't >allocate 2**32 bits for an integer variable. I slightly misspoke here. I was thinking more about layout than size, = and it is possible to have a constrained record with the same layout as = the unconstrained parent, but with a different size. My real concern here is that we don't adopt a set of language rules = which encourage compilers to lie about what they did. (This is common = in Ada 83 compilers for 'Size, I believe) That does not benefit the = users of the language and simply makes more work for the implementors. My strong desire when = it comes to 'Size for objects is to simply return the size allocated, = whatever that was -- and if the size is specified, to allocate that = size. >Sorry, I didn't define what I meant by the deallocate/reallocate >approach. There are actually (at least) two deallocate/reallocate >approaches. The one *I* was talking about, is where the whole object = is >deallocated and then reallocated on assignment. The compiler I was >talking about had a bug that is *directly* *caused* by this >implementation approach. >You, on the other hand, are talking about deallocating/reallocating = just >the discriminant-dependent component. Yes, I agree that this approach >works fine in the presence of aliased objects and renaming. (It's a >rather unusual approach, though, is it not? I was under the impression >that *most* compilers always allocate a single contiguous block of >storage for each object.) I don't know if it is unusual, but certainly it makes more sense than = the one you describe, even for Ada 83. (And, as you say, the approach = you describe is likely wrong for Ada 95). Besides being much more = user-friendly ('Access and 'Address work, mutable subtypes don't need = their size limited), our approach does not have any extra cost over the one you describe in the = most common cases (that is, a single mutable component, when the entire = type is used as a variable or component). And it's much easier to write = the compiler and generate good code when component offsets are always = static. The only real downside is with I/O of these types, but that is = easily handled with attributes similar to the stream attributes (which = is why I wanted the stream attributes to apply to Sequential_IO and = Direct_IO -- the current situation is that we end up with 4 sets of = them.) **************************************************************** !section 13.3(42) !subject Size and Alignment of (aliased) objects !reference AI95-00051/02 !from Erhard Ploedereder 96-07-27 !reference 96-5625.a Erhard Ploedereder 96-7-26>> !discussion AI-00051/02 states (among other things): "For an aliased object, the implementation need not support a Size clause that is different from what the implementation would have chosen by default." I do not understand the rationale for "need not", which tends to imply "tough, but possible". Short of runtime size descriptors with each value (surely not an implementation model for scalar types), this is a "can not" and therefore should be a "must not" in language rules. (I, for one, would not know how to implement the access via an access type, when the size of the designated scalar subtype is not guaranteed to be equal to the size of the designated object.) The sentence above does not cover the case of aliased components with component_clauses forcing a non-default size. The AI states further: "For an aliased object, the implementation need not support an Alignment clause that is less strict than what the implementation would have chosen by default." The same argument as given above applies. This should be a "must not" rule and the transitive alignment effect of component_clauses within records with alignment clauses should be added. Going right along....: "For an aliased object, if an Address clause or a pragma Import is given, the implementation need not support an Alignment clause that is stricter than what the implementation would have chosen by default." There is no !discussion of why this statement is made. Why is there a connection between pragma Import or an Address clause and this advice regarding stricter alignment. If there is such a rationale, why doesn't the sentence above extend component-"claused" components of records with address clauses ? Many questions, but the AI doesn't answer them. **************************************************************** !section 13.3(42) !subject Size and Alignment of (aliased) objects !reference AI95-00051/02 !reference as: 96-5625.a Erhard Ploedereder 96-7-26>> !from Robert I. Eachus !reference 96-5627.a Robert I. Eachus 96-7-29>> !discussion > I do not understand the rationale for "need not", which tends to > imply "tough, but possible". Short of runtime size descriptors > with each value (surely not an implementation model for scalar > types), this is a "can not" and therefore should be a "must not" > in language rules. (I, for one, would not know how to implement > the access via an access type, when the size of the designated > scalar subtype is not guaranteed to be equal to the size of the > designated object.) If the scope of the type is entirely within a single compilation, it can be done without descriptors. So I think tough, but possible in some circumstances is a legitimate rule, and there is no reason to forbid something in all cases just because it is impossible in some. **************************************************************** From: Robert Dewar Sent: Monday, March 22, 1999 3:19 PM <<4. For a non-aliased object, the implementation need not support an Alignment clause unless the Alignment evenly divides the size (in storage elements) of the object. >> I do not like the terminology of evenly divides. It is much clearer to say that x is an exact multiple of y. **************************************************************** From: Gary Dismukes Sent: Thursday, April 8, 2004 1:12 PM [Editor's note: These problems were corrected in version /11 of the AI as filed.] In the !summary: > 2. For a signed integer type, the implementation not support a Size/Alignment I believe that should be: "... the implementation need not support ..." > clause which specifies a value larger than the largest Size/Alignment value > that would be chosen by the implementation by default (i.e. in the absence > of a representation clause) for any signed integer type. Similarly > for modular integer types, fixed point types, enumeration types, record > types, and array types. In the !example section: > type T2 is range 1 .. 10; > for T2'Size use 2 * Unspecified'Size; > -- support might not be recommended > > type T3 is range 1 .. 10; > for T3'Size use 2 * Unspecified'Alignment; > -- support might not be recommended It seems odd to say "support might not be recommended" on T2 and T3. Seems like the phrasing should be simply "-- might not be supported", since this is a comment on implementations, and the RM wording doesn't require this support. **************************************************************** From: Stephen W. Baird Sent: Thursday, April 8, 2004 10:35 PM ... > I believe that should be: "... the implementation need not support ..." Right. > It seems odd to say "support might not be recommended" on T2 and T3. > > Seems like the phrasing should be simply "-- might not be supported", > since this is a comment on implementations, and the RM wording doesn't > require this support. I intended the comments in this section to refer to the recommended level of support (which is, after all, the topic of the AI) rather than to implementations, but I have no objection to changing the comment to "-- might not be supported" if this seems clearer. Thanks for the careful reading and the feedback. ****************************************************************