!standard 13.03 (55) 99-11-29 AI95-00109/07 !class binding interpretation 96-04-04 !status work item 98-04-01 !status ARG Approved (subject to letter ballot) 7-0-3 97-11-14 !status work item (letter ballot was 4-4-3) 96-10-03 !status ARG approved 5-0-3 (subject to letter ballot) 96-06-17 !status work item 96-05-07 !status received 96-04-04 !reference AI95-00051 !priority High !difficulty Hard !subject Size and Alignment Attributes for Subtypes !summary This AI addresses issues related to Size and Alignment attributes for subtypes. For Size and Alignment of objects, see AI95-00051. A non-first subtype inherits each subtype-related aspect (namely, Size and Alignment) from the first subtype, if the two subtypes are statically matching. Recommended Level of Support: For a packed record or array type, the Size of the first subtype should reflect the requirements of RM-13.2(7-9), except that if the record or array does not fit in a single word, the Size may be rounded up, but not past the next word boundary. 13.3(31) is replaced by: For a subtype that satisfies 13.1(23), the implementation need not support an Alignment clause unless: - For record types and type extensions: The size in storage elements is a multiple of the Alignment. - For arrays: This advice is applied recursively to the component subtype. - For a signed integer subtype, the Alignment is less than or equal to that of the largest signed integer type supported by the implementation. - For a modular integer subtype, the Alignment is less than or equal to that of the largest modular type supported by the implementation. - For an enumerated subtype, the Alignment is less than or equal to that of the largest signed integer type, or the largest modular type, which ever is least aligned. - For fixed point, floating point, access, protected and task subtypes: the Alignment is what would have been chosen by default. Implementation Advice: If a type obeys the rules for well-defined unchecked conversion given in 13.9(17), then the implementation should support a Size clause that is the same as what the implementation would have chosen by default. !question The wording of 13.2(7-9) seems vague. For example, it refers to "packed as tightly as possible". What does this imply about the Size of a packed array or record? Suppose a subtype S statically matches a first subtype T, and that the Size of T has been specified ("for T'Size use...;"). 13.1(14) seems to imply that S'Size = T'Size, whereas 13.3(55) seems to imply that S'Size is the minimum needed, based on the range. Which is correct? (The former.) !recommendation (See summary.) !wording To Be Determined. !discussion In the following examples, assume that the word size is 32 bits. 13.2(7-9) says: 7 The recommended level of support for pragma Pack is: 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. 9 For a packed array type, if the component subtype's Size is less than or equal to the word size, and Component_Size is not specified for the type, Component_Size should be less than or equal to the Size of the component subtype, rounded up to the nearest factor of the word size. Consider: type Three_Bits is record A, B, C: Boolean; end record; pragma Pack(Three_Bits); type Sixty_Two_Bits is array(1..62) of Boolean; pragma Pack(Sixty_Two_Bits); Boolean'Size is 1, so clearly Three_Bits can fit in 3 bits, so Three_Bits'Size must be 3. Sixty_Two_Bits can fit in 62 bits, so Sixty_Two_Bits'Size must be 62, 63, or 64. 13.9(16-17) says: 16 The recommended level of support for unchecked conversions is: 17 Unchecked conversions should be supported and should be reversible in the cases where this clause defines the result. To enable meaningful use of unchecked conversion, a contiguous representation should be used for elementary subtypes, for statically constrained array subtypes whose component subtype is one of the subtypes described in this paragraph, and for record subtypes without discriminants whose component subtypes are described in this paragraph. This implies that the following Size clauses are legal: type Three_Bits is record A, B, C: Boolean; end record; pragma Pack(Three_Bits); for Three_Bits'Size use 3; type Sixty_Two_Bits is array(1..62) of Boolean; pragma Pack(Sixty_Two_Bits); for Sixty_Two_Bits'Size use 64; -- This is either the Size the implementation would choose by -- default, or that value rounded up to a multiple of the word -- size. Now, consider the case without pragma Pack: type Three_Bytes is record A, B, C: Boolean; end record; type Sixty_Two_Bytes is array(1..62) of Boolean; And assume that the implementation allocates 8 bits for each Boolean component, so Three_Bytes'Size = 3*8 and Sixty_Two_Bytes'Size = 62*8, by default. This is not required, but is allowed. If the implementation chooses those values by default, then it must support this as well: type Three_Bytes is record A, B, C: Boolean; end record; for Three_Bytes'Size use 3*8; type Sixty_Two_Bytes is array(1..62) of Boolean; for Sixty_Two_Bytes'Size use 62*8; -- Note that this is a multiple of the word size. A different implementation might allocate 32 bits for each Boolean component, and would have to support the corresponding Size clauses. 13.1(23) supports the idea that at least *some* Size clauses ought to be required for composite types: 23 An implementation need not support a specification for the Size for a given composite subtype, nor the size or storage place for an object (including a component) of a given composite subtype, unless the constraints on the subtype and its composite subcomponents (if any) are all static constraints. 13.1(15) says: 15 A derived type inherits each type-related aspect of its parent type that was directly specified before the declaration of the derived type, or (in the case where the parent is derived) that was inherited by the parent type from the grandparent type. A derived subtype inherits each subtype-specific aspect of its parent subtype that was directly specified before the declaration of the derived type, or (in the case where the parent is derived) that was inherited by the parent subtype from the grandparent subtype, but only if the parent subtype statically matches the first subtype of the parent type. An inherited aspect of representation is overridden by a subsequent representation item that specifies the same aspect of the type or subtype. To resolve the contradiction between 13.1(14) and 13.3(55), we add the rule: A non-first subtype inherits each subtype-related aspect (namely, Size and Alignment) from the first subtype, if the two subtype are statically matching. This preserves 13.1(14), which says: 14 If two subtypes statically match, then their subtype-specific aspects (Size and Alignment) are the same. 13.3(54-55) says: 54 The recommended level of support for the Size attribute of subtypes is: 55 The Size (if not specified) of a static discrete or fixed point subtype should be the number of bits needed to represent each value belonging to the subtype using an unbiased representation, leaving space for a sign bit only if the subtype contains negative values. If such a subtype is a first subtype, then an implementation should support a specified Size for it that reflects this representation. Now, consider: type T is range 1..10; for T'Size use 16; -- T'Size is 16. subtype S1 is T range 1..10; -- S1'Size is 16. subtype S2 is T range 1..5; -- S2'Size is 3. The first sentence of 13.3(55) does not apply to T, since its Size is specified, so its Size is as specified -- 16. According to this AI, the Size of S1 is also specified, since S1 statically matches T, and therefore inherits the Size of T. For S2, however, the Size is not specified, so 13.3(55) applies, so S2'Size is 3. Note that for an integer type T, T'Base and T are never statically matching. For example: type T is range -2**31..2**31-1; for T'Size use 32; If the implementation happens to choose a base range for T'Base that is the same as the range of T, that does *not* mean that T'Base statically matches T, because T'Base is unconstrained, whereas T is constrained. Therefore, we do not specify that T'Base is 32 here. The base range is not a constraint. The RM defines the recommended level of support as follows: 13.3 says: 29 The recommended level of support for the Alignment attribute for subtypes is: 30 An implementation should support specified Alignments that are factors and multiples of the number of storage elements per word, subject to the following: 31 An implementation need not support specified Alignments for combinations of Sizes and Alignments that cannot be easily loaded and stored by available machine instructions. 32 An implementation need not support specified Alignments that are greater than the maximum Alignment the implementation ever returns by default. These recommendations are hard requirements for implementations that support the Systems Programming Annex, by C.2(2): 2 The implementation shall support at least the functionality defined by the recommended levels of support in Section 13. These requirements are both vague, and possibly require implementations to support non-natural alignments. Therefore, we replace paragraph 31 by specific advice. It seems unreasonable to require arbitrary values (even within the restricted set defined above) for discrete types. Values already supported for the class make sense, since the implementation already knows how to do such alignments. But other alignments could have implementation problems, and don't add any real functionality. There may be hardware requirements on the alignment of fixed point, floating point, and access types, so we place no requirements other than to support "confirming" Alignment clauses. There is no compelling reason to require any alignments on task and protected types, so we place no requirements on them. For arrays, any alignment supported for the component subtype should be supported for the entire array, but no more. For records, Tucker thinks some reasonable implementations require alignments and sizes to be related, so we adopt a rule to make those implementations possible. (Many of us would like more justification for this rule, but Alignments of subtypes aren't important enough to worry about it). !appendix !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. ****************************************************************